From 12e7bc025f348f7da5570d4dea769696a96b3063 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 7 Nov 2019 15:23:37 +0530 Subject: [PATCH 01/52] fix: Name of Contact cannot be Contact error while creating communication --- .../doctype/communication/communication.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 2be07cadd2..8e4c5c357d 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -351,16 +351,26 @@ def get_contacts(email_strings): email = get_email_without_link(email) contact_name = get_contact_name(email) - if not contact_name: - contact = frappe.get_doc({ - "doctype": "Contact", - "first_name": frappe.unscrub(email.split("@")[0]), - }) - contact.add_email(email_id=email, is_primary=True) - contact.insert(ignore_permissions=True) - contact_name = contact.name + if not contact_name and email: + email_parts = email.split("@") + first_name = frappe.unscrub(email_parts[0]) - contacts.append(contact_name) + try: + contact = frappe.get_doc({ + "doctype": "Contact", + "first_name": first_name, + }) + contact.add_email(email_id=email, is_primary=True) + contact.name = ('{0}-{1}'.format(first_name, email_parts[1]) + if first_name == 'Contact' else first_name) + contact.insert(ignore_permissions=True) + contact_name = contact.name + except Exception: + traceback = frappe.get_traceback() + frappe.log_error(traceback) + + if contact_name: + contacts.append(contact_name) return contacts From 1026800ee06e92eadebe03fa2adccff24dea024c Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 14 Nov 2019 21:42:19 +0530 Subject: [PATCH 02/52] feat: add mandatory_depends_on and read_only_depends_on to docfield --- frappe/core/doctype/docfield/docfield.json | 1539 ++--------------- frappe/core/doctype/doctype/doctype.py | 2 +- frappe/core/doctype/doctype/test_doctype.py | 13 +- frappe/database/mariadb/framework_mariadb.sql | 2 + .../database/postgres/framework_postgres.sql | 2 + frappe/public/js/frappe/form/layout.js | 18 +- 6 files changed, 168 insertions(+), 1408 deletions(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 622663bca4..e3242887c7 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -1,1726 +1,461 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, "autoname": "hash", - "beta": 0, "creation": "2013-02-22 01:27:33", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "label_and_type", + "label", + "fieldtype", + "fieldname", + "reqd", + "precision", + "length", + "search_index", + "in_list_view", + "in_standard_filter", + "in_global_search", + "in_preview", + "allow_in_quick_entry", + "bold", + "translatable", + "collapsible", + "collapsible_depends_on", + "column_break_6", + "options", + "default", + "fetch_from", + "fetch_if_empty", + "permissions", + "depends_on", + "hidden", + "read_only", + "unique", + "set_only_once", + "allow_bulk_edit", + "column_break_13", + "permlevel", + "ignore_user_permissions", + "allow_on_submit", + "report_hide", + "remember_last_selected_value", + "ignore_xss_filter", + "property_depends_on_section", + "mandatory_depends_on", + "column_break_38", + "read_only_depends_on", + "display", + "in_filter", + "no_copy", + "print_hide", + "print_hide_if_no_value", + "print_width", + "width", + "columns", + "column_break_22", + "description", + "oldfieldname", + "oldfieldtype" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "label_and_type", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "label", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_preview": 0, - "in_standard_filter": 0, "label": "Label", - "length": 0, - "no_copy": 0, "oldfieldname": "label", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "163", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "163" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "Data", - "fetch_if_empty": 0, "fieldname": "fieldtype", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_preview": 0, - "in_standard_filter": 0, "label": "Type", - "length": 0, - "no_copy": 0, "oldfieldname": "fieldtype", "oldfieldtype": "Select", "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "fieldname", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_preview": 0, - "in_standard_filter": 0, "label": "Name", - "length": 0, - "no_copy": 0, "oldfieldname": "fieldname", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "reqd", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_preview": 0, - "in_standard_filter": 0, "label": "Mandatory", - "length": 0, - "no_copy": 0, "oldfieldname": "reqd", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", "description": "Set non-standard precision for a Float or Currency field", - "fetch_if_empty": 0, "fieldname": "precision", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Precision", - "length": 0, - "no_copy": 0, "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)", - "fetch_if_empty": 0, "fieldname": "length", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Length", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Length" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "search_index", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Index", - "length": 0, - "no_copy": 0, "oldfieldname": "search_index", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_list_view", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "In List View", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "70px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "70px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_standard_filter", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "In Standard Filter", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In Standard Filter" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", - "fetch_if_empty": 0, "fieldname": "in_global_search", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "In Global Search", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In Global Search" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_preview", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "In Preview", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In Preview" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "allow_in_quick_entry", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Allow in Quick Entry", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Allow in Quick Entry" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "bold", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Bold", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Bold" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", - "fetch_if_empty": 0, "fieldname": "translatable", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Translatable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Translatable" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:doc.fieldtype===\"Section Break\"", - "fetch_if_empty": 0, "fieldname": "collapsible", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Collapsible", - "length": 255, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "length": 255 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.fieldtype==\"Section Break\"", - "fetch_if_empty": 0, "fieldname": "collapsible_depends_on", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Collapsible Depends On", - "length": 0, - "no_copy": 0, - "options": "JS", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "JS" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.", - "fetch_if_empty": 0, "fieldname": "options", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_preview": 0, - "in_standard_filter": 0, "label": "Options", - "length": 0, - "no_copy": 0, "oldfieldname": "options", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Default", - "length": 0, - "no_copy": 0, "oldfieldname": "default", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "fetch_from", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Fetch From", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Fetch From" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", - "fetch_if_empty": 0, "fieldname": "fetch_if_empty", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Fetch If Empty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Fetch If Empty" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "permissions", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Permissions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Permissions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "depends_on", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Display Depends On", "length": 255, - "no_copy": 0, "oldfieldname": "depends_on", "oldfieldtype": "Data", - "options": "JS", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "JS" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "hidden", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Hidden", - "length": 0, - "no_copy": 0, "oldfieldname": "hidden", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "read_only", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Read Only", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "unique", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Unique", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Unique" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "Do not allow user to change after set the first time", - "fetch_if_empty": 0, "fieldname": "set_only_once", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Set Only Once", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Set Only Once" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval: doc.fieldtype == \"Table\"", - "fetch_if_empty": 0, "fieldname": "allow_bulk_edit", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Allow Bulk Edit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Allow Bulk Edit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_13", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, "fieldname": "permlevel", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Perm Level", - "length": 0, - "no_copy": 0, "oldfieldname": "permlevel", "oldfieldtype": "Int", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "User permissions should not apply for this Link", - "fetch_if_empty": 0, "fieldname": "ignore_user_permissions", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Ignore User Permissions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Ignore User Permissions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval: parent.is_submittable", - "fetch_if_empty": 0, "fieldname": "allow_on_submit", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Allow on Submit", - "length": 0, - "no_copy": 0, "oldfieldname": "allow_on_submit", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "report_hide", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Report Hide", - "length": 0, - "no_copy": 0, "oldfieldname": "report_hide", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:(doc.fieldtype == 'Link')", - "fetch_if_empty": 0, "fieldname": "remember_last_selected_value", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Remember Last Selected Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Remember Last Selected Value" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", - "fetch_if_empty": 0, "fieldname": "ignore_xss_filter", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Ignore XSS Filter", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Ignore XSS Filter" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "display", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Display", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Display" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_filter", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "In Filter", - "length": 0, - "no_copy": 0, "oldfieldname": "in_filter", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "no_copy", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "No Copy", - "length": 0, - "no_copy": 0, "oldfieldname": "no_copy", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "print_hide", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Print Hide", - "length": 0, - "no_copy": 0, "oldfieldname": "print_hide", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", - "fetch_if_empty": 0, "fieldname": "print_hide_if_no_value", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Print Hide If No Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Print Hide If No Value" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "print_width", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Print Width", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Print Width" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "width", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, "label": "Width", - "length": 0, - "no_copy": 0, "oldfieldname": "width", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", - "fetch_if_empty": 0, "fieldname": "columns", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "label": "Columns", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Columns" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_22", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_preview": 0, - "in_standard_filter": 0, "label": "Description", - "length": 0, - "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "300px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "300px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "oldfieldname", "fieldtype": "Data", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, "oldfieldname": "oldfieldname", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "oldfieldtype", "fieldtype": "Data", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, "oldfieldname": "oldfieldtype", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" + }, + { + "fieldname": "mandatory_depends_on", + "fieldtype": "Code", + "label": "Mandatory Depends On", + "options": "JS" + }, + { + "fieldname": "read_only_depends_on", + "fieldtype": "Code", + "label": "Read Only Depends On", + "options": "JS" + }, + { + "fieldname": "property_depends_on_section", + "fieldtype": "Section Break", + "label": "Property Depends On" + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" } ], - "has_web_view": 0, - "hide_toolbar": 0, "idx": 1, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, "istable": 1, - "max_attachments": 0, - "modified": "2019-05-28 12:19:53.415372", - "modified_by": "Administrator", + "modified": "2019-11-15 12:28:24.461628", + "modified_by": "umair@erpnext.com", "module": "Core", "name": "DocField", "owner": "Administrator", "permissions": [], - "quick_entry": 0, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 1223d50878..5cb4bafa82 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -905,7 +905,7 @@ def validate_fields(meta): def check_illegal_depends_on_conditions(docfield): ''' assignment operation should not be allowed in the depends on condition.''' - depends_on_fields = ["depends_on", "collapsible_depends_on"] + depends_on_fields = ["depends_on", "collapsible_depends_on", "mandatory_depends_on", "read_only_depends_on"] for field in depends_on_fields: depends_on = docfield.get(field, None) if depends_on and ("=" in depends_on) and \ diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 07a42e73a1..8d8731e012 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -96,14 +96,19 @@ class TestDocType(unittest.TestCase): def test_all_depends_on_fields_conditions(self): import re - docfields = frappe.get_all("DocField", or_filters={ + docfields = frappe.get_all("DocField", + or_filters={ "ifnull(depends_on, '')": ("!=", ''), - "ifnull(collapsible_depends_on, '')": ("!=", '') - }, fields=["parent", "depends_on", "collapsible_depends_on", "fieldname", "fieldtype"]) + "ifnull(collapsible_depends_on, '')": ("!=", ''), + "ifnull(mandatory_depends_on, '')": ("!=", ''), + "ifnull(read_only_depends_on, '')": ("!=", '') + }, + fields=["parent", "depends_on", "collapsible_depends_on", "mandatory_depends_on",\ + "read_only_depends_on", "fieldname", "fieldtype"]) pattern = """[\w\.:_]+\s*={1}\s*[\w\.@'"]+""" for field in docfields: - for depends_on in ["depends_on", "collapsible_depends_on"]: + for depends_on in ["depends_on", "collapsible_depends_on", "mandatory_depends_on", "read_only_depends_on"]: condition = field.get(depends_on) if condition: self.assertFalse(re.match(pattern, condition)) diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index b1a769b189..dbe53df4e4 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -40,6 +40,8 @@ CREATE TABLE `tabDocField` ( `show_preview_popup` int(1) NOT NULL DEFAULT 0, `trigger` varchar(255) DEFAULT NULL, `collapsible_depends_on` text, + `mandatory_depends_on` text, + `read_only_depends_on` text, `depends_on` text, `permlevel` int(11) NOT NULL DEFAULT 0, `ignore_user_permissions` int(1) NOT NULL DEFAULT 0, diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index cd2f02d8e4..457f6c906a 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -40,6 +40,8 @@ CREATE TABLE "tabDocField" ( "show_preview_popup" smallint NOT NULL DEFAULT 0, "trigger" varchar(255) DEFAULT NULL, "collapsible_depends_on" text, + "mandatory_depends_on" text, + "read_only_depends_on" text, "depends_on" text, "permlevel" bigint NOT NULL DEFAULT 0, "ignore_user_permissions" smallint NOT NULL DEFAULT 0, diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 6affdf76e4..16469e5982 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -454,7 +454,7 @@ frappe.ui.form.Layout = Class.extend({ for(var fkey in this.fields_list) { var f = this.fields_list[fkey]; f.dependencies_clear = true; - if(f.df.depends_on) { + if(f.df.depends_on || f.df.mandatory_depends_on || f.df.read_only_depends_on) { has_dep = true; } } @@ -483,10 +483,26 @@ frappe.ui.form.Layout = Class.extend({ } } } + + if(f.df.mandatory_depends_on) { + this.set_dependant_property(f.df.mandatory_depends_on, f.df.fieldname, 'reqd'); + } + + if(f.df.read_only_depends_on) { + this.set_dependant_property(f.df.read_only_depends_on, f.df.fieldname, 'read_only'); + } } this.refresh_section_count(); }, + set_dependant_property: function(condition, fieldname, property) { + let set_property = this.evaluate_depends_on_value(condition); + if (set_property) { + this.frm.set_df_property(fieldname, property, 1); + } else { + this.frm.set_df_property(fieldname, property, 0); + } + }, evaluate_depends_on_value: function(expression) { var out = null; var doc = this.doc; From 6f87f4e7bc2d794d1606b583c4e015d1ea5a92f1 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 15 Nov 2019 12:43:54 +0530 Subject: [PATCH 03/52] fix: code formatting --- frappe/public/js/frappe/form/layout.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 16469e5982..244893d750 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -451,27 +451,27 @@ frappe.ui.form.Layout = Class.extend({ // build dependants' dictionary var has_dep = false; - for(var fkey in this.fields_list) { + for (var fkey in this.fields_list) { var f = this.fields_list[fkey]; f.dependencies_clear = true; - if(f.df.depends_on || f.df.mandatory_depends_on || f.df.read_only_depends_on) { + if (f.df.depends_on || f.df.mandatory_depends_on || f.df.read_only_depends_on) { has_dep = true; } } - if(!has_dep)return; + if (!has_dep) return; // show / hide based on values - for(var i=me.fields_list.length-1;i>=0;i--) { + for (var i=me.fields_list.length-1;i>=0;i--) { var f = me.fields_list[i]; f.guardian_has_value = true; - if(f.df.depends_on) { + if (f.df.depends_on) { // evaluate guardian f.guardian_has_value = this.evaluate_depends_on_value(f.df.depends_on); // show / hide - if(f.guardian_has_value) { + if (f.guardian_has_value) { if(f.df.hidden_due_to_dependency) { f.df.hidden_due_to_dependency = false; f.refresh(); @@ -484,11 +484,11 @@ frappe.ui.form.Layout = Class.extend({ } } - if(f.df.mandatory_depends_on) { + if (f.df.mandatory_depends_on) { this.set_dependant_property(f.df.mandatory_depends_on, f.df.fieldname, 'reqd'); } - if(f.df.read_only_depends_on) { + if (f.df.read_only_depends_on) { this.set_dependant_property(f.df.read_only_depends_on, f.df.fieldname, 'read_only'); } } From 6b07bde29a67925912f06fe5ff57b6e01f9ae448 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 20 Nov 2019 18:21:29 +0530 Subject: [PATCH 04/52] fix: check if this.frm exists --- frappe/public/js/frappe/form/layout.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 244893d750..c1f4c7365a 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -497,10 +497,12 @@ frappe.ui.form.Layout = Class.extend({ }, set_dependant_property: function(condition, fieldname, property) { let set_property = this.evaluate_depends_on_value(condition); - if (set_property) { - this.frm.set_df_property(fieldname, property, 1); - } else { - this.frm.set_df_property(fieldname, property, 0); + if (this.frm) { + if (set_property) { + this.frm.set_df_property(fieldname, property, 1); + } else { + this.frm.set_df_property(fieldname, property, 0); + } } }, evaluate_depends_on_value: function(expression) { From b95129d34ddfe3a47373b39e389e5a3ce48502f0 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 21 Nov 2019 23:51:32 +0530 Subject: [PATCH 05/52] fix(tests): add ui tests --- cypress/integration/depends_on.js | 58 +++++++++++++++++++++++++++++++ frappe/tests/ui_test_helpers.py | 19 +++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 cypress/integration/depends_on.js diff --git a/cypress/integration/depends_on.js b/cypress/integration/depends_on.js new file mode 100644 index 0000000000..73faad845a --- /dev/null +++ b/cypress/integration/depends_on.js @@ -0,0 +1,58 @@ +context('Depends On', () => { + before(() => { + cy.login(); + cy.visit('/desk'); + cy.window().its('frappe').then(frappe => { + frappe.call('frappe.tests.ui_test_helpers.create_doctype', { + name: 'Test Depends On', + fields: [ + { + "label": "Test Field", + "fieldname": "test_field", + "fieldtype": "Data", + }, + { + "label": "Dependant Field", + "fieldname": "dependant_field", + "fieldtype": "Data", + "mandatory_depends_on": "eval:doc.test_field=='Some Value'", + "read_only_depends_on": "eval:doc.test_field=='Some Other Value'", + }, + { + "label": "Display Dependant Field", + "fieldname": "display_dependant_field", + "fieldtype": "Data", + 'depends_on': "eval:doc.test_field=='Value'" + }, + ] + }); + }); + }); + it('should set the field as mandatory depending on other fields value', () => { + cy.new_form('Test Depends On'); + cy.fill_field('test_field', 'Some Value'); + cy.get('button.primary-action').contains('Save').click(); + cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('be.visible'); + cy.get('body').click(); + cy.fill_field('test_field', 'Random value'); + cy.get('button.primary-action').contains('Save').click(); + cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('not.be.visible'); + }); + it('should set the field as read only depending on other fields value', () => { + cy.new_form('Test Depends On'); + cy.fill_field('dependant_field', 'Some Value'); + cy.fill_field('test_field', 'Some Other Value'); + cy.get('body').click(); + cy.get('.control-input [data-fieldname="dependant_field"]').should('be.disabled'); + cy.fill_field('test_field', 'Random Value'); + cy.get('body').click(); + cy.get('.control-input [data-fieldname="dependant_field"]').should('not.be.disabled'); + }); + it('should display the field depending on other fields value', () => { + cy.get('.control-input [data-fieldname="display_dependant_field"]').should('not.be.visible'); + cy.get('.control-input [data-fieldname="test_field"]').clear(); + cy.fill_field('test_field', 'Value'); + cy.get('body').click(); + cy.get('.control-input [data-fieldname="display_dependant_field"]').should('be.visible'); + }); +}); diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index 8eec644115..29cfcf5bac 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -73,4 +73,21 @@ def create_contact_phone_nos_records(): doc.first_name = 'Test Contact' for index in range(1000): doc.append('phone_nos', {'phone': '123456{}'.format(index)}) - doc.insert() \ No newline at end of file + doc.insert() + +@frappe.whitelist() +def create_doctype(name, fields): + fields = frappe.parse_json(fields) + if frappe.db.exists('DocType', name): + return + frappe.get_doc({ + "doctype": "DocType", + "module": "Core", + "custom": 1, + "fields": fields, + "permissions": [{ + "role": "System Manager", + "read": 1 + }], + "name": name + }).insert() From cb8ccffb3792c14b7ada2b5025fd68cb7f717cd5 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 4 Dec 2019 12:29:56 +0530 Subject: [PATCH 06/52] fix: add mandatory_depends_on and read_only_depends_on to customize form --- .../doctype/customize_form/customize_form.py | 2 + .../customize_form_field.json | 1305 ++--------------- 2 files changed, 129 insertions(+), 1178 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index b851d40b83..ef3fb45822 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -59,6 +59,8 @@ docfield_properties = { 'report_hide': 'Check', 'allow_on_submit': 'Check', 'translatable': 'Check', + 'mandatory_depends_on': 'Data', + 'read_only_depends_on': 'Data', 'depends_on': 'Data', 'description': 'Text', 'default': 'Text', diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index 1f808c94c5..eaa0959121 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -1,1438 +1,387 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, "autoname": "hash", - "beta": 0, "creation": "2013-02-22 01:27:32", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "label_and_type", + "label", + "fieldtype", + "fieldname", + "reqd", + "unique", + "in_list_view", + "in_standard_filter", + "in_global_search", + "bold", + "translatable", + "column_break_7", + "precision", + "length", + "options", + "fetch_from", + "fetch_if_empty", + "permissions", + "depends_on", + "permlevel", + "hidden", + "read_only", + "collapsible", + "allow_bulk_edit", + "collapsible_depends_on", + "column_break_14", + "ignore_user_permissions", + "allow_on_submit", + "report_hide", + "remember_last_selected_value", + "property_depends_on_section", + "mandatory_depends_on", + "column_break_33", + "read_only_depends_on", + "display", + "default", + "in_filter", + "column_break_21", + "description", + "print_hide", + "print_hide_if_no_value", + "print_width", + "columns", + "width", + "is_custom_field" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "label_and_type", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Label and Type", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Label and Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "label", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Label", - "length": 0, - "no_copy": 0, "oldfieldname": "label", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Data", - "fetch_if_empty": 0, "fieldname": "fieldtype", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Type", - "length": 0, - "no_copy": 0, "oldfieldname": "fieldtype", "oldfieldtype": "Select", "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "fieldname", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Name", - "length": 0, - "no_copy": 0, "oldfieldname": "fieldname", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "reqd", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Mandatory", - "length": 0, - "no_copy": 0, "oldfieldname": "reqd", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "unique", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Unique", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Unique" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_list_view", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In List View", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In List View" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_standard_filter", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Standard Filter", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In Standard Filter" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", - "fetch_if_empty": 0, "fieldname": "in_global_search", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Global Search", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In Global Search" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "bold", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bold", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Bold" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", - "fetch_if_empty": 0, "fieldname": "translatable", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Translatable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Translatable" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", "description": "Set non-standard precision for a Float or Currency field", - "fetch_if_empty": 0, "fieldname": "precision", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Precision", - "length": 0, - "no_copy": 0, - "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)", - "fetch_if_empty": 0, "fieldname": "length", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Length", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Length" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.", - "fetch_if_empty": 0, "fieldname": "options", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Options", - "length": 0, - "no_copy": 0, "oldfieldname": "options", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "fetch_from", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fetch From", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Fetch From" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", - "fetch_if_empty": 0, "fieldname": "fetch_if_empty", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fetch If Empty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Fetch If Empty" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "permissions", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Permissions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Permissions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): \nmyfield\neval:doc.myfield=='My Value'\neval:doc.age>18", - "fetch_if_empty": 0, "fieldname": "depends_on", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Depends On", - "length": 0, - "no_copy": 0, "oldfieldname": "depends_on", "oldfieldtype": "Data", - "options": "JS", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "JS" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, "fieldname": "permlevel", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Perm Level", - "length": 0, - "no_copy": 0, "oldfieldname": "permlevel", - "oldfieldtype": "Int", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Int" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "hidden", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Hidden", - "length": 0, - "no_copy": 0, "oldfieldname": "hidden", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "read_only", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Read Only", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Read Only" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:doc.fieldtype==\"Section Break\"", - "fetch_if_empty": 0, "fieldname": "collapsible", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collapsible", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Collapsible" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval: doc.fieldtype == \"Table\"", - "fetch_if_empty": 0, "fieldname": "allow_bulk_edit", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Bulk Edit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Allow Bulk Edit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.fieldtype==\"Section Break\"", - "fetch_if_empty": 0, "fieldname": "collapsible_depends_on", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Collapsible Depends On", - "length": 0, - "no_copy": 0, - "options": "JS", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "JS" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "ignore_user_permissions", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ignore User Permissions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Ignore User Permissions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "allow_on_submit", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Allow on Submit", - "length": 0, - "no_copy": 0, "oldfieldname": "allow_on_submit", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Check" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "report_hide", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Report Hide", - "length": 0, - "no_copy": 0, "oldfieldname": "report_hide", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Check" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:(doc.fieldtype == 'Link')", - "fetch_if_empty": 0, "fieldname": "remember_last_selected_value", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remember Last Selected Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Remember Last Selected Value" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "display", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Display", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Display" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default", "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default", - "length": 0, - "no_copy": 0, "oldfieldname": "default", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_filter", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "In Filter", - "length": 0, - "no_copy": 0, "oldfieldname": "in_filter", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Description", - "length": 0, - "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "300px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "300px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "print_hide", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Print Hide", - "length": 0, - "no_copy": 0, "oldfieldname": "print_hide", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Check" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", - "fetch_if_empty": 0, "fieldname": "print_hide_if_no_value", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Hide If No Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Print Hide If No Value" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Print Width of the field, if the field is a column in a table", - "fetch_if_empty": 0, "fieldname": "print_width", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Print Width", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:cur_frm.doc.istable", "description": "Number of columns for a field in a Grid (Total Columns in a grid should be less than 11)", - "fetch_if_empty": 0, "fieldname": "columns", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Columns", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Columns" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "width", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Width", - "length": 0, - "no_copy": 0, "oldfieldname": "width", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_custom_field", "fieldtype": "Check", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Is Custom Field", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 + }, + { + "fieldname": "property_depends_on_section", + "fieldtype": "Section Break", + "label": "Property Depends On" + }, + { + "fieldname": "mandatory_depends_on", + "fieldtype": "Code", + "label": "Mandatory Depends On", + "options": "JS" + }, + { + "fieldname": "column_break_33", + "fieldtype": "Column Break" + }, + { + "fieldname": "read_only_depends_on", + "fieldtype": "Code", + "label": "Read Only Depends On", + "options": "JS" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, "istable": 1, - "max_attachments": 0, - "modified": "2019-03-18 18:03:59.122249", - "modified_by": "Administrator", + "modified": "2019-12-04 12:22:30.222892", + "modified_by": "umair@erpnext.com", "module": "Custom", "name": "Customize Form Field", "owner": "Administrator", "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file From a8d2ed8d45ac61538fbb734c152f54935c222cd2 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Wed, 11 Dec 2019 18:04:09 +0530 Subject: [PATCH 07/52] fix(chat): refactor code to make chat work fixes issues with token being wrongly passed as the owner, instead of current session user (ie. frappe.session.user), which in turn causes the following issue: Traceback (most recent call last): File "/home/frappe/frappe-bench/apps/frappe/frappe/app.py", line 57, in application response = frappe.handler.handle() File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 22, in handle data = execute_cmd(cmd) File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 61, in execute_cmd return frappe.call(method, **frappe.form_dict) File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 1042, in call return fn(*args, **newargs) File "/home/frappe/frappe-bench/apps/frappe/frappe/chat/doctype/chat_room/chat_room.py", line 203, in create room.save(ignore_permissions = True) File "/home/frappe/frappe-bench/apps/frappe/frappe/model/document.py", line 272, in save return self._save(*args, **kwargs) File "/home/frappe/frappe-bench/apps/frappe/frappe/model/document.py", line 295, in _save self.insert() File "/home/frappe/frappe-bench/apps/frappe/frappe/model/document.py", line 223, in insert self._validate_links() File "/home/frappe/frappe-bench/apps/frappe/frappe/model/document.py", line 756, in _validate_links frappe.LinkValidationError) File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 364, in throw msgprint(msg, raise_exception=exc, title=title, indicator='red') File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 350, in msgprint _raise_exception() File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 316, in _raise_exception raise raise_exception(msg) frappe.exceptions.LinkValidationError: Could not find Owner: 0a4d6eb7763ef2065f44ece0653d829a1ba189f753562ab9ac261620 also fixes issue where the chat rooms do not load because of wrongly passed parameters Signed-off-by: Chinmay D. Pai --- frappe/chat/doctype/chat_room/chat_room.py | 39 +++++++++++----------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/frappe/chat/doctype/chat_room/chat_room.py b/frappe/chat/doctype/chat_room/chat_room.py index 44a6ce0f0b..0afe2daa69 100644 --- a/frappe/chat/doctype/chat_room/chat_room.py +++ b/frappe/chat/doctype/chat_room/chat_room.py @@ -93,14 +93,14 @@ class ChatRoom(Document): frappe.publish_realtime('frappe.chat.room:update', update, room = self.name, after_commit = True) -@frappe.whitelist(allow_guest = True) -def get(user, rooms = None, fields = None, filters = None): +@frappe.whitelist(allow_guest=True) +def get(token, rooms=None, fields=None, filters=None): # There is this horrible bug out here. # Looks like if frappe.call sends optional arguments (not in right order), the argument turns to an empty string. # I'm not even going to think searching for it. # Hence, the hack was get_if_empty (previous assign_if_none) # - Achilles Rasquinha achilles@frappe.io - authenticate(user) + authenticate(token) rooms, fields, filters = safe_json_loads(rooms, fields, filters) @@ -123,8 +123,8 @@ def get(user, rooms = None, fields = None, filters = None): rooms = frappe.get_all('Chat Room', or_filters = [ - ['Chat Room', 'owner', '=', user], - ['Chat Room User', 'user', '=', user] + ['Chat Room', 'owner', '=', frappe.session.user], + ['Chat Room User', 'user', '=', frappe.session.user] ], filters = const, fields = param + ['name'] if param else default, @@ -151,9 +151,9 @@ def get(user, rooms = None, fields = None, filters = None): return rooms -@frappe.whitelist(allow_guest = True) -def create(kind, owner, users = None, name = None): - authenticate(owner) +@frappe.whitelist(allow_guest=True) +def create(kind, token, users=None, name=None): + authenticate(token) users = safe_json_loads(users) create = True @@ -163,16 +163,16 @@ def create(kind, owner, users = None, name = None): SELECT name FROM `tabChat Room` WHERE owner = "{owner}" - """.format(owner = owner), as_dict = True)) + """.format(owner=frappe.session.user), as_dict=True)) if room: room = frappe.get_doc('Chat Room', room.name) create = False if create: - room = frappe.new_doc('Chat Room') - room.type = kind - room.owner = owner + room = frappe.new_doc('Chat Room') + room.type = kind + room.owner = frappe.session.user room.room_name = name dusers = [ ] @@ -181,13 +181,13 @@ def create(kind, owner, users = None, name = None): if users: users = listify(users) for user in users: - duser = frappe.new_doc('Chat Room User') + duser = frappe.new_doc('Chat Room User') duser.user = user dusers.append(duser) room.users = dusers else: - dsettings = frappe.get_single('Website Settings') + dsettings = frappe.get_single('Website Settings') room.room_name = dsettings.chat_room_name users = [user for user in room.users] if hasattr(room, 'users') else [ ] @@ -201,11 +201,12 @@ def create(kind, owner, users = None, name = None): room.save(ignore_permissions = True) - room = get(owner, rooms = room.name) - users = [room.owner] + [u for u in room.users] + room = get(token, rooms=room.name) + if room: + users = [room.owner] + [u for u in room.users] - for u in users: - frappe.publish_realtime('frappe.chat.room:create', room, user = u, after_commit = True) + for user in users: + frappe.publish_realtime('frappe.chat.room:create', room, user=user, after_commit=True) return room @@ -219,4 +220,4 @@ def history(room, user, fields = None, limit = 10, start = None, end = None): mess = chat_message.history(room, limit = limit, start = start, end = end) mess = squashify(mess) - return dictify(mess) \ No newline at end of file + return dictify(mess) From 6c27f876a415d1310b120fc836ea0a4a5a325321 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Wed, 11 Dec 2019 21:37:20 +0530 Subject: [PATCH 08/52] fix(chat): make keyword changes to chat.js Signed-off-by: Chinmay D. Pai --- frappe/public/js/frappe/chat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js index f451227815..68b8ebd5cd 100644 --- a/frappe/public/js/frappe/chat.js +++ b/frappe/public/js/frappe/chat.js @@ -718,7 +718,7 @@ frappe.chat.room.create = function (kind, owner, users, name, fn) { return new Promise(resolve => { frappe.call("frappe.chat.doctype.chat_room.chat_room.create", - { kind: kind, owner: owner || frappe.session.user, users: users, name: name }, + { kind: kind, token: owner || frappe.session.user, users: users, name: name }, r => { let room = r.message room = { ...room, creation: new frappe.datetime.datetime(room.creation) } @@ -781,7 +781,7 @@ frappe.chat.room.get = function (names, fields, fn) { return new Promise(resolve => { frappe.call("frappe.chat.doctype.chat_room.chat_room.get", - { user: frappe.session.user, rooms: names, fields: fields }, + { token: frappe.session.user, rooms: names, fields: fields }, response => { let rooms = response.message if ( rooms ) { // frappe.api BOGZ! (emtpy arrays are falsified, not good design). From 03885a058b6782dc880a55dd1ab3e419deac7ad9 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Wed, 11 Dec 2019 22:13:28 +0530 Subject: [PATCH 09/52] fix(chat): change sql formatting Signed-off-by: Chinmay D. Pai --- frappe/chat/doctype/chat_room/chat_room.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/chat/doctype/chat_room/chat_room.py b/frappe/chat/doctype/chat_room/chat_room.py index 0afe2daa69..a30a1daf2b 100644 --- a/frappe/chat/doctype/chat_room/chat_room.py +++ b/frappe/chat/doctype/chat_room/chat_room.py @@ -162,8 +162,8 @@ def create(kind, token, users=None, name=None): room = squashify(frappe.db.sql(""" SELECT name FROM `tabChat Room` - WHERE owner = "{owner}" - """.format(owner=frappe.session.user), as_dict=True)) + WHERE owner=%s + """, (frappe.session.user), as_dict=True)) if room: room = frappe.get_doc('Chat Room', room.name) From ea274cc9994692ef50cf59aa938286e6c613ad6c Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 12 Dec 2019 21:51:20 +0530 Subject: [PATCH 10/52] fix: Add mandatory depends on and read only depends on field in custom field doctype --- .../doctype/custom_field/custom_field.json | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.json b/frappe/custom/doctype/custom_field/custom_field.json index 9b60ea2b11..22295ce833 100644 --- a/frappe/custom/doctype/custom_field/custom_field.json +++ b/frappe/custom/doctype/custom_field/custom_field.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "creation": "2013-01-10 16:34:01", "description": "Adds a custom field to a DocType", @@ -24,10 +25,8 @@ "collapsible_depends_on", "default", "depends_on", - "description", - "permlevel", - "width", - "columns", + "mandatory_depends_on", + "read_only_depends_on", "properties", "reqd", "unique", @@ -46,7 +45,11 @@ "report_hide", "search_index", "ignore_xss_filter", - "translatable" + "translatable", + "description", + "permlevel", + "width", + "columns" ], "fields": [ { @@ -349,11 +352,24 @@ "fieldname": "length", "fieldtype": "Int", "label": "Length" + }, + { + "fieldname": "mandatory_depends_on", + "fieldtype": "Code", + "label": "Mandatory Depends On", + "length": 255 + }, + { + "fieldname": "read_only_depends_on", + "fieldtype": "Code", + "label": "Read Only Depends On", + "length": 255 } ], "icon": "fa fa-glass", "idx": 1, - "modified": "2019-09-11 12:57:19.268934", + "links": [], + "modified": "2019-12-12 21:31:08.209996", "modified_by": "Administrator", "module": "Custom", "name": "Custom Field", From 7347764c87c757247acbfa6b1003a175f9fdb854 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 13 Dec 2019 15:28:55 +0530 Subject: [PATCH 11/52] fix: use filter for removing administrator from email list Signed-off-by: Chinmay D. Pai Co-authored-by: Saurabh Palande --- frappe/core/doctype/communication/email.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 1848136bee..0a628309e0 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -238,8 +238,9 @@ def get_recipients_cc_and_bcc(doc, recipients, cc, bcc, fetched_from_email_accou return recipients, cc, bcc def remove_administrator_from_email_list(email_list): - if 'Administrator' in email_list: - email_list.remove('Administrator') + administrator_email = list(filter(lambda emails: "Administrator" in emails, email_list)) + if administrator_email: + email_list.remove(administrator_email[0]) def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None): """Prepare to make multipart MIME Email @@ -543,4 +544,4 @@ def mark_email_as_seen(name=None): frappe.response["type"] = 'binary' frappe.response["filename"] = "imaginary_pixel.png" - frappe.response["filecontent"] = buffered_obj.getvalue() \ No newline at end of file + frappe.response["filecontent"] = buffered_obj.getvalue() From 6357a4a9a5aece037c618ec0b095cf90b15e6d45 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Wed, 18 Dec 2019 16:18:44 +0530 Subject: [PATCH 12/52] feat(email): allow ssl for outgoing emails, append to sent folder * dont always use default outgoing account for sending * allow ssl for outgoing emails Signed-off-by: Chinmay D. Pai --- frappe/core/doctype/communication/email.py | 19 +------ .../doctype/email_account/email_account.json | 20 +++++++- .../doctype/email_account/email_account.py | 29 +++++++++-- .../doctype/email_domain/email_domain.json | 22 ++++++-- .../doctype/email_domain/email_domain.py | 15 ++++-- frappe/email/queue.py | 13 ++++- frappe/email/smtp.py | 51 ++++++++++++------- 7 files changed, 122 insertions(+), 47 deletions(-) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 0a628309e0..ff206ed02e 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -305,27 +305,12 @@ def set_incoming_outgoing_accounts(doc): doc.incoming_email_account = frappe.db.get_value("Email Account", {"append_to": doc.reference_doctype, }, "email_id") - doc.outgoing_email_account = frappe.db.get_value("Email Account", - {"append_to": doc.reference_doctype, "enable_outgoing": 1}, - ["email_id", "always_use_account_email_id_as_sender", "name", - "always_use_account_name_as_sender_name"], as_dict=True) - if not doc.incoming_email_account: doc.incoming_email_account = frappe.db.get_value("Email Account", {"default_incoming": 1, "enable_incoming": 1}, "email_id") - if not doc.outgoing_email_account: - # if from address is not the default email account - doc.outgoing_email_account = frappe.db.get_value("Email Account", - {"email_id": doc.sender, "enable_outgoing": 1}, - ["email_id", "always_use_account_email_id_as_sender", "name", - "send_unsubscribe_message", "always_use_account_name_as_sender_name"], as_dict=True) or frappe._dict() - - if not doc.outgoing_email_account: - doc.outgoing_email_account = frappe.db.get_value("Email Account", - {"default_outgoing": 1, "enable_outgoing": 1}, - ["email_id", "always_use_account_email_id_as_sender", "name", - "send_unsubscribe_message", "always_use_account_name_as_sender_name"],as_dict=True) or frappe._dict() + doc.outgoing_email_account = frappe.email.smtp.get_outgoing_email_account(raise_exception_not_set=False, append_to=doc.doctype, + sender=doc.sender) if doc.sent_or_received == "Sent": doc.db_set("email_account", doc.outgoing_email_account.name) diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json index 5154514c22..e724102fdf 100644 --- a/frappe/email/doctype/email_account/email_account.json +++ b/frappe/email/doctype/email_account/email_account.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_rename": 1, "autoname": "field:email_account_name", "creation": "2014-09-11 12:04:34.163728", @@ -21,6 +22,7 @@ "use_imap", "email_server", "use_ssl", + "append_emails_to_sent_folder", "incoming_port", "attachment_limit", "append_to", @@ -37,6 +39,7 @@ "enable_outgoing", "smtp_server", "use_tls", + "use_ssl_for_outgoing", "smtp_port", "default_outgoing", "always_use_account_email_id_as_sender", @@ -389,10 +392,25 @@ "fieldname": "incoming_port", "fieldtype": "Data", "label": "Port" + }, + { + "default": "0", + "depends_on": "eval:!doc.domain && doc.enable_outgoing", + "fieldname": "append_emails_to_sent_folder", + "fieldtype": "Check", + "label": "Append Emails to Sent Folder" + }, + { + "default": "0", + "depends_on": "eval:!doc.domain && doc.enable_outgoing", + "fieldname": "use_ssl_for_outgoing", + "fieldtype": "Check", + "label": "Use SSL for Outgoing" } ], "icon": "fa fa-inbox", - "modified": "2019-08-31 18:01:15.568831", + "links": [], + "modified": "2019-12-18 15:56:39.744520", "modified_by": "Administrator", "module": "Email", "name": "Email Account", diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index c05a0f3fe4..3c12b56563 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -7,6 +7,7 @@ import imaplib import re import json import socket +import time from frappe import _ from frappe.model.document import Document from frappe.utils import validate_email_address, cint, get_datetime, DATE_FORMAT, strip, comma_or, sanitize_html @@ -116,7 +117,8 @@ class EmailAccount(Document): fields = [ "name as domain", "use_imap", "email_server", "use_ssl", "smtp_server", "use_tls", - "smtp_port", "incoming_port" + "smtp_port", "incoming_port", "append_emails_to_sent_folder", + "use_ssl_for_outgoing" ] return frappe.db.get_value("Email Domain", domain[1], fields, as_dict=True) except Exception: @@ -130,9 +132,10 @@ class EmailAccount(Document): server = SMTPServer(login = getattr(self, "login_id", None) \ or self.email_id, - server = self.smtp_server, - port = cint(self.smtp_port), - use_tls = cint(self.use_tls) + server=self.smtp_server, + port=cint(self.smtp_port), + use_tls=cint(self.use_tls), + use_ssl=cint(self.use_ssl_for_outgoing) ) if self.password and not self.no_smtp_authentication: server.password = self.get_password() @@ -648,6 +651,24 @@ class EmailAccount(Document): if frappe.db.exists("Email Account", {"enable_automatic_linking": 1, "name": ('!=', self.name)}): frappe.throw(_("Automatic Linking can be activated only for one Email Account.")) + + def append_email_to_sent_folder(self, message): + + email_server = None + try: + email_server = self.get_incoming_server(in_receive=True) + except Exception: + frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name)) + + if not email_server: + return + + email_server.connect() + + if email_server.imap: + email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message) + + @frappe.whitelist() def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None): if not txt: txt = "" diff --git a/frappe/email/doctype/email_domain/email_domain.json b/frappe/email/doctype/email_domain/email_domain.json index 677bf876aa..a4ca19a0bd 100644 --- a/frappe/email/doctype/email_domain/email_domain.json +++ b/frappe/email/doctype/email_domain/email_domain.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "field:domain_name", "creation": "2016-03-29 10:50:48.848239", "doctype": "DocType", @@ -18,6 +19,8 @@ "outgoing_mail_settings", "smtp_server", "use_tls", + "use_ssl_for_outgoing", + "append_emails_to_sent_folder", "smtp_port" ], "fields": [ @@ -30,7 +33,7 @@ "fieldtype": "Data", "label": "domain name", "read_only": 1, - "unique": 0 + "unique": 1 }, { "fieldname": "email_id", @@ -106,10 +109,23 @@ "fieldname": "incoming_port", "fieldtype": "Data", "label": "Port" + }, + { + "default": "0", + "fieldname": "append_emails_to_sent_folder", + "fieldtype": "Check", + "label": "Append Emails to Sent Folder" + }, + { + "default": "0", + "fieldname": "use_ssl_for_outgoing", + "fieldtype": "Check", + "label": "Use SSL for Outgoing" } ], "icon": "icon-inbox", - "modified": "2019-10-09 17:56:48.834704", + "links": [], + "modified": "2019-12-18 15:57:34.445308", "modified_by": "Administrator", "module": "Email", "name": "Email Domain", @@ -127,4 +143,4 @@ ], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/frappe/email/doctype/email_domain/email_domain.py b/frappe/email/doctype/email_domain/email_domain.py index e800b839b9..936381aefb 100644 --- a/frappe/email/doctype/email_domain/email_domain.py +++ b/frappe/email/doctype/email_domain/email_domain.py @@ -49,9 +49,16 @@ class EmailDomain(Document): except Exception: pass try: - if self.use_tls and not self.smtp_port: - self.smtp_port = 587 - sess = smtplib.SMTP((self.smtp_server or "").encode('utf-8'), cint(self.smtp_port) or None) + if self.use_ssl_for_outgoing: + if not self.smtp_port: + self.smtp_port = 465 + + sess = smtplib.SMTP_SSL((self.smtp_server or "").encode('utf-8'), + cint(self.smtp_port) or None) + else: + if self.use_tls and not self.smtp_port: + self.smtp_port = 587 + sess = smtplib.SMTP((self.smtp_server or "").encode('utf-8'), cint(self.smtp_port) or None) sess.quit() except Exception: frappe.throw(_("Outgoing email account not correct")) @@ -73,6 +80,8 @@ class EmailDomain(Document): email_account.set("attachment_limit",self.attachment_limit) email_account.set("smtp_server",self.smtp_server) email_account.set("smtp_port",self.smtp_port) + email_account.set("use_ssl_for_outgoing", self.use_ssl_for_outgoing) + email_account.set("append_emails_to_sent_folder", self.append_emails_to_sent_folder) email_account.save() except Exception as e: frappe.msgprint(email_account.name) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 792b47296a..837f33afdc 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -394,8 +394,16 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) try: + message = None + if not frappe.flags.in_test: - if not smtpserver: smtpserver = SMTPServer() + if not smtpserver: + smtpserver = SMTPServer() + + # to avoid always using default email account for outgoing + if getattr(frappe.local, "outgoing_email_account", None): + frappe.local.outgoing_email_account = {} + smtpserver.setup_email_account(email.reference_doctype, sender=email.sender) for recipient in recipients_list: @@ -423,6 +431,9 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals if email.communication: frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) + if smtpserver.append_emails_to_sent_folder and any("Sent" == s.status for s in recipients_list): + smtpserver.email_account.append_email_to_sent_folder(encode(message)) + except (smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, smtplib.SMTPHeloError, diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py index c09f3f9cdd..7996236d18 100644 --- a/frappe/email/smtp.py +++ b/frappe/email/smtp.py @@ -52,35 +52,38 @@ def get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sen or frappe.local.outgoing_email_account.get("default")): email_account = None - if append_to: - # append_to is only valid when enable_incoming is checked + if sender_email_id: + # check if the sender has an email account with enable_outgoing + email_account = _get_email_account({"enable_outgoing": 1, + "email_id": sender_email_id}) - # in case of multiple Email Accounts with same append_to - # narrow it down based on email_id - email_account = _get_email_account({ + if not email_account and append_to: + # append_to is only valid when enable_incoming is checked + email_accounts = frappe.db.get_values("Email Account", { "enable_outgoing": 1, "enable_incoming": 1, "append_to": append_to, - "email_id": sender_email_id - }) + }, cache=True) - # else find the first Email Account with append_to - if not email_account: + if email_accounts: + _email_account = email_accounts[0] + + else: email_account = _get_email_account({ "enable_outgoing": 1, "enable_incoming": 1, "append_to": append_to }) - if not email_account and sender_email_id: - # check if the sender has email account with enable_outgoing - email_account = _get_email_account({"enable_outgoing": 1, "email_id": sender_email_id}) - if not email_account: # sender don't have the outging email account sender_email_id = None email_account = get_default_outgoing_email_account(raise_exception_not_set=raise_exception_not_set) + if not email_account and _email_account: + # if default email account is not configured then setup first email account based on append to + email_account = _email_account + if not email_account and raise_exception_not_set and cint(frappe.db.get_single_value('System Settings', 'setup_complete')): frappe.throw(_("Please setup default Email Account from Setup > Email > Email Account"), frappe.OutgoingEmailError) @@ -152,16 +155,19 @@ def _get_email_account(filters): return frappe.get_doc("Email Account", name) if name else None class SMTPServer: - def __init__(self, login=None, password=None, server=None, port=None, use_tls=None, append_to=None): + def __init__(self, login=None, password=None, server=None, port=None, use_tls=None, use_ssl=None, append_to=None): # get defaults from mail settings self._sess = None self.email_account = None self.server = None + self.append_emails_to_sent_folder = None + if server: self.server = server self.port = port self.use_tls = cint(use_tls) + self.use_ssl = cint(use_ssl) self.login = login self.password = password @@ -183,6 +189,8 @@ class SMTPServer: self.port = self.email_account.smtp_port self.use_tls = self.email_account.use_tls self.sender = self.email_account.email_id + self.use_ssl = self.email_account.use_ssl_for_outgoing + self.append_emails_to_sent_folder = self.email_account.append_emails_to_sent_folder self.always_use_account_email_id_as_sender = cint(self.email_account.get("always_use_account_email_id_as_sender")) self.always_use_account_name_as_sender_name = cint(self.email_account.get("always_use_account_name_as_sender_name")) @@ -199,11 +207,18 @@ class SMTPServer: raise frappe.OutgoingEmailError(err_msg) try: - if self.use_tls and not self.port: - self.port = 587 + if self.use_ssl: + if not self.port: + self.smtp_port = 465 - self._sess = smtplib.SMTP((self.server or "").encode('utf-8'), - cint(self.port) or None) + self._sess = smtplib.SMTP_SSL((self.server or "").encode('utf-8'), + cint(self.port) or None) + else: + if self.use_tls and not self.port: + self.port = 587 + + self._sess = smtplib.SMTP((self.server or "").encode('utf-8'), + cint(self.port) or None) if not self._sess: err_msg = _('Could not connect to outgoing email server') From 16f6ba4d2f63ab4c03f78aa3f01df718af7a52c8 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 20 Dec 2019 13:36:09 +0530 Subject: [PATCH 13/52] fix: rename first_name --- frappe/core/doctype/communication/communication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 8e4c5c357d..5c8983ea22 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -356,13 +356,13 @@ def get_contacts(email_strings): first_name = frappe.unscrub(email_parts[0]) try: + contact_name = '{0}-{1}'.format(first_name, email_parts[1]) if first_name == 'Contact' else first_name contact = frappe.get_doc({ "doctype": "Contact", - "first_name": first_name, + "first_name": contact_name, + "name": contact_name }) contact.add_email(email_id=email, is_primary=True) - contact.name = ('{0}-{1}'.format(first_name, email_parts[1]) - if first_name == 'Contact' else first_name) contact.insert(ignore_permissions=True) contact_name = contact.name except Exception: From 67791ff194e98cfbb95f81d556cf73e2bf42b33b Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 26 Dec 2019 14:41:33 +0530 Subject: [PATCH 14/52] fix(pdf): error when trying to print PDFs --- frappe/utils/pdf.py | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index b631406a8e..c69dc430cf 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -2,14 +2,24 @@ # MIT License. See license.txt from __future__ import unicode_literals -import pdfkit, os, frappe +import io +import os +import re from distutils.version import LooseVersion -from frappe.utils import scrub_urls, get_wkhtmltopdf_version -from frappe import _ -import six, re, io + +import pdfkit +import six from bs4 import BeautifulSoup from PyPDF2 import PdfFileReader, PdfFileWriter +import frappe +from frappe import _ +from frappe.utils import get_wkhtmltopdf_version, scrub_urls + +PDF_CONTENT_ERRORS = ["ContentNotFoundError", "ContentOperationNotPermittedError", + "UnknownContentError", "RemoteHostClosedError"] + + def get_pdf(html, options=None, output=None): html = scrub_urls(html) html, options = prepare_options(html, options) @@ -30,20 +40,14 @@ def get_pdf(html, options=None, output=None): # https://pythonhosted.org/PyPDF2/PdfFileReader.html # create in-memory binary streams from filedata and create a PdfFileReader object reader = PdfFileReader(io.BytesIO(filedata)) - - except IOError as e: - if ("ContentNotFoundError" in e.message - or "ContentOperationNotPermittedError" in e.message - or "UnknownContentError" in e.message - or "RemoteHostClosedError" in e.message): + except OSError as e: + if any([error in str(e) for error in PDF_CONTENT_ERRORS]): + if not filedata: + frappe.throw(_("PDF generation failed because of broken image links")) # allow pdfs with missing images if file got created - if filedata: - if output: # output is a PdfFileWriter object - output.appendPagesFromReader(reader) - - else: - frappe.throw(_("PDF generation failed because of broken image links")) + if output: # output is a PdfFileWriter object + output.appendPagesFromReader(reader) else: raise @@ -66,6 +70,7 @@ def get_pdf(html, options=None, output=None): return filedata + def get_file_data_from_writer(writer_obj): # https://docs.python.org/3/library/io.html @@ -112,6 +117,7 @@ def prepare_options(html, options): return html, options + def read_options_from_html(html): options = {} soup = BeautifulSoup(html, "html5lib") @@ -132,6 +138,7 @@ def read_options_from_html(html): return soup.prettify(), options + def prepare_header_footer(soup): options = {} @@ -174,6 +181,7 @@ def prepare_header_footer(soup): return options + def cleanup(fname, options): if os.path.exists(fname): os.remove(fname) @@ -182,6 +190,7 @@ def cleanup(fname, options): if options.get(key) and os.path.exists(options[key]): os.remove(options[key]) + def toggle_visible_pdf(soup): for tag in soup.find_all(attrs={"class": "visible-pdf"}): # remove visible-pdf class to unhide From 85628e687f2e9214b09c841f47ceeacd7267b05e Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Thu, 26 Dec 2019 17:56:19 +0530 Subject: [PATCH 15/52] fix: whitelist function Co-Authored-By: Shivam Mishra --- frappe/tests/ui_test_helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index 83b5f5aa60..40ebc8ea6e 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -92,6 +92,7 @@ def create_doctype(name, fields): "name": name }).insert() +@frappe.whitelist() def create_contact_records(): if frappe.db.get_all('Contact', {'first_name': 'Test Form Contact 1'}): return From 13f602f0541cf9f0bb50bad800becf57ef286348 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Fri, 27 Dec 2019 11:43:45 +0530 Subject: [PATCH 16/52] fix: no data fallback on filters --- frappe/public/js/frappe/list/list_sidebar_group_by.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/list/list_sidebar_group_by.js b/frappe/public/js/frappe/list/list_sidebar_group_by.js index e2db471b51..c07983964f 100644 --- a/frappe/public/js/frappe/list/list_sidebar_group_by.js +++ b/frappe/public/js/frappe/list/list_sidebar_group_by.js @@ -90,7 +90,7 @@ frappe.views.ListGroupBy = class ListGroupBy { this.render_dropdown_items(field_count_list, dropdown); this.sidebar.setup_dropdown_search(dropdown, '.group-by-value'); } else { - dropdown.find('.group-by-loading').hide(); + dropdown.find('.group-by-loading').html(`${__("No filters found")}`); } }); }); From 4d9356fb2062ea9a47ccec9cf296c390a19fc8de Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 27 Dec 2019 12:10:55 +0530 Subject: [PATCH 17/52] feat: wider awesomebar --- frappe/public/less/navbar.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/less/navbar.less b/frappe/public/less/navbar.less index f6145e77bb..5cb050b10f 100644 --- a/frappe/public/less/navbar.less +++ b/frappe/public/less/navbar.less @@ -204,7 +204,7 @@ .navbar-form .awesomplete { margin-left: -15px; - width: 300px; + width: 370px; @media (max-width: @screen-md) { width: 280px; From 76c0d3b561b0ace62f42bc3df252bf757e131692 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 27 Dec 2019 12:51:29 +0530 Subject: [PATCH 18/52] fix: adding quick entry fields via customize form --- .../doctype/customize_form/customize_form.py | 3 +- .../customize_form_field.json | 1285 ++--------------- 2 files changed, 110 insertions(+), 1178 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index b851d40b83..7f828f3aa4 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -68,7 +68,8 @@ docfield_properties = { 'columns': 'Int', 'remember_last_selected_value': 'Check', 'allow_bulk_edit': 'Check', - 'auto_repeat': 'Link' + 'auto_repeat': 'Link', + 'allow_in_quick_entry': 'Check' } allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'), diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index 1f808c94c5..830baf1868 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -1,1438 +1,369 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "actions": [], "autoname": "hash", - "beta": 0, "creation": "2013-02-22 01:27:32", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "field_order": [ + "label_and_type", + "label", + "fieldtype", + "fieldname", + "reqd", + "unique", + "in_list_view", + "in_standard_filter", + "in_global_search", + "bold", + "allow_in_quick_entry", + "translatable", + "column_break_7", + "precision", + "length", + "options", + "fetch_from", + "fetch_if_empty", + "permissions", + "depends_on", + "permlevel", + "hidden", + "read_only", + "collapsible", + "allow_bulk_edit", + "collapsible_depends_on", + "column_break_14", + "ignore_user_permissions", + "allow_on_submit", + "report_hide", + "remember_last_selected_value", + "display", + "default", + "in_filter", + "column_break_21", + "description", + "print_hide", + "print_hide_if_no_value", + "print_width", + "columns", + "width", + "is_custom_field" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "label_and_type", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Label and Type", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Label and Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "label", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Label", - "length": 0, - "no_copy": 0, "oldfieldname": "label", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Data", - "fetch_if_empty": 0, "fieldname": "fieldtype", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Type", - "length": 0, - "no_copy": 0, "oldfieldname": "fieldtype", "oldfieldtype": "Select", "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "fieldname", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Name", - "length": 0, - "no_copy": 0, "oldfieldname": "fieldname", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "reqd", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Mandatory", - "length": 0, - "no_copy": 0, "oldfieldname": "reqd", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "unique", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Unique", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Unique" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_list_view", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In List View", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In List View" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_standard_filter", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Standard Filter", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In Standard Filter" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", - "fetch_if_empty": 0, "fieldname": "in_global_search", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Global Search", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "In Global Search" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "bold", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bold", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Bold" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", - "fetch_if_empty": 0, "fieldname": "translatable", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Translatable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Translatable" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", "description": "Set non-standard precision for a Float or Currency field", - "fetch_if_empty": 0, "fieldname": "precision", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Precision", - "length": 0, - "no_copy": 0, - "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)", - "fetch_if_empty": 0, "fieldname": "length", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Length", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Length" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.", - "fetch_if_empty": 0, "fieldname": "options", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Options", - "length": 0, - "no_copy": 0, "oldfieldname": "options", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "fetch_from", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fetch From", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Fetch From" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", - "fetch_if_empty": 0, "fieldname": "fetch_if_empty", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fetch If Empty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Fetch If Empty" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "permissions", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Permissions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Permissions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): \nmyfield\neval:doc.myfield=='My Value'\neval:doc.age>18", - "fetch_if_empty": 0, "fieldname": "depends_on", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Depends On", - "length": 0, - "no_copy": 0, "oldfieldname": "depends_on", "oldfieldtype": "Data", - "options": "JS", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "JS" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, "fieldname": "permlevel", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Perm Level", - "length": 0, - "no_copy": 0, "oldfieldname": "permlevel", - "oldfieldtype": "Int", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Int" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "hidden", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Hidden", - "length": 0, - "no_copy": 0, "oldfieldname": "hidden", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "read_only", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Read Only", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Read Only" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:doc.fieldtype==\"Section Break\"", - "fetch_if_empty": 0, "fieldname": "collapsible", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collapsible", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Collapsible" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval: doc.fieldtype == \"Table\"", - "fetch_if_empty": 0, "fieldname": "allow_bulk_edit", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Bulk Edit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Allow Bulk Edit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.fieldtype==\"Section Break\"", - "fetch_if_empty": 0, "fieldname": "collapsible_depends_on", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Collapsible Depends On", - "length": 0, - "no_copy": 0, - "options": "JS", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "JS" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "ignore_user_permissions", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ignore User Permissions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Ignore User Permissions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "allow_on_submit", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Allow on Submit", - "length": 0, - "no_copy": 0, "oldfieldname": "allow_on_submit", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Check" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "report_hide", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Report Hide", - "length": 0, - "no_copy": 0, "oldfieldname": "report_hide", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Check" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:(doc.fieldtype == 'Link')", - "fetch_if_empty": 0, "fieldname": "remember_last_selected_value", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remember Last Selected Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Remember Last Selected Value" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "display", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Display", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Display" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default", "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default", - "length": 0, - "no_copy": 0, "oldfieldname": "default", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "in_filter", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "In Filter", - "length": 0, - "no_copy": 0, "oldfieldname": "in_filter", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Description", - "length": 0, - "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "300px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "300px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "print_hide", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Print Hide", - "length": 0, - "no_copy": 0, "oldfieldname": "print_hide", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Check" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", - "fetch_if_empty": 0, "fieldname": "print_hide_if_no_value", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Hide If No Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Print Hide If No Value" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Print Width of the field, if the field is a column in a table", - "fetch_if_empty": 0, "fieldname": "print_width", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Print Width", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:cur_frm.doc.istable", "description": "Number of columns for a field in a Grid (Total Columns in a grid should be less than 11)", - "fetch_if_empty": 0, "fieldname": "columns", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Columns", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Columns" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "width", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Width", - "length": 0, - "no_copy": 0, "oldfieldname": "width", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_custom_field", "fieldtype": "Check", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Is Custom Field", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 + }, + { + "default": "0", + "fieldname": "allow_in_quick_entry", + "fieldtype": "Check", + "label": " Allow in Quick Entry " } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, "istable": 1, - "max_attachments": 0, - "modified": "2019-03-18 18:03:59.122249", + "links": [], + "modified": "2019-12-27 12:50:51.419763", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form Field", "owner": "Administrator", "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_order": "ASC" } \ No newline at end of file From 4d4388edbfd22ad9ef0b9c015efaab23f4dd75f2 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 27 Dec 2019 13:17:55 +0530 Subject: [PATCH 19/52] fix(chat): separate keywords for user and token Signed-off-by: Chinmay D. Pai --- frappe/chat/doctype/chat_room/chat_room.py | 7 ++++--- frappe/public/js/frappe/chat.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frappe/chat/doctype/chat_room/chat_room.py b/frappe/chat/doctype/chat_room/chat_room.py index a30a1daf2b..6d1c854b4b 100644 --- a/frappe/chat/doctype/chat_room/chat_room.py +++ b/frappe/chat/doctype/chat_room/chat_room.py @@ -94,13 +94,14 @@ class ChatRoom(Document): frappe.publish_realtime('frappe.chat.room:update', update, room = self.name, after_commit = True) @frappe.whitelist(allow_guest=True) -def get(token, rooms=None, fields=None, filters=None): +def get(user=None, token=None, rooms=None, fields=None, filters=None): # There is this horrible bug out here. # Looks like if frappe.call sends optional arguments (not in right order), the argument turns to an empty string. # I'm not even going to think searching for it. # Hence, the hack was get_if_empty (previous assign_if_none) # - Achilles Rasquinha achilles@frappe.io - authenticate(token) + data = user or token + authenticate(data) rooms, fields, filters = safe_json_loads(rooms, fields, filters) @@ -201,7 +202,7 @@ def create(kind, token, users=None, name=None): room.save(ignore_permissions = True) - room = get(token, rooms=room.name) + room = get(token=token, rooms=room.name) if room: users = [room.owner] + [u for u in room.users] diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js index 68b8ebd5cd..d15f2183a9 100644 --- a/frappe/public/js/frappe/chat.js +++ b/frappe/public/js/frappe/chat.js @@ -781,7 +781,7 @@ frappe.chat.room.get = function (names, fields, fn) { return new Promise(resolve => { frappe.call("frappe.chat.doctype.chat_room.chat_room.get", - { token: frappe.session.user, rooms: names, fields: fields }, + { user: frappe.session.user, rooms: names, fields: fields }, response => { let rooms = response.message if ( rooms ) { // frappe.api BOGZ! (emtpy arrays are falsified, not good design). From b7ad4a00f72d48d4365ae6d6dcb4d7b31c5d2c8c Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 27 Dec 2019 13:24:59 +0530 Subject: [PATCH 20/52] chore: fix indentation and cleanup annoying spaces Signed-off-by: Chinmay D. Pai --- frappe/chat/doctype/chat_room/chat_room.py | 107 +++++++++++---------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/frappe/chat/doctype/chat_room/chat_room.py b/frappe/chat/doctype/chat_room/chat_room.py index 6d1c854b4b..609acaef7d 100644 --- a/frappe/chat/doctype/chat_room/chat_room.py +++ b/frappe/chat/doctype/chat_room/chat_room.py @@ -1,17 +1,14 @@ from __future__ import unicode_literals -# imports - standard imports -import json - # imports - module imports -from frappe.model.document import Document -from frappe import _ +from frappe.model.document import Document +from frappe import _ import frappe # imports - frappe module imports -from frappe.chat import authenticate +from frappe.chat import authenticate from frappe.core.doctype.version.version import get_diff -from frappe.chat.doctype.chat_message import chat_message +from frappe.chat.doctype.chat_message import chat_message from frappe.chat.util import ( safe_json_loads, dictify, @@ -22,13 +19,14 @@ from frappe.chat.util import ( session = frappe.session -def is_direct(owner, other, bidirectional = False): + +def is_direct(owner, other, bidirectional=False): def get_room(owner, other): - room = frappe.get_all('Chat Room', filters = [ - ['Chat Room', 'type' , 'in', ('Direct', 'Visitor')], - ['Chat Room', 'owner', '=' , owner], - ['Chat Room User', 'user' , '=' , other] - ], distinct = True) + room = frappe.get_all('Chat Room', filters=[ + ['Chat Room', 'type', 'in', ('Direct', 'Visitor')], + ['Chat Room', 'owner', '=', owner], + ['Chat Room User', 'user', '=', other] + ], distinct=True) return room @@ -38,7 +36,8 @@ def is_direct(owner, other, bidirectional = False): return exists -def get_chat_room_user_set(users, filter_ = None): + +def get_chat_room_user_set(users, filter_=None): seen, uset = set(), list() for u in users: @@ -48,12 +47,13 @@ def get_chat_room_user_set(users, filter_ = None): return uset + class ChatRoom(Document): def validate(self): if self.is_new(): - users = get_chat_room_user_set(self.users, filter_ = lambda u: u.user != session.user) + users = get_chat_room_user_set(self.users, filter_=lambda u: u.user != session.user) self.update(dict( - users = users + users=users )) if self.type == "Direct": @@ -63,7 +63,7 @@ class ChatRoom(Document): other = squashify(self.users) if self.is_new(): - if is_direct(self.owner, other.user, bidirectional = True): + if is_direct(self.owner, other.user, bidirectional=True): frappe.throw(_('Direct room with {0} already exists.').format(other.user)) if self.type == "Group" and not self.room_name: @@ -74,29 +74,32 @@ class ChatRoom(Document): before = self.get_doc_before_save() if not before: return - after = self - diff = dictify(get_diff(before, after)) + after = self + diff = dictify(get_diff(before, after)) if diff: - update = { } + update = {} for changed in diff.changed: field, old, new = changed if field == 'last_message': new = chat_message.get(new) - update.update({ field: new }) + update.update({field: new}) if diff.added or diff.removed: - update.update(dict(users = [u.user for u in self.users])) + update.update(dict(users=[u.user for u in self.users])) - update = dict(room = self.name, data = update) + update = dict(room=self.name, data=update) + + frappe.publish_realtime('frappe.chat.room:update', update, room=self.name, + after_commit=True) - frappe.publish_realtime('frappe.chat.room:update', update, room = self.name, after_commit = True) @frappe.whitelist(allow_guest=True) def get(user=None, token=None, rooms=None, fields=None, filters=None): # There is this horrible bug out here. - # Looks like if frappe.call sends optional arguments (not in right order), the argument turns to an empty string. + # Looks like if frappe.call sends optional arguments (not in right order), + # the argument turns to an empty string. # I'm not even going to think searching for it. # Hence, the hack was get_if_empty (previous assign_if_none) # - Achilles Rasquinha achilles@frappe.io @@ -105,10 +108,10 @@ def get(user=None, token=None, rooms=None, fields=None, filters=None): rooms, fields, filters = safe_json_loads(rooms, fields, filters) - rooms = listify(get_if_empty(rooms, [ ])) - fields = listify(get_if_empty(fields, [ ])) + rooms = listify(get_if_empty(rooms, [])) + fields = listify(get_if_empty(fields, [])) - const = [ ] # constraints + const = [] # constraints if rooms: const.append(['Chat Room', 'name', 'in', rooms]) if filters: @@ -118,24 +121,24 @@ def get(user=None, token=None, rooms=None, fields=None, filters=None): const.append(filters) default = ['name', 'type', 'room_name', 'creation', 'owner', 'avatar'] - handle = ['users', 'last_message'] + handle = ['users', 'last_message'] - param = [f for f in fields if f not in handle] + param = [f for f in fields if f not in handle] - rooms = frappe.get_all('Chat Room', - or_filters = [ - ['Chat Room', 'owner', '=', frappe.session.user], - ['Chat Room User', 'user', '=', frappe.session.user] - ], - filters = const, - fields = param + ['name'] if param else default, - distinct = True - ) + rooms = frappe.get_all('Chat Room', + or_filters=[ + ['Chat Room', 'owner', '=', frappe.session.user], + ['Chat Room User', 'user', '=', frappe.session.user] + ], + filters=const, + fields=param + ['name'] if param else default, + distinct=True + ) if not fields or 'users' in fields: for i, r in enumerate(rooms): droom = frappe.get_doc('Chat Room', r.name) - rooms[i]['users'] = [ ] + rooms[i]['users'] = [] for duser in droom.users: rooms[i]['users'].append(duser.user) @@ -152,11 +155,12 @@ def get(user=None, token=None, rooms=None, fields=None, filters=None): return rooms + @frappe.whitelist(allow_guest=True) def create(kind, token, users=None, name=None): authenticate(token) - users = safe_json_loads(users) + users = safe_json_loads(users) create = True if kind == 'Visitor': @@ -167,7 +171,7 @@ def create(kind, token, users=None, name=None): """, (frappe.session.user), as_dict=True)) if room: - room = frappe.get_doc('Chat Room', room.name) + room = frappe.get_doc('Chat Room', room.name) create = False if create: @@ -176,11 +180,11 @@ def create(kind, token, users=None, name=None): room.owner = frappe.session.user room.room_name = name - dusers = [ ] + dusers = [] if kind != 'Visitor': if users: - users = listify(users) + users = listify(users) for user in users: duser = frappe.new_doc('Chat Room User') duser.user = user @@ -191,7 +195,7 @@ def create(kind, token, users=None, name=None): dsettings = frappe.get_single('Website Settings') room.room_name = dsettings.chat_room_name - users = [user for user in room.users] if hasattr(room, 'users') else [ ] + users = [user for user in room.users] if hasattr(room, 'users') else [] for user in dsettings.chat_operators: if user.user not in users: @@ -200,9 +204,9 @@ def create(kind, token, users=None, name=None): chat_room_user = {"doctype": "Chat Room User", "user": user.user} room.append('users', chat_room_user) - room.save(ignore_permissions = True) + room.save(ignore_permissions=True) - room = get(token=token, rooms=room.name) + room = get(token=token, rooms=room.name) if room: users = [room.owner] + [u for u in room.users] @@ -211,14 +215,15 @@ def create(kind, token, users=None, name=None): return room -@frappe.whitelist(allow_guest = True) -def history(room, user, fields = None, limit = 10, start = None, end = None): + +@frappe.whitelist(allow_guest=True) +def history(room, user, fields=None, limit=10, start=None, end=None): if frappe.get_doc('Chat Room', room).type != 'Visitor': authenticate(user) fields = safe_json_loads(fields) - mess = chat_message.history(room, limit = limit, start = start, end = end) - mess = squashify(mess) + mess = chat_message.history(room, limit=limit, start=start, end=end) + mess = squashify(mess) return dictify(mess) From 58e1bc5ce196ccda07d75a3228ac7d9b695a55c7 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 27 Dec 2019 15:07:11 +0530 Subject: [PATCH 21/52] fix: update version pinning for python 3.8 compatibility --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 96e57fd1f7..b2c5c12f46 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,8 +34,8 @@ passlib==1.7.1 pdfkit==0.6.1 Pillow==6.2.1 premailer==3.6.1 -psycopg2-binary==2.7.5 -psycopg2==2.7.5 +psycopg2-binary==2.8.4 +psycopg2==2.8.4 pyasn1==0.4.7 Pygments==2.2.0 PyJWT==1.7.1 From 8f875fa4b7b730bf733688099c6b77e0f4289a8b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 27 Dec 2019 15:41:42 +0530 Subject: [PATCH 22/52] fix: formatting for check in prints (#9139) * fix: formatting for check in prints * refactor: remove important Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/public/js/frappe/form/formatters.js | 2 +- frappe/templates/styles/standard.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index ff573889e2..9b6c35d300 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -84,7 +84,7 @@ frappe.form.formatters = { }, Check: function(value) { if(value) { - return ''; + return ''; } else { return ''; } diff --git a/frappe/templates/styles/standard.css b/frappe/templates/styles/standard.css index 0f41d01c1c..f5eeb1c7fb 100644 --- a/frappe/templates/styles/standard.css +++ b/frappe/templates/styles/standard.css @@ -49,6 +49,10 @@ } } +.disabled-check { + color: #eee; +} + .data-field { margin-top: 5px; margin-bottom: 5px; From 4d48a2a0328e4a3b810a008ecfb5d356e398a899 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 27 Dec 2019 16:11:31 +0530 Subject: [PATCH 23/52] fix(email): handle case where cstr returns text_type of str chardet requires input to be bytes or bytesarray, but sometimes frappe.cstr() returns text_type of str without encoding it to utf-8 Signed-off-by: Chinmay D. Pai --- frappe/email/receive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index e5c8457b4e..9ba080bfda 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -480,7 +480,7 @@ class Email: """Detect chartset.""" charset = part.get_content_charset() if not charset: - charset = chardet.detect(cstr(part))['encoding'] + charset = chardet.detect(safe_encode(cstr(part)))['encoding'] return charset From 3c3516a652f9984db62bb2d4ebdbeac96cd3f8fc Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Sat, 28 Dec 2019 11:21:08 +0530 Subject: [PATCH 24/52] fix(email): Do not encode smtp_server value (#9137) * fix: Do not encode smtp server Because it breaks in python 3 * fix: use cstr to change text_type to str Signed-off-by: Chinmay D. Pai Co-authored-by: Chinmay Pai --- frappe/email/doctype/email_domain/email_domain.py | 4 ++-- frappe/email/smtp.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/email/doctype/email_domain/email_domain.py b/frappe/email/doctype/email_domain/email_domain.py index e800b839b9..c601bb1a09 100644 --- a/frappe/email/doctype/email_domain/email_domain.py +++ b/frappe/email/doctype/email_domain/email_domain.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import validate_email_address ,cint +from frappe.utils import validate_email_address ,cint, cstr import imaplib,poplib,smtplib from frappe.email.utils import get_port @@ -51,7 +51,7 @@ class EmailDomain(Document): try: if self.use_tls and not self.smtp_port: self.smtp_port = 587 - sess = smtplib.SMTP((self.smtp_server or "").encode('utf-8'), cint(self.smtp_port) or None) + sess = smtplib.SMTP(cstr(self.smtp_server or ""), cint(self.smtp_port) or None) sess.quit() except Exception: frappe.throw(_("Outgoing email account not correct")) diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py index c09f3f9cdd..9eb588576d 100644 --- a/frappe/email/smtp.py +++ b/frappe/email/smtp.py @@ -8,7 +8,7 @@ import smtplib import email.utils import _socket, sys from frappe import _ -from frappe.utils import cint, parse_addr +from frappe.utils import cint, cstr, parse_addr def send(email, append_to=None, retry=1): """Deprecated: Send the message or add it to Outbox Email""" @@ -202,7 +202,7 @@ class SMTPServer: if self.use_tls and not self.port: self.port = 587 - self._sess = smtplib.SMTP((self.server or "").encode('utf-8'), + self._sess = smtplib.SMTP(cstr(self.server or ""), cint(self.port) or None) if not self._sess: From 8425110af585f02493669889f03a27c2ca6d16e0 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Sun, 29 Dec 2019 18:12:45 +0530 Subject: [PATCH 25/52] =?UTF-8?q?fix:=20hide=20Cancel=20button=20if=20work?= =?UTF-8?q?flow=20state=20and=20action=20for=20Cancel=E2=80=A6=20(#9151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frappe/model/workflow.py | 12 ++++++++++++ frappe/public/js/frappe/form/workflow.js | 12 +++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py index 2851db6780..548d713e6f 100644 --- a/frappe/model/workflow.py +++ b/frappe/model/workflow.py @@ -105,6 +105,18 @@ def apply_workflow(doc, action): return doc +@frappe.whitelist() +def can_cancel_document(doc): + doc = frappe.get_doc(frappe.parse_json(doc)) + workflow = get_workflow(doc.doctype) + for state_doc in workflow.states: + if state_doc.doc_status == '2': + for transition in workflow.transitions: + if transition.next_state == state_doc.state: + return False + return True + return True + def validate_workflow(doc): '''Validate Workflow State and Transition for the current user. diff --git a/frappe/public/js/frappe/form/workflow.js b/frappe/public/js/frappe/form/workflow.js index 4eafc12a06..4eb33a5f28 100644 --- a/frappe/public/js/frappe/form/workflow.js +++ b/frappe/public/js/frappe/form/workflow.js @@ -105,7 +105,17 @@ frappe.ui.form.States = Class.extend({ }); } }); - this.setup_btn(added); + if (!added) { + //call function and clear cancel button if Cancel doc state is defined in the workfloe + frappe.xcall('frappe.model.workflow.can_cancel_document', {doc: this.frm.doc}).then((can_cancel) => { + if (!can_cancel) { + this.frm.page.clear_secondary_action(); + } + }); + } else { + this.setup_btn(added); + } + }); }, From fb029082192c65d68491fafac997a98049beaf5b Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 30 Dec 2019 10:52:19 +0530 Subject: [PATCH 26/52] fix: (re)allow custom filters/methods to be used with jinja --- frappe/utils/jinja.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index befb9336fa..7c3b9b0482 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -16,6 +16,7 @@ def get_jenv(): set_filters(jenv) jenv.globals.update(get_safe_globals()) + jenv.globals.update(get_jenv_customization('methods')) frappe.local.jenv = jenv @@ -124,4 +125,27 @@ def set_filters(jenv): jenv.filters["flt"] = flt jenv.filters["abs_url"] = abs_url - if frappe.flags.in_setup_help: return + if frappe.flags.in_setup_help: + return + + jenv.filters.update(get_jenv_customization('filters')) + + +def get_jenv_customization(customization_type): + '''Returns a dict with filter/method name as key and definition as value''' + + import frappe + + out = {} + if not getattr(frappe.local, "site", None): + return out + + values = frappe.get_hooks("jenv", {}).get(customization_type) + if not values: + return out + + for value in values: + fn_name, fn_string = value.split(":") + out[fn_name] = frappe.get_attr(fn_string) + + return out From 43ae8adeb4d7b01fd4a7b51608fb6916582401eb Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 30 Dec 2019 12:52:35 +0530 Subject: [PATCH 27/52] feat: show_alert flag in rename_doc API --- frappe/core/doctype/user/user.py | 4 ++-- frappe/model/rename_doc.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 7de2bb20e5..65980780de 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -366,10 +366,10 @@ class User(Document): (tab, field, '%s', field, '%s'), (new_name, old_name)) if frappe.db.exists("Chat Profile", old_name): - frappe.rename_doc("Chat Profile", old_name, new_name, force=True) + frappe.rename_doc("Chat Profile", old_name, new_name, force=True, show_alert=False) if frappe.db.exists("Notification Settings", old_name): - frappe.rename_doc("Notification Settings", old_name, new_name, force=True) + frappe.rename_doc("Notification Settings", old_name, new_name, force=True, show_alert=False) # set email frappe.db.sql("""UPDATE `tabUser` diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 7dc3944750..a42b83fe97 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -27,7 +27,7 @@ def update_document_title(doctype, docname, title_field=None, old_title=None, ne @frappe.whitelist() -def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False, ignore_if_exists=False): +def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False, ignore_if_exists=False, show_alert=True): """ Renames a doc(dt, old) to doc(dt, new) and updates all linked fields of type "Link" @@ -99,7 +99,9 @@ def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=F frappe.clear_cache() frappe.enqueue('frappe.utils.global_search.rebuild_for_doctype', doctype=doctype) - frappe.msgprint(_('Document renamed from {0} to {1}').format(bold(old), bold(new)), alert=True, indicator='green') + + if show_alert: + frappe.msgprint(_('Document renamed from {0} to {1}').format(bold(old), bold(new)), alert=True, indicator='green') return new From 652e97eb71e1471c368f39edec34a9520a7e640b Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 30 Dec 2019 15:13:16 +0530 Subject: [PATCH 28/52] fix: Fallback value for time format --- frappe/public/js/frappe/form/controls/time.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/time.js b/frappe/public/js/frappe/form/controls/time.js index d6dcf9d33d..9e4f2048f3 100644 --- a/frappe/public/js/frappe/form/controls/time.js +++ b/frappe/public/js/frappe/form/controls/time.js @@ -45,7 +45,8 @@ frappe.ui.form.ControlTime = frappe.ui.form.ControlDate.extend({ && ((this.last_value && this.last_value !== this.value) || (!this.datepicker.selectedDates.length))) { - var date_obj = frappe.datetime.moment_to_date_obj(moment(value, frappe.sys_defaults['time_format'])); + let time_format = frappe.sys_defaults.time_format || 'HH:mm:ss'; + var date_obj = frappe.datetime.moment_to_date_obj(moment(value, time_format)); this.datepicker.selectDate(date_obj); } }, From 7f7760db33a5751765ff410cecbc9d9b02e60b74 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 23 Nov 2019 13:08:36 +0530 Subject: [PATCH 29/52] fix: serialize child table document objects --- frappe/integrations/doctype/webhook/webhook.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/integrations/doctype/webhook/webhook.py b/frappe/integrations/doctype/webhook/webhook.py index f1f50c7662..c6dcdfc931 100644 --- a/frappe/integrations/doctype/webhook/webhook.py +++ b/frappe/integrations/doctype/webhook/webhook.py @@ -111,6 +111,9 @@ def get_webhook_data(doc, webhook): value = doc.get(w.fieldname) if isinstance(value, datetime.datetime): value = frappe.utils.get_datetime_str(value) + if isinstance(value, list): + serialize_doc = lambda val: val.as_dict() if isinstance(val, Document) else val + value = list(map(serialize_doc, value)) data[w.key] = value elif webhook.webhook_json: data = frappe.render_template(webhook.webhook_json, get_context(doc)) From 9ab56d65abef903a9ab82c477e4ab2be72e70e77 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 23 Nov 2019 14:52:06 +0530 Subject: [PATCH 30/52] fix: serialize datetime objects in child table documents --- frappe/integrations/doctype/webhook/webhook.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/frappe/integrations/doctype/webhook/webhook.py b/frappe/integrations/doctype/webhook/webhook.py index c6dcdfc931..b70a3e2d8c 100644 --- a/frappe/integrations/doctype/webhook/webhook.py +++ b/frappe/integrations/doctype/webhook/webhook.py @@ -106,16 +106,12 @@ def get_webhook_headers(doc, webhook): def get_webhook_data(doc, webhook): data = {} + doc = doc.as_dict(convert_dates_to_str=True) + if webhook.webhook_data: - for w in webhook.webhook_data: - value = doc.get(w.fieldname) - if isinstance(value, datetime.datetime): - value = frappe.utils.get_datetime_str(value) - if isinstance(value, list): - serialize_doc = lambda val: val.as_dict() if isinstance(val, Document) else val - value = list(map(serialize_doc, value)) - data[w.key] = value + data = {w.key: doc.get(w.fieldname) for w in webhook.webhook_data} elif webhook.webhook_json: data = frappe.render_template(webhook.webhook_json, get_context(doc)) data = json.loads(data) + return data From 547a4d3ca00bed676b8bb62c5007d3fc4d6198ab Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Tue, 31 Dec 2019 13:24:13 +0530 Subject: [PATCH 31/52] fix: allow tables to be sent in webhook --- frappe/model/base_document.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index a50bf9fdaf..cbbe2b267e 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -275,7 +275,7 @@ class BaseDocument(object): doc["doctype"] = self.doctype for df in self.meta.get_table_fields(): children = self.get(df.fieldname) or [] - doc[df.fieldname] = [d.as_dict(no_nulls=no_nulls) for d in children] + doc[df.fieldname] = [d.as_dict(convert_dates_to_str=convert_dates_to_str, no_nulls=no_nulls) for d in children] if no_nulls: for k in list(doc): From 78ee7c79f6c1d3f3df3131cb30aa6ce60f67b545 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 31 Dec 2019 16:01:59 +0530 Subject: [PATCH 32/52] fix: fix depends on ui test --- cypress/integration/depends_on.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cypress/integration/depends_on.js b/cypress/integration/depends_on.js index 73faad845a..8cb4e42d3e 100644 --- a/cypress/integration/depends_on.js +++ b/cypress/integration/depends_on.js @@ -1,4 +1,8 @@ context('Depends On', () => { + beforeEach(() => { + cy.login(); + cy.visit('/desk'); + }); before(() => { cy.login(); cy.visit('/desk'); @@ -49,6 +53,7 @@ context('Depends On', () => { cy.get('.control-input [data-fieldname="dependant_field"]').should('not.be.disabled'); }); it('should display the field depending on other fields value', () => { + cy.new_form('Test Depends On'); cy.get('.control-input [data-fieldname="display_dependant_field"]').should('not.be.visible'); cy.get('.control-input [data-fieldname="test_field"]').clear(); cy.fill_field('test_field', 'Value'); From 459e550a3f0364ae86ee351ac888b4d9a74cf96c Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Tue, 31 Dec 2019 18:19:15 +0530 Subject: [PATCH 33/52] fix(requirements): remove psycopg2 from requirements apparently, since version 2.8, psycopg2 does not install the binary version by default (read source), and hence fails on setup with the error: Error: pg_config executable not found since nobody can really be arsed to compile this binary on their own, we'll stick to using psycopg2-binary instead. source: https://www.postgresql.org/message-id/CA%2Bmi_8bd6kJHLTGkuyHSnqcgDrJ1uHgQWvXCKQFD3tPQBUa2Bw%40mail.gmail.com Signed-off-by: Chinmay D. Pai --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index b2c5c12f46..77f49156e9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,7 +35,6 @@ pdfkit==0.6.1 Pillow==6.2.1 premailer==3.6.1 psycopg2-binary==2.8.4 -psycopg2==2.8.4 pyasn1==0.4.7 Pygments==2.2.0 PyJWT==1.7.1 @@ -64,4 +63,4 @@ urllib3==1.25.7 watchdog==0.8.0 Werkzeug==0.16.0 xlrd==1.2.0 -zxcvbn-python==4.4.24 \ No newline at end of file +zxcvbn-python==4.4.24 From 0778c337ae06f265b402b7297a4f4f463504962e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 31 Dec 2019 18:51:09 +0530 Subject: [PATCH 34/52] fix(Auto Repeat): derive next date from start date and offset (#9177) --- .../doctype/auto_repeat/auto_repeat.py | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index e618c7d63e..2d9428d1fe 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -9,7 +9,7 @@ from frappe.desk.form import assign_to from frappe.utils.jinja import validate_template from dateutil.relativedelta import relativedelta from frappe.utils.user import get_system_managers -from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day +from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day, month_diff from frappe.model.document import Document from frappe.core.doctype.communication.email import make from frappe.utils.background_jobs import get_jobs @@ -48,7 +48,7 @@ class AutoRepeat(Document): if self.disabled: self.next_schedule_date = None else: - self.next_schedule_date = get_next_schedule_date(self.start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, self.end_date) + self.next_schedule_date = get_next_schedule_date(self.start_date, self.frequency, self.start_date, self.repeat_on_day, self.repeat_on_last_day, self.end_date) def unlink_if_applicable(self): if self.status == 'Completed' or self.disabled: @@ -107,27 +107,27 @@ class AutoRepeat(Document): end_date = getdate(self.end_date) if not self.end_date: - start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day) + next_date = get_next_schedule_date(start_date, self.frequency, self.start_date, self.repeat_on_day, self.repeat_on_last_day) row = { "reference_document": self.reference_document, "frequency": self.frequency, - "next_scheduled_date": start_date + "next_scheduled_date": next_date } schedule_details.append(row) - start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day) if self.end_date: - start_date = get_next_schedule_date( - start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, for_full_schedule=True) - while (getdate(start_date) < getdate(end_date)): + next_date = get_next_schedule_date( + start_date, self.frequency, self.start_date, self.repeat_on_day, self.repeat_on_last_day, for_full_schedule=True) + + while (getdate(next_date) < getdate(end_date)): row = { "reference_document" : self.reference_document, "frequency" : self.frequency, - "next_scheduled_date" : start_date + "next_scheduled_date" : next_date } schedule_details.append(row) - start_date = get_next_schedule_date( - start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, end_date, for_full_schedule=True) + next_date = get_next_schedule_date( + next_date, self.frequency, self.start_date, self.repeat_on_day, self.repeat_on_last_day, end_date, for_full_schedule=True) return schedule_details @@ -268,8 +268,12 @@ class AutoRepeat(Document): ) -def get_next_schedule_date(start_date, frequency, repeat_on_day, repeat_on_last_day=False, end_date=None, for_full_schedule=False): - month_count = month_map.get(frequency) +def get_next_schedule_date(schedule_date, frequency, start_date, repeat_on_day=None, repeat_on_last_day=False, end_date=None, for_full_schedule=False): + if month_map.get(frequency): + month_count = month_map.get(frequency) + month_diff(schedule_date, start_date) - 1 + else: + month_count = 0 + day_count = 0 if month_count and repeat_on_last_day: next_date = get_next_date(start_date, month_count, 31) @@ -288,7 +292,9 @@ def get_next_schedule_date(start_date, frequency, repeat_on_day, repeat_on_last_ # next schedule date should be after or on current date if not for_full_schedule: while getdate(next_date) < getdate(today()): - next_date = get_next_date(next_date, month_count, day_count) + if month_count: + month_count += month_map.get(frequency) + next_date = get_next_date(start_date, month_count, day_count) return next_date @@ -316,8 +322,7 @@ def create_repeated_entries(data): if schedule_date == current_date and not doc.disabled: doc.create_documents() - schedule_date = get_next_schedule_date(schedule_date, doc.frequency, doc.repeat_on_day, doc.repeat_on_last_day, doc.end_date) - + schedule_date = get_next_schedule_date(schedule_date, doc.frequency, doc.start_date, doc.repeat_on_day, doc.repeat_on_last_day, doc.end_date) if schedule_date and not doc.disabled: frappe.db.set_value('Auto Repeat', doc.name, 'next_schedule_date', schedule_date) From e2935b5ba6ff263c8107fa7bea749080dadec0a7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 31 Dec 2019 21:09:40 +0530 Subject: [PATCH 35/52] refactor: Commonify sent mail checks -and fix formatting --- frappe/core/doctype/communication/email.py | 4 ++-- frappe/email/doctype/email_account/email_account.py | 4 ++-- frappe/email/queue.py | 12 +++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index ff206ed02e..8793c60934 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -309,8 +309,8 @@ def set_incoming_outgoing_accounts(doc): doc.incoming_email_account = frappe.db.get_value("Email Account", {"default_incoming": 1, "enable_incoming": 1}, "email_id") - doc.outgoing_email_account = frappe.email.smtp.get_outgoing_email_account(raise_exception_not_set=False, append_to=doc.doctype, - sender=doc.sender) + doc.outgoing_email_account = frappe.email.smtp.get_outgoing_email_account(raise_exception_not_set=False, + append_to=doc.doctype, sender=doc.sender) if doc.sent_or_received == "Sent": doc.db_set("email_account", doc.outgoing_email_account.name) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index d690985ae6..50daf1cf72 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -130,8 +130,8 @@ class EmailAccount(Document): if not self.smtp_server: frappe.throw(_("{0} is required").format("SMTP Server")) - server = SMTPServer(login = getattr(self, "login_id", None) \ - or self.email_id, + server = SMTPServer( + login = getattr(self, "login_id", None) or self.email_id, server=self.smtp_server, port=cint(self.smtp_port), use_tls=cint(self.use_tls), diff --git a/frappe/email/queue.py b/frappe/email/queue.py index c26443139d..9ced45ffdc 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -380,7 +380,9 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals for update''', email, as_dict=True)[0] recipients_list = frappe.db.sql('''select name, recipient, status from - `tabEmail Queue Recipient` where parent=%s''',email.name,as_dict=1) + `tabEmail Queue Recipient` where parent=%s''', email.name, as_dict=1) + + email_sent_to_any_recipient = any("Sent" == s.status for s in recipients_list) if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) @@ -426,7 +428,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals (now_datetime(), recipient.name), auto_commit=auto_commit) #if all are sent set status - if any("Sent" == s.status for s in recipients_list): + if email_sent_to_any_recipient: frappe.db.sql("""update `tabEmail Queue` set status='Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) else: @@ -438,7 +440,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals if email.communication: frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) - if smtpserver.append_emails_to_sent_folder and any("Sent" == s.status for s in recipients_list): + if smtpserver.append_emails_to_sent_folder and email_sent_to_any_recipient: smtpserver.email_account.append_email_to_sent_folder(encode(message)) except (smtplib.SMTPServerDisconnected, @@ -450,7 +452,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals # bad connection/timeout, retry later - if any("Sent" == s.status for s in recipients_list): + if email_sent_to_any_recipient: frappe.db.sql("""update `tabEmail Queue` set status='Partially Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) else: @@ -470,7 +472,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s, retry=retry+1 where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) else: - if any("Sent" == s.status for s in recipients_list): + if email_sent_to_any_recipient: frappe.db.sql("""update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""", (text_type(e), email.name), auto_commit=auto_commit) else: From a2075477fc2d84cb61c66f663f1d672a7283635c Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 1 Jan 2020 09:11:40 +0530 Subject: [PATCH 36/52] fix: Bug in show/hide global cards (#9163) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/public/js/frappe/views/components/Desktop.vue | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/views/components/Desktop.vue b/frappe/public/js/frappe/views/components/Desktop.vue index dd9aff3cb7..ec663a876a 100644 --- a/frappe/public/js/frappe/views/components/Desktop.vue +++ b/frappe/public/js/frappe/views/components/Desktop.vue @@ -116,6 +116,7 @@ export default { user_section = [ { + fieldname: 'user_section', fieldtype: 'Section Break', depends_on: doc => doc.setup_for === user_value } @@ -134,6 +135,7 @@ export default { global_section = [ { + fieldname: 'global_section', fieldtype: 'Section Break', depends_on: doc => doc.setup_for === 'Everyone' } @@ -188,8 +190,11 @@ export default { update_global_modules(d) { let blocked_modules = []; for (let category of this.module_categories) { - let unchecked_options = d.get_field(`global:${category}`).get_unchecked_options(); - blocked_modules = blocked_modules.concat(unchecked_options); + let field = d.get_field(`global:${category}`); + if (field) { + let unchecked_options = field.get_unchecked_options(); + blocked_modules = blocked_modules.concat(unchecked_options); + } } frappe.call({ From d5b08ca36f5cb73c3cf8084b92ce68677a5d0f6a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 1 Jan 2020 09:29:12 +0530 Subject: [PATCH 37/52] fix: Use _ for translation since frappe._ is not supported --- frappe/templates/emails/energy_points_summary.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/templates/emails/energy_points_summary.html b/frappe/templates/emails/energy_points_summary.html index c560a24cfc..3cbc6e97cb 100644 --- a/frappe/templates/emails/energy_points_summary.html +++ b/frappe/templates/emails/energy_points_summary.html @@ -2,7 +2,7 @@

{{ _('Top Performer') }} 🏆

{{ frappe.get_fullname(top_performer.user) }} - {{ frappe._('gained {0} points').format(frappe.utils.cint(top_performer.energy_points)) }} + {{ _('gained {0} points').format(frappe.utils.cint(top_performer.energy_points)) }}

{% endif %} @@ -11,7 +11,7 @@

{{ _('Top Reviewer') }} ❤️

{{ frappe.get_fullname(top_reviewer.user) }} - {{ frappe._('gave {0} points').format(frappe.utils.cint(top_reviewer.given_points)) }} + {{ _('gave {0} points').format(frappe.utils.cint(top_reviewer.given_points)) }}

@@ -24,9 +24,9 @@ - - - + + + {% for user in standings %} From f343d15995b50433d5fd151af313827e9d98b494 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 1 Jan 2020 09:34:37 +0530 Subject: [PATCH 38/52] fix: Remove _ & _dict from frappe because add_module_properties ignores it --- frappe/utils/safe_exec.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 5bc2a3157a..62d0286e03 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -48,11 +48,9 @@ def get_safe_globals(): # make available limited methods of frappe json=json, dict=dict, + _dict=frappe._dict, frappe=frappe._dict( - _=frappe._, - _dict=frappe._dict, flags=frappe.flags, - format=frappe.format_value, format_value=frappe.format_value, date_format=date_format, From 4c138390b60a2537c1b4e7ee38bfdb140be3ccb4 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Wed, 1 Jan 2020 11:21:53 +0530 Subject: [PATCH 39/52] fix: move email status check to fix travis mail status wasn't being set when the check was run, so the output for any() would always be false, and none of the mails would have status set to sent in the backend. moving the check to a point after setting status for each email should fix this issue Signed-off-by: Chinmay D. Pai --- frappe/email/queue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 9ced45ffdc..4a0a34c76e 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -382,8 +382,6 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals recipients_list = frappe.db.sql('''select name, recipient, status from `tabEmail Queue Recipient` where parent=%s''', email.name, as_dict=1) - email_sent_to_any_recipient = any("Sent" == s.status for s in recipients_list) - if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return @@ -427,6 +425,8 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals frappe.db.sql("""update `tabEmail Queue Recipient` set status='Sent', modified=%s where name=%s""", (now_datetime(), recipient.name), auto_commit=auto_commit) + email_sent_to_any_recipient = any("Sent" == s.status for s in recipients_list) + #if all are sent set status if email_sent_to_any_recipient: frappe.db.sql("""update `tabEmail Queue` set status='Sent', modified=%s where name=%s""", From 49fc64618a726ee727c6ce898b72157601b2992b Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 23 Dec 2019 11:24:28 +0530 Subject: [PATCH 40/52] fix: bench build "Cannot link {assets} to {site assets}" --- frappe/build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/build.py b/frappe/build.py index 265a8c3976..456f02e971 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -118,6 +118,7 @@ def make_asset_dirs(make_copy=False, restore=False): else: shutil.rmtree(target) try: + os.unlink(target) os.symlink(source, target) except OSError: print('Cannot link {} to {}'.format(source, target)) From 658fcac45433ff7ab32f79eee8e7d0703e4b93d0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 23 Dec 2019 13:15:36 +0530 Subject: [PATCH 41/52] fix: avoid race condition to create symlinks --- frappe/build.py | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/frappe/build.py b/frappe/build.py index 456f02e971..cd96634ca9 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -2,7 +2,7 @@ # MIT License. See license.txt from __future__ import unicode_literals, print_function -import os, frappe, json, shutil, re, warnings +import os, frappe, json, shutil, re, warnings, tempfile from os.path import exists as path_exists, join as join_path, abspath, isdir from distutils.spawn import find_executable from six import iteritems, text_type @@ -12,6 +12,48 @@ from frappe.utils.minify import JavascriptMinify Build the `public` folders and setup languages """ + +def symlink(target, link_name, overwrite=False): + ''' + Create a symbolic link named link_name pointing to target. + If link_name exists then FileExistsError is raised, unless overwrite=True. + When trying to overwrite a directory, IsADirectoryError is raised. + + Source: https://stackoverflow.com/a/55742015/10309266 + ''' + + if not overwrite: + os.symlink(target, linkname) + return + + # os.replace() may fail if files are on different filesystems + link_dir = os.path.dirname(link_name) + + # Create link to target with temporary filename + while True: + temp_link_name = tempfile.mktemp(dir=link_dir) + + # os.* functions mimic as closely as possible system functions + # The POSIX symlink() returns EEXIST if link_name already exists + # https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html + try: + os.symlink(target, temp_link_name) + break + except FileExistsError: + pass + + # Replace link_name with temp_link_name + try: + # Pre-empt os.replace on a directory with a nicer message + if os.path.isdir(link_name): + raise IsADirectoryError("Cannot symlink over existing directory: '{}'".format(link_name)) + os.replace(temp_link_name, link_name) + except: + if os.path.islink(temp_link_name): + os.remove(temp_link_name) + raise + + app_paths = None def setup(): global app_paths @@ -118,8 +160,7 @@ def make_asset_dirs(make_copy=False, restore=False): else: shutil.rmtree(target) try: - os.unlink(target) - os.symlink(source, target) + symlink(source, target, overwrite=True) except OSError: print('Cannot link {} to {}'.format(source, target)) else: From 04bc21696668db4486f89fb513abd5dd596bfda1 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 26 Dec 2019 09:36:27 +0530 Subject: [PATCH 42/52] fix: python 2 compatibility for symlink creation --- frappe/build.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/build.py b/frappe/build.py index cd96634ca9..f7437acf8f 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -47,7 +47,10 @@ def symlink(target, link_name, overwrite=False): # Pre-empt os.replace on a directory with a nicer message if os.path.isdir(link_name): raise IsADirectoryError("Cannot symlink over existing directory: '{}'".format(link_name)) - os.replace(temp_link_name, link_name) + try: + os.replace(temp_link_name, link_name) + except AttributeError: + os.renames(temp_link_name, link_name) except: if os.path.islink(temp_link_name): os.remove(temp_link_name) From 4ac83a0cc8a521b3403196eaf66a624c0092265c Mon Sep 17 00:00:00 2001 From: Mohammed Safwat Abu Kwaik Date: Thu, 2 Jan 2020 19:50:04 +0300 Subject: [PATCH 43/52] fix: allow setting custom database schema in site_config (#9182) * Update database.py * Update frappe/database/postgres/database.py Co-Authored-By: Chinmay Pai Co-authored-by: Chinmay Pai --- frappe/database/postgres/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index abacc5ab4c..243d0f934e 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -107,7 +107,7 @@ class PostgresDatabase(Database): from information_schema.tables where table_catalog='{0}' and table_type = 'BASE TABLE' - and table_schema='public'""".format(frappe.conf.db_name))] + and table_schema='{1}'""".format(frappe.conf.db_name, frappe.conf.get("db_schema", "public")))] def format_date(self, date): if not date: From ce2a0854db58ff0d03e6a89a2e255874fd794e43 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 3 Jan 2020 14:56:15 +0530 Subject: [PATCH 44/52] fix(email): safe_encode to avoid smtp ascii encoding issue fixes issue where smtplib fails to encode the mail as ascii Traceback (most recent call last): File "/home/frappe/frappe-bench/apps/frappe/frappe/app.py", line 60, in application response = frappe.api.handle() File "/home/frappe/frappe-bench/apps/frappe/frappe/api.py", line 55, in handle return frappe.handler.handle() File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 22, in handle data = execute_cmd(cmd) File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 61, in execute_cmd return frappe.call(method, **frappe.form_dict) File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 1042, in call return fn(*args, **newargs) File "/home/frappe/frappe-bench/apps/frappe/frappe/core/doctype/user/user.py", line 801, in reset_password user.reset_password(send_email=True) File "/home/frappe/frappe-bench/apps/frappe/frappe/core/doctype/user/user.py", line 234, in reset_password self.password_reset_mail(link) File "/home/frappe/frappe-bench/apps/frappe/frappe/core/doctype/user/user.py", line 253, in password_reset_mail "password_reset", {"link": link}, now=True) File "/home/frappe/frappe-bench/apps/frappe/frappe/core/doctype/user/user.py", line 298, in send_login_mail delayed=(not now) if now!=None else self.flags.delay_emails, retry=3) File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 476, in sendmail inline_images=inline_images, header=header, print_letterhead=print_letterhead) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/queue.py", line 162, in send print_letterhead=print_letterhead) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/queue.py", line 185, in add send_one(email_queue.name, now=True) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/queue.py", line 475, in send_one raise e File "/home/frappe/frappe-bench/apps/frappe/frappe/email/queue.py", line 415, in send_one smtpserver.sess.sendmail(email.sender, recipient.recipient, message) File "/usr/lib64/python3.6/smtplib.py", line 855, in sendmail msg = _fix_eols(msg).encode('ascii') UnicodeEncodeError: 'ascii' codec can't encode characters in position 335-339: ordinal not in range(128) Signed-off-by: Chinmay D. Pai --- frappe/email/queue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 4a0a34c76e..62b0d9ea3f 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -6,7 +6,7 @@ import frappe import sys from six.moves import html_parser as HTMLParser import smtplib, quopri, json -from frappe import msgprint, _, safe_decode +from frappe import msgprint, _, safe_decode, safe_encode from frappe.email.smtp import SMTPServer, get_outgoing_email_account from frappe.email.email_body import get_email, get_formatted_html, add_attachment from frappe.utils.verified_command import get_signed_params, verify_request @@ -563,7 +563,7 @@ def prepare_message(email, recipient, recipients_list): print_format_file.update({"parent": message}) add_attachment(**print_format_file) - return message.as_string() + return safe_encode(message.as_string()) def clear_outbox(): """Remove low priority older than 31 days in Outbox and expire mails not sent for 7 days. From 58825e89a814e6757b395becaca94b4048bdb285 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 3 Jan 2020 22:08:06 +0530 Subject: [PATCH 45/52] feat: Disable testing for all sites by default To avoid accidental test execution on production sites --- frappe/commands/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index d29f0a9c97..11f8e69b95 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -460,6 +460,15 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), tests = test site = get_site(context) + + allow_tests = frappe.get_conf(site).allow_tests + + if not (allow_tests or os.environ.get('CI')): + click.secho('Testing is disabled for the site!', bold=True) + click.secho('You can enable tests by entering following command:') + click.secho('bench --site {0} set-config allow_tests true'.format(site), fg='green') + return + frappe.init(site=site) frappe.flags.skip_before_tests = skip_before_tests From 250c44bed89478aedc194ede8ae403d9253790da Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 3 Jan 2020 23:13:22 +0530 Subject: [PATCH 46/52] chore: pass email_body test Signed-off-by: Chinmay D. Pai --- frappe/email/test_email_body.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index 26c4e5ba5d..f44c6e775a 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -3,9 +3,10 @@ from __future__ import unicode_literals import unittest, os, base64 +from frappe import safe_decode from frappe.email.receive import Email from frappe.email.email_body import (replace_filename_with_cid, - get_email, inline_style_in_html, get_header) + get_email, inline_style_in_html, get_header) from frappe.email.queue import prepare_message, get_email_queue from six import PY3 @@ -57,7 +58,7 @@ This is the text version of this email formatted='

' + uni_chr1 + 'abcd' + uni_chr2 + '

', text_content='whatever') result = prepare_message(email=email, recipient='test@test.com', recipients_list=[]) - self.assertTrue("

=EA=80=80abcd=DE=B4

" in result) + self.assertTrue(b"

=EA=80=80abcd=DE=B4

" in result) def test_prepare_message_returns_cr_lf(self): email = get_email_queue( @@ -67,7 +68,8 @@ This is the text version of this email content='

\n this is a test of newlines\n' + '

', formatted='

\n this is a test of newlines\n' + '

', text_content='whatever') - result = prepare_message(email=email, recipient='test@test.com', recipients_list=[]) + result = safe_decode(prepare_message(email=email, + recipient='test@test.com', recipients_list=[])) if PY3: self.assertTrue(result.count('\n') == result.count("\r")) else: @@ -81,9 +83,10 @@ This is the text version of this email subject='Test Subject', content='

Whatever

', text_content='whatever', - message_id= "a.really.long.message.id.that.should.not.wrap.until.998.if.it.does.then.exchange.will.break" + - ".really.long.message.id.that.should.not.wrap.unti") - result = prepare_message(email=email, recipient='test@test.com', recipients_list=[]) + message_id="a.really.long.message.id.that.should.not.wrap.until.998.if.it.does.then.exchange.will.break" + + ".really.long.message.id.that.should.not.wrap.unti") + result = safe_decode(prepare_message(email=email, recipient='test@test.com', + recipients_list=[])) self.assertTrue( "a.really.long.message.id.that.should.not.wrap.until.998.if.it.does.then.exchange.will.break" + ".really.long.message.id.that.should.not.wrap.unti" in result) From 1b4ecdf79293b1e72666774970b6ad625e5a0c5c Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sat, 4 Jan 2020 09:26:23 +0530 Subject: [PATCH 47/52] fix: unreferenced variable --- frappe/email/smtp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py index d735f5c445..aa025465e5 100644 --- a/frappe/email/smtp.py +++ b/frappe/email/smtp.py @@ -41,6 +41,8 @@ def get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sen try getting settings from `site_config.json`.""" sender_email_id = None + _email_account = None + if sender: sender_email_id = parse_addr(sender)[1] From 68659c55193f055d1d73a7d1cdc9fa2469865c74 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Sat, 4 Jan 2020 11:51:49 +0530 Subject: [PATCH 48/52] fix(email): define email_server before condition fixes issue where email_server is unhandled in test Traceback (most recent call last): File "/home/chnmy/workspace/frappe/benches/master-bench/apps/frappe/frappe/email/doctype/email_account/test_email_account.py", line 44, in test_unread_notification self.test_incoming() File "/home/chnmy/workspace/frappe/benches/master-bench/apps/frappe/frappe/email/doctype/email_account/test_email_account.py", line 35, in test_incoming email_account.receive(test_mails=test_mails) File "/home/chnmy/workspace/frappe/benches/master-bench/apps/frappe/frappe/email/doctype/email_account/email_account.py", line 291, in receive self.handle_bad_emails(email_server, uid, msg, frappe.get_traceback()) UnboundLocalError: local variable 'email_server' referenced before assignment Signed-off-by: Chinmay D. Pai --- frappe/email/doctype/email_account/email_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 50daf1cf72..7b19ddccc6 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -247,13 +247,13 @@ class EmailAccount(Document): exceptions = [] seen_status = [] uid_reindexed = False + email_server = None if frappe.local.flags.in_test: incoming_mails = test_mails else: email_sync_rule = self.build_email_sync_rule() - email_server = None try: email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) except Exception: From ba9d6020e65e8dc248ca80e2821adfe832c9c2fd Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Sat, 4 Jan 2020 12:19:57 +0530 Subject: [PATCH 49/52] chore: check if email_server exists before handling bad emails Signed-off-by: Chinmay D. Pai --- frappe/email/doctype/email_account/email_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 7b19ddccc6..337bfac79b 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -305,7 +305,7 @@ class EmailAccount(Document): raise Exception(frappe.as_json(exceptions)) def handle_bad_emails(self, email_server, uid, raw, reason): - if cint(email_server.settings.use_imap): + if email_server and cint(email_server.settings.use_imap): import email try: mail = email.message_from_string(raw) From 18d8e65cca606a1100b74d5864c9782036aa6574 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Sat, 4 Jan 2020 19:12:59 +0530 Subject: [PATCH 50/52] =?UTF-8?q?fix:=20Make=20background=20jobs=20logging?= =?UTF-8?q?=20quieter=20if=20quite=20flag=20is=20pass=E2=80=A6=20(#9200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frappe/utils/background_jobs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index c1ac7581dc..65b2326733 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -142,6 +142,8 @@ def start_worker(queue=None, quiet = False): with Connection(redis_connection): queues = get_queue_list(queue) logging_level = "INFO" + if quiet: + logging_level = "WARNING" Worker(queues, name=get_worker_name(queue)).work(logging_level = logging_level) def get_worker_name(queue): From 0e5e3497b1d474e1ff4f9655fb122529c2cdfce1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Sat, 4 Jan 2020 21:05:02 +0530 Subject: [PATCH 51/52] fix: as_table msgprint (#9174) * chore: improve readability * fix: as_table msgprint - fix as_table msgprint style and functionality - Make table creation code more readable - Make a text translatable * style: Fix few deepsource issues * style: Fix few deepsource issues fix indentation * fix: Incorrect assignment * fix: Scrub URLs only for html format * fix(typo): msg -> message Co-authored-by: Raffael Meyer --- frappe/__init__.py | 140 ++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 59 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index b383ae958e..5e797fd8cd 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -321,10 +321,19 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, indicator=None, return if as_table and type(msg) in (list, tuple): - out.msg = '
# {{ frappe._('User') }}{{ frappe._('Energy Points') }}{{ frappe._('Points Given') }}{{ _('User') }}{{ _('Energy Points') }}{{ _('Points Given') }}
' + ''.join([''+''.join(['' % c for c in r])+'' for r in msg]) + '
%s
' - if flags.print_messages and out.msg: - print("Message: " + repr(out.msg).encode("utf-8")) + table_rows = '' + for row in msg: + table_row_data = '' + for data in row: + table_row_data += '{}'.format(data) + table_rows += '{}'.format(table_row_data) + + out.message = '''{}
'''.format(table_rows) + + if flags.print_messages and out.message: + print("Message: " + repr(out.message).encode("utf-8")) if title: out.title = title @@ -363,7 +372,6 @@ def throw(msg, exc=ValidationError, title=None): msgprint(msg, raise_exception=exc, title=title, indicator='red') def emit_js(js, user=False, **kwargs): - from frappe.realtime import publish_realtime if user == False: user = session.user publish_realtime('eval_js', js, user=user, **kwargs) @@ -767,8 +775,8 @@ def get_meta_module(doctype): import frappe.modules return frappe.modules.load_doctype_module(doctype) -def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, - ignore_permissions=False, flags=None, ignore_on_trash=False, ignore_missing=True): +def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, + for_reload=False, ignore_permissions=False, flags=None, ignore_on_trash=False, ignore_missing=True): """Delete a document. Calls `frappe.model.delete_doc.delete_doc`. :param doctype: DocType of document to be delete. @@ -805,8 +813,8 @@ def reload_doc(module, dt=None, dn=None, force=False, reset_permissions=False): def rename_doc(*args, **kwargs): """Rename a document. Calls `frappe.model.rename_doc.rename_doc`""" - from frappe.model.rename_doc import rename_doc - return rename_doc(*args, **kwargs) + from frappe.model.rename_doc import rename_doc as _rename_doc + return _rename_doc(*args, **kwargs) def get_module(modulename): """Returns a module object for given Python module name using `importlib.import_module`.""" @@ -814,11 +822,11 @@ def get_module(modulename): def scrub(txt): """Returns sluggified string. e.g. `Sales Order` becomes `sales_order`.""" - return txt.replace(' ','_').replace('-', '_').lower() + return txt.replace(' ', '_').replace('-', '_').lower() def unscrub(txt): """Returns titlified string. e.g. `sales_order` becomes `Sales Order`.""" - return txt.replace('_',' ').replace('-', ' ').title() + return txt.replace('_', ' ').replace('-', ' ').title() def get_module_path(module, *joins): """Get the path of the given module name. @@ -980,7 +988,8 @@ def setup_module_map(): if not (local.app_modules and local.module_app): local.module_app, local.app_modules = {}, {} for app in get_all_apps(True): - if app=="webnotes": app="frappe" + if app == "webnotes": + app = "frappe" local.app_modules.setdefault(app, []) for module in get_module_list(app): module = scrub(module) @@ -999,7 +1008,10 @@ def get_file_items(path, raise_not_found=False, ignore_empty_lines=True): if content: content = frappe.utils.strip(content) - return [p.strip() for p in content.splitlines() if (not ignore_empty_lines) or (p.strip() and not p.startswith("#"))] + return [ + p.strip() for p in content.splitlines() + if (not ignore_empty_lines) or (p.strip() and not p.startswith("#")) + ] else: return [] @@ -1161,8 +1173,8 @@ def compare(val1, condition, val2): import frappe.utils return frappe.utils.compare(val1, condition, val2) -def respond_as_web_page(title, html, success=None, http_status_code=None, - context=None, indicator_color=None, primary_action='/', primary_label = None, fullpage=False, +def respond_as_web_page(title, html, success=None, http_status_code=None, context=None, + indicator_color=None, primary_action='/', primary_label = None, fullpage=False, width=None, template='message'): """Send response as a web page with a message rather than JSON. Used to show permission errors etc. @@ -1351,7 +1363,8 @@ def format(*args, **kwargs): import frappe.utils.formatters return frappe.utils.formatters.format_value(*args, **kwargs) -def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None, output = None, no_letterhead = 0, password=None): +def get_print(doctype=None, name=None, print_format=None, style=None, + html=None, as_pdf=False, doc=None, output=None, no_letterhead=0, password=None): """Get Print Format for given document. :param doctype: DocType of document. @@ -1382,7 +1395,8 @@ def get_print(doctype=None, name=None, print_format=None, style=None, html=None, else: return html -def attach_print(doctype, name, file_name=None, print_format=None, style=None, html=None, doc=None, lang=None, print_letterhead=True, password=None): +def attach_print(doctype, name, file_name=None, print_format=None, + style=None, html=None, doc=None, lang=None, print_letterhead=True, password=None): from frappe.utils import scrub_urls if not file_name: file_name = name @@ -1398,16 +1412,28 @@ def attach_print(doctype, name, file_name=None, print_format=None, style=None, h no_letterhead = not print_letterhead + kwargs = dict( + print_format=print_format, + style=style, + html=html, + doc=doc, + no_letterhead=no_letterhead, + password=password + ) + + content = '' if int(print_settings.send_print_as_pdf or 0): - out = { - "fname": file_name + ".pdf", - "fcontent": get_print(doctype, name, print_format=print_format, style=style, html=html, as_pdf=True, doc=doc, no_letterhead=no_letterhead, password=password) - } + ext = ".pdf" + kwargs["as_pdf"] = True + content = get_print(doctype, name, **kwargs) else: - out = { - "fname": file_name + ".html", - "fcontent": scrub_urls(get_print(doctype, name, print_format=print_format, style=style, html=html, doc=doc, no_letterhead=no_letterhead, password=password)).encode("utf-8") - } + ext = ".html" + content = scrub_urls(get_print(doctype, name, **kwargs)).encode('utf-8') + + out = { + "fname": file_name + ext, + "fcontent": content + } local.flags.ignore_print_permissions = False #reset lang to original local lang @@ -1526,7 +1552,12 @@ def log_error(message=None, title=None): method=title)).insert(ignore_permissions=True) def get_desk_link(doctype, name): - return '{2} {1}'.format(doctype, name, _(doctype)) + html = '{doctype_local} {name}' + return html.format( + doctype=doctype, + name=name, + doctype_local=_(doctype) + ) def bold(text): return '{0}'.format(text) @@ -1545,10 +1576,9 @@ def safe_eval(code, eval_globals=None, eval_locals=None): if not eval_globals: eval_globals = {} + eval_globals['__builtins__'] = {} - eval_globals.update(whitelisted_globals) - return eval(code, eval_globals, eval_locals) def get_system_settings(key): @@ -1560,9 +1590,11 @@ def get_active_domains(): from frappe.core.doctype.domain_settings.domain_settings import get_active_domains return get_active_domains() -def get_version(doctype, name, limit = None, head = False, raise_err = True): +def get_version(doctype, name, limit=None, head=False, raise_err=True): ''' - Returns a list of version information of a given DocType (Applicable only if DocType has changes tracked). + Returns a list of version information of a given DocType. + + Note: Applicable only if DocType has changes tracked. Example >>> frappe.get_version('User', 'foobar@gmail.com') @@ -1575,34 +1607,29 @@ def get_version(doctype, name, limit = None, head = False, raise_err = True): } ] ''' - meta = get_meta(doctype) + meta = get_meta(doctype) if meta.track_changes: - names = db.sql(""" - SELECT name from tabVersion - WHERE ref_doctype = '{doctype}' AND docname = '{name}' - {order_by} - {limit} - """.format( - doctype = doctype, - name = name, - order_by = 'ORDER BY creation' if head else '', - limit = 'LIMIT {limit}'.format(limit = limit) if limit else '' - )) + names = db.get_all('Version', filters={ + 'ref_doctype': doctype, + 'docname': name, + 'order_by': 'creation' if head else None, + 'limit': limit + }, as_list=1) from frappe.chat.util import squashify, dictify, safe_json_loads - versions = [ ] + versions = [] for name in names: name = squashify(name) - doc = get_doc('Version', name) + doc = get_doc('Version', name) data = doc.data data = safe_json_loads(data) data = dictify(dict( - version = data, - user = doc.owner, - creation = doc.creation + version=data, + user=doc.owner, + creation=doc.creation )) versions.append(data) @@ -1610,16 +1637,14 @@ def get_version(doctype, name, limit = None, head = False, raise_err = True): return versions else: if raise_err: - raise ValueError('{doctype} has no versions tracked.'.format( - doctype = doctype - )) + raise ValueError(_('{0} has no versions tracked.').format(doctype)) -@whitelist(allow_guest = True) +@whitelist(allow_guest=True) def ping(): return "pong" -def safe_encode(param, encoding = 'utf-8'): +def safe_encode(param, encoding='utf-8'): try: param = param.encode(encoding) except Exception: @@ -1627,7 +1652,7 @@ def safe_encode(param, encoding = 'utf-8'): return param -def safe_decode(param, encoding = 'utf-8'): +def safe_decode(param, encoding='utf-8'): try: param = param.decode(encoding) except Exception: @@ -1638,9 +1663,9 @@ def parse_json(val): from frappe.utils import parse_json return parse_json(val) -def mock(type, size = 1, locale = 'en'): - results = [ ] - faker = Faker(locale) +def mock(type, size=1, locale='en'): + results = [] + faker = Faker(locale) if not type in dir(faker): raise ValueError('Not a valid mock type.') else: @@ -1649,7 +1674,4 @@ def mock(type, size = 1, locale = 'en'): results.append(data) from frappe.chat.util import squashify - - results = squashify(results) - - return results + return squashify(results) From fb511b1dfd1d9fd5a2acf6bb93d2b67c5b69a878 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Sun, 5 Jan 2020 00:28:55 +0530 Subject: [PATCH 52/52] fix(email): check if communication hasattr for attachment fixes issue where previous communication has no attribute _attachments before it fails, causing the following error: Traceback (most recent call last): File "/home/frappe/frappe-bench/apps/frappe/frappe/utils/background_jobs.py", line 99, in execute_job method(**kwargs) File "/home/frappe/frappe-bench/apps/frappe/frappe/email/doctype/email_account/email_account.py", line 724, in pull_from_email_account email_account.receive() File "/home/frappe/frappe-bench/apps/frappe/frappe/email/doctype/email_account/email_account.py", line 295, in receive attachments = [d.file_name for d in communication._attachments] AttributeError: 'Communication' object has no attribute '_attachments' Signed-off-by: Chinmay D. Pai --- frappe/email/doctype/email_account/email_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 50daf1cf72..cc02cfa0ff 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -293,7 +293,7 @@ class EmailAccount(Document): else: frappe.db.commit() - if communication: + if communication and hasattr(communication, "_attachments"): attachments = [d.file_name for d in communication._attachments] communication.notify(attachments=attachments, fetched_from_email_account=True)