From 0ef3130ebd58313862c84edc685954a3fc294636 Mon Sep 17 00:00:00 2001 From: cameron Date: Mon, 29 Apr 2019 22:59:52 +0800 Subject: [PATCH 01/23] Initial update to ldap3 --- .../doctype/ldap_settings/ldap_settings.json | 844 +++++++++++------- .../doctype/ldap_settings/ldap_settings.py | 224 ++--- frappe/templates/includes/login/login.js | 2 +- frappe/www/login.py | 5 +- 4 files changed, 620 insertions(+), 455 deletions(-) diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.json b/frappe/integrations/doctype/ldap_settings/ldap_settings.json index 6eb44a2db8..aa43b2e9d0 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.json +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.json @@ -1,317 +1,363 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-09-22 04:16:48.829658", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 1, + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-09-22 04:16:48.829658", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "editable_grid": 1, "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enabled", - "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": "Enabled", - "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, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ldap_server_url", - "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": "LDAP Server Url", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "organizational_unit", - "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": "Organizational Unit", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_dn", - "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": "Base Distinguished Name (DN)", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "password", - "fieldtype": "Password", - "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": "Password for Base DN", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "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, - "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, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ldap_search_string", - "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": "LDAP Search String", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ldap_first_name_field", - "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": "LDAP First Name Field", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ldap_email_field", - "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": "LDAP Email Field", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ldap_username_field", - "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": "LDAP Username Field", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "enabled", + "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": "Enabled", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "ldap_server_url", + "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": "LDAP Server Url", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "organizational_unit", + "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": "Organizational Unit", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "base_dn", + "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": "Base Distinguished Name (DN)", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "password", + "fieldtype": "Password", + "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": "Password for Base DN", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_5", + "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, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "ldap_search_string", + "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": "LDAP Search String", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "ldap_first_name_field", + "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": "LDAP First Name Field", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "ldap_email_field", + "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": "LDAP Email Field", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "ldap_username_field", + "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": "LDAP Username Field", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "ldap_security", "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": "LDAP Security", "length": 0, "no_copy": 0, @@ -325,22 +371,28 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "default": "Off", "description": "", + "fetch_if_empty": 0, "fieldname": "ssl_tls_mode", "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": "SSL/TLS Mode", "length": 0, "no_copy": 0, @@ -355,21 +407,27 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "default": "No", + "fetch_if_empty": 0, "fieldname": "require_trusted_certificate", "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": "Require Trusted Certificate", "length": 0, "no_copy": 0, @@ -384,53 +442,153 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "local_private_key_file", + "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": "Path to private Key File", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "local_server_certificate_file", + "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": "Path to Server Certificate", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "local_ca_certs_file", + "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": "Path to CA Certs File", + "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 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-01-30 11:02:41.011412", - "modified_by": "Administrator", - "module": "Integrations", - "name": "LDAP Settings", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 1, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2019-04-29 10:56:42.322696", + "modified_by": "Administrator", + "module": "Integrations", + "name": "LDAP Settings", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 0, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 0, + "read_only": 1, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index e12a6fce05..aa5b945131 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -5,136 +5,144 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cstr from frappe.model.document import Document + class LDAPSettings(Document): - def validate(self): - if not self.flags.ignore_mandatory: - self.validate_ldap_credentails() + def validate(self): + if not self.flags.ignore_mandatory: + if self.ldap_search_string.endswith("={0}"): + if self.enabled: + connect_to_ldap(server_url=self.ldap_server_url, + base_dn=self.base_dn, + password=self.get_password(raise_exception=False), + ssl_tls_mode=self.ssl_tls_mode, + trusted_cert=self.require_trusted_certificate) + else: + frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}")) - def validate_ldap_credentails(self): - try: - import ldap - conn = ldap.initialize(self.ldap_server_url) - try: - if self.ssl_tls_mode == 'StartTLS': - conn.set_option(ldap.OPT_X_TLS_DEMAND, True) - if self.require_trusted_certificate == 'Yes': - conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND) - conn.start_tls_s() - except: - frappe.throw(_("StartTLS is not supported")) - conn.simple_bind_s(self.base_dn, self.get_password(raise_exception=False)) - except ImportError: - msg = """ -
- {{_("Seems ldap is not installed on system.
Guidelines to install ldap dependancies and python package")}}, - {{_("Click here")}}, -
- """ - frappe.throw(msg, title=_("LDAP Not Installed")) +def get_ldap_client_settings(): + # return the settings to be used on the client side. + result = { + "enabled": False + } + settings = frappe.get_doc("LDAP Settings") - except ldap.LDAPError: - conn.unbind_s() - frappe.throw(_("Incorrect UserId or Password")) + if settings and settings.enabled: + result["enabled"] = True + result["method"] = "frappe.integrations.doctype.ldap_settings.ldap_settings.login" + return result -def get_ldap_settings(): - try: - settings = frappe.get_doc("LDAP Settings") - settings.update({ - "method": "frappe.integrations.doctype.ldap_settings.ldap_settings.login" - }) - return settings - except Exception: - # this will return blank settings - return frappe._dict() +def connect_to_ldap(server_url, + base_dn, + password, + ssl_tls_mode, + trusted_cert): + try: + import ldap3 + import ssl + + if trusted_cert == 'Yes': + tls_configuration = ldap3.Tls(validate=ssl.CERT_REQUIRED, + version=ssl.PROTOCOL_TLSv1) + else: + tls_configuration = ldap3.Tls(validate=ssl.CERT_NONE, + version=ssl.PROTOCOL_TLSv1) + + server = ldap3.Server(host=server_url, + tls=tls_configuration) + bind_type = ldap3.AUTO_BIND_TLS_BEFORE_BIND if ssl_tls_mode == "StartTLS" else True + + conn = ldap3.Connection(server=server, + user=base_dn, + password=password, + auto_bind=bind_type, + read_only=True, + raise_exceptions=True) + + return conn + + except ImportError: + msg = _("Please Install the ldap3 library via pip to use ldap functionality.") + frappe.throw(msg, title=_("LDAP Not Installed")) + except ldap3.core.exceptions.LDAPInvalidCredentialsResult: + frappe.throw(_("Invalid Credentials")) + except Exception as ex: + frappe.throw(_(str(ex))) + @frappe.whitelist(allow_guest=True) def login(): - #### LDAP LOGIN LOGIC ##### - args = frappe.form_dict - user = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd)) + # LDAP LOGIN LOGIC + args = frappe.form_dict + user = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd)) - frappe.local.login_manager.user = user.name - frappe.local.login_manager.post_login() + frappe.local.login_manager.user = user.name + frappe.local.login_manager.post_login() - # because of a GET request! - frappe.db.commit() + # because of a GET request! + frappe.db.commit() -def authenticate_ldap_user(user=None, password=None): - dn = None - params = {} - settings = get_ldap_settings() - try: - import ldap - except: - msg = """ -
- {{_("Seems ldap is not installed on system.")}}
- {{_("Click here")}}, - {{_("Guidelines to install ldap dependancies and python")}} -
- """ - frappe.throw(msg, title=_("LDAP Not Installed")) +def authenticate_ldap_user(user=None, + password=None): - conn = ldap.initialize(settings.ldap_server_url) + params = {} + settings = frappe.get_doc("LDAP Settings") + if settings and settings.enabled: + import ldap3 - try: - try: - # set TLS settings for secure connection - if settings.ssl_tls_mode == 'StartTLS': - conn.set_option(ldap.OPT_X_TLS_DEMAND, True) - if settings.require_trusted_certificate == 'Yes': - conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND) - conn.start_tls_s() - except: - frappe.throw(_("StartTLS is not supported")) + conn = connect_to_ldap(server_url=settings.ldap_server_url, + base_dn=settings.base_dn, + password=settings.get_password(raise_exception=False), + ssl_tls_mode=settings.ssl_tls_mode, + trusted_cert=settings.require_trusted_certificate) - # simple_bind_s is synchronous binding to server, it takes two param DN and password - conn.simple_bind_s(settings.base_dn, settings.get_password(raise_exception=False)) + filter = settings.ldap_search_string.format(user) + conn.search(search_base=settings.organizational_unit, + search_filter="({0})".format(filter), + attributes=[settings.ldap_email_field, + settings.ldap_username_field, + settings.ldap_first_name_field]) - #search for surnames beginning with a - #available options for how deep a search you want. - #LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL,LDAP_SCOPE_SUBTREE, - result = conn.search_s(settings.organizational_unit, ldap.SCOPE_SUBTREE, - settings.ldap_search_string.format(user)) + if len(conn.entries) > 0 and conn.entries[0]: + user = conn.entries[0] + params["email"] = str(user[settings.ldap_email_field]) + params["username"] = str(user[settings.ldap_username_field]) + params["first_name"] = str(user[settings.ldap_first_name_field]) + connect_to_ldap(server_url=settings.ldap_server_url, + base_dn=user.entry_dn, + password=frappe.as_unicode(password), + ssl_tls_mode=settings.ssl_tls_mode, + trusted_cert=settings.require_trusted_certificate) + return create_user(params) + else: + frappe.throw(_("Not a valid LDAP user")) + else: + frappe.throw(_("LDAP is not enabled.")) - for dn, r in result: - dn = cstr(dn) - params["email"] = cstr(r[settings.ldap_email_field][0]) - params["username"] = cstr(r[settings.ldap_username_field][0]) - params["first_name"] = cstr(r[settings.ldap_first_name_field][0]) - - if dn: - conn.simple_bind_s(dn, frappe.as_unicode(password)) - return create_user(params) - else: - frappe.throw(_("Not a valid LDAP user")) - - except ldap.LDAPError: - conn.unbind_s() - frappe.throw(_("Incorrect UserId or Password")) def create_user(params): - if frappe.db.exists("User", params["email"]): - return frappe.get_doc("User", params["email"]) + if frappe.db.exists("User", params["email"]): + user = frappe.get_doc("User", params["email"]) + user.first_name = params["first_name"] + user.username = params["username"] + user.save(ignore_permissions=True) + return user - else: - params.update({ - "doctype": "User", - "send_welcome_email": 0, - "language": "", - "user_type": "System User", - "roles": [{ - "role": _("Blogger") - }] - }) + else: + params.update({ + "doctype": "User", + "send_welcome_email": 0, + "language": "", + "user_type": "System User", + "roles": [{ + "role": _("Blogger") + }] + }) - user = frappe.get_doc(params).insert(ignore_permissions=True) - frappe.db.commit() + user = frappe.get_doc(params).insert(ignore_permissions=True) - return user + return user diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index dd0f57eb4c..992051bc45 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -66,7 +66,7 @@ login.bind_events = function() { } }); - {% if ldap_settings %} + {% if ldap_settings.enabled %} $(".btn-ldap-login").on("click", function(){ var args = {}; args.cmd = "{{ ldap_settings.method }}"; diff --git a/frappe/www/login.py b/frappe/www/login.py index c2f83e45c3..793b57d28a 100644 --- a/frappe/www/login.py +++ b/frappe/www/login.py @@ -8,7 +8,7 @@ from frappe.utils.oauth import get_oauth2_authorize_url, get_oauth_keys, login_v import json from frappe import _ from frappe.auth import LoginManager -from frappe.integrations.doctype.ldap_settings.ldap_settings import get_ldap_settings +from frappe.integrations.doctype.ldap_settings.ldap_settings import get_ldap_client_settings from frappe.utils.password import get_decrypted_password from frappe.utils.html_utils import get_icon_html @@ -38,8 +38,7 @@ def get_context(context): "icon": icon }) context["social_login"] = True - - ldap_settings = get_ldap_settings() + ldap_settings = get_ldap_client_settings() context["ldap_settings"] = ldap_settings login_name_placeholder = [_("Email address")] From eb9d2e6182c196844dee98c1033bef98205fa758 Mon Sep 17 00:00:00 2001 From: cameron Date: Mon, 29 Apr 2019 23:21:50 +0800 Subject: [PATCH 02/23] add tests for cert files. --- .../doctype/ldap_settings/ldap_settings.py | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index aa5b945131..1d258d4bd5 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -17,7 +17,10 @@ class LDAPSettings(Document): base_dn=self.base_dn, password=self.get_password(raise_exception=False), ssl_tls_mode=self.ssl_tls_mode, - trusted_cert=self.require_trusted_certificate) + trusted_cert=self.require_trusted_certificate, + private_key_file=self.local_private_key_file, + server_cert_file=self.local_server_certificate_file, + ca_certs_file=self.local_ca_certs_file) else: frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}")) @@ -39,7 +42,10 @@ def connect_to_ldap(server_url, base_dn, password, ssl_tls_mode, - trusted_cert): + trusted_cert, + private_key_file, + server_cert_file, + ca_certs_file): try: import ldap3 import ssl @@ -51,6 +57,13 @@ def connect_to_ldap(server_url, tls_configuration = ldap3.Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1) + if private_key_file: + tls_configuration.private_key_file = private_key_file + if server_cert_file: + tls_configuration.certificate_file = server_cert_file + if ca_certs_file: + tls_configuration.ca_certs_file = ca_certs_file + server = ldap3.Server(host=server_url, tls=tls_configuration) bind_type = ldap3.AUTO_BIND_TLS_BEFORE_BIND if ssl_tls_mode == "StartTLS" else True @@ -98,7 +111,11 @@ def authenticate_ldap_user(user=None, base_dn=settings.base_dn, password=settings.get_password(raise_exception=False), ssl_tls_mode=settings.ssl_tls_mode, - trusted_cert=settings.require_trusted_certificate) + trusted_cert=settings.require_trusted_certificate, + private_key_file=settings.local_private_key_file, + server_cert_file=settings.local_server_certificate_file, + ca_certs_file=settings.local_ca_certs_file + ) filter = settings.ldap_search_string.format(user) conn.search(search_base=settings.organizational_unit, @@ -116,7 +133,11 @@ def authenticate_ldap_user(user=None, base_dn=user.entry_dn, password=frappe.as_unicode(password), ssl_tls_mode=settings.ssl_tls_mode, - trusted_cert=settings.require_trusted_certificate) + trusted_cert=settings.require_trusted_certificate, + private_key_file=settings.local_private_key_file, + server_cert_file=settings.local_server_certificate_file, + ca_certs_file=settings.local_ca_certs_file + ) return create_user(params) else: frappe.throw(_("Not a valid LDAP user")) From c4ea153d4fed218725e49ae4bb537dfa1e52b1ff Mon Sep 17 00:00:00 2001 From: cameron Date: Tue, 30 Apr 2019 09:53:44 +0800 Subject: [PATCH 03/23] remove ldap3 import, cleanup syntax --- .../integrations/doctype/ldap_settings/ldap_settings.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index 1d258d4bd5..c9799c9867 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -105,8 +105,6 @@ def authenticate_ldap_user(user=None, params = {} settings = frappe.get_doc("LDAP Settings") if settings and settings.enabled: - import ldap3 - conn = connect_to_ldap(server_url=settings.ldap_server_url, base_dn=settings.base_dn, password=settings.get_password(raise_exception=False), @@ -114,12 +112,11 @@ def authenticate_ldap_user(user=None, trusted_cert=settings.require_trusted_certificate, private_key_file=settings.local_private_key_file, server_cert_file=settings.local_server_certificate_file, - ca_certs_file=settings.local_ca_certs_file - ) + ca_certs_file=settings.local_ca_certs_file) - filter = settings.ldap_search_string.format(user) + user_filter = settings.ldap_search_string.format(user) conn.search(search_base=settings.organizational_unit, - search_filter="({0})".format(filter), + search_filter="({0})".format(user_filter), attributes=[settings.ldap_email_field, settings.ldap_username_field, settings.ldap_first_name_field]) From 42045552ac6ab5fd14edc95594f826c10a51c754 Mon Sep 17 00:00:00 2001 From: cameron Date: Fri, 3 May 2019 23:28:23 +0800 Subject: [PATCH 04/23] convert spaces to tabs --- .../doctype/ldap_settings/ldap_settings.py | 232 +++++++++--------- 1 file changed, 116 insertions(+), 116 deletions(-) diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index c9799c9867..0a4d871be8 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -9,33 +9,33 @@ from frappe.model.document import Document class LDAPSettings(Document): - def validate(self): - if not self.flags.ignore_mandatory: - if self.ldap_search_string.endswith("={0}"): - if self.enabled: - connect_to_ldap(server_url=self.ldap_server_url, - base_dn=self.base_dn, - password=self.get_password(raise_exception=False), - ssl_tls_mode=self.ssl_tls_mode, - trusted_cert=self.require_trusted_certificate, - private_key_file=self.local_private_key_file, - server_cert_file=self.local_server_certificate_file, - ca_certs_file=self.local_ca_certs_file) - else: - frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}")) + def validate(self): + if not self.flags.ignore_mandatory: + if self.ldap_search_string.endswith("={0}"): + if self.enabled: + connect_to_ldap(server_url=self.ldap_server_url, + base_dn=self.base_dn, + password=self.get_password(raise_exception=False), + ssl_tls_mode=self.ssl_tls_mode, + trusted_cert=self.require_trusted_certificate, + private_key_file=self.local_private_key_file, + server_cert_file=self.local_server_certificate_file, + ca_certs_file=self.local_ca_certs_file) + else: + frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}")) def get_ldap_client_settings(): - # return the settings to be used on the client side. - result = { - "enabled": False - } - settings = frappe.get_doc("LDAP Settings") + #return the settings to be used on the client side. + result = { + "enabled": False + } + settings = frappe.get_doc("LDAP Settings") - if settings and settings.enabled: - result["enabled"] = True - result["method"] = "frappe.integrations.doctype.ldap_settings.ldap_settings.login" - return result + if settings and settings.enabled: + result["enabled"] = True + result["method"] = "frappe.integrations.doctype.ldap_settings.ldap_settings.login" + return result def connect_to_ldap(server_url, @@ -46,121 +46,121 @@ def connect_to_ldap(server_url, private_key_file, server_cert_file, ca_certs_file): - try: - import ldap3 - import ssl + try: + import ldap3 + import ssl - if trusted_cert == 'Yes': - tls_configuration = ldap3.Tls(validate=ssl.CERT_REQUIRED, - version=ssl.PROTOCOL_TLSv1) - else: - tls_configuration = ldap3.Tls(validate=ssl.CERT_NONE, - version=ssl.PROTOCOL_TLSv1) + if trusted_cert == 'Yes': + tls_configuration = ldap3.Tls(validate=ssl.CERT_REQUIRED, + version=ssl.PROTOCOL_TLSv1) + else: + tls_configuration = ldap3.Tls(validate=ssl.CERT_NONE, + version=ssl.PROTOCOL_TLSv1) - if private_key_file: - tls_configuration.private_key_file = private_key_file - if server_cert_file: - tls_configuration.certificate_file = server_cert_file - if ca_certs_file: - tls_configuration.ca_certs_file = ca_certs_file + if private_key_file: + tls_configuration.private_key_file = private_key_file + if server_cert_file: + tls_configuration.certificate_file = server_cert_file + if ca_certs_file: + tls_configuration.ca_certs_file = ca_certs_file - server = ldap3.Server(host=server_url, - tls=tls_configuration) - bind_type = ldap3.AUTO_BIND_TLS_BEFORE_BIND if ssl_tls_mode == "StartTLS" else True + server = ldap3.Server(host=server_url, + tls=tls_configuration) + bind_type = ldap3.AUTO_BIND_TLS_BEFORE_BIND if ssl_tls_mode == "StartTLS" else True - conn = ldap3.Connection(server=server, - user=base_dn, - password=password, - auto_bind=bind_type, - read_only=True, - raise_exceptions=True) + conn = ldap3.Connection(server=server, + user=base_dn, + password=password, + auto_bind=bind_type, + read_only=True, + raise_exceptions=True) - return conn + return conn - except ImportError: - msg = _("Please Install the ldap3 library via pip to use ldap functionality.") - frappe.throw(msg, title=_("LDAP Not Installed")) - except ldap3.core.exceptions.LDAPInvalidCredentialsResult: - frappe.throw(_("Invalid Credentials")) - except Exception as ex: - frappe.throw(_(str(ex))) + except ImportError: + msg = _("Please Install the ldap3 library via pip to use ldap functionality.") + frappe.throw(msg, title=_("LDAP Not Installed")) + except ldap3.core.exceptions.LDAPInvalidCredentialsResult: + frappe.throw(_("Invalid Credentials")) + except Exception as ex: + frappe.throw(_(str(ex))) @frappe.whitelist(allow_guest=True) def login(): - # LDAP LOGIN LOGIC - args = frappe.form_dict - user = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd)) + # LDAP LOGIN LOGIC + args = frappe.form_dict + user = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd)) - frappe.local.login_manager.user = user.name - frappe.local.login_manager.post_login() + frappe.local.login_manager.user = user.name + frappe.local.login_manager.post_login() - # because of a GET request! - frappe.db.commit() + # because of a GET request! + frappe.db.commit() def authenticate_ldap_user(user=None, password=None): - params = {} - settings = frappe.get_doc("LDAP Settings") - if settings and settings.enabled: - conn = connect_to_ldap(server_url=settings.ldap_server_url, - base_dn=settings.base_dn, - password=settings.get_password(raise_exception=False), - ssl_tls_mode=settings.ssl_tls_mode, - trusted_cert=settings.require_trusted_certificate, - private_key_file=settings.local_private_key_file, - server_cert_file=settings.local_server_certificate_file, - ca_certs_file=settings.local_ca_certs_file) + params = {} + settings = frappe.get_doc("LDAP Settings") + if settings and settings.enabled: + conn = connect_to_ldap(server_url=settings.ldap_server_url, + base_dn=settings.base_dn, + password=settings.get_password(raise_exception=False), + ssl_tls_mode=settings.ssl_tls_mode, + trusted_cert=settings.require_trusted_certificate, + private_key_file=settings.local_private_key_file, + server_cert_file=settings.local_server_certificate_file, + ca_certs_file=settings.local_ca_certs_file) - user_filter = settings.ldap_search_string.format(user) - conn.search(search_base=settings.organizational_unit, - search_filter="({0})".format(user_filter), - attributes=[settings.ldap_email_field, - settings.ldap_username_field, - settings.ldap_first_name_field]) + user_filter = settings.ldap_search_string.format(user) + conn.search(search_base=settings.organizational_unit, + search_filter="({0})".format(user_filter), + attributes=[settings.ldap_email_field, + settings.ldap_username_field, + settings.ldap_first_name_field]) - if len(conn.entries) > 0 and conn.entries[0]: - user = conn.entries[0] - params["email"] = str(user[settings.ldap_email_field]) - params["username"] = str(user[settings.ldap_username_field]) - params["first_name"] = str(user[settings.ldap_first_name_field]) - connect_to_ldap(server_url=settings.ldap_server_url, - base_dn=user.entry_dn, - password=frappe.as_unicode(password), - ssl_tls_mode=settings.ssl_tls_mode, - trusted_cert=settings.require_trusted_certificate, - private_key_file=settings.local_private_key_file, - server_cert_file=settings.local_server_certificate_file, - ca_certs_file=settings.local_ca_certs_file - ) - return create_user(params) - else: - frappe.throw(_("Not a valid LDAP user")) - else: - frappe.throw(_("LDAP is not enabled.")) + if len(conn.entries) > 0 and conn.entries[0]: + user = conn.entries[0] + params["email"] = str(user[settings.ldap_email_field]) + params["username"] = str(user[settings.ldap_username_field]) + params["first_name"] = str(user[settings.ldap_first_name_field]) + connect_to_ldap(server_url=settings.ldap_server_url, + base_dn=user.entry_dn, + password=frappe.as_unicode(password), + ssl_tls_mode=settings.ssl_tls_mode, + trusted_cert=settings.require_trusted_certificate, + private_key_file=settings.local_private_key_file, + server_cert_file=settings.local_server_certificate_file, + ca_certs_file=settings.local_ca_certs_file + ) + return create_user(params) + else: + frappe.throw(_("Not a valid LDAP user")) + else: + frappe.throw(_("LDAP is not enabled.")) def create_user(params): - if frappe.db.exists("User", params["email"]): - user = frappe.get_doc("User", params["email"]) - user.first_name = params["first_name"] - user.username = params["username"] - user.save(ignore_permissions=True) - return user + if frappe.db.exists("User", params["email"]): + user = frappe.get_doc("User", params["email"]) + user.first_name = params["first_name"] + user.username = params["username"] + user.save(ignore_permissions=True) + return user - else: - params.update({ - "doctype": "User", - "send_welcome_email": 0, - "language": "", - "user_type": "System User", - "roles": [{ - "role": _("Blogger") - }] - }) + else: + params.update({ + "doctype": "User", + "send_welcome_email": 0, + "language": "", + "user_type": "System User", + "roles": [{ + "role": _("Blogger") + }] + }) - user = frappe.get_doc(params).insert(ignore_permissions=True) + user = frappe.get_doc(params).insert(ignore_permissions=True) - return user + return user From 8aa0b5103d376938eee7924082307711ad9e17f3 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 6 May 2019 20:00:11 +0530 Subject: [PATCH 05/23] fix: link_title not getting set in address and contact --- frappe/contacts/doctype/address/address.py | 13 +++++++++++++ frappe/contacts/doctype/contact/contact.py | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index bfd63d2f80..c5de5706a8 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -39,6 +39,7 @@ class Address(Document): def validate(self): self.link_address() self.validate_reference() + self.set_link_title() deduplicate_dynamic_links(self) def link_address(self): @@ -53,6 +54,18 @@ class Address(Document): return False + def set_link_title(self): + if not self.links: + return + else: + for address in self.links: + if not address.link_title: + linked_doc = frappe.get_doc(address.link_doctype, address.link_name) + try: + address.link_title = linked_doc.title_field + except AttributeError: + address.link_title = linked_doc.name + def validate_reference(self): if self.is_your_company_address: if not [row for row in self.links if row.link_doctype == "Company"]: diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index e1117be4d7..82e2258bd5 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -31,6 +31,7 @@ class Contact(Document): if self.email_id: self.email_id = self.email_id.strip() self.set_user() + self.set_link_title() if self.email_id and not self.image: self.image = has_gravatar(self.email_id) @@ -40,6 +41,18 @@ class Contact(Document): if not self.user and self.email_id: self.user = frappe.db.get_value("User", {"email": self.email_id}) + def set_link_title(self): + if not self.links: + return + else: + for contact in self.links: + if not contact.link_title: + linked_doc = frappe.get_doc(contact.link_doctype, contact.link_name) + try: + contact.link_title = linked_doc.title_field + except AttributeError: + contact.link_title = linked_doc.name + def get_link_for(self, link_doctype): '''Return the link name, if exists for the given link DocType''' for link in self.links: From a9bf40b7dbd8e6ce606c5e37f947c693b8a6d6ef Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 7 May 2019 15:24:59 +0530 Subject: [PATCH 06/23] chore: code improvements --- frappe/contacts/doctype/address/address.py | 12 ++++-------- frappe/contacts/doctype/contact/contact.py | 12 ++++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index c5de5706a8..c09f6a0c59 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -57,14 +57,10 @@ class Address(Document): def set_link_title(self): if not self.links: return - else: - for address in self.links: - if not address.link_title: - linked_doc = frappe.get_doc(address.link_doctype, address.link_name) - try: - address.link_title = linked_doc.title_field - except AttributeError: - address.link_title = linked_doc.name + for address in self.links: + if not address.link_title: + linked_doc = frappe.get_doc(address.link_doctype, address.link_name) + address.link_title = linked_doc.get("title_field") or linked_doc.get("name") def validate_reference(self): if self.is_your_company_address: diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 82e2258bd5..3cd98c57e5 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -44,14 +44,10 @@ class Contact(Document): def set_link_title(self): if not self.links: return - else: - for contact in self.links: - if not contact.link_title: - linked_doc = frappe.get_doc(contact.link_doctype, contact.link_name) - try: - contact.link_title = linked_doc.title_field - except AttributeError: - contact.link_title = linked_doc.name + for contact in self.links: + if not contact.link_title: + linked_doc = frappe.get_doc(contact.link_doctype, contact.link_name) + contact.link_title = linked_doc.get("title_field") or linked_doc.get("name") def get_link_for(self, link_doctype): '''Return the link name, if exists for the given link DocType''' From 255dd6a89a7aba0929a039e38847a3449b172efd Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 7 May 2019 16:41:03 +0530 Subject: [PATCH 07/23] style: indendation fix --- frappe/contacts/doctype/address/address.py | 2 +- frappe/contacts/doctype/contact/contact.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index c09f6a0c59..fb2b9909fc 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -60,7 +60,7 @@ class Address(Document): for address in self.links: if not address.link_title: linked_doc = frappe.get_doc(address.link_doctype, address.link_name) - address.link_title = linked_doc.get("title_field") or linked_doc.get("name") + address.link_title = linked_doc.get("title_field") or linked_doc.get("name") def validate_reference(self): if self.is_your_company_address: diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 3cd98c57e5..5c5581b294 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -47,7 +47,7 @@ class Contact(Document): for contact in self.links: if not contact.link_title: linked_doc = frappe.get_doc(contact.link_doctype, contact.link_name) - contact.link_title = linked_doc.get("title_field") or linked_doc.get("name") + contact.link_title = linked_doc.get("title_field") or linked_doc.get("name") def get_link_for(self, link_doctype): '''Return the link name, if exists for the given link DocType''' From d07d3605b76b9d737f9ec211535f4f6751605998 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 3 May 2019 18:17:04 +0530 Subject: [PATCH 08/23] fix: naming and provision to use same db credentails for secondary --- frappe/__init__.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 1e84785bc0..3960351016 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -187,14 +187,19 @@ def connect(site=None, db_name=None): local.db = Database(user=db_name or local.conf.db_name) set_user("Administrator") -def connect_read_only(): - from frappe.database import Database +def connect_secondary(): + from frappe.database import get_db + user = local.conf.db_name + password = local.conf.db_password - local.read_only_db = Database(local.conf.slave_host, local.conf.slave_db_name, - local.conf.slave_db_password) + if local.conf.different_credentials_for_secondary: + user = local.conf.secondary_db_name + password = local.conf.secondary_db_password + + local.read_only_db = get_db(host=local.conf.secondary_host, user=user, password=password) # swap db connections - local.master_db = local.db + local.primary_db = local.db local.db = local.read_only_db def get_site_config(sites_path=None, site_path=None): @@ -495,16 +500,17 @@ def whitelist(allow_guest=False, xss_safe=False): def read_only(): def innfn(fn): def wrapper_fn(*args, **kwargs): - if conf.use_slave_for_read_only: - connect_read_only() + if conf.read_from_secondary: + connect_secondary() + try: retval = fn(*args, **get_newargs(fn, kwargs)) except: raise finally: - if local and hasattr(local, 'master_db'): + if local and hasattr(local, 'primary_db'): local.db.close() - local.db = local.master_db + local.db = local.primary_db return retval return wrapper_fn From 8586b885152d91d8d04117763fe68fce54acf93c Mon Sep 17 00:00:00 2001 From: Saurabh Date: Thu, 9 May 2019 20:31:27 +0530 Subject: [PATCH 09/23] fix: v11 compatible changes --- frappe/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 3960351016..e0f4e6ded8 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -188,7 +188,7 @@ def connect(site=None, db_name=None): set_user("Administrator") def connect_secondary(): - from frappe.database import get_db + from frappe.database import Database user = local.conf.db_name password = local.conf.db_password @@ -196,7 +196,7 @@ def connect_secondary(): user = local.conf.secondary_db_name password = local.conf.secondary_db_password - local.read_only_db = get_db(host=local.conf.secondary_host, user=user, password=password) + local.read_only_db = Database(host=local.conf.secondary_host, user=user, password=password) # swap db connections local.primary_db = local.db From 636efb70466a60ffc84d83e2f6bc7056f7843e9e Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 10 May 2019 10:48:45 +0530 Subject: [PATCH 10/23] fix: naming --- frappe/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index e0f4e6ded8..7e96653f64 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -187,20 +187,20 @@ def connect(site=None, db_name=None): local.db = Database(user=db_name or local.conf.db_name) set_user("Administrator") -def connect_secondary(): +def connect_replica(): from frappe.database import Database user = local.conf.db_name password = local.conf.db_password - if local.conf.different_credentials_for_secondary: - user = local.conf.secondary_db_name - password = local.conf.secondary_db_password + if local.conf.different_credentials_for_replica: + user = local.conf.replica_db_name + password = local.conf.replica_db_password - local.read_only_db = Database(host=local.conf.secondary_host, user=user, password=password) + local.replica_db = Database(host=local.conf.replica_host, user=user, password=password) # swap db connections local.primary_db = local.db - local.db = local.read_only_db + local.db = local.replica_db def get_site_config(sites_path=None, site_path=None): """Returns `site_config.json` combined with `sites/common_site_config.json`. @@ -500,8 +500,8 @@ def whitelist(allow_guest=False, xss_safe=False): def read_only(): def innfn(fn): def wrapper_fn(*args, **kwargs): - if conf.read_from_secondary: - connect_secondary() + if conf.read_from_replica: + connect_replica() try: retval = fn(*args, **get_newargs(fn, kwargs)) From de3560f1b21f3b56c490c8862627ce9886282f27 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 14 May 2019 14:00:06 +0530 Subject: [PATCH 11/23] fix: auto repeat showing next schedule date wrong for backdated entries --- frappe/desk/doctype/auto_repeat/auto_repeat.py | 10 ++++++++-- .../desk/doctype/auto_repeat/auto_repeat_schedule.html | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat.py b/frappe/desk/doctype/auto_repeat/auto_repeat.py index c2d04e4fce..a3b3c014b2 100644 --- a/frappe/desk/doctype/auto_repeat/auto_repeat.py +++ b/frappe/desk/doctype/auto_repeat/auto_repeat.py @@ -34,9 +34,14 @@ class AutoRepeat(Document): validate_template(self.message or "") def before_submit(self): + start_date_copy = self.start_date + + if start_date_copy < today(): + start_date_copy = today() + if not self.next_schedule_date: self.next_schedule_date = get_next_schedule_date( - self.start_date, self.frequency, self.repeat_on_day) + start_date_copy, self.frequency, self.repeat_on_day) def on_submit(self): self.update_auto_repeat_id() @@ -116,14 +121,15 @@ class AutoRepeat(Document): days = 60 if self.frequency in ['Daily', 'Weekly'] else 365 end_date_copy = add_days(today_copy, days) + start_date_copy = get_next_schedule_date(start_date_copy, self.frequency, self.repeat_on_day) while (getdate(start_date_copy) < getdate(end_date_copy)): - start_date_copy = get_next_schedule_date(start_date_copy, self.frequency, self.repeat_on_day) row = { "reference_document" : self.reference_document, "frequency" : self.frequency, "next_scheduled_date" : start_date_copy } schedule_details.append(row) + start_date_copy = get_next_schedule_date(start_date_copy, self.frequency, self.repeat_on_day) return schedule_details diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html b/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html index 7e579821c5..562a527797 100644 --- a/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html +++ b/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html @@ -12,7 +12,7 @@ {{ schedule_details[i].reference_document }} {{ schedule_details[i].frequency }} - {{ schedule_details[i].next_scheduled_date }} + {{ frappe.format_date(schedule_details[i].next_scheduled_date) }} {% } %} From 7aa258e6b8d387ec5a4dc7bc88208cf1e4051d46 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 14 May 2019 16:13:22 +0530 Subject: [PATCH 12/23] style: moved set_link_title to address_and_contact --- frappe/contacts/address_and_contact.py | 8 ++++++++ frappe/contacts/doctype/address/address.py | 11 ++--------- frappe/contacts/doctype/contact/contact.py | 11 ++--------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/frappe/contacts/address_and_contact.py b/frappe/contacts/address_and_contact.py index 38d292e9b4..6523b0b1e5 100644 --- a/frappe/contacts/address_and_contact.py +++ b/frappe/contacts/address_and_contact.py @@ -152,3 +152,11 @@ def filter_dynamic_link_doctypes(doctype, txt, searchfield, start, page_len, fil valid_doctypes = [[doctype] for doctype in valid_doctypes] return valid_doctypes + +def set_link_title(doc): + if not doc.links: + return + for link in doc.links: + if not link.link_title: + linked_doc = frappe.get_doc(link.link_doctype, link.link_name) + link.link_title = linked_doc.get("title_field") or linked_doc.get("name") diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index fb2b9909fc..0d8eb661f3 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -15,6 +15,7 @@ from frappe.model.naming import make_autoname from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links from six import iteritems, string_types from past.builtins import cmp +from frappe.contacts.address_and_contact import set_link_title import functools @@ -39,7 +40,7 @@ class Address(Document): def validate(self): self.link_address() self.validate_reference() - self.set_link_title() + set_link_title(self) deduplicate_dynamic_links(self) def link_address(self): @@ -54,14 +55,6 @@ class Address(Document): return False - def set_link_title(self): - if not self.links: - return - for address in self.links: - if not address.link_title: - linked_doc = frappe.get_doc(address.link_doctype, address.link_name) - address.link_title = linked_doc.get("title_field") or linked_doc.get("name") - def validate_reference(self): if self.is_your_company_address: if not [row for row in self.links if row.link_doctype == "Company"]: diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 5c5581b294..8c1d0b4ac4 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -10,6 +10,7 @@ from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_li from six import iteritems from past.builtins import cmp from frappe.model.naming import append_number_if_name_exists +from frappe.contacts.address_and_contact import set_link_title import functools @@ -31,7 +32,7 @@ class Contact(Document): if self.email_id: self.email_id = self.email_id.strip() self.set_user() - self.set_link_title() + set_link_title(self) if self.email_id and not self.image: self.image = has_gravatar(self.email_id) @@ -41,14 +42,6 @@ class Contact(Document): if not self.user and self.email_id: self.user = frappe.db.get_value("User", {"email": self.email_id}) - def set_link_title(self): - if not self.links: - return - for contact in self.links: - if not contact.link_title: - linked_doc = frappe.get_doc(contact.link_doctype, contact.link_name) - contact.link_title = linked_doc.get("title_field") or linked_doc.get("name") - def get_link_for(self, link_doctype): '''Return the link name, if exists for the given link DocType''' for link in self.links: From e2a945efaf68e2b55d9cd9b38fc3c2f56995c46d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 14 May 2019 17:32:57 +0530 Subject: [PATCH 13/23] fix: if more than 20 records are available the system removing last row --- frappe/public/js/frappe/form/multi_select_dialog.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index 609cd8be80..1c04ab0dd1 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -208,7 +208,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ txt: me.dialog.fields_dict["search_term"].get_value(), filters: filters, filter_fields: Object.keys(me.setters).concat([me.date_field]), - page_length: this.page_length + 1, + page_length: this.page_length, query: this.get_query ? this.get_query().query : '', as_dict: 1 } @@ -220,10 +220,6 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ callback: function(r) { let results = [], more = 0; if(r.values.length) { - if(r.values.length > me.page_length){ - r.values.pop(); - more = 1; - } r.values.forEach(function(result) { if(me.date_field in result) { result["Date"] = result[me.date_field] From 84b926801972eac4ba1c597d17ce8815989dd21c Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Tue, 14 May 2019 18:50:51 +0530 Subject: [PATCH 14/23] fix(patch): reload dynamic link doctype --- frappe/patches/v11_0/create_contact_for_user.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/patches/v11_0/create_contact_for_user.py b/frappe/patches/v11_0/create_contact_for_user.py index d5e9c87e8b..c91caf9189 100644 --- a/frappe/patches/v11_0/create_contact_for_user.py +++ b/frappe/patches/v11_0/create_contact_for_user.py @@ -6,6 +6,7 @@ import re def execute(): """ Create Contact for each User if not present """ frappe.reload_doc('contacts', 'doctype', 'contact') + frappe.reload_doc('core', 'doctype', 'dynamic_link') users = frappe.get_all('User', filters={"name": ('not in', 'Administrator, Guest')}, fields=["*"]) for user in users: From 65fca28d87d700b48c4ae464cb7c97c031ed2d2b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 15 May 2019 11:39:31 +0530 Subject: [PATCH 15/23] fix: Add currency code for Surinamese dollar --- frappe/geo/country_info.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/geo/country_info.json b/frappe/geo/country_info.json index a3f039ca17..9656d62b5d 100644 --- a/frappe/geo/country_info.json +++ b/frappe/geo/country_info.json @@ -2329,6 +2329,7 @@ }, "Suriname": { "code": "sr", + "currency": "SRD", "currency_fraction": "Cent", "currency_fraction_units": 100, "currency_symbol": "$", From e51025cc58082e45d24b09e48dd56deec5eb57ca Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 14 May 2019 21:02:26 +0530 Subject: [PATCH 16/23] feat: added more button in the multi select modal --- .../js/frappe/form/multi_select_dialog.js | 81 ++++++++++--------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index 1c04ab0dd1..3438c59a1f 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -19,6 +19,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ let me = this; this.page_length = 20; + this.start = 0; let fields = [ { @@ -55,7 +56,12 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ }, { fieldtype: "Section Break" }, { fieldtype: "HTML", fieldname: "results_area" }, - { fieldtype: "Button", fieldname: "make_new", label: __("Make a new " + me.doctype) } + { fieldtype: "Button", fieldname: "more_btn", label: __("More"), + click: function(){ + me.start += 20; + me.get_results(); + } + } ]); let doctype_plural = !this.doctype.endsWith('y') ? this.doctype + 's' @@ -65,25 +71,30 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ title: __("Select {0}", [(this.doctype=='[Select]') ? __("value") : __(doctype_plural)]), fields: fields, primary_action_label: __("Get Items"), + secondary_action_label: __("Make {0}", [me.doctype]), primary_action: function() { me.action(me.get_checked_values(), me.args); + }, + secondary_action: function(e) { + // If user wants to close the modal + if (e) { + frappe.route_options = {}; + + Object.keys(me.setters).forEach(function(setter) { + frappe.route_options[setter] = me.dialog.fields_dict[setter].get_value() || undefined; + }); + + frappe.new_doc(me.doctype, true); + } } }); this.$parent = $(this.dialog.body); this.$wrapper = this.dialog.fields_dict.results_area.$wrapper.append(`
`); - this.$results = this.$wrapper.find('.results'); - this.$make_new_btn = this.dialog.fields_dict.make_new.$wrapper; - this.$placeholder = $(`
- - -

No ${this.doctype} found

- -
-
`); + this.$results = this.$wrapper.find('.results'); + this.$results.append(this.make_list_row()); this.args = {}; @@ -94,6 +105,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ bind_events: function() { let me = this; + this.$results.on('click', '.list-item-container', function (e) { if (!$(e.target).is(':checkbox') && !$(e.target).is('a')) { $(this).find(':checkbox').trigger('click'); @@ -119,14 +131,6 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ me.get_results(); }, 300)); }); - - this.$parent.on('click', '.btn[data-fieldname="make_new"]', (e) => { - frappe.route_options = {}; - Object.keys(this.setters).forEach(function(setter) { - frappe.route_options[setter] = me.dialog.fields_dict[setter].get_value() || undefined; - }); - frappe.new_doc(this.doctype, true); - }); }, get_checked_values: function() { @@ -170,23 +174,21 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ render_result_list: function(results, more = 0) { var me = this; - this.$results.empty(); - if(results.length === 0) { - this.$make_new_btn.addClass('hide'); - this.$results.append(me.$placeholder); - return; - } - this.$make_new_btn.removeClass('hide'); - this.$results.append(this.make_list_row()); + var more_btn = me.dialog.fields_dict.more_btn.$wrapper; + if(results.length === 0) { + this.$results.empty(); + more_btn.hide(); + return; + } else { + more_btn.show(); + } + results.forEach((result) => { me.$results.append(me.make_list_row(result)); - }) - if (more) { - let message = __("Only {0} entries shown. Please filter for more specific results.", [this.page_length]); - me.$results.append($(`
${message}
`)); - } + }); + + this.$results.animate({scrollTop: me.$results.prop('scrollHeight')}, 500); }, get_results: function() { @@ -208,7 +210,8 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ txt: me.dialog.fields_dict["search_term"].get_value(), filters: filters, filter_fields: Object.keys(me.setters).concat([me.date_field]), - page_length: this.page_length, + start: this.start, + page_length: this.page_length + 1, query: this.get_query ? this.get_query().query : '', as_dict: 1 } @@ -219,7 +222,11 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ args: args, callback: function(r) { let results = [], more = 0; - if(r.values.length) { + if (r.values.length) { + if (r.values.length > me.page_length) { + r.values.pop(); + more = 1; + } r.values.forEach(function(result) { if(me.date_field in result) { result["Date"] = result[me.date_field] @@ -237,7 +244,9 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ }); // Preselect oldest entry - results[0].checked = 1 + if (me.start < 1) { + results[0].checked = 1; + } } me.render_result_list(results, more); } From c7c1d88adc7f8991a3ac2a24981db3687d1f005c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 14 May 2019 14:34:14 +0530 Subject: [PATCH 17/23] fixed test cases --- frappe/desk/doctype/auto_repeat/auto_repeat.py | 5 +++-- frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html | 2 +- frappe/desk/doctype/auto_repeat/test_auto_repeat.py | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat.py b/frappe/desk/doctype/auto_repeat/auto_repeat.py index a3b3c014b2..8a90b011d8 100644 --- a/frappe/desk/doctype/auto_repeat/auto_repeat.py +++ b/frappe/desk/doctype/auto_repeat/auto_repeat.py @@ -35,9 +35,10 @@ class AutoRepeat(Document): def before_submit(self): start_date_copy = self.start_date + today_copy = add_days(today(), -1) - if start_date_copy < today(): - start_date_copy = today() + if start_date_copy <= today_copy: + start_date_copy = today_copy if not self.next_schedule_date: self.next_schedule_date = get_next_schedule_date( diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html b/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html index 562a527797..7e579821c5 100644 --- a/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html +++ b/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html @@ -12,7 +12,7 @@ {{ schedule_details[i].reference_document }} {{ schedule_details[i].frequency }} - {{ frappe.format_date(schedule_details[i].next_scheduled_date) }} + {{ schedule_details[i].next_scheduled_date }} {% } %} diff --git a/frappe/desk/doctype/auto_repeat/test_auto_repeat.py b/frappe/desk/doctype/auto_repeat/test_auto_repeat.py index 19cf039759..cf8ff610d5 100644 --- a/frappe/desk/doctype/auto_repeat/test_auto_repeat.py +++ b/frappe/desk/doctype/auto_repeat/test_auto_repeat.py @@ -8,7 +8,7 @@ import unittest import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.desk.doctype.auto_repeat.auto_repeat import get_auto_repeat_entries, create_repeated_entries, disable_auto_repeat -from frappe.utils import today, add_days, getdate +from frappe.utils import today, add_days, getdate, add_months def add_custom_fields(): @@ -44,8 +44,8 @@ class TestAutoRepeat(unittest.TestCase): self.assertEqual(todo.get('description'), new_todo.get('description')) def test_monthly_auto_repeat(self): - start_date = '2018-01-01' - end_date = '2018-12-31' + start_date = today() + end_date = add_months(start_date, 12) todo = frappe.get_doc( dict(doctype='ToDo', description='test recurring todo', assigned_by='Administrator')).insert() @@ -103,7 +103,7 @@ def make_auto_repeat(**args): 'reference_document': args.reference_document or frappe.db.get_value('ToDo', {'docstatus': 1}, 'name'), 'frequency': args.frequency or 'Daily', 'start_date': args.start_date or add_days(today(), -1), - 'end_date': args.end_date or add_days(today(), 1), + 'end_date': args.end_date or add_days(today(), 2), 'submit_on_creation': args.submit_on_creation or 0, 'notify_by_email': args.notify or 0, 'recipients': args.recipients or "", From 73964e7a615d561f9a187e50bcaa3c9c716456ee Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 15 May 2019 13:42:42 +0530 Subject: [PATCH 18/23] fix: permission fixes for prepared report --- frappe/core/doctype/prepared_report/prepared_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index cc087ae784..e83a73d1ec 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -51,14 +51,14 @@ def run_background(prepared_report): instance.status = "Completed" instance.columns = json.dumps(result["columns"]) instance.report_end_time = frappe.utils.now() - instance.save() + instance.save(ignore_permissions=True) except Exception: frappe.log_error(frappe.get_traceback()) instance = frappe.get_doc("Prepared Report", prepared_report) instance.status = "Error" instance.error_message = frappe.get_traceback() - instance.save() + instance.save(ignore_permissions=True) frappe.publish_realtime( 'report_generated', From f59c4eb554a23ba345c1397293313da4a6ba767b Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 15 May 2019 15:41:46 +0530 Subject: [PATCH 19/23] fix: Don't show no value type fields in options for custom columns (#7496) * fix: Don't show no value type fields in options for custom columns * fix: Use filter instead of pushing in list --- frappe/public/js/frappe/views/reports/query_report.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index edebb11a43..938fe915c9 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -999,9 +999,11 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { change: () => { let doctype = d.get_value('doctype'); frappe.model.with_doctype(doctype, () => { - let fields = frappe.meta.get_docfields(doctype) + let options = frappe.meta.get_docfields(doctype) + .filter(frappe.model.is_value_type) .map(df => ({ label: df.label, value: df.fieldname })); - d.set_df_property('field', 'options', fields); + + d.set_df_property('field', 'options', options); }); } From 2d62cd60ee71ca92cb91ec788186186d51214b75 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 16 May 2019 17:37:06 +0530 Subject: [PATCH 20/23] fix: Report Print format for indented rows (#7506) --- .../js/frappe/views/reports/print_grid.html | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/print_grid.html b/frappe/public/js/frappe/views/reports/print_grid.html index 12854e74f3..e99600c2e6 100644 --- a/frappe/public/js/frappe/views/reports/print_grid.html +++ b/frappe/public/js/frappe/views/reports/print_grid.html @@ -31,15 +31,17 @@ {% var value = col.fieldname ? row[col.fieldname] : row[col.id]; %} - {{ - col.formatter - ? col.formatter(row._index, col._index, value, col, row, true) - : col.format - ? col.format(value, row, col, data) - : col.docfield - ? frappe.format(value, col.docfield) - : value - }} + + {{ + col.formatter + ? col.formatter(row._index, col._index, value, col, row, true) + : col.format + ? col.format(value, row, col, data) + : col.docfield + ? frappe.format(value, col.docfield) + : value + }} + {% endif %} {% endfor %} From 183564f3af2bc4b070bf86fcfd9045bb89c845ca Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 19 May 2019 17:44:46 +0530 Subject: [PATCH 21/23] fix: Module view fix for custom reports --- frappe/desk/moduleview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index 0b4f1eb853..f697211ac3 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -253,7 +253,7 @@ def get_report_list(module, is_standard="No"): out.append({ "type": "report", "doctype": r.ref_doctype, - "is_query_report": 1 if r.report_type in ("Query Report", "Script Report") else 0, + "is_query_report": 1 if r.report_type in ("Query Report", "Script Report", "Custom Report") else 0, "label": _(r.name), "name": r.name }) From 90bc741c982d8ddd4242d83cc574fc26bb288965 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 20 May 2019 12:19:22 +0530 Subject: [PATCH 22/23] fix: Create new button for dynamic link fields (#7510) * fix: Create new button for dynamic links * fix: Use get_options for fetching options --- frappe/public/js/frappe/form/controls/link.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 86a99c970f..458f3efa6d 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -167,13 +167,12 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ } if(!me.df.only_select) { - if(frappe.model.can_create(doctype) - && me.df.fieldtype !== "Dynamic Link") { + if(frappe.model.can_create(doctype)) { // new item r.results.push({ label: "" + " " - + __("Create a new {0}", [__(me.df.options)]) + + __("Create a new {0}", [__(me.get_options())]) + "", value: "create_new__link_option", action: me.new_doc From e4aedc3e66173bdc79f7e11ab9becb0b8f337171 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Tue, 21 May 2019 14:27:05 +0550 Subject: [PATCH 23/23] bumped to version 11.1.29 --- frappe/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 164e5c6a3a..8624e8df2b 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -23,7 +23,7 @@ if sys.version[0] == '2': reload(sys) sys.setdefaultencoding("utf-8") -__version__ = '11.1.28' +__version__ = '11.1.29' __title__ = "Frappe Framework" local = Local()