Merge branch 'frappe:develop' into develop
This commit is contained in:
commit
77ae41728f
48 changed files with 317 additions and 157 deletions
|
|
@ -2,32 +2,58 @@ context('Query Report', () => {
|
|||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.insert_doc('Report', {
|
||||
'report_name': 'Test ToDo Report',
|
||||
'ref_doctype': 'ToDo',
|
||||
'report_type': 'Query Report',
|
||||
'query': 'select * from tabToDo'
|
||||
}, true).as('doc');
|
||||
});
|
||||
|
||||
it('add custom column in report', () => {
|
||||
cy.visit('/app/query-report/Permitted Documents For User');
|
||||
|
||||
cy.get('.page-form.flex', { timeout: 60000 }).should('have.length', 1).then(() => {
|
||||
cy.get('#page-query-report input[data-fieldname="user"]').as('input');
|
||||
cy.get('@input').focus().type('test@erpnext.com', { delay: 100 }).blur();
|
||||
cy.get('#page-query-report input[data-fieldname="user"]').as('input-user');
|
||||
cy.get('@input-user').focus().type('test@erpnext.com', { delay: 100 }).blur();
|
||||
cy.wait(300);
|
||||
cy.get('#page-query-report input[data-fieldname="doctype"]').as('input-test');
|
||||
cy.get('@input-test').focus().type('Role', { delay: 100 }).blur();
|
||||
cy.get('#page-query-report input[data-fieldname="doctype"]').as('input-role');
|
||||
cy.get('@input-role').focus().type('Role', { delay: 100 }).blur();
|
||||
|
||||
cy.get('.datatable').should('exist');
|
||||
cy.get('.menu-btn-group button').click({ force: true });
|
||||
cy.get('.dropdown-menu li').contains('Add Column').click({ force: true });
|
||||
cy.get('.modal-dialog').should('contain', 'Add Column');
|
||||
cy.get('#page-query-report .page-actions .menu-btn-group button').click({ force: true });
|
||||
cy.get('#page-query-report .menu-btn-group .dropdown-menu').contains('Add Column').click({ force: true });
|
||||
cy.get_open_dialog().get('.modal-title').should('contain', 'Add Column');
|
||||
cy.get('select[data-fieldname="doctype"]').select("Role", { force: true });
|
||||
cy.get('select[data-fieldname="field"]').select("Role Name", { force: true });
|
||||
cy.get('select[data-fieldname="insert_after"]').select("Name", { force: true });
|
||||
cy.get('button').contains('Submit').click({ force: true });
|
||||
cy.get('.menu-btn-group button').click({ force: true });
|
||||
cy.get('.dropdown-menu li').contains('Save').click({ force: true });
|
||||
cy.get('.modal-dialog').should('contain', 'Save Report');
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Submit'}).click({ force: true });
|
||||
cy.get('#page-query-report .page-actions .menu-btn-group button').click({ force: true });
|
||||
cy.get('#page-query-report .menu-btn-group .dropdown-menu').contains('Save').click({ timeout: 100, force: true });
|
||||
cy.get_open_dialog().get('.modal-title').should('contain', 'Save Report');
|
||||
|
||||
cy.get('input[data-fieldname="report_name"]').type("Test Report", { delay: 100, force: true });
|
||||
cy.get('button').contains('Submit').click({ timeout: 1000, force: true });
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Submit'}).click({ timeout: 1000, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
let save_report_and_open = (report, update_name) => {
|
||||
cy.get('#page-query-report .page-actions .menu-btn-group button').click({ force: true });
|
||||
cy.get('#page-query-report .menu-btn-group .dropdown-menu').contains('Save').click({ timeout: 100, force: true });
|
||||
cy.get_open_dialog().get('.modal-title').should('contain', 'Save Report');
|
||||
|
||||
cy.get('input[data-fieldname="report_name"]').type(update_name, { delay: 100, force: true });
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Submit'}).click({ timeout: 1000, force: true });
|
||||
|
||||
cy.visit('/app/query-report/'+report);
|
||||
cy.get('.datatable').should('exist');
|
||||
};
|
||||
|
||||
it('test multi level query report', () => {
|
||||
cy.visit('/app/query-report/Test ToDo Report');
|
||||
cy.get('.datatable').should('exist');
|
||||
|
||||
save_report_and_open('Test ToDo Report 1', ' 1');
|
||||
save_report_and_open('Test ToDo Report 11', '1');
|
||||
});
|
||||
});
|
||||
|
|
@ -17,7 +17,7 @@ context('Sidebar', () => {
|
|||
cy.get('.group-by-item > .dropdown-item').should('contain', 'Me');
|
||||
|
||||
//Assigning a doctype to a user
|
||||
cy.click_listview_row_item(0);
|
||||
cy.visit('/app/doctype/ToDo');
|
||||
cy.get('.form-assignments > .flex > .text-muted').click();
|
||||
cy.get_field('assign_to_me', 'Check').click();
|
||||
cy.get('.modal-footer > .standard-actions > .btn-primary').click();
|
||||
|
|
@ -44,8 +44,7 @@ context('Sidebar', () => {
|
|||
cy.clear_filters();
|
||||
|
||||
//To remove the assignment
|
||||
cy.visit('/app/doctype');
|
||||
cy.click_listview_row_item(0);
|
||||
cy.visit('/app/doctype/ToDo');
|
||||
cy.get('.assignments > .avatar-group > .avatar > .avatar-frame').click();
|
||||
cy.get('.remove-btn').click({force: true});
|
||||
cy.hide_dialog();
|
||||
|
|
@ -53,4 +52,4 @@ context('Sidebar', () => {
|
|||
cy.click_sidebar_button("Assigned To");
|
||||
cy.get('.empty-state').should('contain', 'No filters found');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -487,11 +487,11 @@ def get_request_header(key, default=None):
|
|||
:param default: Default value."""
|
||||
return request.headers.get(key, default)
|
||||
|
||||
def sendmail(recipients=[], sender="", subject="No Subject", message="No Message",
|
||||
def sendmail(recipients=None, sender="", subject="No Subject", message="No Message",
|
||||
as_markdown=False, delayed=True, reference_doctype=None, reference_name=None,
|
||||
unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, add_unsubscribe_link=1,
|
||||
attachments=None, content=None, doctype=None, name=None, reply_to=None, queue_separately=False,
|
||||
cc=[], bcc=[], message_id=None, in_reply_to=None, send_after=None, expose_recipients=None,
|
||||
cc=None, bcc=None, message_id=None, in_reply_to=None, send_after=None, expose_recipients=None,
|
||||
send_priority=1, communication=None, retry=1, now=None, read_receipt=None, is_notification=False,
|
||||
inline_images=None, template=None, args=None, header=None, print_letterhead=False, with_container=False):
|
||||
"""Send email using user's default **Email Account** or global default **Email Account**.
|
||||
|
|
@ -521,6 +521,14 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
|
|||
:param header: Append header in email
|
||||
:param with_container: Wraps email inside a styled container
|
||||
"""
|
||||
|
||||
if recipients is None:
|
||||
recipients = []
|
||||
if cc is None:
|
||||
cc = []
|
||||
if bcc is None:
|
||||
bcc = []
|
||||
|
||||
text_content = None
|
||||
if template:
|
||||
message, text_content = get_email_from_template(template, args)
|
||||
|
|
@ -718,18 +726,20 @@ def only_has_select_perm(doctype, user=None, ignore_permissions=False):
|
|||
else:
|
||||
return False
|
||||
|
||||
def has_permission(doctype=None, ptype="read", doc=None, user=None, verbose=False, throw=False):
|
||||
def has_permission(doctype=None, ptype="read", doc=None, user=None, verbose=False, throw=False, parent_doctype=None):
|
||||
"""Raises `frappe.PermissionError` if not permitted.
|
||||
|
||||
:param doctype: DocType for which permission is to be check.
|
||||
:param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`.
|
||||
:param doc: [optional] Checks User permissions for given doc.
|
||||
:param user: [optional] Check for given user. Default: current user."""
|
||||
:param user: [optional] Check for given user. Default: current user.
|
||||
:param parent_doctype: Required when checking permission for a child DocType (unless doc is specified)."""
|
||||
if not doctype and doc:
|
||||
doctype = doc.doctype
|
||||
|
||||
import frappe.permissions
|
||||
out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user, raise_exception=throw)
|
||||
out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user,
|
||||
raise_exception=throw, parent_doctype=parent_doctype)
|
||||
if throw and not out:
|
||||
if doc:
|
||||
frappe.throw(_("No permission for {0}").format(doc.doctype + " " + doc.name))
|
||||
|
|
|
|||
|
|
@ -659,10 +659,14 @@ def publish_realtime(context, event, message, room, user, doctype, docname, afte
|
|||
|
||||
@click.command('browse')
|
||||
@click.argument('site', required=False)
|
||||
@click.option('--user', required=False, help='Login as user')
|
||||
@pass_context
|
||||
def browse(context, site):
|
||||
def browse(context, site, user=None):
|
||||
'''Opens the site on web browser'''
|
||||
from frappe.auth import LoginManager
|
||||
from frappe.auth import CookieManager
|
||||
import webbrowser
|
||||
|
||||
site = context.sites[0] if context.sites else site
|
||||
|
||||
if not site:
|
||||
|
|
@ -672,7 +676,24 @@ def browse(context, site):
|
|||
site = site.lower()
|
||||
|
||||
if site in frappe.utils.get_sites():
|
||||
webbrowser.open(frappe.utils.get_site_url(site), new=2)
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
|
||||
sid = ''
|
||||
if user:
|
||||
if frappe.conf.developer_mode or user == "Administrator":
|
||||
frappe.utils.set_request(path="/")
|
||||
frappe.local.cookie_manager = CookieManager()
|
||||
frappe.local.login_manager = LoginManager()
|
||||
frappe.local.login_manager.login_as(user)
|
||||
sid = f'/app?sid={frappe.session.sid}'
|
||||
else:
|
||||
print("Please enable developer mode to login as a user")
|
||||
|
||||
url = f'{frappe.utils.get_site_url(site)}{sid}'
|
||||
if user == "Administrator":
|
||||
print(f'Login URL: {url}')
|
||||
webbrowser.open(url, new=2)
|
||||
else:
|
||||
click.echo("\nSite named \033[1m{}\033[0m doesn't exist\n".format(site))
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ def load_address_and_contact(doc, key=None):
|
|||
["Dynamic Link", "link_name", "=", doc.name],
|
||||
["Dynamic Link", "parenttype", "=", "Address"],
|
||||
]
|
||||
address_list = frappe.get_all("Address", filters=filters, fields=["*"])
|
||||
address_list = frappe.get_list("Address", filters=filters, fields=["*"])
|
||||
|
||||
address_list = [a.update({"display": get_address_display(a)})
|
||||
for a in address_list]
|
||||
|
|
@ -34,16 +34,16 @@ def load_address_and_contact(doc, key=None):
|
|||
["Dynamic Link", "link_name", "=", doc.name],
|
||||
["Dynamic Link", "parenttype", "=", "Contact"],
|
||||
]
|
||||
contact_list = frappe.get_all("Contact", filters=filters, fields=["*"])
|
||||
contact_list = frappe.get_list("Contact", filters=filters, fields=["*"])
|
||||
|
||||
for contact in contact_list:
|
||||
contact["email_ids"] = frappe.get_list("Contact Email", filters={
|
||||
contact["email_ids"] = frappe.get_all("Contact Email", filters={
|
||||
"parenttype": "Contact",
|
||||
"parent": contact.name,
|
||||
"is_primary": 0
|
||||
}, fields=["email_id"])
|
||||
|
||||
contact["phone_nos"] = frappe.get_list("Contact Phone", filters={
|
||||
contact["phone_nos"] = frappe.get_all("Contact Phone", filters={
|
||||
"parenttype": "Contact",
|
||||
"parent": contact.name,
|
||||
"is_primary_phone": 0,
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ def get_contact_with_phone_number(number):
|
|||
return contacts[0].parent if contacts else None
|
||||
|
||||
def get_contact_name(email_id):
|
||||
contact = frappe.get_list("Contact Email", filters={"email_id": email_id}, fields=["parent"], limit=1)
|
||||
contact = frappe.get_all("Contact Email", filters={"email_id": email_id}, fields=["parent"], limit=1)
|
||||
return contact[0].parent if contact else None
|
||||
|
||||
def get_contacts_linking_to(doctype, docname, fields=None):
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@
|
|||
"icon": "fa fa-comment",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-28 11:43:57.504565",
|
||||
"modified": "2021-10-25 11:43:57.504565",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Activity Log",
|
||||
|
|
@ -182,6 +182,5 @@
|
|||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "subject",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ def get_contacts(email_strings, auto_create_contact=False):
|
|||
return contacts
|
||||
|
||||
def add_contact_links_to_communication(communication, contact_name):
|
||||
contact_links = frappe.get_list("Dynamic Link", filters={
|
||||
contact_links = frappe.get_all("Dynamic Link", filters={
|
||||
"parenttype": "Contact",
|
||||
"parent": contact_name
|
||||
}, fields=["link_doctype", "link_name"])
|
||||
|
|
|
|||
|
|
@ -763,7 +763,9 @@ class Column:
|
|||
seen = []
|
||||
fields_column_map = {}
|
||||
|
||||
def __init__(self, index, header, doctype, column_values, map_to_field=None, seen=[]):
|
||||
def __init__(self, index, header, doctype, column_values, map_to_field=None, seen=None):
|
||||
if seen is None:
|
||||
seen = []
|
||||
self.index = index
|
||||
self.column_number = index + 1
|
||||
self.doctype = doctype
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@
|
|||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"default": "0",
|
||||
"depends_on": "eval:!doc.istable",
|
||||
"description": "If enabled, changes to the document are tracked and shown in timeline",
|
||||
"fieldname": "track_changes",
|
||||
|
|
@ -649,7 +649,7 @@
|
|||
"link_fieldname": "reference_doctype"
|
||||
}
|
||||
],
|
||||
"modified": "2021-09-05 15:39:13.233403",
|
||||
"modified": "2021-10-29 11:39:13.233403",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocType",
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-03-14 12:21:44.292471",
|
||||
"modified": "2021-10-25 12:21:44.292471",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Error Log",
|
||||
|
|
@ -144,6 +144,5 @@
|
|||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -359,7 +359,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-12-29 14:40:38.619106",
|
||||
"modified": "2021-10-25 14:40:38.619106",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Error Snapshot",
|
||||
|
|
@ -394,6 +394,5 @@
|
|||
"sort_field": "timestamp",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "evalue",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-01-22 00:00:00.000000",
|
||||
"modified": "2021-10-25 00:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Scheduled Job Log",
|
||||
|
|
@ -59,6 +59,5 @@
|
|||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,16 +69,6 @@ frappe.method_that_doesnt_exist("do some magic")
|
|||
disabled = 1,
|
||||
script = '''
|
||||
frappe.db.commit()
|
||||
'''
|
||||
),
|
||||
dict(
|
||||
name='test_cache_methods',
|
||||
script_type = 'DocType Event',
|
||||
doctype_event = 'Before Save',
|
||||
reference_doctype = 'ToDo',
|
||||
disabled = 1,
|
||||
script = '''
|
||||
frappe.cache().set_value('test_key', doc.name)
|
||||
'''
|
||||
)
|
||||
]
|
||||
|
|
@ -149,14 +139,3 @@ class TestServerScript(unittest.TestCase):
|
|||
|
||||
server_script.disabled = 1
|
||||
server_script.save()
|
||||
|
||||
def test_cache_methods_in_server_script(self):
|
||||
server_script = frappe.get_doc('Server Script', 'test_cache_methods')
|
||||
server_script.disabled = 0
|
||||
server_script.save()
|
||||
|
||||
todo = frappe.get_doc(dict(doctype='ToDo', description='test me')).insert()
|
||||
self.assertEqual(todo.name, frappe.cache().get_value('test_key'))
|
||||
|
||||
server_script.disabled = 1
|
||||
server_script.save()
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-09-05 14:22:27.664645",
|
||||
"modified": "2021-10-25 14:22:27.664645",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "View Log",
|
||||
|
|
@ -158,7 +158,6 @@
|
|||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@
|
|||
"hide_toolbar": 1,
|
||||
"in_create": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified": "2021-10-25 17:26:09.703215",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Notification Log",
|
||||
|
|
@ -139,6 +139,5 @@
|
|||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "subject",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-10-05 13:26:03.106050",
|
||||
"modified": "2021-10-25 13:26:03.106050",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Route History",
|
||||
|
|
@ -121,7 +121,6 @@
|
|||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ def get_submitted_linked_docs(doctype, name, docs=None, visited=None):
|
|||
|
||||
|
||||
@frappe.whitelist()
|
||||
def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=[]):
|
||||
def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=None):
|
||||
"""
|
||||
Cancel all linked doctype, optionally ignore doctypes specified in a list.
|
||||
|
||||
|
|
@ -85,6 +85,8 @@ def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=[]):
|
|||
docs (json str) - It contains list of dictionaries of a linked documents.
|
||||
ignore_doctypes_on_cancel_all (list) - List of doctypes to ignore while cancelling.
|
||||
"""
|
||||
if ignore_doctypes_on_cancel_all is None:
|
||||
ignore_doctypes_on_cancel_all = []
|
||||
|
||||
docs = json.loads(docs)
|
||||
if isinstance(ignore_doctypes_on_cancel_all, str):
|
||||
|
|
@ -96,7 +98,7 @@ def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=[]):
|
|||
frappe.publish_progress(percent=i/len(docs) * 100, title=_("Cancelling documents"))
|
||||
|
||||
|
||||
def validate_linked_doc(docinfo, ignore_doctypes_on_cancel_all=[]):
|
||||
def validate_linked_doc(docinfo, ignore_doctypes_on_cancel_all=None):
|
||||
"""
|
||||
Validate a document to be submitted and non-exempted from auto-cancel.
|
||||
|
||||
|
|
@ -109,7 +111,7 @@ def validate_linked_doc(docinfo, ignore_doctypes_on_cancel_all=[]):
|
|||
"""
|
||||
|
||||
#ignore doctype to cancel
|
||||
if docinfo.get("doctype") in ignore_doctypes_on_cancel_all:
|
||||
if docinfo.get("doctype") in (ignore_doctypes_on_cancel_all or []):
|
||||
return False
|
||||
|
||||
# skip non-submittable doctypes since they don't need to be cancelled
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ def get_preview_data(doctype, docname):
|
|||
|
||||
for key, val in preview_data.items():
|
||||
if val and meta.has_field(key) and key not in [image_field, title_field, 'name']:
|
||||
formatted_preview_data[meta.get_field(key).label] = frappe.format(val, meta.get_field(key).fieldtype)
|
||||
formatted_preview_data[meta.get_field(key).label] = frappe.format(
|
||||
val,
|
||||
meta.get_field(key).fieldtype,
|
||||
translated=True,
|
||||
)
|
||||
|
||||
return formatted_preview_data
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ def get_filters_for(doctype):
|
|||
|
||||
@frappe.whitelist()
|
||||
@frappe.read_only()
|
||||
def get_open_count(doctype, name, items=[]):
|
||||
def get_open_count(doctype, name, items=None):
|
||||
'''Get open count for given transactions and filters
|
||||
|
||||
:param doctype: Reference DocType
|
||||
|
|
@ -235,7 +235,8 @@ def get_open_count(doctype, name, items=[]):
|
|||
links = meta.get_dashboard_data()
|
||||
|
||||
# compile all items in a list
|
||||
if not items:
|
||||
if items is None:
|
||||
items = []
|
||||
for group in links.transactions:
|
||||
items.extend(group.get("items"))
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,19 @@ def get_report_doc(report_name):
|
|||
return doc
|
||||
|
||||
|
||||
def get_report_result(report, filters):
|
||||
if report.report_type == "Query Report":
|
||||
res = report.execute_query_report(filters)
|
||||
|
||||
elif report.report_type == "Script Report":
|
||||
res = report.execute_script_report(filters)
|
||||
|
||||
elif report.report_type == "Custom Report":
|
||||
ref_report = get_report_doc(report.report_name)
|
||||
res = get_report_result(ref_report, filters)
|
||||
|
||||
return res
|
||||
|
||||
def generate_report_result(report, filters=None, user=None, custom_columns=None):
|
||||
user = user or frappe.session.user
|
||||
filters = filters or []
|
||||
|
|
@ -66,13 +79,7 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None)
|
|||
if filters and isinstance(filters, str):
|
||||
filters = json.loads(filters)
|
||||
|
||||
res = []
|
||||
|
||||
if report.report_type == "Query Report":
|
||||
res = report.execute_query_report(filters)
|
||||
|
||||
elif report.report_type == "Script Report":
|
||||
res = report.execute_script_report(filters)
|
||||
res = get_report_result(report, filters) or []
|
||||
|
||||
columns, result, message, chart, report_summary, skip_total_row = ljust_list(res, 6)
|
||||
columns = [get_column_as_dict(col) for col in columns]
|
||||
|
|
|
|||
|
|
@ -180,15 +180,16 @@ def update_wildcard_field_param(data):
|
|||
|
||||
|
||||
def clean_params(data):
|
||||
data.pop('cmd', None)
|
||||
data.pop('data', None)
|
||||
data.pop('ignore_permissions', None)
|
||||
data.pop('view', None)
|
||||
data.pop('user', None)
|
||||
|
||||
if "csrf_token" in data:
|
||||
del data["csrf_token"]
|
||||
|
||||
for param in (
|
||||
"cmd",
|
||||
"data",
|
||||
"ignore_permissions",
|
||||
"view",
|
||||
"user",
|
||||
"csrf_token",
|
||||
"join"
|
||||
):
|
||||
data.pop(param, None)
|
||||
|
||||
def parse_json(data):
|
||||
if isinstance(data.get("filters"), str):
|
||||
|
|
@ -214,11 +215,13 @@ def get_parenttype_and_fieldname(field, data):
|
|||
|
||||
return parenttype, fieldname
|
||||
|
||||
def compress(data, args = {}):
|
||||
def compress(data, args=None):
|
||||
"""separate keys and values"""
|
||||
from frappe.desk.query_report import add_total_row
|
||||
|
||||
if not data: return data
|
||||
if args is None:
|
||||
args = {}
|
||||
values = []
|
||||
keys = list(data[0])
|
||||
for row in data:
|
||||
|
|
@ -423,15 +426,20 @@ def delete_bulk(doctype, items):
|
|||
|
||||
@frappe.whitelist()
|
||||
@frappe.read_only()
|
||||
def get_sidebar_stats(stats, doctype, filters=[]):
|
||||
def get_sidebar_stats(stats, doctype, filters=None):
|
||||
if filters is None:
|
||||
filters = []
|
||||
|
||||
return {"stats": get_stats(stats, doctype, filters)}
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.read_only()
|
||||
def get_stats(stats, doctype, filters=[]):
|
||||
def get_stats(stats, doctype, filters=None):
|
||||
"""get tag info"""
|
||||
import json
|
||||
|
||||
if filters is None:
|
||||
filters = []
|
||||
tags = json.loads(stats)
|
||||
if filters:
|
||||
filters = json.loads(filters)
|
||||
|
|
@ -480,12 +488,11 @@ def get_stats(stats, doctype, filters=[]):
|
|||
return stats
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_filter_dashboard_data(stats, doctype, filters=[]):
|
||||
def get_filter_dashboard_data(stats, doctype, filters=None):
|
||||
"""get tags info"""
|
||||
import json
|
||||
tags = json.loads(stats)
|
||||
if filters:
|
||||
filters = json.loads(filters)
|
||||
filters = json.loads(filters or [])
|
||||
stats = {}
|
||||
|
||||
columns = frappe.db.get_table_columns(doctype)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ from email import policy
|
|||
|
||||
def get_email(recipients, sender='', msg='', subject='[No Subject]',
|
||||
text_content = None, footer=None, print_html=None, formatted=None, attachments=None,
|
||||
content=None, reply_to=None, cc=[], bcc=[], email_account=None, expose_recipients=None,
|
||||
inline_images=[], header=None):
|
||||
content=None, reply_to=None, cc=None, bcc=None, email_account=None, expose_recipients=None,
|
||||
inline_images=None, header=None):
|
||||
""" Prepare an email with the following format:
|
||||
- multipart/mixed
|
||||
- multipart/alternative
|
||||
|
|
@ -25,6 +25,14 @@ def get_email(recipients, sender='', msg='', subject='[No Subject]',
|
|||
- attachment
|
||||
"""
|
||||
content = content or msg
|
||||
|
||||
if cc is None:
|
||||
cc = []
|
||||
if bcc is None:
|
||||
bcc = []
|
||||
if inline_images is None:
|
||||
inline_images = []
|
||||
|
||||
emailobj = EMail(sender, recipients, subject, reply_to=reply_to, cc=cc, bcc=bcc, email_account=email_account, expose_recipients=expose_recipients)
|
||||
|
||||
if not content.strip().startswith("<"):
|
||||
|
|
|
|||
|
|
@ -340,7 +340,7 @@ class EmailServer:
|
|||
|
||||
return error_msg
|
||||
|
||||
def update_flag(self, uid_list={}):
|
||||
def update_flag(self, uid_list=None):
|
||||
""" set all uids mails the flag as seen """
|
||||
|
||||
if not uid_list:
|
||||
|
|
|
|||
|
|
@ -286,12 +286,16 @@ class FrappeClient(object):
|
|||
doc.modified = frappe.db.get_single_value(doctype, "modified")
|
||||
frappe.get_doc(doc).insert()
|
||||
|
||||
def get_api(self, method, params={}):
|
||||
def get_api(self, method, params=None):
|
||||
if params is None:
|
||||
params = {}
|
||||
res = self.session.get(self.url + "/api/method/" + method + "/",
|
||||
params=params, verify=self.verify, headers=self.headers)
|
||||
return self.post_process(res)
|
||||
|
||||
def post_api(self, method, params={}):
|
||||
def post_api(self, method, params=None):
|
||||
if params is None:
|
||||
params = {}
|
||||
res = self.session.post(self.url + "/api/method/" + method + "/",
|
||||
params=params, verify=self.verify, headers=self.headers)
|
||||
return self.post_process(res)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ class BaseDocument(object):
|
|||
if hasattr(self, "__setup__"):
|
||||
self.__setup__()
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.get(key) if hasattr(self, key) else frappe.throw(msg=key, exc=KeyError)
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
if not getattr(self, "_meta", None):
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ class DatabaseQuery(object):
|
|||
join='left join', distinct=False, start=None, page_length=None, limit=None,
|
||||
ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False,
|
||||
update=None, add_total_row=None, user_settings=None, reference_doctype=None,
|
||||
run=True, strict=True, pluck=None, ignore_ddl=False) -> List:
|
||||
run=True, strict=True, pluck=None, ignore_ddl=False, parent_doctype=None) -> List:
|
||||
if not ignore_permissions and \
|
||||
not frappe.has_permission(self.doctype, "select", user=user) and \
|
||||
not frappe.has_permission(self.doctype, "read", user=user):
|
||||
not frappe.has_permission(self.doctype, "select", user=user, parent_doctype=parent_doctype) and \
|
||||
not frappe.has_permission(self.doctype, "read", user=user, parent_doctype=parent_doctype):
|
||||
|
||||
frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(self.doctype))
|
||||
raise frappe.PermissionError(self.doctype)
|
||||
|
|
@ -318,7 +318,8 @@ class DatabaseQuery(object):
|
|||
doctype = table_name[4:-1]
|
||||
ptype = 'select' if frappe.only_has_select_perm(doctype) else 'read'
|
||||
|
||||
if not self.flags.ignore_permissions and not frappe.has_permission(doctype, ptype=ptype):
|
||||
if not self.flags.ignore_permissions and \
|
||||
not frappe.has_permission(doctype, ptype=ptype, parent_doctype=self.doctype):
|
||||
frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(doctype))
|
||||
raise frappe.PermissionError(doctype)
|
||||
|
||||
|
|
@ -487,9 +488,9 @@ class DatabaseQuery(object):
|
|||
f.value = date_range
|
||||
fallback = "'0001-01-01 00:00:00'"
|
||||
|
||||
if (f.fieldname in ('creation', 'modified')):
|
||||
if f.operator in ('>', '<') and (f.fieldname in ('creation', 'modified')):
|
||||
value = cstr(f.value)
|
||||
fallback = "NULL"
|
||||
fallback = "'0001-01-01 00:00:00'"
|
||||
|
||||
elif f.operator.lower() in ('between') and \
|
||||
(f.fieldname in ('creation', 'modified') or (df and (df.fieldtype=="Date" or df.fieldtype=="Datetime"))):
|
||||
|
|
@ -544,6 +545,7 @@ class DatabaseQuery(object):
|
|||
fallback = 0
|
||||
|
||||
if isinstance(f.value, Column):
|
||||
can_be_null = False # added to avoid the ifnull/coalesce addition
|
||||
quote = '"' if frappe.conf.db_type == 'postgres' else "`"
|
||||
value = f"{tname}.{quote}{f.value.name}{quote}"
|
||||
|
||||
|
|
|
|||
|
|
@ -238,7 +238,9 @@ class ParallelTestWithOrchestrator(ParallelTestRunner):
|
|||
self.call_orchestrator('test-completed')
|
||||
return super().print_result()
|
||||
|
||||
def call_orchestrator(self, endpoint, data={}):
|
||||
def call_orchestrator(self, endpoint, data=None):
|
||||
if data is None:
|
||||
data = {}
|
||||
# add repo token header
|
||||
# build id in header
|
||||
headers = {
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ frappe.patches.v13_0.rename_list_view_setting_to_list_view_settings
|
|||
frappe.patches.v13_0.remove_twilio_settings
|
||||
frappe.patches.v12_0.rename_uploaded_files_with_proper_name
|
||||
frappe.patches.v13_0.queryreport_columns
|
||||
execute:frappe.reload_doc('core', 'doctype', 'doctype')
|
||||
frappe.patches.v13_0.jinja_hook
|
||||
frappe.patches.v13_0.update_notification_channel_if_empty
|
||||
frappe.patches.v14_0.drop_data_import_legacy
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ def print_has_permission_check_logs(func):
|
|||
return inner
|
||||
|
||||
@print_has_permission_check_logs
|
||||
def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None, raise_exception=True):
|
||||
def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None, raise_exception=True, parent_doctype=None):
|
||||
"""Returns True if user has permission `ptype` for given `doctype`.
|
||||
If `doc` is passed, it also checks user, share and owner permissions.
|
||||
|
||||
|
|
@ -47,11 +47,12 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None, ra
|
|||
doc = doctype
|
||||
doctype = doc.doctype
|
||||
|
||||
if frappe.is_table(doctype):
|
||||
if user == "Administrator":
|
||||
return True
|
||||
|
||||
if user=="Administrator":
|
||||
return True
|
||||
if frappe.is_table(doctype):
|
||||
return has_child_table_permission(doctype, ptype, doc, verbose,
|
||||
user, raise_exception, parent_doctype)
|
||||
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
||||
|
|
@ -96,7 +97,7 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None, ra
|
|||
if not perm:
|
||||
perm = false_if_not_shared()
|
||||
|
||||
return perm
|
||||
return bool(perm)
|
||||
|
||||
def get_doc_permissions(doc, user=None, ptype=None):
|
||||
"""Returns a dict of evaluated permissions for given `doc` like `{"read":1, "write":1}`"""
|
||||
|
|
@ -560,3 +561,35 @@ def filter_allowed_docs_for_doctype(user_permissions, doctype, with_default_doc=
|
|||
def push_perm_check_log(log):
|
||||
if frappe.flags.get('has_permission_check_logs') == None: return
|
||||
frappe.flags.get('has_permission_check_logs').append(_(log))
|
||||
|
||||
def has_child_table_permission(child_doctype, ptype="read", child_doc=None,
|
||||
verbose=False, user=None, raise_exception=True, parent_doctype=None):
|
||||
parent_doc = None
|
||||
|
||||
if child_doc:
|
||||
parent_doctype = child_doc.get("parenttype")
|
||||
parent_doc = frappe.get_cached_doc({
|
||||
"doctype": parent_doctype,
|
||||
"docname": child_doc.get("parent")
|
||||
})
|
||||
|
||||
if parent_doctype:
|
||||
if not is_parent_valid(child_doctype, parent_doctype):
|
||||
frappe.throw(_("{0} is not a valid parent DocType for {1}").format(
|
||||
frappe.bold(parent_doctype),
|
||||
frappe.bold(child_doctype)
|
||||
), title=_("Invalid Parent DocType"))
|
||||
else:
|
||||
frappe.throw(_("Please specify a valid parent DocType for {0}").format(
|
||||
frappe.bold(child_doctype)
|
||||
), title=_("Parent DocType Required"))
|
||||
|
||||
return has_permission(parent_doctype, ptype=ptype, doc=parent_doc,
|
||||
verbose=verbose, user=user, raise_exception=raise_exception)
|
||||
|
||||
|
||||
def is_parent_valid(child_doctype, parent_doctype):
|
||||
from frappe.core.utils import find
|
||||
parent_meta = frappe.get_meta(parent_doctype)
|
||||
child_table_field_exists = find(parent_meta.get_table_fields(), lambda d: d.options == child_doctype)
|
||||
return not parent_meta.istable and child_table_field_exists
|
||||
|
|
@ -55,6 +55,10 @@ Quill.register(FontStyle, true);
|
|||
Quill.register(AlignStyle, true);
|
||||
Quill.register(DirectionStyle, true);
|
||||
|
||||
// direction class
|
||||
const DirectionClass = Quill.import('attributors/class/direction');
|
||||
Quill.register(DirectionClass, true);
|
||||
|
||||
// replace font tag with span
|
||||
const Inline = Quill.import('blots/inline');
|
||||
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ frappe.search.SearchDialog = class {
|
|||
get_link(result) {
|
||||
let link = "";
|
||||
if (result.route) {
|
||||
link = `href="#${result.route.join("/")}"`;
|
||||
link = `href="/app/${result.route.join("/")}"`;
|
||||
} else if (result.data_path) {
|
||||
link = `data-path=${result.data_path}"`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
.navbar-light {
|
||||
border-bottom: 1px solid $border-color;
|
||||
background: $navbar-bg;
|
||||
|
||||
.navbar-toggler .icon {
|
||||
stroke: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-primary {
|
||||
|
|
@ -25,6 +29,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.navbar-search {
|
||||
background-color: var(--blue-400);
|
||||
width: 300px;
|
||||
|
|
@ -36,6 +44,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
border-color: rgba(255,255,255, 0.1);
|
||||
|
||||
.icon {
|
||||
stroke: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
svg use {
|
||||
--icon-stroke: white;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-06 17:25:40.477044",
|
||||
"modified": "2021-10-25 17:25:40.477044",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Social",
|
||||
"name": "Energy Point Log",
|
||||
|
|
@ -131,6 +131,5 @@
|
|||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "user",
|
||||
"track_changes": 1
|
||||
}
|
||||
"title_field": "user"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@
|
|||
aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
<span>
|
||||
<svg class="icon icon-lg">
|
||||
<use href="#icon-menu"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@
|
|||
aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
<span>
|
||||
<svg class="icon icon-lg">
|
||||
<use href="#icon-menu"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
|
|
|
|||
|
|
@ -60,7 +60,10 @@ def main(app=None, module=None, doctype=None, verbose=False, tests=(),
|
|||
|
||||
# workaround! since there is no separate test db
|
||||
frappe.clear_cache()
|
||||
frappe.utils.scheduler.disable_scheduler()
|
||||
scheduler_disabled_by_user = frappe.utils.scheduler.is_scheduler_disabled()
|
||||
if not scheduler_disabled_by_user:
|
||||
frappe.utils.scheduler.disable_scheduler()
|
||||
|
||||
set_test_email_config()
|
||||
frappe.conf.update({'bench_id': 'test_bench', 'use_rq_auth': False})
|
||||
|
||||
|
|
@ -77,6 +80,9 @@ def main(app=None, module=None, doctype=None, verbose=False, tests=(),
|
|||
else:
|
||||
ret = run_all_tests(app, verbose, profile, ui_tests, failfast=failfast, junit_xml_output=junit_xml_output)
|
||||
|
||||
if not scheduler_disabled_by_user:
|
||||
frappe.utils.scheduler.enable_scheduler()
|
||||
|
||||
if frappe.db: frappe.db.commit()
|
||||
|
||||
# workaround! since there is no separate test db
|
||||
|
|
|
|||
|
|
@ -142,6 +142,12 @@ class TestReportview(unittest.TestCase):
|
|||
self.assertTrue({ "name": event1.name } not in data)
|
||||
self.assertTrue({ "name": event2.name } not in data)
|
||||
|
||||
# test between is formatted for creation column
|
||||
data = DatabaseQuery("Event").execute(
|
||||
filters={"creation": ["between", ["2016-07-06", "2016-07-07"]]},
|
||||
fields=["name"])
|
||||
|
||||
|
||||
def test_ignore_permissions_for_get_filters_cond(self):
|
||||
frappe.set_user('test2@example.com')
|
||||
self.assertRaises(frappe.PermissionError, get_filters_cond, 'DocType', dict(istable=1), [])
|
||||
|
|
|
|||
|
|
@ -593,3 +593,13 @@ class TestPermissions(unittest.TestCase):
|
|||
|
||||
# reset the user
|
||||
frappe.set_user(current_user)
|
||||
|
||||
def test_child_table_permissions(self):
|
||||
frappe.set_user("test@example.com")
|
||||
self.assertIsInstance(frappe.get_list("Has Role", parent_doctype="User", limit=1), list)
|
||||
self.assertRaisesRegex(frappe.exceptions.ValidationError,
|
||||
".* is not a valid parent DocType for .*", frappe.get_list, doctype="Has Role", parent_doctype="ToDo")
|
||||
self.assertRaisesRegex(frappe.exceptions.ValidationError,
|
||||
"Please specify a valid parent DocType for .*", frappe.get_list, "Has Role")
|
||||
self.assertRaisesRegex(frappe.exceptions.ValidationError,
|
||||
".* is not a valid parent DocType for .*", frappe.get_list, doctype="Has Role", parent_doctype="Has Role")
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ from frappe.utils import get_url, get_datetime, time_diff_in_seconds, cint
|
|||
|
||||
class ExpiredLoginException(Exception): pass
|
||||
|
||||
def toggle_two_factor_auth(state, roles=[]):
|
||||
def toggle_two_factor_auth(state, roles=None):
|
||||
'''Enable or disable 2FA in site_config and roles'''
|
||||
for role in roles:
|
||||
for role in roles or []:
|
||||
role = frappe.get_doc('Role', {'role_name': role})
|
||||
role.two_factor_auth = cint(state)
|
||||
role.save(ignore_permissions=True)
|
||||
|
|
@ -417,4 +417,4 @@ def reset_otp_secret(user):
|
|||
enqueue(method=frappe.sendmail, queue='short', timeout=300, event=None, is_async=True, job_name=None, now=False, **email_args)
|
||||
return frappe.msgprint(_("OTP Secret has been reset. Re-registration will be required on next login."))
|
||||
else:
|
||||
return frappe.throw(_("OTP secret can only be reset by the Administrator."))
|
||||
return frappe.throw(_("OTP secret can only be reset by the Administrator."))
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,10 @@ def image_to_base64(image, extn):
|
|||
|
||||
def pdf_to_base64(filename):
|
||||
from frappe.utils.file_manager import get_file_path
|
||||
|
||||
if '../' in filename or filename.rsplit('.')[-1] not in ['pdf', 'PDF']:
|
||||
return
|
||||
|
||||
file_path = get_file_path(filename)
|
||||
if not file_path:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -302,6 +302,9 @@ def get_file(fname):
|
|||
|
||||
def get_file_path(file_name):
|
||||
"""Returns file path from given file name"""
|
||||
if '../' in file_name:
|
||||
return
|
||||
|
||||
f = frappe.db.sql("""select file_url from `tabFile`
|
||||
where name=%s or file_name=%s""", (file_name, file_name))
|
||||
if f:
|
||||
|
|
|
|||
|
|
@ -68,6 +68,13 @@ def web_blocks(blocks):
|
|||
return html
|
||||
|
||||
|
||||
def get_dom_id(seed=None):
|
||||
from frappe import generate_hash
|
||||
if not seed:
|
||||
seed = 'DOM'
|
||||
return 'id-' + generate_hash(seed, 12)
|
||||
|
||||
|
||||
def include_script(path):
|
||||
path = bundled_asset(path)
|
||||
return f'<script type="text/javascript" src="{path}"></script>'
|
||||
|
|
@ -94,4 +101,4 @@ def is_rtl(rtl=None):
|
|||
from frappe import local
|
||||
if rtl is None:
|
||||
return local.lang in ["ar", "he", "fa", "ps"]
|
||||
return rtl
|
||||
return rtl
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
import copy
|
||||
import inspect
|
||||
import json
|
||||
import mimetypes
|
||||
|
|
@ -129,7 +130,7 @@ def get_safe_globals():
|
|||
make_get_request=frappe.integrations.utils.make_get_request,
|
||||
make_post_request=frappe.integrations.utils.make_post_request,
|
||||
socketio_port=frappe.conf.socketio_port,
|
||||
get_hooks=frappe.get_hooks,
|
||||
get_hooks=get_hooks,
|
||||
sanitize_html=frappe.utils.sanitize_html,
|
||||
log_error=frappe.log_error
|
||||
),
|
||||
|
|
@ -173,8 +174,6 @@ def get_safe_globals():
|
|||
rollback=frappe.db.rollback,
|
||||
)
|
||||
|
||||
out.frappe.cache = cache
|
||||
|
||||
if frappe.response:
|
||||
out.frappe.response = frappe.response
|
||||
|
||||
|
|
@ -192,13 +191,9 @@ def get_safe_globals():
|
|||
|
||||
return out
|
||||
|
||||
def cache():
|
||||
return NamespaceDict(
|
||||
get_value = frappe.cache().get_value,
|
||||
set_value = frappe.cache().set_value,
|
||||
hset = frappe.cache().hset,
|
||||
hget = frappe.cache().hget
|
||||
)
|
||||
def get_hooks(hook=None, default=None, app_name=None):
|
||||
hooks = frappe.get_hooks(hook=hook, default=default, app_name=app_name)
|
||||
return copy.deepcopy(hooks)
|
||||
|
||||
def read_sql(query, *args, **kwargs):
|
||||
'''a wrapper for frappe.db.sql to allow reads'''
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
],
|
||||
"in_create": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-05 14:11:24.718770",
|
||||
"modified": "2021-10-25 14:11:24.718770",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Web Page View",
|
||||
|
|
@ -82,6 +82,5 @@
|
|||
"read_only": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "path",
|
||||
"track_changes": 1
|
||||
}
|
||||
"title_field": "path"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@
|
|||
aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
<span>
|
||||
<svg class="icon icon-lg">
|
||||
<use href="#icon-menu"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ def get_list_data(doctype, txt=None, limit_start=0, fields=None, cmd=None, limit
|
|||
"""Returns processed HTML page for a standard listing."""
|
||||
limit_start = cint(limit_start)
|
||||
|
||||
if frappe.is_table(doctype):
|
||||
frappe.throw(_("Child DocTypes are not allowed"), title=_("Invalid DocType"))
|
||||
|
||||
if not txt and frappe.form_dict.search:
|
||||
txt = frappe.form_dict.search
|
||||
del frappe.form_dict['search']
|
||||
|
|
@ -183,8 +186,7 @@ def get_list_context(context, doctype, web_form_name=None):
|
|||
|
||||
return list_context
|
||||
|
||||
def get_list(doctype, txt, filters, limit_start, limit_page_length=20, ignore_permissions=False,
|
||||
fields=None, order_by=None):
|
||||
def get_list(doctype, txt, filters, limit_start, limit_page_length=20, ignore_permissions=False, fields=None, order_by=None):
|
||||
meta = frappe.get_meta(doctype)
|
||||
if not filters:
|
||||
filters = []
|
||||
|
|
|
|||
13
yarn.lock
13
yarn.lock
|
|
@ -554,9 +554,9 @@ caniuse-api@^3.0.0:
|
|||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001196, caniuse-lite@^1.0.30001219:
|
||||
version "1.0.30001228"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa"
|
||||
integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==
|
||||
version "1.0.30001272"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001272.tgz"
|
||||
integrity sha512-DV1j9Oot5dydyH1v28g25KoVm7l8MTxazwuiH3utWiAS6iL/9Nh//TGwqFEeqqN8nnWYQ8HHhUq+o4QPt9kvYw==
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
|
|
@ -4934,13 +4934,6 @@ vuedraggable@^2.24.3:
|
|||
dependencies:
|
||||
sortablejs "1.10.2"
|
||||
|
||||
wcwidth@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
||||
integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
|
||||
dependencies:
|
||||
defaults "^1.0.3"
|
||||
|
||||
which-boxed-primitive@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue