@@ -7,22 +7,28 @@ start_pattern = re.compile(r"_{1,2}\([\"']{1,3}") | |||
# skip first argument | |||
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: | |||
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: | |||
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: | |||
print('Good To Go!') | |||
print('\nGood To Go!') |
@@ -61,10 +61,10 @@ context('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.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 .btn-secondary').should('contain', 'Clear').click(); | |||
@@ -1110,8 +1110,8 @@ def get_newargs(fn, kwargs): | |||
if (a in fnargs) or varkw: | |||
newargs[a] = kwargs.get(a) | |||
if "flags" in newargs: | |||
del newargs["flags"] | |||
newargs.pop("ignore_permissions", None) | |||
newargs.pop("flags", None) | |||
return newargs | |||
@@ -133,6 +133,7 @@ def reset_perms(context): | |||
def execute(context, method, args=None, kwargs=None, profile=False): | |||
"Execute a function" | |||
for site in context.sites: | |||
ret = "" | |||
try: | |||
frappe.init(site=site) | |||
frappe.connect() | |||
@@ -154,7 +155,10 @@ def execute(context, method, args=None, kwargs=None, profile=False): | |||
pr = cProfile.Profile() | |||
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: | |||
pr.disable() | |||
@@ -1,731 +1,184 @@ | |||
{ | |||
"allow_copy": 0, | |||
"allow_guest_to_view": 0, | |||
{ | |||
"actions": [], | |||
"allow_import": 1, | |||
"allow_rename": 0, | |||
"autoname": "", | |||
"beta": 0, | |||
"creation": "2017-10-05 11:10:38.780133", | |||
"custom": 0, | |||
"description": "Keep track of all update feeds", | |||
"docstatus": 0, | |||
"doctype": "DocType", | |||
"document_type": "Setup", | |||
"editable_grid": 0, | |||
"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": [ | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "subject", | |||
"fieldtype": "Small Text", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_global_search": 1, | |||
"in_list_view": 1, | |||
"in_standard_filter": 0, | |||
"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", | |||
"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", | |||
"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", | |||
"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" | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"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, | |||
"columns": 0, | |||
"fieldname": "additional_info", | |||
"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", | |||
"fieldname": "communication_date", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"fieldtype": "Select", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_global_search": 0, | |||
"in_list_view": 1, | |||
"in_standard_filter": 0, | |||
"label": "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, | |||
"columns": 0, | |||
"fieldname": "reference_section", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"fieldname": "reference_owner", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"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", | |||
"length": 0, | |||
"no_copy": 0, | |||
"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", | |||
"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", | |||
"length": 0, | |||
"no_copy": 0, | |||
"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", | |||
"fieldname": "user", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
"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", | |||
"length": 0, | |||
"no_copy": 0, | |||
"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", | |||
"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": "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", | |||
"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", | |||
"module": "Core", | |||
"name": "Activity Log", | |||
"name_case": "", | |||
"owner": "Administrator", | |||
"permissions": [ | |||
{ | |||
"amend": 0, | |||
"cancel": 0, | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"export": 0, | |||
"if_owner": 0, | |||
"import": 0, | |||
"permlevel": 0, | |||
"print": 0, | |||
"read": 1, | |||
"report": 1, | |||
"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, | |||
"export": 1, | |||
"if_owner": 0, | |||
"import": 0, | |||
"permlevel": 1, | |||
"if_owner": 1, | |||
"print": 1, | |||
"read": 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", | |||
"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", | |||
"show_name_in_global_search": 0, | |||
"sort_field": "modified", | |||
"sort_order": "DESC", | |||
"title_field": "subject", | |||
@@ -25,9 +25,6 @@ class ActivityLog(Document): | |||
if self.reference_doctype and self.reference_name: | |||
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(): | |||
"""Add indexes in `tabActivity Log`""" | |||
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) | |||
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) | |||
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 | |||
try: | |||
value = doc.get(df.fieldname) | |||
if not value.startswith(("/files", "/private/files")): | |||
if not (value or '').startswith(("/files", "/private/files")): | |||
return | |||
if frappe.db.exists("File", { | |||
@@ -100,6 +100,7 @@ def export_package(): | |||
@frappe.whitelist() | |||
def import_package(package=None): | |||
"""Import package from JSON.""" | |||
frappe.only_for("System Manager") | |||
if isinstance(package, string_types): | |||
package = json.loads(package) | |||
@@ -40,7 +40,7 @@ class Workspace: | |||
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 | |||
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': | |||
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: | |||
return True | |||
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: | |||
return True | |||
@@ -38,7 +38,7 @@ class DeskPage(Document): | |||
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(): | |||
return frappe.flags.in_install or \ | |||
@@ -21,6 +21,7 @@ | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Callback URL", | |||
"read_only": 1, | |||
"reqd": 1, | |||
"unique": 1 | |||
}, | |||
@@ -28,19 +29,20 @@ | |||
"fieldname": "api_key", | |||
"fieldtype": "Data", | |||
"label": "API Key", | |||
"read_only": 1 | |||
"reqd": 1 | |||
}, | |||
{ | |||
"fieldname": "api_secret", | |||
"fieldtype": "Password", | |||
"label": "API Secret", | |||
"read_only": 1 | |||
"reqd": 1 | |||
}, | |||
{ | |||
"fieldname": "user", | |||
"fieldtype": "Link", | |||
"label": "Event Subscriber", | |||
"options": "User", | |||
"read_only": 1, | |||
"reqd": 1 | |||
}, | |||
{ | |||
@@ -69,7 +71,7 @@ | |||
], | |||
"in_create": 1, | |||
"links": [], | |||
"modified": "2020-09-06 15:42:00.746493", | |||
"modified": "2020-09-08 16:42:39.828085", | |||
"modified_by": "Administrator", | |||
"module": "Event Streaming", | |||
"name": "Event Consumer", | |||
@@ -7,6 +7,7 @@ import frappe | |||
import json | |||
import requests | |||
import os | |||
from frappe import _ | |||
from frappe.model.document import Document | |||
from frappe.frappeclient import FrappeClient | |||
from frappe.utils.data import get_url | |||
@@ -23,6 +24,10 @@ class EventConsumer(Document): | |||
def on_update(self): | |||
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() | |||
else: | |||
frappe.db.set_value(self.doctype, self.name, 'incoming_change', 0) | |||
@@ -58,17 +63,26 @@ class EventConsumer(Document): | |||
return 'offline' | |||
return 'online' | |||
@frappe.whitelist(allow_guest=True) | |||
@frappe.whitelist() | |||
def register_consumer(data): | |||
"""create an event consumer document for registering a consumer""" | |||
data = json.loads(data) | |||
# to ensure that consumer is created only once | |||
if frappe.db.exists('Event Consumer', data['event_consumer']): | |||
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.callback_url = data['event_consumer'] | |||
consumer.user = data['user'] | |||
consumer.api_key = data['api_key'] | |||
consumer.api_secret = data['api_secret'] | |||
consumer.incoming_change = True | |||
consumer_doctypes = json.loads(data['consumer_doctypes']) | |||
@@ -78,18 +92,13 @@ def register_consumer(data): | |||
'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 | |||
# in producer's update log when subscribing | |||
# so that, updates after subscribing are consumed and not the old ones. | |||
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): | |||
@@ -98,8 +107,7 @@ def get_consumer_site(consumer_url): | |||
consumer_site = FrappeClient( | |||
url=consumer_url, | |||
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 | |||
@@ -32,23 +32,26 @@ | |||
"read_only": 1 | |||
}, | |||
{ | |||
"description": "API Key of the user(Event Subscriber) on the producer site", | |||
"fieldname": "api_key", | |||
"fieldtype": "Data", | |||
"label": "API Key", | |||
"read_only": 1 | |||
"reqd": 1 | |||
}, | |||
{ | |||
"description": "API Secret of the user(Event Subscriber) on the producer site", | |||
"fieldname": "api_secret", | |||
"fieldtype": "Password", | |||
"label": "API Secret", | |||
"read_only": 1 | |||
"reqd": 1 | |||
}, | |||
{ | |||
"fieldname": "user", | |||
"fieldtype": "Link", | |||
"label": "Event Subscriber", | |||
"options": "User", | |||
"reqd": 1 | |||
"reqd": 1, | |||
"set_only_once": 1 | |||
}, | |||
{ | |||
"fieldname": "column_break_6", | |||
@@ -74,7 +77,7 @@ | |||
} | |||
], | |||
"links": [], | |||
"modified": "2019-12-26 13:04:11.438349", | |||
"modified": "2020-09-08 18:50:57.687979", | |||
"modified_by": "Administrator", | |||
"module": "Event Streaming", | |||
"name": "Event Producer", | |||
@@ -12,7 +12,8 @@ from frappe import _ | |||
from frappe.model.document import Document | |||
from frappe.frappeclient import FrappeClient | |||
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.integrations.oauth2 import validate_url | |||
@@ -20,19 +21,35 @@ from frappe.integrations.oauth2 import validate_url | |||
class EventProducer(Document): | |||
def before_insert(self): | |||
self.check_url() | |||
self.validate_event_subscriber() | |||
self.incoming_change = True | |||
self.create_event_consumer() | |||
self.create_custom_fields() | |||
def validate(self): | |||
self.validate_event_subscriber() | |||
if frappe.flags.in_test: | |||
for entry in self.producer_doctypes: | |||
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): | |||
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: | |||
# when producer doc is updated it updates the consumer doc, set flag to avoid deadlock | |||
self.db_set('incoming_change', 0) | |||
@@ -50,15 +67,18 @@ class EventProducer(Document): | |||
def create_event_consumer(self): | |||
"""register event consumer on the producer site""" | |||
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( | |||
'frappe.event_streaming.doctype.event_consumer.event_consumer.register_consumer', | |||
params={'data': json.dumps(self.get_request_data())} | |||
) | |||
if response: | |||
response = json.loads(response) | |||
self.api_key = response['api_key'] | |||
self.api_secret = response['api_secret'] | |||
self.last_update = response['last_update'] | |||
else: | |||
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: | |||
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 { | |||
'event_consumer': get_url(), | |||
'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): | |||
@@ -131,8 +155,7 @@ def get_producer_site(producer_url): | |||
producer_site = FrappeClient( | |||
url=producer_url, | |||
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 | |||
@@ -8,6 +8,7 @@ import unittest | |||
import json | |||
from frappe.frappeclient import FrappeClient | |||
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' | |||
@@ -166,16 +167,6 @@ class TestEventProducer(unittest.TestCase): | |||
def pull_producer_data(self): | |||
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): | |||
producer = get_remote_site() | |||
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() | |||
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.producer_doctypes = [] | |||
event_producer.producer_url = producer_url | |||
@@ -310,6 +315,8 @@ def create_event_producer(producer_url): | |||
'use_same_name': 1 | |||
}) | |||
event_producer.user = 'Administrator' | |||
event_producer.api_key = api_key | |||
event_producer.api_secret = api_secret | |||
event_producer.save() | |||
def reset_configuration(producer_url): | |||
@@ -331,9 +338,9 @@ def get_remote_site(): | |||
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' | |||
username='Administrator', | |||
password='admin', | |||
verify=False | |||
) | |||
return producer_site | |||
@@ -341,4 +348,17 @@ def unsubscribe_doctypes(producer_url): | |||
event_producer = frappe.get_doc('Event Producer', producer_url) | |||
for entry in event_producer.producer_doctypes: | |||
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 | |||
@frappe.whitelist() | |||
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 | |||
@@ -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.enable_custom_script | |||
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; | |||
} | |||
this.export_dialog = frappe.prompt([ | |||
let export_dialog_fields = [ | |||
{ | |||
label: __('Select File Format'), | |||
fieldname: 'file_format', | |||
@@ -1267,13 +1267,18 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||
options: ['Excel', 'CSV'], | |||
default: 'Excel', | |||
reqd: 1 | |||
}, | |||
{ | |||
} | |||
]; | |||
if (this.tree_report) { | |||
export_dialog_fields.push({ | |||
label: __("Include indentation"), | |||
fieldname: "include_indentation", | |||
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); | |||
if (file_format === 'CSV') { | |||
const column_row = this.columns.reduce((acc, col) => { | |||
@@ -72,7 +72,7 @@ | |||
margin-bottom: 10px; | |||
} | |||
.report-wrapper { | |||
.report-wrapper, .datatable-wrapper { | |||
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 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) | |||
return None | |||
else: | |||
@@ -33,9 +33,9 @@ frappe.ui.form.on('Blog Post', { | |||
}); | |||
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 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 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('/') : []; | |||
@@ -26,6 +26,7 @@ | |||
"content_html", | |||
"email_sent", | |||
"meta_tags", | |||
"meta_title", | |||
"meta_description", | |||
"column_break_18", | |||
"meta_image", | |||
@@ -110,7 +111,6 @@ | |||
"depends_on": "eval:doc.content_type === 'Markdown'", | |||
"fieldname": "content_md", | |||
"fieldtype": "Markdown Editor", | |||
"ignore_xss_filter": 1, | |||
"label": "Content (Markdown)" | |||
}, | |||
{ | |||
@@ -185,6 +185,12 @@ | |||
"fieldtype": "Check", | |||
"hidden": 1, | |||
"label": "Hide CTA" | |||
}, | |||
{ | |||
"fieldname": "meta_title", | |||
"fieldtype": "Data", | |||
"label": "Meta Title", | |||
"length": 60 | |||
} | |||
], | |||
"has_web_view": 1, | |||
@@ -194,7 +200,7 @@ | |||
"is_published_field": "published", | |||
"links": [], | |||
"max_attachments": 5, | |||
"modified": "2020-08-31 16:55:03.687862", | |||
"modified": "2020-08-31 21:01:51.100349", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Blog Post", | |||
@@ -36,6 +36,11 @@ class BlogPost(WebsiteGenerator): | |||
if self.blog_intro: | |||
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: | |||
self.meta_description = self.blog_intro[:140] | |||
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.metatags = { | |||
"name": self.title, | |||
"name": self.meta_title, | |||
"description": context.description, | |||
} | |||