Merge branch 'develop' into version-12
This commit is contained in:
commit
61bd8b71ad
23 changed files with 323 additions and 669 deletions
14
.mergify.yml
14
.mergify.yml
|
|
@ -7,7 +7,21 @@ pull_request_rules:
|
|||
- status-success=security/snyk - package.json (frappe)
|
||||
- status-success=security/snyk - requirements.txt (frappe)
|
||||
- label!=don't-merge
|
||||
- label!=squash
|
||||
- "#approved-reviews-by>=1"
|
||||
actions:
|
||||
merge:
|
||||
method: merge
|
||||
- name: Automatic squash on CI success and review
|
||||
conditions:
|
||||
- status-success=Codacy/PR Quality Review
|
||||
- status-success=Semantic Pull Request
|
||||
- status-success=continuous-integration/travis-ci/pr
|
||||
- status-success=security/snyk - package.json (frappe)
|
||||
- status-success=security/snyk - requirements.txt (frappe)
|
||||
- label!=don't-merge
|
||||
- label=squash
|
||||
- "#approved-reviews-by>=1"
|
||||
actions:
|
||||
merge:
|
||||
method: squash
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ if sys.version[0] == '2':
|
|||
reload(sys)
|
||||
sys.setdefaultencoding("utf-8")
|
||||
|
||||
__version__ = '12.0.13'
|
||||
__version__ = '12.0.14'
|
||||
__title__ = "Frappe Framework"
|
||||
|
||||
local = Local()
|
||||
|
|
|
|||
|
|
@ -53,5 +53,7 @@ class TestComment(unittest.TestCase):
|
|||
reference_name = test_blog.name
|
||||
))), 0)
|
||||
|
||||
test_blog.delete()
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -586,9 +586,11 @@ class DocType(Document):
|
|||
if not self.get('is_tree'):
|
||||
return
|
||||
self.add_nestedset_fields()
|
||||
# set field as mandatory
|
||||
field = self.meta.get_field('nsm_parent_field')
|
||||
field.reqd = 1
|
||||
|
||||
if not self.nsm_parent_field:
|
||||
field_label = frappe.bold(_("Parent Field (Tree)"))
|
||||
frappe.throw(_("{0} is a mandatory field").format(field_label), frappe.MandatoryError)
|
||||
|
||||
# check if field is valid
|
||||
fieldnames = [df.fieldname for df in self.fields]
|
||||
if self.nsm_parent_field and self.nsm_parent_field not in fieldnames:
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@
|
|||
"column_break_10",
|
||||
"attached_to_name",
|
||||
"attached_to_field",
|
||||
"lft",
|
||||
"rgt",
|
||||
"old_parent",
|
||||
"content_hash",
|
||||
"uploaded_to_dropbox",
|
||||
|
|
@ -145,18 +143,6 @@
|
|||
"label": "Attached To Field",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "lft",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "lft"
|
||||
},
|
||||
{
|
||||
"fieldname": "rgt",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "rgt"
|
||||
},
|
||||
{
|
||||
"fieldname": "old_parent",
|
||||
"fieldtype": "Data",
|
||||
|
|
@ -186,7 +172,7 @@
|
|||
],
|
||||
"icon": "fa fa-file",
|
||||
"idx": 1,
|
||||
"modified": "2019-08-16 16:41:03.086023",
|
||||
"modified": "2019-08-30 19:46:20.796453",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "File",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from frappe.utils import get_hook_method, get_files_path, random_string, encode,
|
|||
from frappe import _
|
||||
from frappe import conf
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import strip
|
||||
from PIL import Image, ImageOps
|
||||
from six import StringIO, string_types
|
||||
|
|
@ -42,8 +43,7 @@ class FolderNotEmpty(frappe.ValidationError): pass
|
|||
exclude_from_linked_with = True
|
||||
|
||||
|
||||
class File(NestedSet):
|
||||
nsm_parent_field = 'folder'
|
||||
class File(Document):
|
||||
no_feed_on_delete = True
|
||||
|
||||
def before_insert(self):
|
||||
|
|
@ -72,8 +72,6 @@ class File(NestedSet):
|
|||
self.name = frappe.generate_hash("", 10)
|
||||
|
||||
def after_insert(self):
|
||||
self.update_parent_folder_size()
|
||||
|
||||
if not self.is_folder:
|
||||
self.add_comment_in_reference_doc('Attachment',
|
||||
_('Added {0}').format("<a href='{file_url}' target='_blank'>{file_name}</a>{icon}".format(**{
|
||||
|
|
@ -100,7 +98,6 @@ class File(NestedSet):
|
|||
self.validate_file()
|
||||
self.generate_content_hash()
|
||||
|
||||
self.set_folder_size()
|
||||
self.validate_url()
|
||||
|
||||
if frappe.db.exists('File', {'name': self.name, 'is_folder': 0}):
|
||||
|
|
@ -136,31 +133,6 @@ class File(NestedSet):
|
|||
frappe.db.set_value(self.attached_to_doctype, self.attached_to_name,
|
||||
self.attached_to_field, self.file_url)
|
||||
|
||||
|
||||
def set_folder_size(self):
|
||||
"""Set folder size if folder"""
|
||||
if self.is_folder and not self.is_new():
|
||||
self.file_size = cint(self.get_folder_size())
|
||||
self.db_set('file_size', self.file_size)
|
||||
|
||||
for folder in self.get_ancestors():
|
||||
frappe.db.set_value("File", folder, "file_size", self.get_folder_size(folder))
|
||||
|
||||
def get_folder_size(self, folder=None):
|
||||
"""Returns folder size for current folder"""
|
||||
if not folder:
|
||||
folder = self.name
|
||||
|
||||
file_size = frappe.db.sql("""select ifnull(sum(file_size), 0)
|
||||
from tabFile where folder=%s """, (folder))[0][0]
|
||||
|
||||
return file_size
|
||||
|
||||
def update_parent_folder_size(self):
|
||||
"""Update size of parent folder"""
|
||||
if self.folder and not self.is_folder: # it not home
|
||||
frappe.get_doc("File", self.folder).set_folder_size()
|
||||
|
||||
def set_folder_name(self):
|
||||
"""Make parent folders if not exists based on reference doctype and name"""
|
||||
if self.attached_to_doctype and not self.folder:
|
||||
|
|
@ -232,7 +204,6 @@ class File(NestedSet):
|
|||
if self.is_home_folder or self.is_attachments_folder:
|
||||
frappe.throw(_("Cannot delete Home and Attachments folders"))
|
||||
self.check_folder_is_empty()
|
||||
super(File, self).on_trash()
|
||||
self.call_delete_file()
|
||||
if not self.is_folder:
|
||||
self.add_comment_in_reference_doc('Attachment Removed', _("Removed {0}").format(self.file_name))
|
||||
|
|
@ -274,9 +245,6 @@ class File(NestedSet):
|
|||
|
||||
return thumbnail_url
|
||||
|
||||
def after_delete(self):
|
||||
self.update_parent_folder_size()
|
||||
|
||||
def check_folder_is_empty(self):
|
||||
"""Throw exception if folder is not empty"""
|
||||
files = frappe.get_all("File", filters={"folder": self.name}, fields=("name", "file_name"))
|
||||
|
|
@ -598,7 +566,6 @@ class File(NestedSet):
|
|||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("File", ["attached_to_doctype", "attached_to_name"])
|
||||
frappe.db.add_index("File", ["lft", "rgt"])
|
||||
|
||||
def make_home_folder():
|
||||
home = frappe.get_doc({
|
||||
|
|
@ -619,12 +586,16 @@ def make_home_folder():
|
|||
@frappe.whitelist()
|
||||
def get_breadcrumbs(folder):
|
||||
"""returns name, file_name of parent folder"""
|
||||
values = frappe.db.get_value("File", folder, ["lft", "rgt"], as_dict=True)
|
||||
if not values:
|
||||
frappe.throw(_("Folder {0} does not exist").format(folder))
|
||||
path = folder.split('/')
|
||||
|
||||
return frappe.db.sql("""select name, file_name from tabFile
|
||||
where lft < %s and rgt > %s order by lft asc""", (values.lft, values.rgt), as_dict=1)
|
||||
folders = []
|
||||
for i, _ in enumerate(path):
|
||||
indexes = range(0, i)
|
||||
folder = '/'.join([path[i] for i in indexes])
|
||||
if folder:
|
||||
folders.append(folder)
|
||||
|
||||
return [frappe._dict(file_name=f) for f in folders]
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_new_folder(file_name, folder):
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ class TestFile(unittest.TestCase):
|
|||
|
||||
def delete_test_data(self):
|
||||
for f in frappe.db.sql('''select name, file_name from tabFile where
|
||||
is_home_folder = 0 and is_attachments_folder = 0 order by rgt-lft asc'''):
|
||||
is_home_folder = 0 and is_attachments_folder = 0 order by creation desc'''):
|
||||
frappe.delete_doc("File", f[0])
|
||||
|
||||
|
||||
|
|
@ -212,11 +212,8 @@ class TestFile(unittest.TestCase):
|
|||
|
||||
def tests_after_upload(self):
|
||||
self.assertEqual(self.saved_folder, _("Home/Test Folder 1"))
|
||||
|
||||
folder_size = frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size")
|
||||
saved_file_size = frappe.db.get_value("File", self.saved_name, "file_size")
|
||||
|
||||
self.assertEqual(folder_size, saved_file_size)
|
||||
file_folder = frappe.db.get_value("File", self.saved_name, "folder")
|
||||
self.assertEqual(file_folder, _("Home/Test Folder 1"))
|
||||
|
||||
|
||||
def test_file_copy(self):
|
||||
|
|
@ -227,8 +224,6 @@ class TestFile(unittest.TestCase):
|
|||
file = frappe.get_doc("File", {"file_name": "file_copy.txt"})
|
||||
|
||||
self.assertEqual(_("Home/Test Folder 2"), file.folder)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), file.file_size)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), 0)
|
||||
|
||||
|
||||
def test_folder_copy(self):
|
||||
|
|
@ -251,8 +246,6 @@ class TestFile(unittest.TestCase):
|
|||
frappe.get_doc("File", file_copy_txt).delete()
|
||||
|
||||
self.assertEqual(_("Home/Test Folder 1/Test Folder 3"), file.folder)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), file.file_size)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), 0)
|
||||
|
||||
|
||||
def test_default_folder(self):
|
||||
|
|
|
|||
|
|
@ -1,463 +1,128 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "REP.#####",
|
||||
"beta": 0,
|
||||
"creation": "2018-06-25 18:39:11.152960",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"autoname": "REP.#####",
|
||||
"creation": "2018-06-25 18:39:11.152960",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"report_name",
|
||||
"ref_report_doctype",
|
||||
"status",
|
||||
"column_break_4",
|
||||
"report_start_time",
|
||||
"report_end_time",
|
||||
"section_break_7",
|
||||
"error_message",
|
||||
"filters_sb",
|
||||
"filters",
|
||||
"filter_values",
|
||||
"columns"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "report_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Report Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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
|
||||
},
|
||||
"fieldname": "report_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Report Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "ref_report_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Ref Report DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Report",
|
||||
"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
|
||||
},
|
||||
"fieldname": "ref_report_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Ref Report DocType",
|
||||
"options": "Report",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Queued",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"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": "Error\nQueued\nCompleted",
|
||||
"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
|
||||
},
|
||||
"default": "Queued",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"options": "Error\nQueued\nCompleted",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"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
|
||||
},
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "report_start_time",
|
||||
"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": "Report Start Time",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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
|
||||
},
|
||||
"fieldname": "report_start_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Report Start Time",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "report_end_time",
|
||||
"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": "Report End Time",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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
|
||||
},
|
||||
"fieldname": "report_end_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Report End Time",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.status == 'Error'",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_7",
|
||||
"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
|
||||
},
|
||||
"depends_on": "eval:doc.status == 'Error'",
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "error_message",
|
||||
"fieldtype": "Text",
|
||||
"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": "Error Message",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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
|
||||
},
|
||||
"fieldname": "error_message",
|
||||
"fieldtype": "Text",
|
||||
"label": "Error Message",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "filters_sb",
|
||||
"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": "Filters",
|
||||
"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
|
||||
},
|
||||
"fieldname": "filters_sb",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Filters"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "filters",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Filters",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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
|
||||
},
|
||||
"fieldname": "filters",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Filters",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "filter_values",
|
||||
"fieldtype": "HTML",
|
||||
"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": "Filter Values",
|
||||
"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
|
||||
},
|
||||
"fieldname": "filter_values",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Filter Values"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "columns",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Columns",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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
|
||||
"fieldname": "columns",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"label": "Columns",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-19 12:39:47.211516",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Prepared Report",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"in_create": 1,
|
||||
"modified": "2019-09-18 04:00:55.644257",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Prepared Report",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "report_name",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "report_name",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -4,32 +4,25 @@
|
|||
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import base64
|
||||
|
||||
import json
|
||||
import io
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from frappe.desk.query_report import generate_report_result, get_columns_dict
|
||||
from frappe.core.doctype.file.file import remove_all
|
||||
from frappe.utils.csvutils import to_csv, read_csv_content_from_attached_file
|
||||
from frappe.desk.form.load import get_attachments
|
||||
from frappe.desk.query_report import generate_report_result
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import gzip_compress, gzip_decompress
|
||||
from six import PY2
|
||||
from frappe.utils import encode
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from frappe.core.doctype.file.file import remove_all
|
||||
|
||||
|
||||
class PreparedReport(Document):
|
||||
|
||||
def before_insert(self):
|
||||
self.status = "Queued"
|
||||
self.report_start_time = frappe.utils.now()
|
||||
|
||||
def enqueue_report(self):
|
||||
enqueue(
|
||||
run_background,
|
||||
prepared_report=self.name, timeout=6000
|
||||
)
|
||||
enqueue(run_background, prepared_report=self.name, timeout=6000)
|
||||
|
||||
def on_trash(self):
|
||||
remove_all("PreparedReport", self.name, from_delete=True)
|
||||
|
|
@ -42,14 +35,18 @@ def run_background(prepared_report):
|
|||
try:
|
||||
report.custom_columns = []
|
||||
|
||||
if report.report_type == 'Custom Report':
|
||||
if report.report_type == "Custom Report":
|
||||
custom_report_doc = report
|
||||
reference_report = custom_report_doc.reference_report
|
||||
report = frappe.get_doc("Report", reference_report)
|
||||
report.custom_columns = custom_report_doc.json
|
||||
|
||||
result = generate_report_result(report, filters=instance.filters, user=instance.owner)
|
||||
create_json_gz_file(result['result'], 'Prepared Report', instance.name)
|
||||
result = generate_report_result(
|
||||
report=report,
|
||||
filters=instance.filters,
|
||||
user=instance.owner
|
||||
)
|
||||
create_json_gz_file(result["result"], "Prepared Report", instance.name)
|
||||
|
||||
instance.status = "Completed"
|
||||
instance.columns = json.dumps(result["columns"])
|
||||
|
|
@ -64,8 +61,11 @@ def run_background(prepared_report):
|
|||
instance.save(ignore_permissions=True)
|
||||
|
||||
frappe.publish_realtime(
|
||||
'report_generated',
|
||||
{"report_name": instance.report_name, "name": instance.name},
|
||||
"report_generated",
|
||||
{
|
||||
"report_name": instance.report_name,
|
||||
"name": instance.name
|
||||
},
|
||||
user=frappe.session.user
|
||||
)
|
||||
|
||||
|
|
@ -73,7 +73,9 @@ def run_background(prepared_report):
|
|||
def create_json_gz_file(data, dt, dn):
|
||||
# Storing data in CSV file causes information loss
|
||||
# Reports like P&L Statement were completely unsuable because of this
|
||||
json_filename = '{0}.json.gz'.format(frappe.utils.data.format_datetime(frappe.utils.now(), "Y-m-d-H:M"))
|
||||
json_filename = "{0}.json.gz".format(
|
||||
frappe.utils.data.format_datetime(frappe.utils.now(), "Y-m-d-H:M")
|
||||
)
|
||||
encoded_content = frappe.safe_encode(frappe.as_json(data))
|
||||
compressed_content = gzip_compress(encoded_content)
|
||||
|
||||
|
|
@ -87,10 +89,11 @@ def create_json_gz_file(data, dt, dn):
|
|||
})
|
||||
_file.save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_attachment(dn):
|
||||
attachment = get_attachments("Prepared Report", dn)[0]
|
||||
frappe.local.response.filename = attachment.file_name[:-2]
|
||||
attached_file = frappe.get_doc('File', attachment.name)
|
||||
attached_file = frappe.get_doc("File", attachment.name)
|
||||
frappe.local.response.filecontent = gzip_decompress(attached_file.get_content())
|
||||
frappe.local.response.type = "binary"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ def get_emails_from_role(role):
|
|||
|
||||
for user in users:
|
||||
user_email, enabled = frappe.db.get_value("User", user.parent, ["email", "enabled"])
|
||||
if enabled:
|
||||
if enabled and user_email not in ["admin@example.com", "guest@example.com"]:
|
||||
emails.append(user_email)
|
||||
|
||||
return emails
|
||||
|
|
@ -758,7 +758,10 @@ class Database(object):
|
|||
|
||||
def field_exists(self, dt, fn):
|
||||
"""Return true of field exists."""
|
||||
return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn))
|
||||
return self.exists('DocField', {
|
||||
'fieldname': fn,
|
||||
'parent': dt
|
||||
})
|
||||
|
||||
def table_exists(self, doctype):
|
||||
"""Returns True if table for given doctype exists."""
|
||||
|
|
|
|||
|
|
@ -213,6 +213,8 @@ def get_labels(fields, doctype):
|
|||
for key in fields:
|
||||
key = key.split(" as ")[0]
|
||||
|
||||
if key.startswith(('count(', 'sum(', 'avg(')): continue
|
||||
|
||||
if "." in key:
|
||||
parenttype, fieldname = key.split(".")[0][4:-1], key.split(".")[1].strip("`")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -309,9 +309,7 @@ def evaluate_alert(doc, alert, event):
|
|||
else:
|
||||
raise
|
||||
db_value = parse_val(db_value)
|
||||
if (doc.get(alert.value_changed) == db_value) or \
|
||||
(not db_value and not doc.get(alert.value_changed)):
|
||||
|
||||
if (doc.get(alert.value_changed) == db_value) or (not db_value and not doc.get(alert.value_changed)):
|
||||
return # value not changed
|
||||
|
||||
if event != "Value Change" and not doc.is_new():
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ def get_controller(doctype):
|
|||
or ["Core", False]
|
||||
|
||||
if custom:
|
||||
if frappe.db.field_exists(doctype, "is_tree"):
|
||||
is_tree = frappe.db.get_value("DocType", doctype, ("is_tree"), cache=True)
|
||||
if frappe.db.field_exists("DocType", "is_tree"):
|
||||
is_tree = frappe.db.get_value("DocType", doctype, "is_tree", cache=True)
|
||||
else:
|
||||
is_tree = False
|
||||
_class = NestedSet if is_tree else Document
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ def clear_user_permissions_for_doctype(doctype, user=None):
|
|||
filters = {'allow': doctype}
|
||||
if user:
|
||||
filters['user'] = user
|
||||
user_permissions_for_doctype = frappe.db.get_list('User Permission', filters=filters)
|
||||
user_permissions_for_doctype = frappe.db.get_all('User Permission', filters=filters)
|
||||
for d in user_permissions_for_doctype:
|
||||
frappe.delete_doc('User Permission', d.name)
|
||||
|
||||
|
|
|
|||
|
|
@ -186,7 +186,9 @@ frappe.ui.form.QuickEntryForm = Class.extend({
|
|||
}
|
||||
},
|
||||
error: function() {
|
||||
me.open_doc();
|
||||
if (!me.skip_redirect_on_error) {
|
||||
me.open_doc();
|
||||
}
|
||||
},
|
||||
always: function() {
|
||||
me.dialog.working = false;
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ frappe.views.FileView = class FileView extends frappe.views.ListView {
|
|||
|
||||
get_left_html(file) {
|
||||
file = this.prepare_datum(file);
|
||||
const file_size = frappe.form.formatters.FileSize(file.file_size);
|
||||
const file_size = file.file_size ? frappe.form.formatters.FileSize(file.file_size) : '';
|
||||
const route_url = this.get_route_url(file);
|
||||
|
||||
let created_on;
|
||||
|
|
|
|||
|
|
@ -1231,23 +1231,27 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
action: () => {
|
||||
const args = this.get_args();
|
||||
const selected_items = this.get_checked_items(true);
|
||||
let fields = [
|
||||
{
|
||||
fieldtype: 'Select',
|
||||
label: __('Select File Type'),
|
||||
fieldname:'file_format_type',
|
||||
options: ['Excel', 'CSV'],
|
||||
default: 'Excel'
|
||||
}
|
||||
];
|
||||
|
||||
if (this.total_count > args.page_length) {
|
||||
fields.push({
|
||||
fieldtype: 'Check',
|
||||
fieldname: 'export_all_rows',
|
||||
label: __('Export All {0} rows?', [(this.total_count + "").bold()])
|
||||
});
|
||||
}
|
||||
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __("Export Report: {0}",[__(this.doctype)]),
|
||||
fields: [
|
||||
{
|
||||
fieldtype: 'Select',
|
||||
label: __('Select File Type'),
|
||||
fieldname:'file_format_type',
|
||||
options: ['Excel', 'CSV'],
|
||||
default: 'Excel'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Check',
|
||||
fieldname: 'export_all_rows',
|
||||
label: __('Export All {0} rows?', [(this.total_count + "").bold()])
|
||||
}
|
||||
],
|
||||
fields: fields,
|
||||
primary_action_label: __('Download'),
|
||||
primary_action: (data) => {
|
||||
args.cmd = 'frappe.desk.reportview.export_query';
|
||||
|
|
|
|||
|
|
@ -107,7 +107,6 @@ export default class WebForm extends frappe.ui.FieldGroup {
|
|||
let for_payment = Boolean(this.accept_payment && !this.doc.paid);
|
||||
|
||||
this.doc.doctype = this.doc_type;
|
||||
this.doc.name = this.doc_name;
|
||||
this.doc.web_form_name = this.name;
|
||||
|
||||
// Save
|
||||
|
|
|
|||
|
|
@ -201,14 +201,13 @@ export default class WebFormList {
|
|||
() => (window.location.href = window.location.pathname + "?new=1")
|
||||
);
|
||||
|
||||
if (!(this.rows.length < this.page_length)) {
|
||||
if (this.rows.length <= this.page_length) {
|
||||
addButton(footer, "more", "secondary", false, "More", () => this.more());
|
||||
}
|
||||
|
||||
function addButton(wrapper, id, type, hidden, name, action) {
|
||||
const button = document.createElement("button");
|
||||
button.classList.add("btn", "btn-primary", "btn-sm", "ml-2");
|
||||
if (type == "secondary")
|
||||
if (type == "secondary") {
|
||||
button.classList.add(
|
||||
"btn",
|
||||
"btn-secondary",
|
||||
|
|
@ -216,7 +215,8 @@ export default class WebFormList {
|
|||
"ml-2",
|
||||
"text-white"
|
||||
);
|
||||
if (type == "danger")
|
||||
}
|
||||
else if (type == "danger") {
|
||||
button.classList.add(
|
||||
"btn",
|
||||
"btn-danger",
|
||||
|
|
@ -224,6 +224,10 @@ export default class WebFormList {
|
|||
"btn-sm",
|
||||
"ml-2"
|
||||
);
|
||||
}
|
||||
else {
|
||||
button.classList.add("btn", "btn-primary", "btn-sm", "ml-2");
|
||||
}
|
||||
|
||||
button.id = id;
|
||||
button.innerText = name;
|
||||
|
|
|
|||
|
|
@ -185,37 +185,29 @@ class TestReportview(unittest.TestCase):
|
|||
self.assertTrue('date_diff' in data[0])
|
||||
|
||||
def test_nested_permission(self):
|
||||
clear_user_permissions_for_doctype("File")
|
||||
delete_test_file_hierarchy() # delete already existing folders
|
||||
from frappe.core.doctype.file.file import create_new_folder
|
||||
frappe.set_user('Administrator')
|
||||
|
||||
create_new_folder('level1-A', 'Home')
|
||||
create_new_folder('level2-A', 'Home/level1-A')
|
||||
create_new_folder('level2-B', 'Home/level1-A')
|
||||
create_new_folder('level3-A', 'Home/level1-A/level2-A')
|
||||
|
||||
create_new_folder('level1-B', 'Home')
|
||||
create_new_folder('level2-A', 'Home/level1-B')
|
||||
create_nested_doctype()
|
||||
create_nested_doctype_records()
|
||||
clear_user_permissions_for_doctype('Nested DocType')
|
||||
|
||||
# user permission for only one root folder
|
||||
add_user_permission('File', 'Home/level1-A', 'test2@example.com')
|
||||
add_user_permission('Nested DocType', 'Level 1 A', 'test2@example.com')
|
||||
|
||||
from frappe.core.page.permission_manager.permission_manager import update
|
||||
update('File', 'All', 0, 'if_owner', 0) # to avoid if_owner filter
|
||||
# to avoid if_owner filter
|
||||
update('Nested DocType', 'All', 0, 'if_owner', 0)
|
||||
|
||||
frappe.set_user('test2@example.com')
|
||||
data = DatabaseQuery("File").execute()
|
||||
data = DatabaseQuery('Nested DocType').execute()
|
||||
|
||||
# children of root folder (for which we added user permission) should be accessible
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertTrue({'name': 'Level 2 A'} in data)
|
||||
self.assertTrue({'name': 'Level 2 A'} in data)
|
||||
|
||||
# other folders should not be accessible
|
||||
self.assertFalse({"name": "Home/level1-B"} in data)
|
||||
self.assertFalse({"name": "Home/level1-B/level2-B"} in data)
|
||||
update('File', 'All', 0, 'if_owner', 1)
|
||||
self.assertFalse({'name': 'Level 1 B'} in data)
|
||||
self.assertFalse({'name': 'Level 2 B'} in data)
|
||||
update('Nested DocType', 'All', 0, 'if_owner', 1)
|
||||
frappe.set_user('Administrator')
|
||||
|
||||
def test_filter_sanitizer(self):
|
||||
|
|
@ -259,83 +251,74 @@ class TestReportview(unittest.TestCase):
|
|||
self.assertTrue('DefaultValue' in [d['name'] for d in out])
|
||||
|
||||
def test_of_not_of_descendant_ancestors(self):
|
||||
clear_user_permissions_for_doctype("File")
|
||||
delete_test_file_hierarchy() # delete already existing folders
|
||||
from frappe.core.doctype.file.file import create_new_folder
|
||||
|
||||
create_new_folder('level1-A', 'Home')
|
||||
create_new_folder('level2-A', 'Home/level1-A')
|
||||
create_new_folder('level2-B', 'Home/level1-A')
|
||||
create_new_folder('level3-A', 'Home/level1-A/level2-A')
|
||||
|
||||
create_new_folder('level1-B', 'Home')
|
||||
create_new_folder('level2-A', 'Home/level1-B')
|
||||
frappe.set_user('Administrator')
|
||||
clear_user_permissions_for_doctype('Nested DocType')
|
||||
|
||||
# in descendants filter
|
||||
data = frappe.get_all('File', {'name': ('descendants of', 'Home/level1-A/level2-A')})
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('descendants of', 'Level 2 A')})
|
||||
self.assertTrue({"name": "Level 3 A"} in data)
|
||||
|
||||
data = frappe.get_all('File', {'name': ('descendants of', 'Home/level1-A')})
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertFalse({"name": "Home/level1-B"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A"} in data)
|
||||
self.assertFalse({"name": "Home"} in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('descendants of', 'Level 1 A')})
|
||||
self.assertTrue({"name": "Level 3 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 A"} in data)
|
||||
self.assertFalse({"name": "Level 2 B"} in data)
|
||||
self.assertFalse({"name": "Level 1 B"} in data)
|
||||
self.assertFalse({"name": "Level 1 A"} in data)
|
||||
self.assertFalse({"name": "Root"} in data)
|
||||
|
||||
# in ancestors of filter
|
||||
data = frappe.get_all('File', {'name': ('ancestors of', 'Home/level1-A/level2-A')})
|
||||
self.assertFalse({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertFalse({"name": "Home/level1-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A"} in data)
|
||||
self.assertTrue({"name": "Home"} in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('ancestors of', 'Level 2 A')})
|
||||
self.assertFalse({"name": "Level 3 A"} in data)
|
||||
self.assertFalse({"name": "Level 2 A"} in data)
|
||||
self.assertFalse({"name": "Level 2 B"} in data)
|
||||
self.assertFalse({"name": "Level 1 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 A"} in data)
|
||||
self.assertTrue({"name": "Root"} in data)
|
||||
|
||||
data = frappe.get_all('File', {'name': ('ancestors of', 'Home/level1-A')})
|
||||
self.assertFalse({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertFalse({"name": "Home/level1-B"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A"} in data)
|
||||
self.assertTrue({"name": "Home"} in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('ancestors of', 'Level 1 A')})
|
||||
self.assertFalse({"name": "Level 3 A"} in data)
|
||||
self.assertFalse({"name": "Level 2 A"} in data)
|
||||
self.assertFalse({"name": "Level 2 B"} in data)
|
||||
self.assertFalse({"name": "Level 1 B"} in data)
|
||||
self.assertFalse({"name": "Level 1 A"} in data)
|
||||
self.assertTrue({"name": "Root"} in data)
|
||||
|
||||
# not descendants filter
|
||||
data = frappe.get_all('File', {'name': ('not descendants of', 'Home/level1-A/level2-A')})
|
||||
self.assertFalse({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A"} in data)
|
||||
self.assertTrue({"name": "Home"} in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('not descendants of', 'Level 2 A')})
|
||||
self.assertFalse({"name": "Level 3 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 A"} in data)
|
||||
self.assertTrue({"name": "Root"} in data)
|
||||
|
||||
data = frappe.get_all('File', {'name': ('not descendants of', 'Home/level1-A')})
|
||||
self.assertFalse({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertFalse({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A"} in data)
|
||||
self.assertTrue({"name": "Home"} in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('not descendants of', 'Level 1 A')})
|
||||
self.assertFalse({"name": "Level 3 A"} in data)
|
||||
self.assertFalse({"name": "Level 2 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 A"} in data)
|
||||
self.assertTrue({"name": "Root"} in data)
|
||||
|
||||
# not ancestors of filter
|
||||
data = frappe.get_all('File', {'name': ('not ancestors of', 'Home/level1-A/level2-A')})
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A"} not in data)
|
||||
self.assertTrue({"name": "Home"} not in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('not ancestors of', 'Level 2 A')})
|
||||
self.assertTrue({"name": "Level 3 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 A"} not in data)
|
||||
self.assertTrue({"name": "Root"} not in data)
|
||||
|
||||
data = frappe.get_all('File', {'name': ('not ancestors of', 'Home/level1-A')})
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A/level3-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-A"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A/level2-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-B"} in data)
|
||||
self.assertTrue({"name": "Home/level1-A"} in data)
|
||||
self.assertFalse({"name": "Home"} in data)
|
||||
data = frappe.get_all('Nested DocType', {'name': ('not ancestors of', 'Level 1 A')})
|
||||
self.assertTrue({"name": "Level 3 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 A"} in data)
|
||||
self.assertTrue({"name": "Level 2 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 B"} in data)
|
||||
self.assertTrue({"name": "Level 1 A"} in data)
|
||||
self.assertFalse({"name": "Root"} in data)
|
||||
|
||||
data = frappe.get_all('File', {'name': ('ancestors of', 'Home')})
|
||||
data = frappe.get_all('Nested DocType', {'name': ('ancestors of', 'Root')})
|
||||
self.assertTrue(len(data) == 0)
|
||||
self.assertTrue(len(frappe.get_all('File', {'name': ('not ancestors of', 'Home')})) == len(frappe.get_all('File')))
|
||||
self.assertTrue(len(frappe.get_all('Nested DocType', {'name': ('not ancestors of', 'Root')})) == len(frappe.get_all('Nested DocType')))
|
||||
|
||||
|
||||
def test_is_set_is_not_set(self):
|
||||
|
|
@ -364,14 +347,45 @@ def create_event(subject="_Test Event", starts_on=None):
|
|||
|
||||
return event
|
||||
|
||||
def delete_test_file_hierarchy():
|
||||
files_to_delete = [
|
||||
'Home/level1-A/level2-A/level3-A',
|
||||
'Home/level1-A/level2-A',
|
||||
'Home/level1-A/level2-B',
|
||||
'Home/level1-A',
|
||||
'Home/level1-B/level2-A',
|
||||
'Home/level1-B'
|
||||
def create_nested_doctype():
|
||||
if frappe.db.exists('DocType', 'Nested DocType'):
|
||||
return
|
||||
|
||||
frappe.get_doc({
|
||||
'doctype': 'DocType',
|
||||
'name': 'Nested DocType',
|
||||
'module': 'Custom',
|
||||
'is_tree': 1,
|
||||
'custom': 1,
|
||||
'autoname': 'Prompt',
|
||||
'fields': [
|
||||
{'label': 'Description', 'fieldname': 'description'}
|
||||
],
|
||||
'permissions': [
|
||||
{'role': 'Blogger'}
|
||||
]
|
||||
}).insert()
|
||||
|
||||
def create_nested_doctype_records():
|
||||
'''
|
||||
Create a structure like:
|
||||
- Root
|
||||
- Level 1 A
|
||||
- Level 2 A
|
||||
- Level 3 A
|
||||
- Level 1 B
|
||||
- Level 2 B
|
||||
'''
|
||||
records = [
|
||||
{'name': 'Root', 'is_group': 1},
|
||||
{'name': 'Level 1 A', 'parent_nested_doctype': 'Root', 'is_group': 1},
|
||||
{'name': 'Level 2 A', 'parent_nested_doctype': 'Level 1 A', 'is_group': 1},
|
||||
{'name': 'Level 3 A', 'parent_nested_doctype': 'Level 2 A'},
|
||||
{'name': 'Level 1 B', 'parent_nested_doctype': 'Root', 'is_group': 1},
|
||||
{'name': 'Level 2 B', 'parent_nested_doctype': 'Level 1 B'},
|
||||
]
|
||||
for file_name in files_to_delete:
|
||||
frappe.delete_doc('File', file_name)
|
||||
|
||||
for r in records:
|
||||
d = frappe.new_doc('Nested DocType')
|
||||
d.update(r)
|
||||
d.insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ from frappe.auth import HTTPRequest
|
|||
from frappe.utils import cint
|
||||
from frappe.tests import set_request
|
||||
from frappe.twofactor import (should_run_2fa, authenticate_for_2factor, get_cached_user_pass,
|
||||
two_factor_is_enabled_for_, confirm_otp_token, get_otpsecret_for_, get_verification_obj,
|
||||
render_string_template)
|
||||
two_factor_is_enabled_for_, confirm_otp_token, get_otpsecret_for_, get_verification_obj)
|
||||
|
||||
import time
|
||||
|
||||
|
|
@ -123,7 +122,7 @@ class TestTwoFactor(unittest.TestCase):
|
|||
'''String template renders as expected with variables.'''
|
||||
args = {'issuer_name':'Frappe Technologies'}
|
||||
_str = 'Verification Code from {{issuer_name}}'
|
||||
_str = render_string_template(_str,args)
|
||||
_str = frappe.render_template(_str,args)
|
||||
self.assertEqual(_str,'Verification Code from Frappe Technologies')
|
||||
|
||||
def test_bypass_restict_ip(self):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import frappe
|
|||
from frappe import _
|
||||
import pyotp, os
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from jinja2 import Template
|
||||
from pyqrcode import create as qrcreate
|
||||
from six import BytesIO
|
||||
from base64 import b64encode, b32encode
|
||||
|
|
@ -223,33 +222,27 @@ def process_2fa_for_email(user, token, otp_secret, otp_issuer, method='Email'):
|
|||
def get_email_subject_for_2fa(kwargs_dict):
|
||||
'''Get email subject for 2fa.'''
|
||||
subject_template = _('Login Verification Code from {}').format(frappe.db.get_value('System Settings', 'System Settings', 'otp_issuer_name'))
|
||||
subject = render_string_template(subject_template, kwargs_dict)
|
||||
subject = frappe.render_template(subject_template, kwargs_dict)
|
||||
return subject
|
||||
|
||||
def get_email_body_for_2fa(kwargs_dict):
|
||||
'''Get email body for 2fa.'''
|
||||
body_template = 'Enter this code to complete your login:<br><br> <b>{{otp}}</b>'
|
||||
body = render_string_template(body_template, kwargs_dict)
|
||||
body = frappe.render_template(body_template, kwargs_dict)
|
||||
return body
|
||||
|
||||
def get_email_subject_for_qr_code(kwargs_dict):
|
||||
'''Get QRCode email subject.'''
|
||||
subject_template = _('One Time Password (OTP) Registration Code from {}').format(frappe.db.get_value('System Settings', 'System Settings', 'otp_issuer_name'))
|
||||
subject = render_string_template(subject_template, kwargs_dict)
|
||||
subject = frappe.render_template(subject_template, kwargs_dict)
|
||||
return subject
|
||||
|
||||
def get_email_body_for_qr_code(kwargs_dict):
|
||||
'''Get QRCode email body.'''
|
||||
body_template = 'Please click on the following link and follow the instructions on the page.<br><br> {{qrcode_link}}'
|
||||
body = render_string_template(body_template, kwargs_dict)
|
||||
body = frappe.render_template(body_template, kwargs_dict)
|
||||
return body
|
||||
|
||||
def render_string_template(_str, kwargs_dict):
|
||||
'''Render string with jinja.'''
|
||||
s = Template(_str)
|
||||
s = s.render(**kwargs_dict)
|
||||
return s
|
||||
|
||||
def get_link_for_qrcode(user, totp_uri):
|
||||
'''Get link to temporary page showing QRCode.'''
|
||||
key = frappe.generate_hash(length=20)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue