Bläddra i källkod

Merge branch 'develop' of https://github.com/frappe/frappe into fix-blog-post-pagination

version-14
Aditya Hase 4 år sedan
förälder
incheckning
dce13faa52
Ingen känd nyckel hittad för denna signaturen i databasen GPG-nyckel ID: A55F0FCA0234972
26 ändrade filer med 289 tillägg och 697 borttagningar
  1. +21
    -15
      .github/frappe_linter/translation.py
  2. +2
    -2
      cypress/integration/recorder.js
  3. +2
    -2
      frappe/__init__.py
  4. +5
    -1
      frappe/commands/utils.py
  5. +49
    -596
      frappe/core/doctype/activity_log/activity_log.json
  6. +0
    -3
      frappe/core/doctype/activity_log/activity_log.py
  7. +21
    -20
      frappe/core/doctype/file/file.py
  8. +1
    -0
      frappe/custom/doctype/package_publish_tool/package_publish_tool.py
  9. +1
    -1
      frappe/desk/desktop.py
  10. +2
    -2
      frappe/desk/doctype/dashboard_chart/dashboard_chart.py
  11. +1
    -1
      frappe/desk/doctype/desk_page/desk_page.py
  12. +5
    -3
      frappe/event_streaming/doctype/event_consumer/event_consumer.json
  13. +19
    -11
      frappe/event_streaming/doctype/event_consumer/event_consumer.py
  14. +7
    -4
      frappe/event_streaming/doctype/event_producer/event_producer.json
  15. +32
    -9
      frappe/event_streaming/doctype/event_producer/event_producer.py
  16. +34
    -14
      frappe/event_streaming/doctype/event_producer/test_event_producer.py
  17. +0
    -1
      frappe/model/rename_doc.py
  18. +1
    -0
      frappe/patches.txt
  19. +11
    -0
      frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py
  20. +10
    -5
      frappe/public/js/frappe/views/reports/query_report.js
  21. +1
    -1
      frappe/public/less/report.less
  22. +46
    -0
      frappe/tests/test_commands.py
  23. +2
    -1
      frappe/utils/__init__.py
  24. +2
    -2
      frappe/website/doctype/blog_post/blog_post.js
  25. +8
    -2
      frappe/website/doctype/blog_post/blog_post.json
  26. +6
    -1
      frappe/website/doctype/blog_post/blog_post.py

+ 21
- 15
.github/frappe_linter/translation.py Visa fil

@@ -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!')

+ 2
- 2
cypress/integration/recorder.js Visa fil

@@ -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();


+ 2
- 2
frappe/__init__.py Visa fil

@@ -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



+ 5
- 1
frappe/commands/utils.py Visa fil

@@ -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()


+ 49
- 596
frappe/core/doctype/activity_log/activity_log.json Visa fil

@@ -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",


+ 0
- 3
frappe/core/doctype/activity_log/activity_log.py Visa fil

@@ -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"])


+ 21
- 20
frappe/core/doctype/file/file.py Visa fil

@@ -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", {


+ 1
- 0
frappe/custom/doctype/package_publish_tool/package_publish_tool.py Visa fil

@@ -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)



+ 1
- 1
frappe/desk/desktop.py Visa fil

@@ -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)


+ 2
- 2
frappe/desk/doctype/dashboard_chart/dashboard_chart.py Visa fil

@@ -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



+ 1
- 1
frappe/desk/doctype/desk_page/desk_page.py Visa fil

@@ -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 \


+ 5
- 3
frappe/event_streaming/doctype/event_consumer/event_consumer.json Visa fil

@@ -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",


+ 19
- 11
frappe/event_streaming/doctype/event_consumer/event_consumer.py Visa fil

@@ -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



+ 7
- 4
frappe/event_streaming/doctype/event_producer/event_producer.json Visa fil

@@ -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",


+ 32
- 9
frappe/event_streaming/doctype/event_producer/event_producer.py Visa fil

@@ -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



+ 34
- 14
frappe/event_streaming/doctype/event_producer/test_event_producer.py Visa fil

@@ -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()

+ 0
- 1
frappe/model/rename_doc.py Visa fil

@@ -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


+ 1
- 0
frappe/patches.txt Visa fil

@@ -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

+ 11
- 0
frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py Visa fil

@@ -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=''""")

+ 10
- 5
frappe/public/js/frappe/views/reports/query_report.js Visa fil

@@ -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) => {


+ 1
- 1
frappe/public/less/report.less Visa fil

@@ -72,7 +72,7 @@
margin-bottom: 10px;
}

.report-wrapper {
.report-wrapper, .datatable-wrapper {
overflow: auto;
}



+ 46
- 0
frappe/tests/test_commands.py Visa fil

@@ -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'))

+ 2
- 1
frappe/utils/__init__.py Visa fil

@@ -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:


+ 2
- 2
frappe/website/doctype/blog_post/blog_post.js Visa fil

@@ -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('/') : [];


+ 8
- 2
frappe/website/doctype/blog_post/blog_post.json Visa fil

@@ -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",


+ 6
- 1
frappe/website/doctype/blog_post/blog_post.py Visa fil

@@ -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,
}



Laddar…
Avbryt
Spara