@@ -7,22 +7,28 @@ start_pattern = re.compile(r"_{1,2}\([\"']{1,3}") | |||||
# skip first argument | # skip first argument | ||||
files = sys.argv[1:] | files = sys.argv[1:] | ||||
for _file in files: | |||||
if not _file.endswith(('.py', '.js')): | |||||
continue | |||||
files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))] | |||||
for _file in files_to_scan: | |||||
with open(_file, 'r') as f: | with open(_file, 'r') as f: | ||||
print(f'Checking: {_file}') | print(f'Checking: {_file}') | ||||
for num, line in enumerate(f, 1): | |||||
all_matches = start_pattern.finditer(line) | |||||
if all_matches: | |||||
for match in all_matches: | |||||
verify = pattern.search(line) | |||||
if not verify: | |||||
errors_encounter += 1 | |||||
print(f'A syntax error has been discovered at line number: {num}') | |||||
print(f'Syntax error occurred with: {line}') | |||||
file_lines = f.readlines() | |||||
for line_number, line in enumerate(file_lines, 1): | |||||
start_matches = start_pattern.search(line) | |||||
if start_matches: | |||||
match = pattern.search(line) | |||||
if not match and line.endswith(',\n'): | |||||
# concat remaining text to validate multiline pattern | |||||
line = "".join(file_lines[line_number - 1:]) | |||||
line = line[start_matches.start() + 1:] | |||||
match = pattern.match(line) | |||||
if not match: | |||||
errors_encounter += 1 | |||||
print(f'\nTranslation syntax error at line number: {line_number + 1}\n{line.strip()[:100]}') | |||||
if errors_encounter > 0: | if errors_encounter > 0: | ||||
print('You can visit "https://frappeframework.com/docs/user/en/translations" to resolve this error.') | |||||
assert 1+1 == 3 | |||||
print('\nYou can visit "https://frappeframework.com/docs/user/en/translations" to resolve this error.') | |||||
sys.exit(1) | |||||
else: | else: | ||||
print('Good To Go!') | |||||
print('\nGood To Go!') |
@@ -61,10 +61,10 @@ context('Recorder', () => { | |||||
cy.visit('/desk#recorder'); | cy.visit('/desk#recorder'); | ||||
cy.get('.list-row-container span').contains('frappe.desk.reportview.get').click(); | |||||
cy.get('.list-row-container span').contains('/api/method/frappe').click(); | |||||
cy.location('hash').should('contain', '#recorder/request/'); | cy.location('hash').should('contain', '#recorder/request/'); | ||||
cy.get('form').should('contain', 'frappe.desk.reportview.get'); | |||||
cy.get('form').should('contain', '/api/method/frappe'); | |||||
cy.get('#page-recorder .primary-action').should('contain', 'Stop').click(); | cy.get('#page-recorder .primary-action').should('contain', 'Stop').click(); | ||||
cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click(); | cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click(); | ||||
@@ -1110,8 +1110,8 @@ def get_newargs(fn, kwargs): | |||||
if (a in fnargs) or varkw: | if (a in fnargs) or varkw: | ||||
newargs[a] = kwargs.get(a) | newargs[a] = kwargs.get(a) | ||||
if "flags" in newargs: | |||||
del newargs["flags"] | |||||
newargs.pop("ignore_permissions", None) | |||||
newargs.pop("flags", None) | |||||
return newargs | return newargs | ||||
@@ -133,6 +133,7 @@ def reset_perms(context): | |||||
def execute(context, method, args=None, kwargs=None, profile=False): | def execute(context, method, args=None, kwargs=None, profile=False): | ||||
"Execute a function" | "Execute a function" | ||||
for site in context.sites: | for site in context.sites: | ||||
ret = "" | |||||
try: | try: | ||||
frappe.init(site=site) | frappe.init(site=site) | ||||
frappe.connect() | frappe.connect() | ||||
@@ -154,7 +155,10 @@ def execute(context, method, args=None, kwargs=None, profile=False): | |||||
pr = cProfile.Profile() | pr = cProfile.Profile() | ||||
pr.enable() | pr.enable() | ||||
ret = frappe.get_attr(method)(*args, **kwargs) | |||||
try: | |||||
ret = frappe.get_attr(method)(*args, **kwargs) | |||||
except Exception: | |||||
ret = frappe.safe_eval(method + "(*args, **kwargs)", eval_globals=globals(), eval_locals=locals()) | |||||
if profile: | if profile: | ||||
pr.disable() | pr.disable() | ||||
@@ -1,731 +1,184 @@ | |||||
{ | |||||
"allow_copy": 0, | |||||
"allow_guest_to_view": 0, | |||||
{ | |||||
"actions": [], | |||||
"allow_import": 1, | "allow_import": 1, | ||||
"allow_rename": 0, | |||||
"autoname": "", | |||||
"beta": 0, | |||||
"creation": "2017-10-05 11:10:38.780133", | "creation": "2017-10-05 11:10:38.780133", | ||||
"custom": 0, | |||||
"description": "Keep track of all update feeds", | "description": "Keep track of all update feeds", | ||||
"docstatus": 0, | |||||
"doctype": "DocType", | "doctype": "DocType", | ||||
"document_type": "Setup", | "document_type": "Setup", | ||||
"editable_grid": 0, | |||||
"engine": "InnoDB", | "engine": "InnoDB", | ||||
"field_order": [ | |||||
"subject", | |||||
"section_break_8", | |||||
"content", | |||||
"column_break_5", | |||||
"additional_info", | |||||
"communication_date", | |||||
"column_break_7", | |||||
"operation", | |||||
"status", | |||||
"reference_section", | |||||
"reference_doctype", | |||||
"reference_name", | |||||
"reference_owner", | |||||
"column_break_14", | |||||
"timeline_doctype", | |||||
"timeline_name", | |||||
"link_doctype", | |||||
"link_name", | |||||
"user", | |||||
"full_name" | |||||
], | |||||
"fields": [ | "fields": [ | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "subject", | "fieldname": "subject", | ||||
"fieldtype": "Small Text", | "fieldtype": "Small Text", | ||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_global_search": 1, | "in_global_search": 1, | ||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"in_standard_filter": 0, | |||||
"label": "Subject", | "label": "Subject", | ||||
"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 | |||||
"reqd": 1 | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "section_break_8", | "fieldname": "section_break_8", | ||||
"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 | |||||
"fieldtype": "Section Break" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "content", | "fieldname": "content", | ||||
"fieldtype": "Text Editor", | "fieldtype": "Text Editor", | ||||
"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": "Message", | "label": "Message", | ||||
"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, | |||||
"width": "400" | "width": "400" | ||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "column_break_5", | "fieldname": "column_break_5", | ||||
"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_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 1, | "collapsible": 1, | ||||
"columns": 0, | |||||
"fieldname": "additional_info", | "fieldname": "additional_info", | ||||
"fieldtype": "Section Break", | "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": "More Information", | |||||
"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": "More Information" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"default": "Now", | "default": "Now", | ||||
"fieldname": "communication_date", | "fieldname": "communication_date", | ||||
"fieldtype": "Datetime", | "fieldtype": "Datetime", | ||||
"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": "Date", | |||||
"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": "Date" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "column_break_7", | "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_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "operation", | "fieldname": "operation", | ||||
"fieldtype": "Select", | "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": "Operation", | "label": "Operation", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "\nLogin\nLogout", | |||||
"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": "\nLogin\nLogout" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "status", | "fieldname": "status", | ||||
"fieldtype": "Select", | "fieldtype": "Select", | ||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"in_standard_filter": 0, | |||||
"label": "Status", | "label": "Status", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "\nSuccess\nFailed\nLinked\nClosed", | |||||
"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": "\nSuccess\nFailed\nLinked\nClosed" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 1, | "collapsible": 1, | ||||
"columns": 0, | |||||
"fieldname": "reference_section", | "fieldname": "reference_section", | ||||
"fieldtype": "Section Break", | "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": "Reference", | |||||
"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": "Reference" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "reference_doctype", | "fieldname": "reference_doctype", | ||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"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": "Reference Document Type", | "label": "Reference Document Type", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "DocType", | |||||
"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": "DocType" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "reference_name", | "fieldname": "reference_name", | ||||
"fieldtype": "Dynamic Link", | "fieldtype": "Dynamic Link", | ||||
"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": "Reference Name", | "label": "Reference Name", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "reference_doctype", | |||||
"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": "reference_doctype" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fetch_from": "reference_name.owner", | "fetch_from": "reference_name.owner", | ||||
"fieldname": "reference_owner", | "fieldname": "reference_owner", | ||||
"fieldtype": "Read Only", | "fieldtype": "Read Only", | ||||
"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": "Reference Owner", | "label": "Reference Owner", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "", | |||||
"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": 1, | |||||
"set_only_once": 0, | |||||
"translatable": 0, | |||||
"unique": 0 | |||||
"search_index": 1 | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "column_break_14", | "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_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "timeline_doctype", | "fieldname": "timeline_doctype", | ||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"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": "Timeline DocType", | "label": "Timeline DocType", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "DocType", | |||||
"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": "DocType" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "timeline_name", | "fieldname": "timeline_name", | ||||
"fieldtype": "Dynamic Link", | "fieldtype": "Dynamic Link", | ||||
"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": "Timeline Name", | "label": "Timeline Name", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "timeline_doctype", | |||||
"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": "timeline_doctype" | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "link_doctype", | "fieldname": "link_doctype", | ||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"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": "Link DocType", | "label": "Link DocType", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "DocType", | "options": "DocType", | ||||
"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 | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "link_name", | "fieldname": "link_name", | ||||
"fieldtype": "Dynamic Link", | "fieldtype": "Dynamic Link", | ||||
"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": "Link Name", | "label": "Link Name", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "link_doctype", | "options": "link_doctype", | ||||
"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 | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"default": "__user", | "default": "__user", | ||||
"fieldname": "user", | "fieldname": "user", | ||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"hidden": 0, | |||||
"ignore_user_permissions": 1, | "ignore_user_permissions": 1, | ||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "User", | "label": "User", | ||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "User", | "options": "User", | ||||
"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 | |||||
}, | }, | ||||
{ | { | ||||
"allow_bulk_edit": 0, | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "full_name", | "fieldname": "full_name", | ||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"in_standard_filter": 0, | |||||
"label": "Full Name", | |||||
"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": "Full Name" | |||||
} | } | ||||
], | ], | ||||
"has_web_view": 0, | |||||
"hide_heading": 0, | |||||
"hide_toolbar": 0, | |||||
"icon": "fa fa-comment", | "icon": "fa fa-comment", | ||||
"idx": 0, | |||||
"image_view": 0, | |||||
"in_create": 0, | |||||
"is_submittable": 0, | |||||
"issingle": 0, | |||||
"istable": 0, | |||||
"max_attachments": 0, | |||||
"modified": "2019-09-05 14:22:27.664645", | |||||
"index_web_pages_for_search": 1, | |||||
"links": [], | |||||
"modified": "2020-08-28 11:43:57.504565", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "Activity Log", | "name": "Activity Log", | ||||
"name_case": "", | |||||
"owner": "Administrator", | "owner": "Administrator", | ||||
"permissions": [ | "permissions": [ | ||||
{ | { | ||||
"amend": 0, | |||||
"cancel": 0, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | "email": 1, | ||||
"export": 0, | |||||
"if_owner": 0, | |||||
"import": 0, | |||||
"permlevel": 0, | |||||
"print": 0, | |||||
"read": 1, | "read": 1, | ||||
"report": 1, | "report": 1, | ||||
"role": "System Manager", | "role": "System Manager", | ||||
"set_user_permissions": 0, | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 0 | |||||
"share": 1 | |||||
}, | }, | ||||
{ | { | ||||
"amend": 0, | |||||
"cancel": 0, | |||||
"create": 0, | |||||
"delete": 1, | |||||
"email": 1, | "email": 1, | ||||
"export": 1, | "export": 1, | ||||
"if_owner": 0, | |||||
"import": 0, | |||||
"permlevel": 1, | |||||
"if_owner": 1, | |||||
"print": 1, | "print": 1, | ||||
"read": 1, | "read": 1, | ||||
"report": 1, | "report": 1, | ||||
"role": "System Manager", | |||||
"set_user_permissions": 0, | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 0 | |||||
}, | |||||
{ | |||||
"amend": 0, | |||||
"cancel": 0, | |||||
"create": 0, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"export": 0, | |||||
"if_owner": 1, | |||||
"import": 0, | |||||
"permlevel": 0, | |||||
"print": 0, | |||||
"read": 1, | |||||
"report": 0, | |||||
"role": "All", | "role": "All", | ||||
"set_user_permissions": 0, | |||||
"share": 0, | |||||
"submit": 0, | |||||
"write": 0 | |||||
"share": 1 | |||||
} | } | ||||
], | ], | ||||
"quick_entry": 0, | |||||
"read_only": 0, | |||||
"read_only_onload": 0, | |||||
"search_fields": "subject", | "search_fields": "subject", | ||||
"show_name_in_global_search": 0, | |||||
"sort_field": "modified", | "sort_field": "modified", | ||||
"sort_order": "DESC", | "sort_order": "DESC", | ||||
"title_field": "subject", | "title_field": "subject", | ||||
@@ -25,9 +25,6 @@ class ActivityLog(Document): | |||||
if self.reference_doctype and self.reference_name: | if self.reference_doctype and self.reference_name: | ||||
self.status = "Linked" | self.status = "Linked" | ||||
def on_trash(self): # pylint: disable=no-self-use | |||||
frappe.throw(_("Sorry! You cannot delete auto-generated comments")) | |||||
def on_doctype_update(): | def on_doctype_update(): | ||||
"""Add indexes in `tabActivity Log`""" | """Add indexes in `tabActivity Log`""" | ||||
frappe.db.add_index("Activity Log", ["reference_doctype", "reference_name"]) | frappe.db.add_index("Activity Log", ["reference_doctype", "reference_name"]) | ||||
@@ -278,25 +278,26 @@ class File(Document): | |||||
base_url = os.path.dirname(self.file_url) | base_url = os.path.dirname(self.file_url) | ||||
files = [] | files = [] | ||||
with zipfile.ZipFile(zip_path) as zf: | |||||
zf.extractall(os.path.dirname(zip_path)) | |||||
for info in zf.infolist(): | |||||
if not info.filename.startswith('__MACOSX'): | |||||
file_url = file_url = base_url + '/' + info.filename | |||||
file_name = frappe.db.get_value('File', dict(file_url=file_url)) | |||||
if file_name: | |||||
file_doc = frappe.get_doc('File', file_name) | |||||
else: | |||||
file_doc = frappe.new_doc("File") | |||||
file_doc.file_name = info.filename | |||||
file_doc.file_size = info.file_size | |||||
file_doc.folder = self.folder | |||||
file_doc.is_private = self.is_private | |||||
file_doc.file_url = file_url | |||||
file_doc.attached_to_doctype = self.attached_to_doctype | |||||
file_doc.attached_to_name = self.attached_to_name | |||||
file_doc.save() | |||||
files.append(file_doc) | |||||
with zipfile.ZipFile(zip_path) as z: | |||||
for file in z.filelist: | |||||
if file.is_dir() or file.filename.startswith('__MACOSX/'): | |||||
# skip directories and macos hidden directory | |||||
continue | |||||
filename = os.path.basename(file.filename) | |||||
if filename.startswith('.'): | |||||
# skip hidden files | |||||
continue | |||||
file_doc = frappe.new_doc('File') | |||||
file_doc.content = z.read(file.filename) | |||||
file_doc.file_name = filename | |||||
file_doc.folder = self.folder | |||||
file_doc.is_private = self.is_private | |||||
file_doc.attached_to_doctype = self.attached_to_doctype | |||||
file_doc.attached_to_name = self.attached_to_name | |||||
file_doc.save() | |||||
files.append(file_doc) | |||||
frappe.delete_doc('File', self.name) | frappe.delete_doc('File', self.name) | ||||
return files | return files | ||||
@@ -941,7 +942,7 @@ def attach_files_to_document(doc, event): | |||||
# we dont want the update to fail if file cannot be attached for some reason | # we dont want the update to fail if file cannot be attached for some reason | ||||
try: | try: | ||||
value = doc.get(df.fieldname) | value = doc.get(df.fieldname) | ||||
if not value.startswith(("/files", "/private/files")): | |||||
if not (value or '').startswith(("/files", "/private/files")): | |||||
return | return | ||||
if frappe.db.exists("File", { | if frappe.db.exists("File", { | ||||
@@ -100,6 +100,7 @@ def export_package(): | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def import_package(package=None): | def import_package(package=None): | ||||
"""Import package from JSON.""" | """Import package from JSON.""" | ||||
frappe.only_for("System Manager") | |||||
if isinstance(package, string_types): | if isinstance(package, string_types): | ||||
package = json.loads(package) | package = json.loads(package) | ||||
@@ -40,7 +40,7 @@ class Workspace: | |||||
self.doc = self.get_page_for_user() | self.doc = self.get_page_for_user() | ||||
if self.doc.module not in self.allowed_modules: | |||||
if self.doc.module and self.doc.module not in self.allowed_modules: | |||||
raise frappe.PermissionError | raise frappe.PermissionError | ||||
self.can_read = self.get_cached('user_perm_can_read', self.get_can_read_items) | self.can_read = self.get_cached('user_perm_can_read', self.get_can_read_items) | ||||
@@ -60,11 +60,11 @@ def has_permission(doc, ptype, user): | |||||
if doc.chart_type == 'Report': | if doc.chart_type == 'Report': | ||||
allowed_reports = tuple([key.encode('UTF8') for key in get_allowed_reports()]) | |||||
allowed_reports = [key if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()] | |||||
if doc.report_name in allowed_reports: | if doc.report_name in allowed_reports: | ||||
return True | return True | ||||
else: | else: | ||||
allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read()) | |||||
allowed_doctypes = [frappe.permissions.get_doctypes_with_read()] | |||||
if doc.document_type in allowed_doctypes: | if doc.document_type in allowed_doctypes: | ||||
return True | return True | ||||
@@ -38,7 +38,7 @@ class DeskPage(Document): | |||||
pages = frappe.get_all("Desk Page", fields=["name", "module"], filters=filters, as_list=1) | pages = frappe.get_all("Desk Page", fields=["name", "module"], filters=filters, as_list=1) | ||||
return { page[1]: page[0] for page in pages } | |||||
return { page[1]: page[0] for page in pages if page[1] } | |||||
def disable_saving_as_standard(): | def disable_saving_as_standard(): | ||||
return frappe.flags.in_install or \ | return frappe.flags.in_install or \ | ||||
@@ -21,6 +21,7 @@ | |||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"label": "Callback URL", | "label": "Callback URL", | ||||
"read_only": 1, | |||||
"reqd": 1, | "reqd": 1, | ||||
"unique": 1 | "unique": 1 | ||||
}, | }, | ||||
@@ -28,19 +29,20 @@ | |||||
"fieldname": "api_key", | "fieldname": "api_key", | ||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"label": "API Key", | "label": "API Key", | ||||
"read_only": 1 | |||||
"reqd": 1 | |||||
}, | }, | ||||
{ | { | ||||
"fieldname": "api_secret", | "fieldname": "api_secret", | ||||
"fieldtype": "Password", | "fieldtype": "Password", | ||||
"label": "API Secret", | "label": "API Secret", | ||||
"read_only": 1 | |||||
"reqd": 1 | |||||
}, | }, | ||||
{ | { | ||||
"fieldname": "user", | "fieldname": "user", | ||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"label": "Event Subscriber", | "label": "Event Subscriber", | ||||
"options": "User", | "options": "User", | ||||
"read_only": 1, | |||||
"reqd": 1 | "reqd": 1 | ||||
}, | }, | ||||
{ | { | ||||
@@ -69,7 +71,7 @@ | |||||
], | ], | ||||
"in_create": 1, | "in_create": 1, | ||||
"links": [], | "links": [], | ||||
"modified": "2020-09-06 15:42:00.746493", | |||||
"modified": "2020-09-08 16:42:39.828085", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Event Streaming", | "module": "Event Streaming", | ||||
"name": "Event Consumer", | "name": "Event Consumer", | ||||
@@ -7,6 +7,7 @@ import frappe | |||||
import json | import json | ||||
import requests | import requests | ||||
import os | import os | ||||
from frappe import _ | |||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
from frappe.frappeclient import FrappeClient | from frappe.frappeclient import FrappeClient | ||||
from frappe.utils.data import get_url | from frappe.utils.data import get_url | ||||
@@ -23,6 +24,10 @@ class EventConsumer(Document): | |||||
def on_update(self): | def on_update(self): | ||||
if not self.incoming_change: | if not self.incoming_change: | ||||
doc_before_save = self.get_doc_before_save() | |||||
if doc_before_save.api_key != self.api_key or doc_before_save.api_secret != self.api_secret: | |||||
return | |||||
self.update_consumer_status() | self.update_consumer_status() | ||||
else: | else: | ||||
frappe.db.set_value(self.doctype, self.name, 'incoming_change', 0) | frappe.db.set_value(self.doctype, self.name, 'incoming_change', 0) | ||||
@@ -58,17 +63,26 @@ class EventConsumer(Document): | |||||
return 'offline' | return 'offline' | ||||
return 'online' | return 'online' | ||||
@frappe.whitelist(allow_guest=True) | |||||
@frappe.whitelist() | |||||
def register_consumer(data): | def register_consumer(data): | ||||
"""create an event consumer document for registering a consumer""" | """create an event consumer document for registering a consumer""" | ||||
data = json.loads(data) | data = json.loads(data) | ||||
# to ensure that consumer is created only once | # to ensure that consumer is created only once | ||||
if frappe.db.exists('Event Consumer', data['event_consumer']): | if frappe.db.exists('Event Consumer', data['event_consumer']): | ||||
return None | return None | ||||
user = data['user'] | |||||
if not frappe.db.exists('User', user): | |||||
frappe.throw(_('User {0} not found on the producer site').format(user)) | |||||
if "System Manager" not in frappe.get_roles(user): | |||||
frappe.throw(_("Event Subscriber has to be a System Manager.")) | |||||
consumer = frappe.new_doc('Event Consumer') | consumer = frappe.new_doc('Event Consumer') | ||||
consumer.callback_url = data['event_consumer'] | consumer.callback_url = data['event_consumer'] | ||||
consumer.user = data['user'] | consumer.user = data['user'] | ||||
consumer.api_key = data['api_key'] | |||||
consumer.api_secret = data['api_secret'] | |||||
consumer.incoming_change = True | consumer.incoming_change = True | ||||
consumer_doctypes = json.loads(data['consumer_doctypes']) | consumer_doctypes = json.loads(data['consumer_doctypes']) | ||||
@@ -78,18 +92,13 @@ def register_consumer(data): | |||||
'status': 'Pending' | 'status': 'Pending' | ||||
}) | }) | ||||
api_key = frappe.generate_hash(length=10) | |||||
api_secret = frappe.generate_hash(length=10) | |||||
consumer.api_key = api_key | |||||
consumer.api_secret = api_secret | |||||
consumer.insert(ignore_permissions=True) | |||||
frappe.db.commit() | |||||
consumer.insert() | |||||
# consumer's 'last_update' field should point to the latest update | # consumer's 'last_update' field should point to the latest update | ||||
# in producer's update log when subscribing | # in producer's update log when subscribing | ||||
# so that, updates after subscribing are consumed and not the old ones. | # so that, updates after subscribing are consumed and not the old ones. | ||||
last_update = str(get_last_update()) | last_update = str(get_last_update()) | ||||
return json.dumps({'api_key': api_key, 'api_secret': api_secret, 'last_update': last_update}) | |||||
return json.dumps({'last_update': last_update}) | |||||
def get_consumer_site(consumer_url): | def get_consumer_site(consumer_url): | ||||
@@ -98,8 +107,7 @@ def get_consumer_site(consumer_url): | |||||
consumer_site = FrappeClient( | consumer_site = FrappeClient( | ||||
url=consumer_url, | url=consumer_url, | ||||
api_key=consumer_doc.api_key, | api_key=consumer_doc.api_key, | ||||
api_secret=consumer_doc.get_password('api_secret'), | |||||
frappe_authorization_source='Event Producer' | |||||
api_secret=consumer_doc.get_password('api_secret') | |||||
) | ) | ||||
return consumer_site | return consumer_site | ||||
@@ -32,23 +32,26 @@ | |||||
"read_only": 1 | "read_only": 1 | ||||
}, | }, | ||||
{ | { | ||||
"description": "API Key of the user(Event Subscriber) on the producer site", | |||||
"fieldname": "api_key", | "fieldname": "api_key", | ||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"label": "API Key", | "label": "API Key", | ||||
"read_only": 1 | |||||
"reqd": 1 | |||||
}, | }, | ||||
{ | { | ||||
"description": "API Secret of the user(Event Subscriber) on the producer site", | |||||
"fieldname": "api_secret", | "fieldname": "api_secret", | ||||
"fieldtype": "Password", | "fieldtype": "Password", | ||||
"label": "API Secret", | "label": "API Secret", | ||||
"read_only": 1 | |||||
"reqd": 1 | |||||
}, | }, | ||||
{ | { | ||||
"fieldname": "user", | "fieldname": "user", | ||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"label": "Event Subscriber", | "label": "Event Subscriber", | ||||
"options": "User", | "options": "User", | ||||
"reqd": 1 | |||||
"reqd": 1, | |||||
"set_only_once": 1 | |||||
}, | }, | ||||
{ | { | ||||
"fieldname": "column_break_6", | "fieldname": "column_break_6", | ||||
@@ -74,7 +77,7 @@ | |||||
} | } | ||||
], | ], | ||||
"links": [], | "links": [], | ||||
"modified": "2019-12-26 13:04:11.438349", | |||||
"modified": "2020-09-08 18:50:57.687979", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Event Streaming", | "module": "Event Streaming", | ||||
"name": "Event Producer", | "name": "Event Producer", | ||||
@@ -12,7 +12,8 @@ from frappe import _ | |||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
from frappe.frappeclient import FrappeClient | from frappe.frappeclient import FrappeClient | ||||
from frappe.utils.background_jobs import get_jobs | from frappe.utils.background_jobs import get_jobs | ||||
from frappe.utils.data import get_url | |||||
from frappe.utils.data import get_url, get_link_to_form | |||||
from frappe.utils.password import get_decrypted_password | |||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_field | from frappe.custom.doctype.custom_field.custom_field import create_custom_field | ||||
from frappe.integrations.oauth2 import validate_url | from frappe.integrations.oauth2 import validate_url | ||||
@@ -20,19 +21,35 @@ from frappe.integrations.oauth2 import validate_url | |||||
class EventProducer(Document): | class EventProducer(Document): | ||||
def before_insert(self): | def before_insert(self): | ||||
self.check_url() | self.check_url() | ||||
self.validate_event_subscriber() | |||||
self.incoming_change = True | self.incoming_change = True | ||||
self.create_event_consumer() | self.create_event_consumer() | ||||
self.create_custom_fields() | self.create_custom_fields() | ||||
def validate(self): | def validate(self): | ||||
self.validate_event_subscriber() | |||||
if frappe.flags.in_test: | if frappe.flags.in_test: | ||||
for entry in self.producer_doctypes: | for entry in self.producer_doctypes: | ||||
entry.status = 'Approved' | entry.status = 'Approved' | ||||
def validate_event_subscriber(self): | |||||
if not frappe.db.get_value('User', self.user, 'api_key'): | |||||
frappe.throw(_('Please generate keys for the Event Subscriber User {0} first.').format( | |||||
frappe.bold(get_link_to_form('User', self.user)) | |||||
)) | |||||
def on_update(self): | def on_update(self): | ||||
if not self.incoming_change: | if not self.incoming_change: | ||||
self.update_event_consumer() | |||||
self.create_custom_fields() | |||||
if frappe.db.exists('Event Producer', self.name): | |||||
if not self.api_key or not self.api_secret: | |||||
frappe.throw(_('Please set API Key and Secret on the producer and consumer sites first.')) | |||||
else: | |||||
doc_before_save = self.get_doc_before_save() | |||||
if doc_before_save.api_key != self.api_key or doc_before_save.api_secret != self.api_secret: | |||||
return | |||||
self.update_event_consumer() | |||||
self.create_custom_fields() | |||||
else: | else: | ||||
# when producer doc is updated it updates the consumer doc, set flag to avoid deadlock | # when producer doc is updated it updates the consumer doc, set flag to avoid deadlock | ||||
self.db_set('incoming_change', 0) | self.db_set('incoming_change', 0) | ||||
@@ -50,15 +67,18 @@ class EventProducer(Document): | |||||
def create_event_consumer(self): | def create_event_consumer(self): | ||||
"""register event consumer on the producer site""" | """register event consumer on the producer site""" | ||||
if self.is_producer_online(): | if self.is_producer_online(): | ||||
producer_site = FrappeClient(self.producer_url, verify=False) | |||||
producer_site = FrappeClient( | |||||
url=self.producer_url, | |||||
api_key=self.api_key, | |||||
api_secret=self.get_password('api_secret') | |||||
) | |||||
response = producer_site.post_api( | response = producer_site.post_api( | ||||
'frappe.event_streaming.doctype.event_consumer.event_consumer.register_consumer', | 'frappe.event_streaming.doctype.event_consumer.event_consumer.register_consumer', | ||||
params={'data': json.dumps(self.get_request_data())} | params={'data': json.dumps(self.get_request_data())} | ||||
) | ) | ||||
if response: | if response: | ||||
response = json.loads(response) | response = json.loads(response) | ||||
self.api_key = response['api_key'] | |||||
self.api_secret = response['api_secret'] | |||||
self.last_update = response['last_update'] | self.last_update = response['last_update'] | ||||
else: | else: | ||||
frappe.throw(_('Failed to create an Event Consumer or an Event Consumer for the current site is already registered.')) | frappe.throw(_('Failed to create an Event Consumer or an Event Consumer for the current site is already registered.')) | ||||
@@ -72,10 +92,14 @@ class EventProducer(Document): | |||||
else: | else: | ||||
consumer_doctypes.append(entry.ref_doctype) | consumer_doctypes.append(entry.ref_doctype) | ||||
user_key = frappe.db.get_value('User', self.user, 'api_key') | |||||
user_secret = get_decrypted_password('User', self.user, 'api_secret') | |||||
return { | return { | ||||
'event_consumer': get_url(), | 'event_consumer': get_url(), | ||||
'consumer_doctypes': json.dumps(consumer_doctypes), | 'consumer_doctypes': json.dumps(consumer_doctypes), | ||||
'user': self.user | |||||
'user': self.user, | |||||
'api_key': user_key, | |||||
'api_secret': user_secret | |||||
} | } | ||||
def create_custom_fields(self): | def create_custom_fields(self): | ||||
@@ -131,8 +155,7 @@ def get_producer_site(producer_url): | |||||
producer_site = FrappeClient( | producer_site = FrappeClient( | ||||
url=producer_url, | url=producer_url, | ||||
api_key=producer_doc.api_key, | api_key=producer_doc.api_key, | ||||
api_secret=producer_doc.get_password('api_secret'), | |||||
frappe_authorization_source='Event Consumer' | |||||
api_secret=producer_doc.get_password('api_secret') | |||||
) | ) | ||||
return producer_site | return producer_site | ||||
@@ -8,6 +8,7 @@ import unittest | |||||
import json | import json | ||||
from frappe.frappeclient import FrappeClient | from frappe.frappeclient import FrappeClient | ||||
from frappe.event_streaming.doctype.event_producer.event_producer import pull_from_node | from frappe.event_streaming.doctype.event_producer.event_producer import pull_from_node | ||||
from frappe.core.doctype.user.user import generate_keys | |||||
producer_url = 'http://test_site_producer:8000' | producer_url = 'http://test_site_producer:8000' | ||||
@@ -166,16 +167,6 @@ class TestEventProducer(unittest.TestCase): | |||||
def pull_producer_data(self): | def pull_producer_data(self): | ||||
pull_from_node(producer_url) | pull_from_node(producer_url) | ||||
def get_remote_site(self): | |||||
producer_doc = frappe.get_doc('Event Producer', producer_url) | |||||
producer_site = FrappeClient( | |||||
url=producer_doc.producer_url, | |||||
api_key=producer_doc.api_key, | |||||
api_secret=producer_doc.get_password('api_secret'), | |||||
frappe_authorization_source='Event Consumer' | |||||
) | |||||
return producer_site | |||||
def test_mapping(self): | def test_mapping(self): | ||||
producer = get_remote_site() | producer = get_remote_site() | ||||
event_producer = frappe.get_doc('Event Producer', producer_url, for_update=True) | event_producer = frappe.get_doc('Event Producer', producer_url, for_update=True) | ||||
@@ -298,6 +289,20 @@ def create_event_producer(producer_url): | |||||
event_producer.save() | event_producer.save() | ||||
return | return | ||||
generate_keys('Administrator') | |||||
producer_site = connect() | |||||
response = producer_site.post_api( | |||||
'frappe.core.doctype.user.user.generate_keys', | |||||
params={'user': 'Administrator'} | |||||
) | |||||
api_secret = response.get('api_secret') | |||||
response = producer_site.get_value('User', 'api_key', {'name': 'Administrator'}) | |||||
api_key = response.get('api_key') | |||||
event_producer = frappe.new_doc('Event Producer') | event_producer = frappe.new_doc('Event Producer') | ||||
event_producer.producer_doctypes = [] | event_producer.producer_doctypes = [] | ||||
event_producer.producer_url = producer_url | event_producer.producer_url = producer_url | ||||
@@ -310,6 +315,8 @@ def create_event_producer(producer_url): | |||||
'use_same_name': 1 | 'use_same_name': 1 | ||||
}) | }) | ||||
event_producer.user = 'Administrator' | event_producer.user = 'Administrator' | ||||
event_producer.api_key = api_key | |||||
event_producer.api_secret = api_secret | |||||
event_producer.save() | event_producer.save() | ||||
def reset_configuration(producer_url): | def reset_configuration(producer_url): | ||||
@@ -331,9 +338,9 @@ def get_remote_site(): | |||||
producer_doc = frappe.get_doc('Event Producer', producer_url) | producer_doc = frappe.get_doc('Event Producer', producer_url) | ||||
producer_site = FrappeClient( | producer_site = FrappeClient( | ||||
url=producer_doc.producer_url, | url=producer_doc.producer_url, | ||||
api_key=producer_doc.api_key, | |||||
api_secret=producer_doc.get_password('api_secret'), | |||||
frappe_authorization_source='Event Consumer' | |||||
username='Administrator', | |||||
password='admin', | |||||
verify=False | |||||
) | ) | ||||
return producer_site | return producer_site | ||||
@@ -341,4 +348,17 @@ def unsubscribe_doctypes(producer_url): | |||||
event_producer = frappe.get_doc('Event Producer', producer_url) | event_producer = frappe.get_doc('Event Producer', producer_url) | ||||
for entry in event_producer.producer_doctypes: | for entry in event_producer.producer_doctypes: | ||||
entry.unsubscribe = 1 | entry.unsubscribe = 1 | ||||
event_producer.save() | |||||
event_producer.save() | |||||
def connect(): | |||||
def _connect(): | |||||
return FrappeClient( | |||||
url=producer_url, | |||||
username='Administrator', | |||||
password='admin', | |||||
verify=False | |||||
) | |||||
try: | |||||
return _connect() | |||||
except Exception: | |||||
return _connect() |
@@ -25,7 +25,6 @@ def update_document_title(doctype, docname, title_field=None, old_title=None, ne | |||||
return docname | return docname | ||||
@frappe.whitelist() | |||||
def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False, ignore_if_exists=False, show_alert=True): | 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 | Renames a doc(dt, old) to doc(dt, new) and | ||||
@@ -308,3 +308,4 @@ frappe.patches.v13_0.remove_duplicate_navbar_items | |||||
frappe.patches.v13_0.set_route_for_blog_category | frappe.patches.v13_0.set_route_for_blog_category | ||||
frappe.patches.v13_0.enable_custom_script | frappe.patches.v13_0.enable_custom_script | ||||
frappe.patches.v13_0.update_newsletter_content_type | frappe.patches.v13_0.update_newsletter_content_type | ||||
frappe.patches.v13_0.delete_event_producer_and_consumer_keys |
@@ -0,0 +1,11 @@ | |||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
def execute(): | |||||
if frappe.db.exists("DocType", "Event Producer"): | |||||
frappe.db.sql("""UPDATE `tabEvent Producer` SET api_key='', api_secret=''""") | |||||
if frappe.db.exists("DocType", "Event Consumer"): | |||||
frappe.db.sql("""UPDATE `tabEvent Consumer` SET api_key='', api_secret=''""") |
@@ -1259,7 +1259,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||||
return; | return; | ||||
} | } | ||||
this.export_dialog = frappe.prompt([ | |||||
let export_dialog_fields = [ | |||||
{ | { | ||||
label: __('Select File Format'), | label: __('Select File Format'), | ||||
fieldname: 'file_format', | fieldname: 'file_format', | ||||
@@ -1267,13 +1267,18 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||||
options: ['Excel', 'CSV'], | options: ['Excel', 'CSV'], | ||||
default: 'Excel', | default: 'Excel', | ||||
reqd: 1 | reqd: 1 | ||||
}, | |||||
{ | |||||
} | |||||
]; | |||||
if (this.tree_report) { | |||||
export_dialog_fields.push({ | |||||
label: __("Include indentation"), | label: __("Include indentation"), | ||||
fieldname: "include_indentation", | fieldname: "include_indentation", | ||||
fieldtype: "Check", | fieldtype: "Check", | ||||
} | |||||
], ({ file_format, include_indentation }) => { | |||||
}); | |||||
} | |||||
this.export_dialog = frappe.prompt(export_dialog_fields, ({ file_format, include_indentation }) => { | |||||
this.make_access_log('Export', file_format); | this.make_access_log('Export', file_format); | ||||
if (file_format === 'CSV') { | if (file_format === 'CSV') { | ||||
const column_row = this.columns.reduce((acc, col) => { | const column_row = this.columns.reduce((acc, col) => { | ||||
@@ -72,7 +72,7 @@ | |||||
margin-bottom: 10px; | margin-bottom: 10px; | ||||
} | } | ||||
.report-wrapper { | |||||
.report-wrapper, .datatable-wrapper { | |||||
overflow: auto; | overflow: auto; | ||||
} | } | ||||
@@ -0,0 +1,46 @@ | |||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# imports - standard imports | |||||
import shlex | |||||
import subprocess | |||||
import unittest | |||||
# imports - module imports | |||||
import frappe | |||||
def clean(value): | |||||
if isinstance(value, (bytes, str)): | |||||
value = value.decode().strip() | |||||
return value | |||||
class BaseTestCommands: | |||||
def execute(self, command): | |||||
command = command.format(**{"site": frappe.local.site}) | |||||
command = shlex.split(command) | |||||
self._proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |||||
self.stdout = clean(self._proc.stdout) | |||||
self.stderr = clean(self._proc.stderr) | |||||
self.returncode = clean(self._proc.returncode) | |||||
class TestCommands(BaseTestCommands, unittest.TestCase): | |||||
def test_execute(self): | |||||
# test 1: execute a command expecting a numeric output | |||||
self.execute("bench --site {site} execute frappe.db.get_database_size") | |||||
self.assertEquals(self.returncode, 0) | |||||
self.assertIsInstance(float(self.stdout), float) | |||||
# test 2: execute a command expecting an errored output as local won't exist | |||||
self.execute("bench --site {site} execute frappe.local.site") | |||||
self.assertEquals(self.returncode, 1) | |||||
self.assertIsNotNone(self.stderr) | |||||
# test 3: execute a command with kwargs | |||||
# Note: | |||||
# terminal command has been escaped to avoid .format string replacement | |||||
# The returned value has quotes which have been trimmed for the test | |||||
self.execute("""bench --site {site} execute frappe.bold --kwargs '{{"text": "DocType"}}'""") | |||||
self.assertEquals(self.returncode, 0) | |||||
self.assertEquals(self.stdout[1:-1], frappe.bold(text='DocType')) |
@@ -135,7 +135,8 @@ def validate_email_address(email_str, throw=False): | |||||
if not _valid: | if not _valid: | ||||
if throw: | if throw: | ||||
frappe.throw(frappe._("{0} is not a valid Email Address").format(e), | |||||
invalid_email = frappe.utils.escape_html(e) | |||||
frappe.throw(frappe._("{0} is not a valid Email Address").format(invalid_email), | |||||
frappe.InvalidEmailAddressError) | frappe.InvalidEmailAddressError) | ||||
return None | return None | ||||
else: | else: | ||||
@@ -33,9 +33,9 @@ frappe.ui.form.on('Blog Post', { | |||||
}); | }); | ||||
function generate_google_search_preview(frm) { | function generate_google_search_preview(frm) { | ||||
if (!frm.doc.title) return; | |||||
if (!(frm.doc.meta_title || frm.doc.title)) return; | |||||
let google_preview = frm.get_field("google_preview"); | let google_preview = frm.get_field("google_preview"); | ||||
let seo_title = (frm.doc.title).slice(0, 60); | |||||
let seo_title = (frm.doc.meta_title || frm.doc.title).slice(0, 60); | |||||
let seo_description = (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160); | let seo_description = (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160); | ||||
let date = frm.doc.published_on ? new frappe.datetime.datetime(frm.doc.published_on).moment.format('ll') + ' - ' : ''; | let date = frm.doc.published_on ? new frappe.datetime.datetime(frm.doc.published_on).moment.format('ll') + ' - ' : ''; | ||||
let route_array = frm.doc.route ? frm.doc.route.split('/') : []; | let route_array = frm.doc.route ? frm.doc.route.split('/') : []; | ||||
@@ -26,6 +26,7 @@ | |||||
"content_html", | "content_html", | ||||
"email_sent", | "email_sent", | ||||
"meta_tags", | "meta_tags", | ||||
"meta_title", | |||||
"meta_description", | "meta_description", | ||||
"column_break_18", | "column_break_18", | ||||
"meta_image", | "meta_image", | ||||
@@ -110,7 +111,6 @@ | |||||
"depends_on": "eval:doc.content_type === 'Markdown'", | "depends_on": "eval:doc.content_type === 'Markdown'", | ||||
"fieldname": "content_md", | "fieldname": "content_md", | ||||
"fieldtype": "Markdown Editor", | "fieldtype": "Markdown Editor", | ||||
"ignore_xss_filter": 1, | |||||
"label": "Content (Markdown)" | "label": "Content (Markdown)" | ||||
}, | }, | ||||
{ | { | ||||
@@ -185,6 +185,12 @@ | |||||
"fieldtype": "Check", | "fieldtype": "Check", | ||||
"hidden": 1, | "hidden": 1, | ||||
"label": "Hide CTA" | "label": "Hide CTA" | ||||
}, | |||||
{ | |||||
"fieldname": "meta_title", | |||||
"fieldtype": "Data", | |||||
"label": "Meta Title", | |||||
"length": 60 | |||||
} | } | ||||
], | ], | ||||
"has_web_view": 1, | "has_web_view": 1, | ||||
@@ -194,7 +200,7 @@ | |||||
"is_published_field": "published", | "is_published_field": "published", | ||||
"links": [], | "links": [], | ||||
"max_attachments": 5, | "max_attachments": 5, | ||||
"modified": "2020-08-31 16:55:03.687862", | |||||
"modified": "2020-08-31 21:01:51.100349", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Website", | "module": "Website", | ||||
"name": "Blog Post", | "name": "Blog Post", | ||||
@@ -36,6 +36,11 @@ class BlogPost(WebsiteGenerator): | |||||
if self.blog_intro: | if self.blog_intro: | ||||
self.blog_intro = self.blog_intro[:200] | self.blog_intro = self.blog_intro[:200] | ||||
if not self.meta_title: | |||||
self.meta_title = self.title[:60] | |||||
else: | |||||
self.meta_title = self.meta_title[:60] | |||||
if not self.meta_description: | if not self.meta_description: | ||||
self.meta_description = self.blog_intro[:140] | self.meta_description = self.blog_intro[:140] | ||||
else: | else: | ||||
@@ -88,7 +93,7 @@ class BlogPost(WebsiteGenerator): | |||||
context.description = self.meta_description or self.blog_intro or strip_html_tags(context.content[:140]) | context.description = self.meta_description or self.blog_intro or strip_html_tags(context.content[:140]) | ||||
context.metatags = { | context.metatags = { | ||||
"name": self.title, | |||||
"name": self.meta_title, | |||||
"description": context.description, | "description": context.description, | ||||
} | } | ||||