@@ -9,7 +9,7 @@ Welcome to the Frappe Framework issue tracker! Before creating an issue, please | |||
1. This tracker should only be used to report bugs and request features / enhancements to Frappe | |||
- For questions and general support, use https://stackoverflow.com/questions/tagged/frappe | |||
- For documentation issues, refer to https://frappe.io/docs/user/en or the developer cheetsheet https://github.com/frappe/frappe/wiki/Developer-Cheatsheet | |||
- For documentation issues, refer to https://frappeframework.com/docs/user/en or the developer cheetsheet https://github.com/frappe/frappe/wiki/Developer-Cheatsheet | |||
2. Use the search function before creating a new issue. Duplicates will be closed and directed to | |||
the original discussion. | |||
3. When making a bug report, make sure you provide all required information. The easier it is for | |||
@@ -9,7 +9,7 @@ Welcome to the Frappe Framework issue tracker! Before creating an issue, please | |||
1. This tracker should only be used to report bugs and request features / enhancements to Frappe | |||
- For questions and general support, refer to https://stackoverflow.com/questions/tagged/frappe | |||
- For documentation issues, use https://frappe.io/docs/user/en or the developer cheetsheet https://github.com/frappe/frappe/wiki/Developer-Cheatsheet | |||
- For documentation issues, use https://frappeframework.com/docs/user/en or the developer cheetsheet https://frappeframework.com/docs/user/en/bench/resources/bench-commands-cheatsheet | |||
2. Use the search function before creating a new issue. Duplicates will be closed and directed to | |||
the original discussion. | |||
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen. | |||
@@ -12,7 +12,7 @@ for questions about using `ERPNext`: https://discuss.erpnext.com | |||
for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/frappe/bench) | |||
For documentation issues, use the [Frappe Framework Documentation](https://frappe.io/docs/user/en) or the [developer cheetsheet](https://github.com/frappe/frappe/wiki/Developer-Cheatsheet) | |||
For documentation issues, use the [Frappe Framework Documentation](https://frappeframework.com/docs) or the [developer cheetsheet](https://github.com/frappe/frappe/wiki/Developer-Cheatsheet) | |||
For a slightly outdated yet informative developer guide: https://www.youtube.com/playlist?list=PL3lFfCEoMxvzHtsZHFJ4T3n5yMM3nGJ1W | |||
@@ -22,7 +22,7 @@ for _file in files: | |||
print(f'A syntax error has been discovered at line number: {num}') | |||
print(f'Syntax error occurred with: {line}') | |||
if errors_encounter > 0: | |||
print('You can visit "https://frappe.io/docs/user/en/translations" to resolve this error.') | |||
print('You can visit "https://frappeframework.com/docs/user/en/translations" to resolve this error.') | |||
assert 1+1 == 3 | |||
else: | |||
print('Good To Go!') |
@@ -1,5 +1,5 @@ | |||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. | |||
version: v1.14.1 | |||
version: v1.19.0 | |||
# ignores vulnerabilities until expiry date; change duration by modifying expiry date | |||
ignore: | |||
SNYK-JS-AWESOMPLETE-174474: | |||
@@ -63,3 +63,5 @@ patch: | |||
patched: '2020-04-30T23:02:32.330Z' | |||
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash: | |||
patched: '2020-04-30T23:02:32.330Z' | |||
- quill-image-resize > lodash: | |||
patched: '2020-08-24T23:06:37.710Z' |
@@ -17,7 +17,7 @@ | |||
<a href="https://travis-ci.com/frappe/frappe"> | |||
<img src="https://travis-ci.com/frappe/frappe.svg?branch=develop"> | |||
</a> | |||
<a href='https://frappe.io/docs'> | |||
<a href='https://frappeframework.com/docs'> | |||
<img src='https://img.shields.io/badge/docs-📖-7575FF.svg?style=flat-square'/> | |||
</a> | |||
<a href='https://www.codetriage.com/frappe/frappe'> | |||
@@ -61,7 +61,7 @@ context('Recorder', () => { | |||
cy.visit('/desk#recorder'); | |||
cy.contains('.list-row-container span', 'frappe.desk.reportview.get').click(); | |||
cy.get('.list-row-container span').contains('frappe.desk.reportview.get').click(); | |||
cy.location('hash').should('contain', '#recorder/request/'); | |||
cy.get('form').should('contain', 'frappe.desk.reportview.get'); | |||
@@ -73,6 +73,8 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren | |||
frappe.throw(_("No permission for {0}").format(doctype), frappe.PermissionError) | |||
filters = get_safe_filters(filters) | |||
if isinstance(filters, string_types): | |||
filters = {"name": filters} | |||
try: | |||
fields = json.loads(fieldname) | |||
@@ -85,7 +87,12 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren | |||
if not filters: | |||
filters = None | |||
value = frappe.get_list(doctype, filters=filters, fields=fields, debug=debug, limit=1) | |||
if frappe.get_meta(doctype).issingle: | |||
value = frappe.db.get_values_from_single(fields, filters, doctype, as_dict=as_dict, debug=debug) | |||
else: | |||
value = frappe.get_list(doctype, filters=filters, fields=fields, debug=debug, limit=1) | |||
if as_dict: | |||
value = value[0] if value else {} | |||
else: | |||
@@ -188,7 +188,7 @@ def contact_query(doctype, txt, searchfield, start, page_len, filters): | |||
from frappe.desk.reportview import get_match_cond | |||
if not frappe.get_meta("Contact").get_field(searchfield)\ | |||
or searchfield not in frappe.db.DEFAULT_COLUMNS: | |||
and searchfield not in frappe.db.DEFAULT_COLUMNS: | |||
return [] | |||
link_doctype = filters.pop('link_doctype') | |||
@@ -465,6 +465,8 @@ class ImportFile: | |||
if doctype != self.doctype and table_df: | |||
child_doc = row.parse_doc(doctype, parent_doc, table_df) | |||
if child_doc is None: | |||
continue | |||
parent_doc[table_df.fieldname] = parent_doc.get(table_df.fieldname, []) | |||
parent_doc[table_df.fieldname].append(child_doc) | |||
@@ -570,6 +572,11 @@ class Row: | |||
def parse_doc(self, doctype, parent_doc=None, table_df=None): | |||
col_indexes = self.header.get_column_indexes(doctype, table_df) | |||
values = self.get_values(col_indexes) | |||
if all(v in INVALID_VALUES for v in values): | |||
# if all values are invalid, no need to parse it | |||
return None | |||
columns = self.header.get_columns(col_indexes) | |||
doc = self._parse_doc(doctype, columns, values, parent_doc, table_df) | |||
return doc | |||
@@ -163,6 +163,7 @@ | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", | |||
"fieldname": "in_preview", | |||
"fieldtype": "Check", | |||
"label": "In Preview" | |||
@@ -475,9 +476,10 @@ | |||
} | |||
], | |||
"idx": 1, | |||
"index_web_pages_for_search": 1, | |||
"istable": 1, | |||
"links": [], | |||
"modified": "2020-02-06 09:06:25.224413", | |||
"modified": "2020-08-28 11:28:21.252853", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "DocField", | |||
@@ -989,7 +989,8 @@ def clear_permissions_cache(doctype): | |||
`tabHas Role`, | |||
`tabDocPerm` | |||
WHERE `tabDocPerm`.`parent` = %s | |||
AND `tabDocPerm`.`role` = `tabHas Role`.`role` | |||
AND `tabDocPerm`.`role` = `tabHas Role`.`role` | |||
AND `tabHas Role`.`parenttype` = 'User' | |||
""", doctype): | |||
frappe.clear_cache(user=user) | |||
@@ -922,3 +922,40 @@ def update_existing_file_docs(doc): | |||
content_hash=doc.content_hash, | |||
file_name=doc.name | |||
)) | |||
def attach_files_to_document(doc, event): | |||
""" Runs on on_update hook of all documents. | |||
Goes through every Attach and Attach Image field and attaches | |||
the file url to the document if it is not already attached. | |||
""" | |||
attach_fields = doc.meta.get( | |||
"fields", {"fieldtype": ["in", ["Attach", "Attach Image"]]} | |||
) | |||
for df in attach_fields: | |||
# this method runs in on_update hook of all documents | |||
# we dont want the update to fail if file cannot be attached for some reason | |||
try: | |||
value = doc.get(df.fieldname) | |||
if not value.startswith(("/files", "/private/files")): | |||
return | |||
if frappe.db.exists("File", { | |||
"file_url": value, | |||
"attached_to_name": doc.name, | |||
"attached_to_doctype": doc.doctype, | |||
"attached_to_field": df.fieldname, | |||
}): | |||
return | |||
frappe.get_doc( | |||
doctype="File", | |||
file_url=value, | |||
attached_to_name=doc.name, | |||
attached_to_doctype=doc.doctype, | |||
attached_to_field=df.fieldname, | |||
folder="Home/Attachments", | |||
).insert() | |||
except Exception: | |||
frappe.log_error(title=_("Error Attaching File")) |
@@ -328,3 +328,49 @@ class TestFile(unittest.TestCase): | |||
self.assertTrue(os.path.exists(file2.get_full_path())) | |||
class TestAttachment(unittest.TestCase): | |||
test_doctype = 'Test For Attachment' | |||
def setUp(self): | |||
if frappe.db.exists('DocType', self.test_doctype): | |||
return | |||
frappe.get_doc( | |||
doctype='DocType', | |||
name=self.test_doctype, | |||
module='Custom', | |||
custom=1, | |||
fields=[ | |||
{'label': 'Title', 'fieldname': 'title', 'fieldtype': 'Data'}, | |||
{'label': 'Attachment', 'fieldname': 'attachment', 'fieldtype': 'Attach'}, | |||
] | |||
).insert() | |||
def tearDown(self): | |||
frappe.delete_doc('DocType', self.test_doctype) | |||
def test_file_attachment_on_update(self): | |||
doc = frappe.get_doc( | |||
doctype=self.test_doctype, | |||
title='test for attachment on update' | |||
).insert() | |||
file = frappe.get_doc({ | |||
'doctype': 'File', | |||
'file_name': 'test_attach.txt', | |||
'content': 'Test Content' | |||
}) | |||
file.save() | |||
doc.attachment = file.file_url | |||
doc.save() | |||
exists = frappe.db.exists('File', { | |||
'file_name': 'test_attach.txt', | |||
'file_url': file.file_url, | |||
'attached_to_doctype': self.test_doctype, | |||
'attached_to_name': doc.name, | |||
'attached_to_field': 'attachment' | |||
}) | |||
self.assertTrue(exists) |
@@ -356,7 +356,7 @@ | |||
"depends_on": "enabled", | |||
"fieldname": "email_settings", | |||
"fieldtype": "Section Break", | |||
"label": "Email Settings" | |||
"label": "Email" | |||
}, | |||
{ | |||
"default": "1", | |||
@@ -382,12 +382,6 @@ | |||
"label": "Email Signature", | |||
"no_copy": 1 | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"fieldname": "email_inbox", | |||
"fieldtype": "Section Break", | |||
"label": "Email Inbox" | |||
}, | |||
{ | |||
"fieldname": "user_emails", | |||
"fieldtype": "Table", | |||
@@ -651,7 +645,7 @@ | |||
} | |||
], | |||
"max_attachments": 5, | |||
"modified": "2020-08-06 19:48:49.677800", | |||
"modified": "2020-08-26 19:48:49.677800", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "User", | |||
@@ -685,4 +679,4 @@ | |||
"sort_order": "DESC", | |||
"title_field": "full_name", | |||
"track_changes": 1 | |||
} | |||
} |
@@ -334,6 +334,7 @@ frappe.PermissionEngine = Class.extend({ | |||
}); | |||
this.body.on("click", "input[type='checkbox']", function() { | |||
frappe.dom.freeze(); | |||
var chk = $(this); | |||
var args = { | |||
role: chk.attr("data-role"), | |||
@@ -348,6 +349,7 @@ frappe.PermissionEngine = Class.extend({ | |||
method: "update", | |||
args: args, | |||
callback: function(r) { | |||
frappe.dom.unfreeze(); | |||
if(r.exc) { | |||
// exception: reverse | |||
chk.prop("checked", !chk.prop("checked")); | |||
@@ -374,8 +376,7 @@ frappe.PermissionEngine = Class.extend({ | |||
options:me.options.roles, reqd:1,fieldname:"role"}, | |||
{fieldtype:"Select", label:__("Permission Level"), | |||
options:[0,1,2,3,4,5,6,7,8,9], reqd:1, fieldname: "permlevel", | |||
description: __("Level 0 is for document level permissions, \ | |||
higher levels for field level permissions.")} | |||
description: __("Level 0 is for document level permissions, higher levels for field level permissions.")} | |||
] | |||
}); | |||
if(me.get_doctype()) { | |||
@@ -58,382 +58,384 @@ | |||
], | |||
"fields": [ | |||
{ | |||
"bold": 1, | |||
"fieldname": "dt", | |||
"fieldtype": "Link", | |||
"in_filter": 1, | |||
"in_list_view": 1, | |||
"label": "Document", | |||
"oldfieldname": "dt", | |||
"oldfieldtype": "Link", | |||
"options": "DocType", | |||
"reqd": 1, | |||
"search_index": 1 | |||
"bold": 1, | |||
"fieldname": "dt", | |||
"fieldtype": "Link", | |||
"in_filter": 1, | |||
"in_list_view": 1, | |||
"label": "Document", | |||
"oldfieldname": "dt", | |||
"oldfieldtype": "Link", | |||
"options": "DocType", | |||
"reqd": 1, | |||
"search_index": 1 | |||
}, | |||
{ | |||
"bold": 1, | |||
"fieldname": "label", | |||
"fieldtype": "Data", | |||
"in_filter": 1, | |||
"label": "Label", | |||
"no_copy": 1, | |||
"oldfieldname": "label", | |||
"oldfieldtype": "Data" | |||
"bold": 1, | |||
"fieldname": "label", | |||
"fieldtype": "Data", | |||
"in_filter": 1, | |||
"label": "Label", | |||
"no_copy": 1, | |||
"oldfieldname": "label", | |||
"oldfieldtype": "Data" | |||
}, | |||
{ | |||
"fieldname": "label_help", | |||
"fieldtype": "HTML", | |||
"label": "Label Help", | |||
"oldfieldtype": "HTML" | |||
"fieldname": "label_help", | |||
"fieldtype": "HTML", | |||
"label": "Label Help", | |||
"oldfieldtype": "HTML" | |||
}, | |||
{ | |||
"fieldname": "fieldname", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Fieldname", | |||
"no_copy": 1, | |||
"oldfieldname": "fieldname", | |||
"oldfieldtype": "Data", | |||
"read_only": 1 | |||
"fieldname": "fieldname", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Fieldname", | |||
"no_copy": 1, | |||
"oldfieldname": "fieldname", | |||
"oldfieldtype": "Data", | |||
"read_only": 1 | |||
}, | |||
{ | |||
"description": "Select the label after which you want to insert new field.", | |||
"fieldname": "insert_after", | |||
"fieldtype": "Select", | |||
"label": "Insert After", | |||
"no_copy": 1, | |||
"oldfieldname": "insert_after", | |||
"oldfieldtype": "Select" | |||
"description": "Select the label after which you want to insert new field.", | |||
"fieldname": "insert_after", | |||
"fieldtype": "Select", | |||
"label": "Insert After", | |||
"no_copy": 1, | |||
"oldfieldname": "insert_after", | |||
"oldfieldtype": "Select" | |||
}, | |||
{ | |||
"fieldname": "column_break_6", | |||
"fieldtype": "Column Break" | |||
"fieldname": "column_break_6", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"bold": 1, | |||
"default": "Data", | |||
"fieldname": "fieldtype", | |||
"fieldtype": "Select", | |||
"in_filter": 1, | |||
"in_list_view": 1, | |||
"label": "Field Type", | |||
"oldfieldname": "fieldtype", | |||
"oldfieldtype": "Select", | |||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", | |||
"reqd": 1 | |||
"bold": 1, | |||
"default": "Data", | |||
"fieldname": "fieldtype", | |||
"fieldtype": "Select", | |||
"in_filter": 1, | |||
"in_list_view": 1, | |||
"label": "Field Type", | |||
"oldfieldname": "fieldtype", | |||
"oldfieldtype": "Select", | |||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", | |||
"reqd": 1 | |||
}, | |||
{ | |||
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", | |||
"description": "Set non-standard precision for a Float or Currency field", | |||
"fieldname": "precision", | |||
"fieldtype": "Select", | |||
"label": "Precision", | |||
"options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" | |||
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", | |||
"description": "Set non-standard precision for a Float or Currency field", | |||
"fieldname": "precision", | |||
"fieldtype": "Select", | |||
"label": "Precision", | |||
"options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" | |||
}, | |||
{ | |||
"fieldname": "options", | |||
"fieldtype": "Small Text", | |||
"in_list_view": 1, | |||
"label": "Options", | |||
"oldfieldname": "options", | |||
"oldfieldtype": "Text" | |||
"fieldname": "options", | |||
"fieldtype": "Small Text", | |||
"in_list_view": 1, | |||
"label": "Options", | |||
"oldfieldname": "options", | |||
"oldfieldtype": "Text" | |||
}, | |||
{ | |||
"fieldname": "fetch_from", | |||
"fieldtype": "Small Text", | |||
"label": "Fetch From" | |||
"fieldname": "fetch_from", | |||
"fieldtype": "Small Text", | |||
"label": "Fetch From" | |||
}, | |||
{ | |||
"default": "0", | |||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", | |||
"fieldname": "fetch_if_empty", | |||
"fieldtype": "Check", | |||
"label": "Fetch If Empty" | |||
"default": "0", | |||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", | |||
"fieldname": "fetch_if_empty", | |||
"fieldtype": "Check", | |||
"label": "Fetch If Empty" | |||
}, | |||
{ | |||
"fieldname": "options_help", | |||
"fieldtype": "HTML", | |||
"label": "Options Help", | |||
"oldfieldtype": "HTML" | |||
"fieldname": "options_help", | |||
"fieldtype": "HTML", | |||
"label": "Options Help", | |||
"oldfieldtype": "HTML" | |||
}, | |||
{ | |||
"fieldname": "section_break_11", | |||
"fieldtype": "Section Break" | |||
"fieldname": "section_break_11", | |||
"fieldtype": "Section Break" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible", | |||
"fieldtype": "Check", | |||
"label": "Collapsible" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible", | |||
"fieldtype": "Check", | |||
"label": "Collapsible" | |||
}, | |||
{ | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Collapsible Depends On" | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Collapsible Depends On" | |||
}, | |||
{ | |||
"fieldname": "default", | |||
"fieldtype": "Text", | |||
"label": "Default Value", | |||
"oldfieldname": "default", | |||
"oldfieldtype": "Text" | |||
"fieldname": "default", | |||
"fieldtype": "Text", | |||
"label": "Default Value", | |||
"oldfieldname": "default", | |||
"oldfieldtype": "Text" | |||
}, | |||
{ | |||
"fieldname": "depends_on", | |||
"fieldtype": "Code", | |||
"label": "Depends On", | |||
"length": 255 | |||
"fieldname": "depends_on", | |||
"fieldtype": "Code", | |||
"label": "Depends On", | |||
"length": 255 | |||
}, | |||
{ | |||
"fieldname": "description", | |||
"fieldtype": "Text", | |||
"label": "Field Description", | |||
"oldfieldname": "description", | |||
"oldfieldtype": "Text", | |||
"print_width": "300px", | |||
"width": "300px" | |||
"fieldname": "description", | |||
"fieldtype": "Text", | |||
"label": "Field Description", | |||
"oldfieldname": "description", | |||
"oldfieldtype": "Text", | |||
"print_width": "300px", | |||
"width": "300px" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "permlevel", | |||
"fieldtype": "Int", | |||
"label": "Permission Level", | |||
"oldfieldname": "permlevel", | |||
"oldfieldtype": "Int" | |||
"default": "0", | |||
"fieldname": "permlevel", | |||
"fieldtype": "Int", | |||
"label": "Permission Level", | |||
"oldfieldname": "permlevel", | |||
"oldfieldtype": "Int" | |||
}, | |||
{ | |||
"fieldname": "width", | |||
"fieldtype": "Data", | |||
"label": "Width", | |||
"oldfieldname": "width", | |||
"oldfieldtype": "Data" | |||
"fieldname": "width", | |||
"fieldtype": "Data", | |||
"label": "Width", | |||
"oldfieldname": "width", | |||
"oldfieldtype": "Data" | |||
}, | |||
{ | |||
"description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", | |||
"fieldname": "columns", | |||
"fieldtype": "Int", | |||
"label": "Columns" | |||
"description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", | |||
"fieldname": "columns", | |||
"fieldtype": "Int", | |||
"label": "Columns" | |||
}, | |||
{ | |||
"fieldname": "properties", | |||
"fieldtype": "Column Break", | |||
"oldfieldtype": "Column Break", | |||
"print_width": "50%", | |||
"width": "50%" | |||
"fieldname": "properties", | |||
"fieldtype": "Column Break", | |||
"oldfieldtype": "Column Break", | |||
"print_width": "50%", | |||
"width": "50%" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "reqd", | |||
"fieldtype": "Check", | |||
"in_list_view": 1, | |||
"label": "Is Mandatory Field", | |||
"oldfieldname": "reqd", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "reqd", | |||
"fieldtype": "Check", | |||
"in_list_view": 1, | |||
"label": "Is Mandatory Field", | |||
"oldfieldname": "reqd", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "unique", | |||
"fieldtype": "Check", | |||
"label": "Unique" | |||
"default": "0", | |||
"fieldname": "unique", | |||
"fieldtype": "Check", | |||
"label": "Unique" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "read_only", | |||
"fieldtype": "Check", | |||
"label": "Read Only" | |||
"default": "0", | |||
"fieldname": "read_only", | |||
"fieldtype": "Check", | |||
"label": "Read Only" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype===\"Link\"", | |||
"fieldname": "ignore_user_permissions", | |||
"fieldtype": "Check", | |||
"label": "Ignore User Permissions" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype===\"Link\"", | |||
"fieldname": "ignore_user_permissions", | |||
"fieldtype": "Check", | |||
"label": "Ignore User Permissions" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "hidden", | |||
"fieldtype": "Check", | |||
"label": "Hidden" | |||
"default": "0", | |||
"fieldname": "hidden", | |||
"fieldtype": "Check", | |||
"label": "Hidden" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "print_hide", | |||
"fieldtype": "Check", | |||
"label": "Print Hide", | |||
"oldfieldname": "print_hide", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "print_hide", | |||
"fieldtype": "Check", | |||
"label": "Print Hide", | |||
"oldfieldname": "print_hide", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", | |||
"fieldname": "print_hide_if_no_value", | |||
"fieldtype": "Check", | |||
"label": "Print Hide If No Value" | |||
"default": "0", | |||
"depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", | |||
"fieldname": "print_hide_if_no_value", | |||
"fieldtype": "Check", | |||
"label": "Print Hide If No Value" | |||
}, | |||
{ | |||
"fieldname": "print_width", | |||
"fieldtype": "Data", | |||
"hidden": 1, | |||
"label": "Print Width", | |||
"no_copy": 1, | |||
"print_hide": 1 | |||
"fieldname": "print_width", | |||
"fieldtype": "Data", | |||
"hidden": 1, | |||
"label": "Print Width", | |||
"no_copy": 1, | |||
"print_hide": 1 | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "no_copy", | |||
"fieldtype": "Check", | |||
"label": "No Copy", | |||
"oldfieldname": "no_copy", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "no_copy", | |||
"fieldtype": "Check", | |||
"label": "No Copy", | |||
"oldfieldname": "no_copy", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "allow_on_submit", | |||
"fieldtype": "Check", | |||
"label": "Allow on Submit", | |||
"oldfieldname": "allow_on_submit", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "allow_on_submit", | |||
"fieldtype": "Check", | |||
"label": "Allow on Submit", | |||
"oldfieldname": "allow_on_submit", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "in_list_view", | |||
"fieldtype": "Check", | |||
"label": "In List View" | |||
"default": "0", | |||
"fieldname": "in_list_view", | |||
"fieldtype": "Check", | |||
"label": "In List View" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "in_standard_filter", | |||
"fieldtype": "Check", | |||
"label": "In Standard Filter" | |||
"default": "0", | |||
"fieldname": "in_standard_filter", | |||
"fieldtype": "Check", | |||
"label": "In Standard Filter" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", | |||
"fieldname": "in_global_search", | |||
"fieldtype": "Check", | |||
"label": "In Global Search" | |||
"default": "0", | |||
"depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", | |||
"fieldname": "in_global_search", | |||
"fieldtype": "Check", | |||
"label": "In Global Search" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "bold", | |||
"fieldtype": "Check", | |||
"label": "Bold" | |||
"default": "0", | |||
"fieldname": "bold", | |||
"fieldtype": "Check", | |||
"label": "Bold" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "report_hide", | |||
"fieldtype": "Check", | |||
"label": "Report Hide", | |||
"oldfieldname": "report_hide", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "report_hide", | |||
"fieldtype": "Check", | |||
"label": "Report Hide", | |||
"oldfieldname": "report_hide", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "search_index", | |||
"fieldtype": "Check", | |||
"hidden": 1, | |||
"label": "Index", | |||
"no_copy": 1, | |||
"print_hide": 1 | |||
"default": "0", | |||
"fieldname": "search_index", | |||
"fieldtype": "Check", | |||
"hidden": 1, | |||
"label": "Index", | |||
"no_copy": 1, | |||
"print_hide": 1 | |||
}, | |||
{ | |||
"default": "0", | |||
"description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", | |||
"fieldname": "ignore_xss_filter", | |||
"fieldtype": "Check", | |||
"label": "Ignore XSS Filter" | |||
"default": "0", | |||
"description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", | |||
"fieldname": "ignore_xss_filter", | |||
"fieldtype": "Check", | |||
"label": "Ignore XSS Filter" | |||
}, | |||
{ | |||
"default": "1", | |||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", | |||
"fieldname": "translatable", | |||
"fieldtype": "Check", | |||
"label": "Translatable" | |||
"default": "1", | |||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", | |||
"fieldname": "translatable", | |||
"fieldtype": "Check", | |||
"label": "Translatable" | |||
}, | |||
{ | |||
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)", | |||
"fieldname": "length", | |||
"fieldtype": "Int", | |||
"label": "Length" | |||
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)", | |||
"fieldname": "length", | |||
"fieldtype": "Int", | |||
"label": "Length" | |||
}, | |||
{ | |||
"fieldname": "mandatory_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Mandatory Depends On", | |||
"length": 255 | |||
"fieldname": "mandatory_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Mandatory Depends On", | |||
"length": 255 | |||
}, | |||
{ | |||
"fieldname": "read_only_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Read Only Depends On", | |||
"length": 255 | |||
"fieldname": "read_only_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Read Only Depends On", | |||
"length": 255 | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "allow_in_quick_entry", | |||
"fieldtype": "Check", | |||
"label": "Allow in Quick Entry" | |||
"default": "0", | |||
"fieldname": "allow_in_quick_entry", | |||
"fieldtype": "Check", | |||
"label": "Allow in Quick Entry" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "in_preview", | |||
"fieldtype": "Check", | |||
"label": "In Preview" | |||
"default": "0", | |||
"depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", | |||
"fieldname": "in_preview", | |||
"fieldtype": "Check", | |||
"label": "In Preview" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_seconds", | |||
"fieldtype": "Check", | |||
"label": "Hide Seconds" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_seconds", | |||
"fieldtype": "Check", | |||
"label": "Hide Seconds" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_days", | |||
"fieldtype": "Check", | |||
"label": "Hide Days" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_days", | |||
"fieldtype": "Check", | |||
"label": "Hide Days" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Section Break'", | |||
"fieldname": "hide_border", | |||
"fieldtype": "Check", | |||
"label": "Hide Border" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Section Break'", | |||
"fieldname": "hide_border", | |||
"fieldtype": "Check", | |||
"label": "Hide Border" | |||
} | |||
], | |||
"icon": "fa fa-glass", | |||
"idx": 1, | |||
"index_web_pages_for_search": 1, | |||
"links": [], | |||
"modified": "2020-02-06 23:43:00.123575", | |||
"modified": "2020-08-28 11:28:44.377753", | |||
"modified_by": "Administrator", | |||
"module": "Custom", | |||
"name": "Custom Field", | |||
"owner": "Administrator", | |||
"permissions": [ | |||
{ | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "Administrator", | |||
"share": 1, | |||
"write": 1 | |||
}, | |||
{ | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "System Manager", | |||
"share": 1, | |||
"write": 1 | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "Administrator", | |||
"share": 1, | |||
"write": 1 | |||
}, | |||
{ | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "System Manager", | |||
"share": 1, | |||
"write": 1 | |||
} | |||
], | |||
"search_fields": "dt,label,fieldtype,options", | |||
@@ -1,187 +1,91 @@ | |||
{ | |||
"allow_copy": 0, | |||
"allow_events_in_timeline": 0, | |||
"allow_guest_to_view": 0, | |||
"allow_import": 1, | |||
"allow_rename": 0, | |||
"autoname": "", | |||
"beta": 0, | |||
"creation": "2013-01-10 16:34:01", | |||
"custom": 0, | |||
"description": "Adds a client custom script to a DocType", | |||
"docstatus": 0, | |||
"doctype": "DocType", | |||
"document_type": "Document", | |||
"editable_grid": 0, | |||
"engine": "InnoDB", | |||
"actions": [], | |||
"allow_import": 1, | |||
"creation": "2013-01-10 16:34:01", | |||
"description": "Adds a client custom script to a DocType", | |||
"doctype": "DocType", | |||
"document_type": "Document", | |||
"engine": "InnoDB", | |||
"field_order": [ | |||
"dt", | |||
"enabled", | |||
"script", | |||
"sample" | |||
], | |||
"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": "dt", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_global_search": 0, | |||
"in_list_view": 1, | |||
"in_standard_filter": 1, | |||
"label": "DocType", | |||
"length": 0, | |||
"no_copy": 0, | |||
"oldfieldname": "dt", | |||
"oldfieldtype": "Link", | |||
"options": "DocType", | |||
"permlevel": 0, | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"remember_last_selected_value": 0, | |||
"report_hide": 0, | |||
"reqd": 1, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"translatable": 0, | |||
"unique": 0 | |||
}, | |||
"fieldname": "dt", | |||
"fieldtype": "Link", | |||
"in_list_view": 1, | |||
"in_standard_filter": 1, | |||
"label": "DocType", | |||
"oldfieldname": "dt", | |||
"oldfieldtype": "Link", | |||
"options": "DocType", | |||
"reqd": 1, | |||
"show_days": 1, | |||
"show_seconds": 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": "script", | |||
"fieldtype": "Code", | |||
"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": "Script", | |||
"length": 0, | |||
"no_copy": 0, | |||
"oldfieldname": "script", | |||
"oldfieldtype": "Code", | |||
"options": "JS", | |||
"permlevel": 0, | |||
"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": "script", | |||
"fieldtype": "Code", | |||
"label": "Script", | |||
"oldfieldname": "script", | |||
"oldfieldtype": "Code", | |||
"options": "JS", | |||
"show_days": 1, | |||
"show_seconds": 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": "sample", | |||
"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": "Sample", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "<h3>Custom Script Help</h3>\n<p>Custom Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started</p>\n<pre><code>\n\n// fetch local_tax_no on selection of customer \n// cur_frm.add_fetch(link_field, source_fieldname, target_fieldname); \ncur_frm.add_fetch('customer', 'local_tax_no', 'local_tax_no');\n\n// additional validation on dates \nfrappe.ui.form.on('Task', 'validate', function(frm) {\n if (frm.doc.from_date < get_today()) {\n msgprint('You can not select past date in From Date');\n validated = false;\n } \n});\n\n// make a field read-only after saving \nfrappe.ui.form.on('Task', {\n refresh: function(frm) {\n // use the __islocal value of doc, to check if the doc is saved or not\n frm.set_df_property('myfield', 'read_only', frm.doc.__islocal ? 0 : 1);\n } \n});\n\n// additional permission check\nfrappe.ui.form.on('Task', {\n validate: function(frm) {\n if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {\n msgprint('You are only allowed Material Receipt');\n validated = false;\n }\n } \n});\n\n// calculate sales incentive\nfrappe.ui.form.on('Sales Invoice', {\n validate: function(frm) {\n // calculate incentives for each person on the deal\n total_incentive = 0\n $.each(frm.doc.sales_team, function(i, d) {\n // calculate incentive\n var incentive_percent = 2;\n if(frm.doc.base_grand_total > 400) incentive_percent = 4;\n // actual incentive\n d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;\n total_incentive += flt(d.incentives)\n });\n frm.doc.total_incentive = total_incentive;\n } \n})\n\n</code></pre>", | |||
"permlevel": 0, | |||
"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": "sample", | |||
"fieldtype": "HTML", | |||
"label": "Sample", | |||
"options": "<h3>Custom Script Help</h3>\n<p>Custom Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started</p>\n<pre><code>\n\n// fetch local_tax_no on selection of customer \n// cur_frm.add_fetch(link_field, source_fieldname, target_fieldname); \ncur_frm.add_fetch('customer', 'local_tax_no', 'local_tax_no');\n\n// additional validation on dates \nfrappe.ui.form.on('Task', 'validate', function(frm) {\n if (frm.doc.from_date < get_today()) {\n msgprint('You can not select past date in From Date');\n validated = false;\n } \n});\n\n// make a field read-only after saving \nfrappe.ui.form.on('Task', {\n refresh: function(frm) {\n // use the __islocal value of doc, to check if the doc is saved or not\n frm.set_df_property('myfield', 'read_only', frm.doc.__islocal ? 0 : 1);\n } \n});\n\n// additional permission check\nfrappe.ui.form.on('Task', {\n validate: function(frm) {\n if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {\n msgprint('You are only allowed Material Receipt');\n validated = false;\n }\n } \n});\n\n// calculate sales incentive\nfrappe.ui.form.on('Sales Invoice', {\n validate: function(frm) {\n // calculate incentives for each person on the deal\n total_incentive = 0\n $.each(frm.doc.sales_team, function(i, d) {\n // calculate incentive\n var incentive_percent = 2;\n if(frm.doc.base_grand_total > 400) incentive_percent = 4;\n // actual incentive\n d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;\n total_incentive += flt(d.incentives)\n });\n frm.doc.total_incentive = total_incentive;\n } \n})\n\n</code></pre>", | |||
"show_days": 1, | |||
"show_seconds": 1 | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "enabled", | |||
"fieldtype": "Check", | |||
"label": "Enabled", | |||
"show_days": 1, | |||
"show_seconds": 1 | |||
} | |||
], | |||
"has_web_view": 0, | |||
"hide_heading": 0, | |||
"hide_toolbar": 0, | |||
"icon": "fa fa-glass", | |||
"idx": 1, | |||
"image_view": 0, | |||
"in_create": 0, | |||
"is_submittable": 0, | |||
"issingle": 0, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"menu_index": 0, | |||
"modified": "2019-03-21 14:26:57.402994", | |||
"modified_by": "Administrator", | |||
"module": "Custom", | |||
"name": "Custom Script", | |||
"owner": "Administrator", | |||
], | |||
"icon": "fa fa-glass", | |||
"idx": 1, | |||
"index_web_pages_for_search": 1, | |||
"links": [], | |||
"modified": "2020-08-24 21:56:07.719579", | |||
"modified_by": "Administrator", | |||
"module": "Custom", | |||
"name": "Custom Script", | |||
"owner": "Administrator", | |||
"permissions": [ | |||
{ | |||
"amend": 0, | |||
"cancel": 0, | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"export": 0, | |||
"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, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "System Manager", | |||
"share": 1, | |||
"write": 1 | |||
}, | |||
}, | |||
{ | |||
"amend": 0, | |||
"cancel": 0, | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"export": 0, | |||
"if_owner": 0, | |||
"import": 0, | |||
"permlevel": 0, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "Administrator", | |||
"set_user_permissions": 0, | |||
"share": 1, | |||
"submit": 0, | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "Administrator", | |||
"share": 1, | |||
"write": 1 | |||
} | |||
], | |||
"quick_entry": 0, | |||
"read_only": 0, | |||
"read_only_onload": 0, | |||
"show_name_in_global_search": 0, | |||
"sort_order": "ASC", | |||
"track_changes": 1, | |||
"track_seen": 0, | |||
"track_views": 0 | |||
], | |||
"sort_order": "ASC", | |||
"track_changes": 1 | |||
} |
@@ -60,364 +60,366 @@ | |||
], | |||
"fields": [ | |||
{ | |||
"fieldname": "label_and_type", | |||
"fieldtype": "Section Break", | |||
"label": "Label and Type" | |||
"fieldname": "label_and_type", | |||
"fieldtype": "Section Break", | |||
"label": "Label and Type" | |||
}, | |||
{ | |||
"fieldname": "label", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Label", | |||
"oldfieldname": "label", | |||
"oldfieldtype": "Data", | |||
"search_index": 1 | |||
"fieldname": "label", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Label", | |||
"oldfieldname": "label", | |||
"oldfieldtype": "Data", | |||
"search_index": 1 | |||
}, | |||
{ | |||
"default": "Data", | |||
"fieldname": "fieldtype", | |||
"fieldtype": "Select", | |||
"in_list_view": 1, | |||
"label": "Type", | |||
"oldfieldname": "fieldtype", | |||
"oldfieldtype": "Select", | |||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime", | |||
"reqd": 1, | |||
"search_index": 1 | |||
"default": "Data", | |||
"fieldname": "fieldtype", | |||
"fieldtype": "Select", | |||
"in_list_view": 1, | |||
"label": "Type", | |||
"oldfieldname": "fieldtype", | |||
"oldfieldtype": "Select", | |||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime", | |||
"reqd": 1, | |||
"search_index": 1 | |||
}, | |||
{ | |||
"fieldname": "fieldname", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Name", | |||
"oldfieldname": "fieldname", | |||
"oldfieldtype": "Data", | |||
"read_only": 1, | |||
"search_index": 1 | |||
"fieldname": "fieldname", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Name", | |||
"oldfieldname": "fieldname", | |||
"oldfieldtype": "Data", | |||
"read_only": 1, | |||
"search_index": 1 | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)", | |||
"fieldname": "reqd", | |||
"fieldtype": "Check", | |||
"label": "Mandatory", | |||
"oldfieldname": "reqd", | |||
"oldfieldtype": "Check", | |||
"print_width": "50px", | |||
"width": "50px" | |||
"default": "0", | |||
"depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)", | |||
"fieldname": "reqd", | |||
"fieldtype": "Check", | |||
"label": "Mandatory", | |||
"oldfieldname": "reqd", | |||
"oldfieldtype": "Check", | |||
"print_width": "50px", | |||
"width": "50px" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "unique", | |||
"fieldtype": "Check", | |||
"label": "Unique" | |||
"default": "0", | |||
"fieldname": "unique", | |||
"fieldtype": "Check", | |||
"label": "Unique" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "in_list_view", | |||
"fieldtype": "Check", | |||
"label": "In List View" | |||
"default": "0", | |||
"fieldname": "in_list_view", | |||
"fieldtype": "Check", | |||
"label": "In List View" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "in_standard_filter", | |||
"fieldtype": "Check", | |||
"label": "In Standard Filter" | |||
"default": "0", | |||
"fieldname": "in_standard_filter", | |||
"fieldtype": "Check", | |||
"label": "In Standard Filter" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", | |||
"fieldname": "in_global_search", | |||
"fieldtype": "Check", | |||
"label": "In Global Search" | |||
"default": "0", | |||
"depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", | |||
"fieldname": "in_global_search", | |||
"fieldtype": "Check", | |||
"label": "In Global Search" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "bold", | |||
"fieldtype": "Check", | |||
"label": "Bold" | |||
"default": "0", | |||
"fieldname": "bold", | |||
"fieldtype": "Check", | |||
"label": "Bold" | |||
}, | |||
{ | |||
"default": "1", | |||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", | |||
"fieldname": "translatable", | |||
"fieldtype": "Check", | |||
"label": "Translatable" | |||
"default": "1", | |||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", | |||
"fieldname": "translatable", | |||
"fieldtype": "Check", | |||
"label": "Translatable" | |||
}, | |||
{ | |||
"fieldname": "column_break_7", | |||
"fieldtype": "Column Break" | |||
"fieldname": "column_break_7", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", | |||
"description": "Set non-standard precision for a Float or Currency field", | |||
"fieldname": "precision", | |||
"fieldtype": "Select", | |||
"label": "Precision", | |||
"options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" | |||
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", | |||
"description": "Set non-standard precision for a Float or Currency field", | |||
"fieldname": "precision", | |||
"fieldtype": "Select", | |||
"label": "Precision", | |||
"options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" | |||
}, | |||
{ | |||
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)", | |||
"fieldname": "length", | |||
"fieldtype": "Int", | |||
"label": "Length" | |||
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)", | |||
"fieldname": "length", | |||
"fieldtype": "Int", | |||
"label": "Length" | |||
}, | |||
{ | |||
"description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.", | |||
"fieldname": "options", | |||
"fieldtype": "Small Text", | |||
"in_list_view": 1, | |||
"label": "Options", | |||
"oldfieldname": "options", | |||
"oldfieldtype": "Text" | |||
"description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.", | |||
"fieldname": "options", | |||
"fieldtype": "Small Text", | |||
"in_list_view": 1, | |||
"label": "Options", | |||
"oldfieldname": "options", | |||
"oldfieldtype": "Text" | |||
}, | |||
{ | |||
"fieldname": "fetch_from", | |||
"fieldtype": "Small Text", | |||
"label": "Fetch From" | |||
"fieldname": "fetch_from", | |||
"fieldtype": "Small Text", | |||
"label": "Fetch From" | |||
}, | |||
{ | |||
"default": "0", | |||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", | |||
"fieldname": "fetch_if_empty", | |||
"fieldtype": "Check", | |||
"label": "Fetch If Empty" | |||
"default": "0", | |||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.", | |||
"fieldname": "fetch_if_empty", | |||
"fieldtype": "Check", | |||
"label": "Fetch If Empty" | |||
}, | |||
{ | |||
"fieldname": "permissions", | |||
"fieldtype": "Section Break", | |||
"label": "Permissions" | |||
"fieldname": "permissions", | |||
"fieldtype": "Section Break", | |||
"label": "Permissions" | |||
}, | |||
{ | |||
"description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): \nmyfield\neval:doc.myfield=='My Value'\neval:doc.age>18", | |||
"fieldname": "depends_on", | |||
"fieldtype": "Code", | |||
"label": "Depends On", | |||
"oldfieldname": "depends_on", | |||
"oldfieldtype": "Data", | |||
"options": "JS" | |||
"description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): \nmyfield\neval:doc.myfield=='My Value'\neval:doc.age>18", | |||
"fieldname": "depends_on", | |||
"fieldtype": "Code", | |||
"label": "Depends On", | |||
"oldfieldname": "depends_on", | |||
"oldfieldtype": "Data", | |||
"options": "JS" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "permlevel", | |||
"fieldtype": "Int", | |||
"in_list_view": 1, | |||
"label": "Perm Level", | |||
"oldfieldname": "permlevel", | |||
"oldfieldtype": "Int" | |||
"default": "0", | |||
"fieldname": "permlevel", | |||
"fieldtype": "Int", | |||
"in_list_view": 1, | |||
"label": "Perm Level", | |||
"oldfieldname": "permlevel", | |||
"oldfieldtype": "Int" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "hidden", | |||
"fieldtype": "Check", | |||
"label": "Hidden", | |||
"oldfieldname": "hidden", | |||
"oldfieldtype": "Check", | |||
"print_width": "50px", | |||
"width": "50px" | |||
"default": "0", | |||
"fieldname": "hidden", | |||
"fieldtype": "Check", | |||
"label": "Hidden", | |||
"oldfieldname": "hidden", | |||
"oldfieldtype": "Check", | |||
"print_width": "50px", | |||
"width": "50px" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "read_only", | |||
"fieldtype": "Check", | |||
"label": "Read Only" | |||
"default": "0", | |||
"fieldname": "read_only", | |||
"fieldtype": "Check", | |||
"label": "Read Only" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible", | |||
"fieldtype": "Check", | |||
"label": "Collapsible" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible", | |||
"fieldtype": "Check", | |||
"label": "Collapsible" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval: doc.fieldtype == \"Table\"", | |||
"fieldname": "allow_bulk_edit", | |||
"fieldtype": "Check", | |||
"label": "Allow Bulk Edit" | |||
"default": "0", | |||
"depends_on": "eval: doc.fieldtype == \"Table\"", | |||
"fieldname": "allow_bulk_edit", | |||
"fieldtype": "Check", | |||
"label": "Allow Bulk Edit" | |||
}, | |||
{ | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Collapsible Depends On", | |||
"options": "JS" | |||
"depends_on": "eval:doc.fieldtype==\"Section Break\"", | |||
"fieldname": "collapsible_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Collapsible Depends On", | |||
"options": "JS" | |||
}, | |||
{ | |||
"fieldname": "column_break_14", | |||
"fieldtype": "Column Break" | |||
"fieldname": "column_break_14", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "ignore_user_permissions", | |||
"fieldtype": "Check", | |||
"label": "Ignore User Permissions" | |||
"default": "0", | |||
"fieldname": "ignore_user_permissions", | |||
"fieldtype": "Check", | |||
"label": "Ignore User Permissions" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "allow_on_submit", | |||
"fieldtype": "Check", | |||
"label": "Allow on Submit", | |||
"oldfieldname": "allow_on_submit", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "allow_on_submit", | |||
"fieldtype": "Check", | |||
"label": "Allow on Submit", | |||
"oldfieldname": "allow_on_submit", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "report_hide", | |||
"fieldtype": "Check", | |||
"label": "Report Hide", | |||
"oldfieldname": "report_hide", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "report_hide", | |||
"fieldtype": "Check", | |||
"label": "Report Hide", | |||
"oldfieldname": "report_hide", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:(doc.fieldtype == 'Link')", | |||
"fieldname": "remember_last_selected_value", | |||
"fieldtype": "Check", | |||
"label": "Remember Last Selected Value" | |||
"default": "0", | |||
"depends_on": "eval:(doc.fieldtype == 'Link')", | |||
"fieldname": "remember_last_selected_value", | |||
"fieldtype": "Check", | |||
"label": "Remember Last Selected Value" | |||
}, | |||
{ | |||
"fieldname": "display", | |||
"fieldtype": "Section Break", | |||
"label": "Display" | |||
"fieldname": "display", | |||
"fieldtype": "Section Break", | |||
"label": "Display" | |||
}, | |||
{ | |||
"fieldname": "default", | |||
"fieldtype": "Text", | |||
"label": "Default", | |||
"oldfieldname": "default", | |||
"oldfieldtype": "Text" | |||
"fieldname": "default", | |||
"fieldtype": "Text", | |||
"label": "Default", | |||
"oldfieldname": "default", | |||
"oldfieldtype": "Text" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "in_filter", | |||
"fieldtype": "Check", | |||
"label": "In Filter", | |||
"oldfieldname": "in_filter", | |||
"oldfieldtype": "Check", | |||
"print_width": "50px", | |||
"width": "50px" | |||
"default": "0", | |||
"fieldname": "in_filter", | |||
"fieldtype": "Check", | |||
"label": "In Filter", | |||
"oldfieldname": "in_filter", | |||
"oldfieldtype": "Check", | |||
"print_width": "50px", | |||
"width": "50px" | |||
}, | |||
{ | |||
"fieldname": "column_break_21", | |||
"fieldtype": "Column Break" | |||
"fieldname": "column_break_21", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "description", | |||
"fieldtype": "Text", | |||
"label": "Description", | |||
"oldfieldname": "description", | |||
"oldfieldtype": "Text", | |||
"print_width": "300px", | |||
"width": "300px" | |||
"fieldname": "description", | |||
"fieldtype": "Text", | |||
"label": "Description", | |||
"oldfieldname": "description", | |||
"oldfieldtype": "Text", | |||
"print_width": "300px", | |||
"width": "300px" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "print_hide", | |||
"fieldtype": "Check", | |||
"label": "Print Hide", | |||
"oldfieldname": "print_hide", | |||
"oldfieldtype": "Check" | |||
"default": "0", | |||
"fieldname": "print_hide", | |||
"fieldtype": "Check", | |||
"label": "Print Hide", | |||
"oldfieldname": "print_hide", | |||
"oldfieldtype": "Check" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", | |||
"fieldname": "print_hide_if_no_value", | |||
"fieldtype": "Check", | |||
"label": "Print Hide If No Value" | |||
"default": "0", | |||
"depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", | |||
"fieldname": "print_hide_if_no_value", | |||
"fieldtype": "Check", | |||
"label": "Print Hide If No Value" | |||
}, | |||
{ | |||
"description": "Print Width of the field, if the field is a column in a table", | |||
"fieldname": "print_width", | |||
"fieldtype": "Data", | |||
"label": "Print Width", | |||
"print_width": "50px", | |||
"width": "50px" | |||
"description": "Print Width of the field, if the field is a column in a table", | |||
"fieldname": "print_width", | |||
"fieldtype": "Data", | |||
"label": "Print Width", | |||
"print_width": "50px", | |||
"width": "50px" | |||
}, | |||
{ | |||
"depends_on": "eval:cur_frm.doc.istable", | |||
"description": "Number of columns for a field in a Grid (Total Columns in a grid should be less than 11)", | |||
"fieldname": "columns", | |||
"fieldtype": "Int", | |||
"label": "Columns" | |||
"depends_on": "eval:cur_frm.doc.istable", | |||
"description": "Number of columns for a field in a Grid (Total Columns in a grid should be less than 11)", | |||
"fieldname": "columns", | |||
"fieldtype": "Int", | |||
"label": "Columns" | |||
}, | |||
{ | |||
"fieldname": "width", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Width", | |||
"oldfieldname": "width", | |||
"oldfieldtype": "Data", | |||
"print_width": "50px", | |||
"width": "50px" | |||
"fieldname": "width", | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Width", | |||
"oldfieldname": "width", | |||
"oldfieldtype": "Data", | |||
"print_width": "50px", | |||
"width": "50px" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "is_custom_field", | |||
"fieldtype": "Check", | |||
"hidden": 1, | |||
"label": "Is Custom Field", | |||
"read_only": 1 | |||
"default": "0", | |||
"fieldname": "is_custom_field", | |||
"fieldtype": "Check", | |||
"hidden": 1, | |||
"label": "Is Custom Field", | |||
"read_only": 1 | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "allow_in_quick_entry", | |||
"fieldtype": "Check", | |||
"label": "Allow in Quick Entry" | |||
"default": "0", | |||
"fieldname": "allow_in_quick_entry", | |||
"fieldtype": "Check", | |||
"label": "Allow in Quick Entry" | |||
}, | |||
{ | |||
"fieldname": "property_depends_on_section", | |||
"fieldtype": "Section Break", | |||
"label": "Property Depends On" | |||
"fieldname": "property_depends_on_section", | |||
"fieldtype": "Section Break", | |||
"label": "Property Depends On" | |||
}, | |||
{ | |||
"fieldname": "mandatory_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Mandatory Depends On", | |||
"options": "JS" | |||
"fieldname": "mandatory_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Mandatory Depends On", | |||
"options": "JS" | |||
}, | |||
{ | |||
"fieldname": "column_break_33", | |||
"fieldtype": "Column Break" | |||
"fieldname": "column_break_33", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "read_only_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Read Only Depends On", | |||
"options": "JS" | |||
"fieldname": "read_only_depends_on", | |||
"fieldtype": "Code", | |||
"label": "Read Only Depends On", | |||
"options": "JS" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "in_preview", | |||
"fieldtype": "Check", | |||
"label": "In Preview" | |||
"default": "0", | |||
"depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", | |||
"fieldname": "in_preview", | |||
"fieldtype": "Check", | |||
"label": "In Preview" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_seconds", | |||
"fieldtype": "Check", | |||
"label": "Hide Seconds" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_seconds", | |||
"fieldtype": "Check", | |||
"label": "Hide Seconds" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_days", | |||
"fieldtype": "Check", | |||
"label": "Hide Days" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Duration'", | |||
"fieldname": "hide_days", | |||
"fieldtype": "Check", | |||
"label": "Hide Days" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Section Break'", | |||
"fieldname": "hide_border", | |||
"fieldtype": "Check", | |||
"label": "Hide Border" | |||
"default": "0", | |||
"depends_on": "eval:doc.fieldtype=='Section Break'", | |||
"fieldname": "hide_border", | |||
"fieldtype": "Check", | |||
"label": "Hide Border" | |||
} | |||
], | |||
"idx": 1, | |||
"index_web_pages_for_search": 1, | |||
"istable": 1, | |||
"links": [], | |||
"modified": "2020-06-02 23:45:46.810868", | |||
"modified": "2020-08-28 11:28:59.084060", | |||
"modified_by": "Administrator", | |||
"module": "Custom", | |||
"name": "Customize Form Field", | |||
@@ -92,6 +92,8 @@ def bootstrap_database(db_name, verbose, source_sql=None): | |||
sys.exit(1) | |||
import_db_from_sql(source_sql, verbose) | |||
frappe.connect(db_name=db_name) | |||
if not 'tabDefaultValue' in frappe.db.get_tables(): | |||
print('''Database not installed, this can due to lack of permission, or that the database name exists. | |||
Check your mysql root password, or use --force to reinstall''') | |||
@@ -203,7 +203,7 @@ class Workspace: | |||
cards = cards + get_custom_reports_and_doctypes(self.doc.module) | |||
if len(self.extended_cards): | |||
cards = cards + self.extended_cards | |||
cards = merge_cards_based_on_label(cards + self.extended_cards) | |||
default_country = frappe.db.get_default("country") | |||
def _doctype_contains_a_record(name): | |||
@@ -579,3 +579,16 @@ def update_onboarding_step(name, field, value): | |||
""" | |||
frappe.db.set_value("Onboarding Step", name, field, value) | |||
def merge_cards_based_on_label(cards): | |||
"""Merge cards with common label.""" | |||
cards_dict = {} | |||
for card in cards: | |||
if card.label in cards_dict: | |||
links = loads(cards_dict[card.label].links) + loads(card.links) | |||
cards_dict[card.label].update(dict(links=dumps(links))) | |||
cards_dict[card.label] = cards_dict.pop(card.label) | |||
else: | |||
cards_dict[card.label] = card | |||
return list(cards_dict.values()) |
@@ -130,7 +130,7 @@ class FormMeta(Meta): | |||
def add_custom_script(self): | |||
"""embed all require files""" | |||
# custom script | |||
custom = frappe.db.get_value("Custom Script", {"dt": self.name}, "script") or "" | |||
custom = frappe.db.get_value("Custom Script", {"dt": self.name, "enabled": 1}, "script") or "" | |||
self.set("__custom_js", custom) | |||
@@ -1,5 +1,5 @@ | |||
import frappe | |||
from frappe.model import no_value_fields | |||
from frappe.model import no_value_fields, table_fields | |||
import json | |||
@frappe.whitelist() | |||
@@ -9,11 +9,13 @@ def get_preview_data(doctype, docname): | |||
if not meta.show_preview_popup: return | |||
preview_fields = [field.fieldname for field in meta.fields \ | |||
if field.in_preview and field.fieldtype not in no_value_fields] | |||
if field.in_preview and field.fieldtype not in no_value_fields \ | |||
and field.fieldtype not in table_fields] | |||
# no preview fields defined, build list from mandatory fields | |||
if not preview_fields: | |||
preview_fields = [field.fieldname for field in meta.fields if field.reqd] | |||
preview_fields = [field.fieldname for field in meta.fields if field.reqd \ | |||
and field.fieldtype not in table_fields] | |||
title_field = meta.get_title_field() | |||
image_field = meta.image_field | |||
@@ -216,20 +216,33 @@ def add_data_to_custom_columns(columns, result): | |||
return data | |||
def reorder_data_for_custom_columns(custom_columns, columns, result, report_type): | |||
custom_column_labels = [col["label"] for col in custom_columns] | |||
if not result: | |||
return [] | |||
if report_type == 'Query Report': | |||
# Assume list result for query reports | |||
# Query report columns exclusively use Label | |||
custom_column_labels = [col["label"] for col in custom_columns] | |||
original_column_labels = [col.split(":")[0] for col in columns] | |||
return get_columns_from_list(custom_column_labels, original_column_labels, result) | |||
custom_column_names = [col["fieldname"] for col in custom_columns] | |||
if isinstance(result[0], list) or isinstance(result[0], tuple): | |||
# If the result is a list of lists | |||
original_column_names = [col["fieldname"] for col in columns] | |||
return get_columns_from_list(custom_column_names, original_column_names, result) | |||
else: | |||
original_column_labels = [col["label"] for col in columns] | |||
# If the result is a list of dicts | |||
return get_columns_from_dict(custom_column_names, result) | |||
def get_columns_from_list(columns, target_columns, result): | |||
reordered_result = [] | |||
for res in result: | |||
r = [] | |||
for col_name in custom_column_labels: | |||
for col_name in columns: | |||
try: | |||
idx = original_column_labels.index(col_name) | |||
idx = target_columns.index(col_name) | |||
r.append(res[idx]) | |||
except ValueError: | |||
pass | |||
@@ -238,6 +251,21 @@ def reorder_data_for_custom_columns(custom_columns, columns, result, report_type | |||
return reordered_result | |||
def get_columns_from_dict(columns, result): | |||
reordered_result = [] | |||
for res in result: | |||
r = {} | |||
for col_name in columns: | |||
try: | |||
r[col_name] = res[col_name] | |||
except KeyError: | |||
pass | |||
reordered_result.append(r) | |||
return reordered_result | |||
def get_prepared_report_result(report, filters, dn="", user=None): | |||
latest_report_data = {} | |||
doc = None | |||
@@ -14,9 +14,6 @@ frappe.ui.form.on('Newsletter', { | |||
}); | |||
}, "fa fa-play", "btn-success"); | |||
} | |||
if (!doc.__islocal && cint(doc.email_sent)) { | |||
frm.set_df_property('schedule_send', "read_only", 1); | |||
} | |||
frm.events.setup_dashboard(frm); | |||
@@ -15,7 +15,10 @@ | |||
"email_sent", | |||
"newsletter_content", | |||
"subject", | |||
"content_type", | |||
"message", | |||
"message_md", | |||
"message_html", | |||
"send_unsubscribe_link", | |||
"send_attachments", | |||
"published", | |||
@@ -37,8 +40,7 @@ | |||
"fieldname": "send_from", | |||
"fieldtype": "Data", | |||
"ignore_xss_filter": 1, | |||
"label": "Sender", | |||
"no_copy": 1 | |||
"label": "Sender" | |||
}, | |||
{ | |||
"default": "0", | |||
@@ -50,7 +52,8 @@ | |||
}, | |||
{ | |||
"fieldname": "newsletter_content", | |||
"fieldtype": "Section Break" | |||
"fieldtype": "Section Break", | |||
"label": "Content" | |||
}, | |||
{ | |||
"fieldname": "subject", | |||
@@ -61,11 +64,12 @@ | |||
"reqd": 1 | |||
}, | |||
{ | |||
"depends_on": "eval: doc.content_type === 'Rich Text'", | |||
"fieldname": "message", | |||
"fieldtype": "Text Editor", | |||
"in_list_view": 1, | |||
"label": "Message", | |||
"reqd": 1 | |||
"mandatory_depends_on": "eval: doc.content_type === 'Rich Text'" | |||
}, | |||
{ | |||
"default": "1", | |||
@@ -87,16 +91,20 @@ | |||
"read_only": 1 | |||
}, | |||
{ | |||
"collapsible": 1, | |||
"fieldname": "test_the_newsletter", | |||
"fieldtype": "Section Break" | |||
"fieldtype": "Section Break", | |||
"label": "Testing" | |||
}, | |||
{ | |||
"description": "A Lead with this Email Address should exist", | |||
"fieldname": "test_email_id", | |||
"fieldtype": "Data", | |||
"label": "Test Email Address" | |||
"label": "Test Email Address", | |||
"options": "Email" | |||
}, | |||
{ | |||
"depends_on": "eval: doc.test_email_id", | |||
"fieldname": "test_send", | |||
"fieldtype": "Button", | |||
"label": "Test", | |||
@@ -117,7 +125,8 @@ | |||
"depends_on": "eval: doc.schedule_sending", | |||
"fieldname": "schedule_send", | |||
"fieldtype": "Datetime", | |||
"label": "Schedule Send" | |||
"label": "Schedule Send", | |||
"read_only_depends_on": "eval: doc.email_sent" | |||
}, | |||
{ | |||
"default": "0", | |||
@@ -125,11 +134,32 @@ | |||
"fieldtype": "Check", | |||
"label": "Send Attachments" | |||
}, | |||
{ | |||
"fieldname": "content_type", | |||
"fieldtype": "Select", | |||
"label": "Content Type", | |||
"options": "Rich Text\nMarkdown\nHTML" | |||
}, | |||
{ | |||
"depends_on": "eval:doc.content_type === 'Markdown'", | |||
"fieldname": "message_md", | |||
"fieldtype": "Markdown Editor", | |||
"label": "Message (Markdown)", | |||
"mandatory_depends_on": "eval:doc.content_type === 'Markdown'" | |||
}, | |||
{ | |||
"depends_on": "eval:doc.content_type === 'HTML'", | |||
"fieldname": "message_html", | |||
"fieldtype": "HTML Editor", | |||
"label": "Message (HTML)", | |||
"mandatory_depends_on": "eval:doc.content_type === 'HTML'" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "schedule_sending", | |||
"fieldtype": "Check", | |||
"label": "Schedule Sending" | |||
"label": "Schedule Sending", | |||
"read_only_depends_on": "eval: doc.email_sent" | |||
} | |||
], | |||
"has_web_view": 1, | |||
@@ -139,7 +169,7 @@ | |||
"is_published_field": "published", | |||
"links": [], | |||
"max_attachments": 3, | |||
"modified": "2020-08-17 18:11:59.541686", | |||
"modified": "2020-08-24 19:59:37.262500", | |||
"modified_by": "Administrator", | |||
"module": "Email", | |||
"name": "Newsletter", | |||
@@ -8,12 +8,9 @@ import frappe.utils | |||
from frappe import throw, _ | |||
from frappe.website.website_generator import WebsiteGenerator | |||
from frappe.utils.verified_command import get_signed_params, verify_request | |||
from frappe.utils.background_jobs import enqueue | |||
from frappe.email.queue import send | |||
from frappe.email.doctype.email_group.email_group import add_subscribers | |||
from frappe.utils import parse_addr, now_datetime | |||
from frappe.utils import validate_email_address | |||
from frappe.utils import parse_addr, now_datetime, markdown, validate_email_address | |||
class Newsletter(WebsiteGenerator): | |||
def onload(self): | |||
@@ -29,8 +26,8 @@ class Newsletter(WebsiteGenerator): | |||
def test_send(self, doctype="Lead"): | |||
self.recipients = frappe.utils.split_emails(self.test_email_id) | |||
self.queue_all() | |||
frappe.msgprint(_("Scheduled to send to {0}").format(self.test_email_id)) | |||
self.queue_all(test_email=True) | |||
frappe.msgprint(_("Test email sent to {0}").format(self.test_email_id)) | |||
def send_emails(self): | |||
"""send emails to leads and customers""" | |||
@@ -40,21 +37,13 @@ class Newsletter(WebsiteGenerator): | |||
self.recipients = self.get_recipients() | |||
if self.recipients: | |||
if getattr(frappe.local, "is_ajax", False): | |||
self.validate_send() | |||
# using default queue with a longer timeout as this isn't a scheduled task | |||
enqueue(send_newsletter, queue='default', timeout=6000, event='send_newsletter', | |||
newsletter=self.name) | |||
else: | |||
self.queue_all() | |||
frappe.msgprint(_("Scheduled to send to {0} recipients").format(len(self.recipients))) | |||
self.queue_all() | |||
frappe.msgprint(_("Email queued to {0} recipients").format(len(self.recipients))) | |||
else: | |||
frappe.msgprint(_("Newsletter should have atleast one recipient")) | |||
def queue_all(self): | |||
def queue_all(self, test_email=False): | |||
if not self.get("recipients"): | |||
# in case it is called via worker | |||
self.recipients = self.get_recipients() | |||
@@ -80,7 +69,7 @@ class Newsletter(WebsiteGenerator): | |||
frappe.throw(_("Unable to find attachment {0}").format(file.name)) | |||
send(recipients=self.recipients, sender=sender, | |||
subject=self.subject, message=self.message, | |||
subject=self.subject, message=self.get_message(), | |||
reference_doctype=self.doctype, reference_name=self.name, | |||
add_unsubscribe_link=self.send_unsubscribe_link, attachments=attachments, | |||
unsubscribe_method="/unsubscribe", | |||
@@ -90,9 +79,18 @@ class Newsletter(WebsiteGenerator): | |||
if not frappe.flags.in_test: | |||
frappe.db.auto_commit_on_many_writes = False | |||
self.db_set("email_sent", 1) | |||
self.db_set("schedule_send", now_datetime()) | |||
self.db_set("scheduled_to_send", len(self.recipients)) | |||
if not test_email: | |||
self.db_set("email_sent", 1) | |||
self.db_set("schedule_send", now_datetime()) | |||
self.db_set("scheduled_to_send", len(self.recipients)) | |||
def get_message(self): | |||
return { | |||
'Rich Text': self.message, | |||
'Markdown': markdown(self.message_md), | |||
'HTML': self.message_html | |||
}[self.content_type] | |||
def get_recipients(self): | |||
"""Get recipients from Email Group""" | |||
@@ -1,8 +1,10 @@ | |||
frappe.listview_settings['Newsletter'] = { | |||
add_fields: ["subject", "email_sent"], | |||
add_fields: ["subject", "email_sent", "schedule_sending"], | |||
get_indicator: function(doc) { | |||
if(doc.email_sent) { | |||
if (doc.email_sent) { | |||
return [__("Sent"), "green", "email_sent,=,Yes"]; | |||
} else if (doc.schedule_sending) { | |||
return [__("Scheduled"), "orange", "email_sent,=,No|schedule_sending,=,Yes"]; | |||
} else { | |||
return [__("Not Sent"), "orange", "email_sent,=,No"]; | |||
} | |||
@@ -67,6 +67,7 @@ class TestNewsletter(unittest.TestCase): | |||
"doctype": "Newsletter", | |||
"subject": "_Test Newsletter", | |||
"send_from": "Test Sender <test_sender@example.com>", | |||
"content_type": "Rich Text", | |||
"message": "Testing my news.", | |||
"published": published, | |||
"schedule_sending": bool(schedule_send), | |||
@@ -136,7 +136,8 @@ doc_events = { | |||
"frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions", | |||
"frappe.automation.doctype.assignment_rule.assignment_rule.apply", | |||
"frappe.automation.doctype.milestone_tracker.milestone_tracker.evaluate_milestone", | |||
"frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers" | |||
"frappe.core.doctype.file.file.attach_files_to_document", | |||
"frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers", | |||
], | |||
"after_rename": "frappe.desk.notifications.clear_doctype_notifications", | |||
"on_cancel": [ | |||
@@ -359,8 +359,11 @@ def is_downgrade(sql_file_path, verbose=False): | |||
if head in line: | |||
# 'line' (str) format: ('2056588823','2020-05-11 18:21:31.488367','2020-06-12 11:49:31.079506','Administrator','Administrator',0,'Installed Applications','installed_applications','Installed Applications',1,'frappe','v10.1.71-74 (3c50d5e) (v10.x.x)','v10.x.x'),('855c640b8e','2020-05-11 18:21:31.488367','2020-06-12 11:49:31.079506','Administrator','Administrator',0,'Installed Applications','installed_applications','Installed Applications',2,'your_custom_app','0.0.1','master') | |||
line = line.strip().lstrip(head).rstrip(";").strip() | |||
app_rows = frappe.safe_eval(line) | |||
# check if iterable consists of tuples before trying to transform | |||
apps_list = app_rows if all(isinstance(app_row, (tuple, list, set)) for app_row in app_rows) else (app_rows, ) | |||
# 'all_apps' (list) format: [('frappe', '12.x.x-develop ()', 'develop'), ('your_custom_app', '0.0.1', 'master')] | |||
all_apps = [ x[-3:] for x in frappe.safe_eval(line) ] | |||
all_apps = [ x[-3:] for x in apps_list ] | |||
for app in all_apps: | |||
app_name = app[0] | |||
@@ -10,11 +10,6 @@ | |||
"label": "Google Services", | |||
"links": "[\n {\n \"description\": \"Google API Settings.\",\n \"label\": \"Google Settings\",\n \"name\": \"Google Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Contacts Integration.\",\n \"label\": \"Google Contacts\",\n \"name\": \"Google Contacts\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Calendar Integration.\",\n \"label\": \"Google Calendar\",\n \"name\": \"Google Calendar\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Drive Integration.\",\n \"label\": \"Google Drive\",\n \"name\": \"Google Drive\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Webhook", | |||
"links": "[\n {\n \"description\": \"Webhooks calling API requests into web apps\",\n \"label\": \"Webhook\",\n \"name\": \"Webhook\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Slack Webhooks for internal integration\",\n \"label\": \"Slack Webhook URL\",\n \"name\": \"Slack Webhook URL\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Authentication", | |||
@@ -23,7 +18,12 @@ | |||
{ | |||
"hidden": 0, | |||
"label": "Payments", | |||
"links": "[\n {\n \"description\": \"Braintree payment gateway settings\",\n \"label\": \"Braintree Settings\",\n \"name\": \"Braintree Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"PayPal payment gateway settings\",\n \"label\": \"PayPal Settings\",\n \"name\": \"PayPal Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Razorpay Payment gateway settings\",\n \"label\": \"Razorpay Settings\",\n \"name\": \"Razorpay Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Stripe payment gateway settings\",\n \"label\": \"Stripe Settings\",\n \"name\": \"Stripe Settings\",\n \"type\": \"doctype\"\n }\n]" | |||
"links": "[\n {\n \"description\": \"Braintree payment gateway settings\",\n \"label\": \"Braintree Settings\",\n \"name\": \"Braintree Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"PayPal payment gateway settings\",\n \"label\": \"PayPal Settings\",\n \"name\": \"PayPal Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Razorpay Payment gateway settings\",\n \"label\": \"Razorpay Settings\",\n \"name\": \"Razorpay Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Stripe payment gateway settings\",\n \"label\": \"Stripe Settings\",\n \"name\": \"Stripe Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Paytm payment gateway settings\",\n \"label\": \"Paytm Settings\",\n \"name\": \"Paytm Settings\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Settings", | |||
"links": "[\n {\n \"description\": \"Webhooks calling API requests into web apps\",\n \"label\": \"Webhook\",\n \"name\": \"Webhook\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Slack Webhooks for internal integration\",\n \"label\": \"Slack Webhook URL\",\n \"name\": \"Slack Webhook URL\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Twilio Settings for WhatsApp integration\",\n \"label\": \"Twilio Settings\",\n \"name\": \"Twilio Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"SMS Settings for sending sms\",\n \"label\": \"SMS Settings\",\n \"name\": \"SMS Settings\",\n \"type\": \"doctype\"\n }\n]" | |||
} | |||
], | |||
"category": "Administration", | |||
@@ -34,11 +34,11 @@ | |||
"docstatus": 0, | |||
"doctype": "Desk Page", | |||
"extends_another_page": 0, | |||
"icon": "frapicon-dashboard", | |||
"hide_custom": 0, | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Integrations", | |||
"modified": "2020-04-01 11:24:40.751651", | |||
"modified": "2020-08-20 23:04:04.528572", | |||
"modified_by": "Administrator", | |||
"module": "Integrations", | |||
"name": "Integrations", | |||
@@ -8,7 +8,7 @@ import os.path | |||
import frappe | |||
import boto3 | |||
from frappe import _ | |||
from frappe.integrations.offsite_backup_utils import get_latest_backup_file, send_email, validate_file_size | |||
from frappe.integrations.offsite_backup_utils import get_latest_backup_file, send_email, validate_file_size, generate_files_backup | |||
from frappe.model.document import Document | |||
from frappe.utils import cint | |||
from frappe.utils.background_jobs import enqueue | |||
@@ -125,6 +125,11 @@ def backup_to_s3(): | |||
else: | |||
if backup_files: | |||
db_filename, site_config, files_filename, private_files = get_latest_backup_file(with_files=backup_files) | |||
if not files_filename or not private_files: | |||
generate_files_backup() | |||
db_filename, site_config, files_filename, private_files = get_latest_backup_file(with_files=backup_files) | |||
else: | |||
db_filename, site_config = get_latest_backup_file() | |||
@@ -133,9 +138,14 @@ def backup_to_s3(): | |||
upload_file_to_s3(db_filename, folder, conn, bucket) | |||
upload_file_to_s3(site_config, folder, conn, bucket) | |||
if backup_files: | |||
upload_file_to_s3(private_files, folder, conn, bucket) | |||
upload_file_to_s3(files_filename, folder, conn, bucket) | |||
if private_files: | |||
upload_file_to_s3(private_files, folder, conn, bucket) | |||
if files_filename: | |||
upload_file_to_s3(files_filename, folder, conn, bucket) | |||
delete_old_backups(doc.backup_limit, bucket) | |||
@@ -160,14 +170,15 @@ def delete_old_backups(limit, bucket): | |||
aws_access_key_id=doc.access_key_id, | |||
aws_secret_access_key=doc.get_password('secret_access_key'), | |||
endpoint_url=doc.endpoint_url or 'https://s3.amazonaws.com' | |||
) | |||
) | |||
bucket = s3.Bucket(bucket) | |||
objects = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Delimiter='/') | |||
if objects: | |||
for obj in objects.get('CommonPrefixes'): | |||
all_backups.append(obj.get('Prefix')) | |||
oldest_backup = sorted(all_backups)[0] | |||
oldest_backup = sorted(all_backups)[0] if all_backups else '' | |||
if len(all_backups) > backup_limit: | |||
print("Deleting Backup: {0}".format(oldest_backup)) | |||
@@ -14,12 +14,16 @@ | |||
"fieldtype": "Data", | |||
"in_list_view": 1, | |||
"label": "Phone Number", | |||
"options": "Phone", | |||
"show_days": 1, | |||
"show_seconds": 1, | |||
"unique": 1 | |||
} | |||
], | |||
"index_web_pages_for_search": 1, | |||
"istable": 1, | |||
"links": [], | |||
"modified": "2020-03-02 14:54:34.396254", | |||
"modified": "2020-08-20 22:48:57.166791", | |||
"modified_by": "Administrator", | |||
"module": "Integrations", | |||
"name": "Twilio Number Group", | |||
@@ -11,7 +11,16 @@ from frappe.utils.password import get_decrypted_password | |||
from six import string_types | |||
class TwilioSettings(Document): | |||
pass | |||
def validate(self): | |||
self.validate_twilio_credentials() | |||
def validate_twilio_credentials(self): | |||
try: | |||
auth_token = get_decrypted_password("Twilio Settings", "Twilio Settings", 'auth_token') | |||
client = Client(self.account_sid, auth_token) | |||
client.api.accounts(self.account_sid).fetch() | |||
except Exception: | |||
frappe.throw(_("Invalid Account SID or Auth Token.")) | |||
def send_whatsapp_message(sender, receiver_list, message): | |||
import json | |||
@@ -90,3 +90,13 @@ def validate_file_size(): | |||
if file_size > 1: | |||
frappe.flags.create_new_backup = False | |||
def generate_files_backup(): | |||
from frappe.utils.backups import BackupGenerator | |||
backup = BackupGenerator(frappe.conf.db_name, frappe.conf.db_name, | |||
frappe.conf.db_password, db_host = frappe.db.host, | |||
db_type=frappe.conf.db_type, db_port=frappe.conf.db_port) | |||
backup.set_backup_file_name() | |||
backup.zip_files() |
@@ -299,7 +299,12 @@ class BaseDocument(object): | |||
return frappe.as_json(self.as_dict()) | |||
def get_table_field_doctype(self, fieldname): | |||
return self.meta.get_field(fieldname).options | |||
try: | |||
return self.meta.get_field(fieldname).options | |||
except AttributeError: | |||
if self.doctype == 'DocType': | |||
return dict(links='DocType Link', actions='DocType Action').get(fieldname) | |||
raise | |||
def get_parentfield_of_doctype(self, doctype): | |||
fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype] | |||
@@ -19,11 +19,17 @@ def rename_field(doctype, old_fieldname, new_fieldname): | |||
print("rename_field: " + (new_fieldname) + " not found in " + doctype) | |||
return | |||
if not meta.issingle and not frappe.db.has_column(doctype, old_fieldname): | |||
print("rename_field: " + (old_fieldname) + " not found in table for: " + doctype) | |||
# never had the field? | |||
return | |||
if new_field.fieldtype in table_fields: | |||
# change parentfield of table mentioned in options | |||
frappe.db.sql("""update `tab%s` set parentfield=%s | |||
where parentfield=%s""" % (new_field.options.split("\n")[0], "%s", "%s"), | |||
(new_fieldname, old_fieldname)) | |||
elif new_field.fieldtype not in no_value_fields: | |||
if meta.issingle: | |||
frappe.db.sql("""update `tabSingles` set field=%s | |||
@@ -15,7 +15,7 @@ execute:frappe.reload_doc('core', 'doctype', 'custom_docperm') | |||
execute:frappe.reload_doc('core', 'doctype', 'docperm') #2018-05-29 | |||
execute:frappe.reload_doc('core', 'doctype', 'comment') | |||
frappe.patches.v8_0.drop_is_custom_from_docperm | |||
execute:frappe.reload_doc('core', 'doctype', 'module_def') #2017-09-22 | |||
execute:frappe.reload_doc('core', 'doctype', 'module_def') #2020-08-28 | |||
execute:frappe.reload_doc('core', 'doctype', 'version') #2017-04-01 | |||
execute:frappe.reload_doc('email', 'doctype', 'document_follow') | |||
execute:frappe.reload_doc('core', 'doctype', 'communication_link') #2019-10-02 | |||
@@ -40,7 +40,9 @@ execute:frappe.reload_doc('core', 'doctype', 'role') #2017-05-23 | |||
execute:frappe.reload_doc('core', 'doctype', 'user') #2017-10-27 | |||
execute:frappe.reload_doc('custom', 'doctype', 'custom_field') #2015-10-19 | |||
execute:frappe.reload_doc('core', 'doctype', 'page') #2013-13-26 | |||
execute:frappe.reload_doc('core', 'doctype', 'report') #2014-06-03 | |||
execute:frappe.reload_doc('core', 'doctype', 'report_column') | |||
execute:frappe.reload_doc('core', 'doctype', 'report_filter') | |||
execute:frappe.reload_doc('core', 'doctype', 'report') #2020-08-25 | |||
execute:frappe.reload_doc('core', 'doctype', 'translation') #2016-03-03 | |||
execute:frappe.reload_doc('email', 'doctype', 'email_alert') #2014-07-15 | |||
execute:frappe.reload_doc('desk', 'doctype', 'todo') #2014-12-31-1 | |||
@@ -299,5 +301,9 @@ frappe.patches.v13_0.rename_is_custom_field_in_dashboard_chart | |||
frappe.patches.v13_0.add_standard_navbar_items | |||
frappe.patches.v13_0.generate_theme_files_in_public_folder | |||
frappe.patches.v13_0.increase_password_length | |||
frappe.patches.v12_0.fix_email_id_formatting | |||
frappe.patches.v13_0.add_toggle_width_in_navbar_settings | |||
frappe.patches.v13_0.rename_notification_fields | |||
frappe.patches.v13_0.remove_duplicate_navbar_items | |||
frappe.patches.v13_0.enable_custom_script | |||
frappe.patches.v13_0.update_newsletter_content_type |
@@ -0,0 +1,44 @@ | |||
import frappe | |||
def execute(): | |||
fix_communications() | |||
fix_show_as_cc_email_queue() | |||
fix_email_queue_recipients() | |||
def fix_communications(): | |||
for communication in frappe.db.sql('''select name, recipients, cc, bcc from tabCommunication | |||
where creation > '2020-06-01' | |||
and communication_medium='Email' | |||
and communication_type='Communication' | |||
and (cc like '%<%' or bcc like '%<%' or recipients like '%<%') | |||
''', as_dict=1): | |||
communication['recipients'] = format_email_id(communication.recipients) | |||
communication['cc'] = format_email_id(communication.cc) | |||
communication['bcc'] = format_email_id(communication.bcc) | |||
frappe.db.sql('''update `tabCommunication` set recipients=%s,cc=%s,bcc=%s | |||
where name =%s ''', (communication['recipients'], communication['cc'], | |||
communication['bcc'], communication['name'])) | |||
def fix_show_as_cc_email_queue(): | |||
for queue in frappe.get_all("Email Queue", {'creation': ['>', '2020-06-01'], | |||
'status': 'Not Sent', 'show_as_cc': ['like', '%<%']}, | |||
['name', 'show_as_cc']): | |||
frappe.db.set_value('Email Queue', queue['name'], | |||
'show_as_cc', format_email_id(queue['show_as_cc'])) | |||
def fix_email_queue_recipients(): | |||
for recipient in frappe.db.sql('''select recipient, name from | |||
`tabEmail Queue Recipient` where recipient like '%<%' | |||
and status='Not Sent' and creation > '2020-06-01' ''', as_dict=1): | |||
frappe.db.set_value('Email Queue Recipient', recipient['name'], | |||
'recipient', format_email_id(recipient['recipient'])) | |||
def format_email_id(email): | |||
if email and ('<' in email and '>' in email): | |||
return email.replace('>', '>').replace('<', '<') | |||
return email |
@@ -1,6 +1,8 @@ | |||
import frappe | |||
def execute(): | |||
frappe.reload_doc('core', 'doctype', 'doctype_link') | |||
frappe.reload_doc('core', 'doctype', 'doctype_action') | |||
frappe.reload_doc('core', 'doctype', 'doctype') | |||
frappe.model.delete_fields({ | |||
'DocType': ['hide_heading', 'image_view', 'read_only_onload'] | |||
@@ -4,6 +4,9 @@ import frappe | |||
def execute(): | |||
navbar_settings = frappe.get_single("Navbar Settings") | |||
if frappe.db.exists('Navbar Item', {'item_label': 'Toggle Full Width'}): | |||
return | |||
for navbar_item in navbar_settings.settings_dropdown[5:]: | |||
navbar_item.idx = navbar_item.idx + 1 | |||
@@ -0,0 +1,13 @@ | |||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
def execute(): | |||
"""Enable all the existing custom script""" | |||
frappe.reload_doc("Custom", "doctype", "Custom Script") | |||
frappe.db.sql(""" | |||
UPDATE `tabCustom Script` SET enabled=1 | |||
""") |
@@ -0,0 +1,14 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
def execute(): | |||
navbar_settings = frappe.get_single("Navbar Settings") | |||
duplicate_items = [] | |||
for navbar_item in navbar_settings.settings_dropdown: | |||
if navbar_item.item_label == 'Toggle Full Width': | |||
duplicate_items.append(navbar_item) | |||
if len(duplicate_items) > 1: | |||
navbar_settings.remove(duplicate_items[0]) | |||
navbar_settings.save() |
@@ -0,0 +1,12 @@ | |||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
def execute(): | |||
frappe.reload_doc('email', 'doctype', 'Newsletter') | |||
frappe.db.sql(""" | |||
UPDATE tabNewsletter | |||
SET content_type = 'Rich Text' | |||
""") |
@@ -1,4 +1,5 @@ | |||
{ | |||
"actions": [], | |||
"allow_rename": 1, | |||
"autoname": "Prompt", | |||
"creation": "2013-01-23 19:54:43", | |||
@@ -181,7 +182,7 @@ | |||
"fieldname": "print_format_help", | |||
"fieldtype": "HTML", | |||
"label": "Print Format Help", | |||
"options": "<h3>Print Format Help</h3>\n<hr>\n<h4>Introduction</h4>\n<p>Print itemsFormats are rendered on the server side using the Jinja Templating Language. All forms have access to the <code>doc</code> object which contains information about the document that is being formatted. You can also access common utilities via the <code>frappe</code> module.</p>\n<p>For styling, the Boostrap CSS framework is provided and you can enjoy the full range of classes.</p>\n<hr>\n<h4>References</h4>\n<ol>\n\t<li><a href=\"http://jinja.pocoo.org/docs/templates/\" target=\"_blank\">Jinja Tempalting Language: Reference</a></li>\n\t<li><a href=\"http://getbootstrap.com\" target=\"_blank\">Bootstrap CSS Framework</a></li>\n</ol>\n<hr>\n<h4>Example</h4>\n<pre><code><h3>{{ doc.select_print_heading or \"Invoice\" }}</h3>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Customer Name</div>\n\t<div class=\"col-md-9\">{{ doc.customer_name }}</div>\n</div>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Date</div>\n\t<div class=\"col-md-9\">{{ doc.get_formatted(\"invoice_date\") }}</div>\n</div>\n<table class=\"table table-bordered\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<th>Sr</th>\n\t\t\t<th>Item Name</th>\n\t\t\t<th>Description</th>\n\t\t\t<th class=\"text-right\">Qty</th>\n\t\t\t<th class=\"text-right\">Rate</th>\n\t\t\t<th class=\"text-right\">Amount</th>\n\t\t</tr>\n\t\t{%- for row in doc.items -%}\n\t\t<tr>\n\t\t\t<td style=\"width: 3%;\">{{ row.idx }}</td>\n\t\t\t<td style=\"width: 20%;\">\n\t\t\t\t{{ row.item_name }}\n\t\t\t\t{% if row.item_code != row.item_name -%}\n\t\t\t\t<br>Item Code: {{ row.item_code}}\n\t\t\t\t{%- endif %}\n\t\t\t</td>\n\t\t\t<td style=\"width: 37%;\">\n\t\t\t\t<div style=\"border: 0px;\">{{ row.description }}</div></td>\n\t\t\t<td style=\"width: 10%; text-align: right;\">{{ row.qty }} {{ row.uom or row.stock_uom }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"rate\", doc) }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"amount\", doc) }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table></code></pre>\n<hr>\n<h4>Common Functions</h4>\n<table class=\"table table-bordered\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td style=\"width: 30%;\"><code>doc.get_formatted(\"[fieldname]\", [parent_doc])</code></td>\n\t\t\t<td>Get document value formatted as Date, Currency etc. Pass parent <code>doc</code> for curreny type fields.</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style=\"width: 30%;\"><code>frappe.db.get_value(\"[doctype]\", \"[name]\", \"fieldname\")</code></td>\n\t\t\t<td>Get value from another document.</td>\n\t\t</tr>\n\t</tbody>\n</table>\n" | |||
"options": "<h3>Print Format Help</h3>\n<hr>\n<h4>Introduction</h4>\n<p>Print Formats are rendered on the server side using the Jinja Templating Language. All forms have access to the <code>doc</code> object which contains information about the document that is being formatted. You can also access common utilities via the <code>frappe</code> module.</p>\n<p>For styling, the Boostrap CSS framework is provided and you can enjoy the full range of classes.</p>\n<hr>\n<h4>References</h4>\n<ol>\n\t<li><a href=\"http://jinja.pocoo.org/docs/templates/\" target=\"_blank\">Jinja Templating Language</a></li>\n\t<li><a href=\"http://getbootstrap.com\" target=\"_blank\">Bootstrap CSS Framework</a></li>\n</ol>\n<hr>\n<h4>Example</h4>\n<pre><code><h3>{{ doc.select_print_heading or \"Invoice\" }}</h3>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Customer Name</div>\n\t<div class=\"col-md-9\">{{ doc.customer_name }}</div>\n</div>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Date</div>\n\t<div class=\"col-md-9\">{{ doc.get_formatted(\"invoice_date\") }}</div>\n</div>\n<table class=\"table table-bordered\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<th>Sr</th>\n\t\t\t<th>Item Name</th>\n\t\t\t<th>Description</th>\n\t\t\t<th class=\"text-right\">Qty</th>\n\t\t\t<th class=\"text-right\">Rate</th>\n\t\t\t<th class=\"text-right\">Amount</th>\n\t\t</tr>\n\t\t{%- for row in doc.items -%}\n\t\t<tr>\n\t\t\t<td style=\"width: 3%;\">{{ row.idx }}</td>\n\t\t\t<td style=\"width: 20%;\">\n\t\t\t\t{{ row.item_name }}\n\t\t\t\t{% if row.item_code != row.item_name -%}\n\t\t\t\t<br>Item Code: {{ row.item_code}}\n\t\t\t\t{%- endif %}\n\t\t\t</td>\n\t\t\t<td style=\"width: 37%;\">\n\t\t\t\t<div style=\"border: 0px;\">{{ row.description }}</div></td>\n\t\t\t<td style=\"width: 10%; text-align: right;\">{{ row.qty }} {{ row.uom or row.stock_uom }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"rate\", doc) }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"amount\", doc) }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table></code></pre>\n<hr>\n<h4>Common Functions</h4>\n<table class=\"table table-bordered\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td style=\"width: 30%;\"><code>doc.get_formatted(\"[fieldname]\", [parent_doc])</code></td>\n\t\t\t<td>Get document value formatted as Date, Currency, etc. Pass parent <code>doc</code> for currency type fields.</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style=\"width: 30%;\"><code>frappe.db.get_value(\"[doctype]\", \"[name]\", \"fieldname\")</code></td>\n\t\t\t<td>Get value from another document.</td>\n\t\t</tr>\n\t</tbody>\n</table>\n" | |||
}, | |||
{ | |||
"fieldname": "format_data", | |||
@@ -199,8 +200,10 @@ | |||
], | |||
"icon": "fa fa-print", | |||
"idx": 1, | |||
"modified": "2019-11-28 12:40:40.364699", | |||
"modified_by": "faris@erpnext.com", | |||
"index_web_pages_for_search": 1, | |||
"links": [], | |||
"modified": "2020-08-29 11:44:59.082797", | |||
"modified_by": "Administrator", | |||
"module": "Printing", | |||
"name": "Print Format", | |||
"owner": "Administrator", | |||
@@ -144,6 +144,9 @@ export default { | |||
docname: { | |||
default: null | |||
}, | |||
fieldname: { | |||
default: null | |||
}, | |||
folder: { | |||
default: 'Home' | |||
}, | |||
@@ -406,6 +409,10 @@ export default { | |||
form_data.append('docname', this.docname); | |||
} | |||
if (this.fieldname) { | |||
form_data.append('fieldname', this.fieldname); | |||
} | |||
if (this.method) { | |||
form_data.append('method', this.method); | |||
} | |||
@@ -7,6 +7,7 @@ export default class FileUploader { | |||
on_success, | |||
doctype, | |||
docname, | |||
fieldname, | |||
files, | |||
folder, | |||
restrictions, | |||
@@ -28,6 +29,7 @@ export default class FileUploader { | |||
show_upload_button: !Boolean(this.dialog), | |||
doctype, | |||
docname, | |||
fieldname, | |||
method, | |||
folder, | |||
on_success, | |||
@@ -66,6 +66,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ | |||
if (this.frm) { | |||
options.doctype = this.frm.doctype; | |||
options.docname = this.frm.docname; | |||
options.fieldname = this.df.fieldname; | |||
} | |||
if (this.df.options) { | |||
@@ -1,5 +1,7 @@ | |||
import Quill from 'quill'; | |||
import ImageResize from 'quill-image-resize'; | |||
Quill.register('modules/imageResize', ImageResize); | |||
const CodeBlockContainer = Quill.import('formats/code-block-container'); | |||
CodeBlockContainer.tagName = 'PRE'; | |||
Quill.register(CodeBlockContainer, true); | |||
@@ -145,7 +147,8 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({ | |||
return { | |||
modules: { | |||
toolbar: this.get_toolbar_options(), | |||
table: true | |||
table: true, | |||
imageResize: {} | |||
}, | |||
theme: 'snow' | |||
}; | |||
@@ -525,7 +525,7 @@ frappe.ui.form.Layout = Class.extend({ | |||
return; | |||
} | |||
var parent = this.frm ? this.frm.doc : null; | |||
var parent = this.frm ? this.frm.doc : this.doc || null; | |||
if(typeof(expression) === 'boolean') { | |||
out = expression; | |||
@@ -6,7 +6,7 @@ frappe.ui.misc.about = function() { | |||
$(d.body).html(repl("<div>\ | |||
<p>"+__("Open Source Applications for the Web")+"</p> \ | |||
<p><i class='fa fa-globe fa-fw'></i>\ | |||
Website: <a href='https://frappe.io' target='_blank'>https://frappe.io</a></p>\ | |||
Website: <a href='https://frappeframework.com' target='_blank'>https://frappeframework.com</a></p>\ | |||
<p><i class='fa fa-github fa-fw'></i>\ | |||
Source: <a href='https://github.com/frappe' target='_blank'>https://github.com/frappe</a></p>\ | |||
<p><i class='fa fa-linkedin fa-fw'></i>\ | |||
@@ -592,6 +592,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||
this.render_summary(data.report_summary); | |||
} | |||
if (data.message && !data.prepared_report) this.show_status(data.message); | |||
this.toggle_message(false); | |||
if (data.result && data.result.length) { | |||
this.prepare_report_data(data); | |||
@@ -1041,7 +1043,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||
if (column.colIndex === index && !value) { | |||
value = "Total"; | |||
column.fieldtype = "Data"; // avoid type issues for value if Date column | |||
column = { fieldtype: "Data" }; // avoid type issues for value if Date column | |||
} else if (in_list(["Currency", "Float"], column.fieldtype)) { | |||
// proxy for currency and float | |||
data = this.data[0]; | |||
@@ -151,7 +151,7 @@ function shorten_number(number, country) { | |||
for (const map of number_system) { | |||
const condition = map.condition ? map.condition(x) : x >= map.divisor; | |||
if (condition) { | |||
return Math.round(number/map.divisor) + ' ' + map.symbol; | |||
return (number/map.divisor).toFixed(2) + ' ' + map.symbol; | |||
} | |||
} | |||
return number.toFixed(); | |||
@@ -1,5 +1,5 @@ | |||
<!DOCTYPE html> | |||
<!-- Built on Frappe. https://frappe.io/ --> | |||
<!-- Built on Frappe. https://frappeframework.com/ --> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
@@ -1 +1 @@ | |||
<a href="https://frappe.io?source=website_footer" target="_blank" class="text-muted">Built on Frappe</a> | |||
<a href="https://frappeframework.com?source=website_footer" target="_blank" class="text-muted">Built on Frappe</a> |
@@ -67,9 +67,19 @@ class TestAPI(unittest.TestCase): | |||
{"doctype": "Note", "public": True, "title": "get_value", "content": test_content}, | |||
]) | |||
self.assertEqual(server.get_value("Note", "content", {"title": "get_value"}).get('content'), test_content) | |||
name = server.get_value("Note", "name", {"title": "get_value"}).get('name') | |||
# test by name | |||
self.assertEqual(server.get_value("Note", "content", name).get('content'), test_content) | |||
self.assertRaises(FrappeException, server.get_value, "Note", "(select (password) from(__Auth) order by name desc limit 1)", {"title": "get_value"}) | |||
def test_get_single(self): | |||
server = FrappeClient(get_url(), "Administrator", "admin", verify=False) | |||
server.set_value('Website Settings', 'Website Settings', 'title_prefix', 'test-prefix') | |||
self.assertEqual(server.get_value('Website Settings', 'title_prefix', 'Website Settings').get('title_prefix'), 'test-prefix') | |||
self.assertEqual(server.get_value('Website Settings', 'title_prefix').get('title_prefix'), 'test-prefix') | |||
frappe.db.set_value('Website Settings', None, 'title_prefix', '') | |||
def test_update_doc(self): | |||
server = FrappeClient(get_url(), "Administrator", "admin", verify=False) | |||
@@ -1,5 +1,5 @@ | |||
from __future__ import unicode_literals | |||
import unittest | |||
import unittest, frappe | |||
from frappe.utils.safe_exec import safe_exec | |||
class TestSafeExec(unittest.TestCase): | |||
@@ -7,4 +7,11 @@ class TestSafeExec(unittest.TestCase): | |||
self.assertRaises(ImportError, safe_exec, 'import os') | |||
def test_internal_attributes(self): | |||
self.assertRaises(SyntaxError, safe_exec, '().__class__.__call__') | |||
self.assertRaises(SyntaxError, safe_exec, '().__class__.__call__') | |||
def test_sql(self): | |||
_locals = dict(out=None) | |||
safe_exec('''out = frappe.db.sql("select name from tabDocType where name='DocType'")''', None, _locals) | |||
self.assertEqual(_locals['out'][0][0], 'DocType') | |||
self.assertRaises(frappe.PermissionError, safe_exec, 'frappe.db.sql("update tabToDo set description=NULL")') |
@@ -187,9 +187,7 @@ def process_2fa_for_otp_app(user, otp_secret, otp_issuer): | |||
otp_setup_completed = False | |||
verification_obj = { | |||
'totp_uri': totp_uri, | |||
'method': 'OTP App', | |||
'qrcode': get_qr_svg_code(totp_uri), | |||
'setup': otp_setup_completed | |||
} | |||
return verification_obj | |||
@@ -29,7 +29,7 @@ class BackupGenerator: | |||
""" | |||
def __init__(self, db_name, user, password, backup_path_db=None, backup_path_files=None, | |||
backup_path_private_files=None, db_host="localhost", db_port=None, verbose=False, | |||
db_type='mariadb'): | |||
db_type='mariadb', backup_path_conf=None): | |||
global _verbose | |||
self.db_host = db_host | |||
self.db_port = db_port | |||
@@ -37,10 +37,14 @@ class BackupGenerator: | |||
self.db_type = db_type | |||
self.user = user | |||
self.password = password | |||
self.backup_path_files = backup_path_files | |||
self.backup_path_conf = backup_path_conf | |||
self.backup_path_db = backup_path_db | |||
self.backup_path_files = backup_path_files | |||
self.backup_path_private_files = backup_path_private_files | |||
if not self.db_type: | |||
self.db_type = 'mariadb' | |||
if not self.db_port and self.db_type == 'mariadb': | |||
self.db_port = 3306 | |||
elif not self.db_port and self.db_type == 'postgres': | |||
@@ -48,10 +52,23 @@ class BackupGenerator: | |||
site = frappe.local.site or frappe.generate_hash(length=8) | |||
self.site_slug = site.replace('.', '_') | |||
self.verbose = verbose | |||
self.setup_backup_directory() | |||
_verbose = verbose | |||
def setup_backup_directory(self): | |||
specified = self.backup_path_db or self.backup_path_files or self.backup_path_private_files | |||
if not specified: | |||
backups_folder = get_backup_path() | |||
if not os.path.exists(backups_folder): | |||
os.makedirs(backups_folder) | |||
else: | |||
for file_path in [self.backup_path_files, self.backup_path_db, self.backup_path_private_files]: | |||
dir = os.path.dirname(file_path) | |||
os.makedirs(dir, exist_ok=True) | |||
def get_backup(self, older_than=24, ignore_files=False, force=False): | |||
""" | |||
Takes a new dump if existing file is old | |||
@@ -83,11 +100,14 @@ class BackupGenerator: | |||
def set_backup_file_name(self): | |||
#Generate a random name using today's date and a 8 digit random number | |||
for_conf = self.todays_date + "-" + self.site_slug + "-site_config_backup.json" | |||
for_db = self.todays_date + "-" + self.site_slug + "-database.sql.gz" | |||
for_public_files = self.todays_date + "-" + self.site_slug + "-files.tar" | |||
for_private_files = self.todays_date + "-" + self.site_slug + "-private-files.tar" | |||
backup_path = get_backup_path() | |||
if not self.backup_path_conf: | |||
self.backup_path_conf = os.path.join(backup_path, for_conf) | |||
if not self.backup_path_db: | |||
self.backup_path_db = os.path.join(backup_path, for_db) | |||
if not self.backup_path_files: | |||
@@ -150,19 +170,11 @@ class BackupGenerator: | |||
print('Backed up files', os.path.abspath(backup_path)) | |||
def copy_site_config(self): | |||
site_config_backup_path = os.path.join( | |||
get_backup_path(), | |||
"{time_stamp}-{site_slug}-site_config_backup.json".format( | |||
time_stamp=self.todays_date, | |||
site_slug=self.site_slug)) | |||
site_config_backup_path = self.backup_path_conf | |||
site_config_path = os.path.join(frappe.get_site_path(), "site_config.json") | |||
site_config = {} | |||
if os.path.exists(site_config_path): | |||
site_config.update(frappe.get_file_json(site_config_path)) | |||
with open(site_config_backup_path, "w") as f: | |||
f.write(json.dumps(site_config, indent=2)) | |||
f.flush() | |||
self.site_config_backup_path = site_config_backup_path | |||
with open(site_config_backup_path, "w") as n, open(site_config_path) as c: | |||
n.write(c.read()) | |||
def take_dump(self): | |||
import frappe.utils | |||
@@ -406,6 +406,10 @@ def extract_images_from_html(doc, content): | |||
doctype = doc.parenttype if doc.parent else doc.doctype | |||
name = doc.parent or doc.name | |||
if doc.doctype == "Comment": | |||
doctype = doc.reference_doctype | |||
name = doc.reference_name | |||
# TODO fix this | |||
file_url = save_file(filename, content, doctype, name, decode=True).get("file_url") | |||
if not frappe.flags.has_dataurl: | |||
@@ -50,7 +50,7 @@ def get_safe_globals(): | |||
dict=dict, | |||
_dict=frappe._dict, | |||
frappe=frappe._dict( | |||
flags=frappe.flags, | |||
flags=frappe._dict(), | |||
format=frappe.format_value, | |||
format_value=frappe.format_value, | |||
date_format=date_format, | |||
@@ -71,6 +71,8 @@ def get_safe_globals(): | |||
msgprint=frappe.msgprint, | |||
throw=frappe.throw, | |||
sendmail = frappe.sendmail, | |||
get_print = frappe.get_print, | |||
attach_print = frappe.attach_print, | |||
user=user, | |||
get_fullname=frappe.utils.get_fullname, | |||
@@ -114,6 +116,7 @@ def get_safe_globals(): | |||
get_single_value = frappe.db.get_single_value, | |||
get_default = frappe.db.get_default, | |||
escape = frappe.db.escape, | |||
sql = read_sql | |||
) | |||
if frappe.response: | |||
@@ -132,6 +135,13 @@ def get_safe_globals(): | |||
return out | |||
def read_sql(query, *args, **kwargs): | |||
'''a wrapper for frappe.db.sql to allow reads''' | |||
if query.strip().split(None, 1)[0].lower() == 'select': | |||
return frappe.db.sql(query, *args, **kwargs) | |||
else: | |||
raise frappe.PermissionError('Only SELECT SQL allowed in scripting') | |||
def _getitem(obj, key): | |||
# guard function for RestrictedPython | |||
# allow any key to be accessed as long as it does not start with underscore | |||
@@ -110,6 +110,7 @@ | |||
"depends_on": "eval:doc.content_type === 'Markdown'", | |||
"fieldname": "content_md", | |||
"fieldtype": "Markdown Editor", | |||
"ignore_xss_filter": 1, | |||
"label": "Content (Markdown)" | |||
}, | |||
{ | |||
@@ -193,7 +194,7 @@ | |||
"is_published_field": "published", | |||
"links": [], | |||
"max_attachments": 5, | |||
"modified": "2020-07-21 16:25:17.154911", | |||
"modified": "2020-08-31 16:55:03.687862", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Blog Post", | |||
@@ -209,7 +209,7 @@ | |||
"label": "Client Script" | |||
}, | |||
{ | |||
"description": "For help see <a href=\"https://frappe.io/docs/user/en/guides/portal-development/web-forms\" target=\"_blank\">Client Script API and Examples</a>", | |||
"description": "For help see <a href=\"https://frappeframework.com/docs/user/en/guides/portal-development/web-forms\" target=\"_blank\">Client Script API and Examples</a>", | |||
"fieldname": "client_script", | |||
"fieldtype": "Code", | |||
"label": "Client Script" | |||
@@ -126,6 +126,7 @@ | |||
"depends_on": "eval:doc.content_type==='Markdown'", | |||
"fieldname": "main_section_md", | |||
"fieldtype": "Markdown Editor", | |||
"ignore_xss_filter": 1, | |||
"label": "Main Section (Markdown)" | |||
}, | |||
{ | |||
@@ -294,7 +295,7 @@ | |||
"is_published_field": "published", | |||
"links": [], | |||
"max_attachments": 20, | |||
"modified": "2020-08-07 10:55:54.885448", | |||
"modified": "2020-08-31 16:55:52.015249", | |||
"modified_by": "Administrator", | |||
"module": "Website", | |||
"name": "Web Page", | |||
@@ -149,8 +149,8 @@ def add_preload_headers(response): | |||
preload.append(("style", elem.get("href"))) | |||
links = [] | |||
for type, link in preload: | |||
links.append("</{}>; rel=preload; as={}".format(link.lstrip("/"), type)) | |||
for _type, link in preload: | |||
links.append("<{}>; rel=preload; as={}".format(link, _type)) | |||
if links: | |||
response.headers["Link"] = ",".join(links) | |||
@@ -17,7 +17,7 @@ | |||
"bugs": { | |||
"url": "https://github.com/frappe/frappe/issues" | |||
}, | |||
"homepage": "https://frappe.io", | |||
"homepage": "https://frappeframework.com", | |||
"dependencies": { | |||
"ace-builds": "^1.4.8", | |||
"air-datepicker": "http://github.com/frappe/air-datepicker", | |||
@@ -39,10 +39,11 @@ | |||
"moment-timezone": "^0.5.28", | |||
"quagga": "^0.12.1", | |||
"quill": "2.0.0-dev.4", | |||
"quill-image-resize": "^3.0.9", | |||
"qz-tray": "^2.0.8", | |||
"redis": "^2.8.0", | |||
"showdown": "^1.9.1", | |||
"snyk": "^1.316.1", | |||
"snyk": "^1.382.0", | |||
"socket.io": "^2.3.0", | |||
"superagent": "^3.8.2", | |||
"touch": "^3.1.0", | |||
@@ -34,7 +34,7 @@ oauthlib==3.1.0 | |||
openpyxl==2.6.4 | |||
passlib==1.7.2 | |||
pdfkit==0.6.1 | |||
Pillow==6.2.2 | |||
Pillow==7.1.0 | |||
premailer==3.6.1 | |||
psycopg2-binary==2.8.4 | |||
pyasn1==0.4.8 | |||