Merge branch 'master' into staging-fixes

This commit is contained in:
Sagar Vora 2019-01-29 17:08:58 +05:30
commit fe3fe0a4b8
13 changed files with 127 additions and 85 deletions

View file

@ -23,7 +23,7 @@ if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
__version__ = '10.1.70'
__version__ = '10.1.71'
__title__ = "Frappe Framework"
local = Local()

View file

@ -571,7 +571,7 @@ def get_version():
@click.command('setup-global-help')
@click.option('--mariadb_root_password')
def setup_global_help(mariadb_root_password=None):
'''Removed: setup help table in a separate database that will be
'''Deprecated: setup help table in a separate database that will be
shared by the whole bench and set `global_help_setup` as 1 in
common_site_config.json'''
print_in_app_help_deprecation()
@ -579,18 +579,18 @@ def setup_global_help(mariadb_root_password=None):
@click.command('get-docs-app')
@click.argument('app')
def get_docs_app(app):
'''Removed: Get the docs app for given app'''
'''Deprecated: Get the docs app for given app'''
print_in_app_help_deprecation()
@click.command('get-all-docs-apps')
def get_all_docs_apps():
'''Removed: Get docs apps for all apps'''
'''Deprecated: Get docs apps for all apps'''
print_in_app_help_deprecation()
@click.command('setup-help')
@pass_context
def setup_help(context):
'''Removed: Setup help table in the current site (called after migrate)'''
'''Deprecated: Setup help table in the current site (called after migrate)'''
print_in_app_help_deprecation()
@click.command('rebuild-global-search')
@ -686,6 +686,5 @@ commands = [
watch,
_bulk_rename,
add_to_email_queue,
rebuild_global_search,
auto_deploy
rebuild_global_search
]

View file

@ -9,6 +9,9 @@ frappe.ui.form.on('Custom Field', {
frm.set_query('dt', function(doc) {
var filters = [
['DocType', 'issingle', '=', 0],
['DocType', 'custom', '=', 0],
['DocType', 'name', 'not in', frappe.model.core_doctypes_list],
['DocType', 'restrict_to_domain', 'in', frappe.boot.active_domains]
];
if(frappe.session.user!=="Administrator") {
filters.push(['DocType', 'module', 'not in', ['Core', 'Custom']])
@ -32,15 +35,21 @@ frappe.ui.form.on('Custom Field', {
return frappe.call({
method: 'frappe.custom.doctype.custom_field.custom_field.get_fields_label',
args: { doctype: frm.doc.dt, fieldname: frm.doc.fieldname },
callback: function(r, rt) {
set_field_options('insert_after', r.message);
var fieldnames = $.map(r.message, function(v) { return v.value; });
callback: function(r) {
if(r) {
if(r._server_messages && r._server_messages.length) {
frm.set_value("dt", "");
} else {
set_field_options('insert_after', r.message);
var fieldnames = $.map(r.message, function(v) { return v.value; });
if(insert_after==null || !in_list(fieldnames, insert_after)) {
insert_after = fieldnames[-1];
if(insert_after==null || !in_list(fieldnames, insert_after)) {
insert_after = fieldnames[-1];
}
frm.set_value('insert_after', insert_after);
}
}
frm.set_value('insert_after', insert_after);
}
});

View file

@ -8,6 +8,7 @@ from frappe.utils import cstr
from frappe import _
from frappe.model.document import Document
from frappe.model.docfield import supports_translation
from frappe.model import core_doctypes_list
class CustomField(Document):
def autoname(self):
@ -86,6 +87,14 @@ class CustomField(Document):
@frappe.whitelist()
def get_fields_label(doctype=None):
meta = frappe.get_meta(doctype)
if doctype in core_doctypes_list:
return frappe.msgprint(_("Custom Fields cannot be added to core DocTypes."))
if meta.custom:
return frappe.msgprint(_("Custom Fields can only be added to a standard DocType."))
return [{"value": df.fieldname or "", "label": _(df.label or "")}
for df in frappe.get_meta(doctype).get("fields")]

View file

@ -13,9 +13,7 @@ frappe.ui.form.on("Customize Form", {
filters: [
['DocType', 'issingle', '=', 0],
['DocType', 'custom', '=', 0],
['DocType', 'name', 'not in', 'DocType, DocField, DocPerm, User, Role, Has Role, \
Page, Has Role, Module Def, Print Format, Report, Customize Form, \
Customize Form Field, Property Setter, Custom Field, Custom Script'],
['DocType', 'name', 'not in', frappe.model.core_doctypes_list],
['DocType', 'restrict_to_domain', 'in', frappe.boot.active_domains]
]
};
@ -39,8 +37,14 @@ frappe.ui.form.on("Customize Form", {
doc: frm.doc,
freeze: true,
callback: function(r) {
frm.refresh();
frm.trigger("setup_sortable");
if(r) {
if(r._server_messages && r._server_messages.length) {
frm.set_value("doc_type", "");
} else {
frm.refresh();
frm.trigger("setup_sortable");
}
}
}
});
} else {

View file

@ -11,7 +11,7 @@ import frappe.translate
from frappe import _
from frappe.utils import cint
from frappe.model.document import Document
from frappe.model import no_value_fields
from frappe.model import no_value_fields, core_doctypes_list
from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype
from frappe.model.docfield import supports_translation
@ -85,6 +85,12 @@ class CustomizeForm(Document):
meta = frappe.get_meta(self.doc_type)
if self.doc_type in core_doctypes_list:
return frappe.msgprint(_("Core DocTypes cannot be customized."))
if meta.custom:
return frappe.msgprint(_("Only standard DocTypes are allowed to be customized from Customize Form."))
# doctype properties
for property in doctype_properties:
self.set(property, meta.get(property))

View file

@ -9,28 +9,28 @@ from frappe.core.doctype.doctype.doctype import InvalidFieldNameError
test_dependencies = ["Custom Field", "Property Setter"]
class TestCustomizeForm(unittest.TestCase):
def insert_custom_field(self):
frappe.delete_doc_if_exists("Custom Field", "User-test_custom_field")
frappe.delete_doc_if_exists("Custom Field", "Event-test_custom_field")
frappe.get_doc({
"doctype": "Custom Field",
"dt": "User",
"dt": "Event",
"label": "Test Custom Field",
"description": "A Custom Field for Testing",
"fieldtype": "Select",
"in_list_view": 1,
"options": "\nCustom 1\nCustom 2\nCustom 3",
"default": "Custom 3",
"insert_after": frappe.get_meta('User').fields[-1].fieldname
"insert_after": frappe.get_meta('Event').fields[-1].fieldname
}).insert()
def setUp(self):
self.insert_custom_field()
frappe.db.commit()
frappe.clear_cache(doctype="User")
frappe.clear_cache(doctype="Event")
def tearDown(self):
frappe.delete_doc("Custom Field", "User-test_custom_field")
frappe.delete_doc("Custom Field", "Event-test_custom_field")
frappe.db.commit()
frappe.clear_cache(doctype="User")
frappe.clear_cache(doctype="Event")
def get_customize_form(self, doctype=None):
d = frappe.get_doc("Customize Form")
@ -45,78 +45,67 @@ class TestCustomizeForm(unittest.TestCase):
self.assertEqual(len(d.get("fields")), 0)
d = self.get_customize_form("Event")
self.assertEqual(d.doc_type, "Event")
self.assertEqual(len(d.get("fields")), 27)
self.assertEquals(d.doc_type, "Event")
self.assertEquals(len(d.get("fields")), 30)
d = self.get_customize_form("User")
self.assertEqual(d.doc_type, "User")
d = self.get_customize_form("Event")
self.assertEquals(d.doc_type, "Event")
self.assertEqual(len(d.get("fields")),
len(frappe.get_doc("DocType", d.doc_type).fields) + 1)
self.assertEqual(d.get("fields")[-1].fieldname, "test_custom_field")
self.assertEqual(d.get("fields", {"fieldname": "location"})[0].in_list_view, 1)
self.assertEquals(d.get("fields")[-1].fieldname, "test_custom_field")
self.assertEquals(d.get("fields", {"fieldname": "event_type"})[0].in_list_view, 1)
return d
def test_save_customization_property(self):
d = self.get_customize_form("User")
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "allow_copy"}, "value"), None)
d = self.get_customize_form("Event")
self.assertEquals(frappe.db.get_value("Property Setter",
{"doc_type": "Event", "property": "allow_copy"}, "value"), None)
d.allow_copy = 1
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "allow_copy"}, "value"), '1')
self.assertEquals(frappe.db.get_value("Property Setter",
{"doc_type": "Event", "property": "allow_copy"}, "value"), '1')
d.allow_copy = 0
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "allow_copy"}, "value"), None)
self.assertEquals(frappe.db.get_value("Property Setter",
{"doc_type": "Event", "property": "allow_copy"}, "value"), None)
def test_save_customization_field_property(self):
d = self.get_customize_form("User")
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), None)
d = self.get_customize_form("Event")
self.assertEquals(frappe.db.get_value("Property Setter",
{"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), None)
location_field = d.get("fields", {"fieldname": "location"})[0]
location_field.reqd = 1
repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0]
repeat_this_event_field.reqd = 1
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), '1')
self.assertEquals(frappe.db.get_value("Property Setter",
{"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), '1')
location_field = d.get("fields", {"fieldname": "location"})[0]
location_field.reqd = 0
repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0]
repeat_this_event_field.reqd = 0
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), None)
# for not allowing to change mandatory property of standard fields
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "reqd", "field_name": "email"}, "value"), None)
email_field = d.get("fields", {"fieldname": "email"})[0]
email_field.reqd = 0
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Property Setter",
{"doc_type": "User", "property": "reqd", "field_name": "email"}, "value"), None)
self.assertEquals(frappe.db.get_value("Property Setter",
{"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), None)
def test_save_customization_custom_field_property(self):
d = self.get_customize_form("User")
self.assertEqual(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 0)
d = self.get_customize_form("Event")
self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0)
custom_field = d.get("fields", {"fieldname": "test_custom_field"})[0]
custom_field.reqd = 1
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 1)
self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 1)
custom_field = d.get("fields", {"is_custom_field": True})[0]
custom_field.reqd = 0
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 0)
self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0)
def test_save_customization_new_field(self):
d = self.get_customize_form("User")
d = self.get_customize_form("Event")
last_fieldname = d.fields[-1].fieldname
d.append("fields", {
"label": "Test Add Custom Field Via Customize Form",
@ -124,19 +113,19 @@ class TestCustomizeForm(unittest.TestCase):
"is_custom_field": 1
})
d.run_method("save_customization")
self.assertEqual(frappe.db.get_value("Custom Field",
"User-test_add_custom_field_via_customize_form", "fieldtype"), "Data")
self.assertEquals(frappe.db.get_value("Custom Field",
"Event-test_add_custom_field_via_customize_form", "fieldtype"), "Data")
self.assertEqual(frappe.db.get_value("Custom Field",
"User-test_add_custom_field_via_customize_form", 'insert_after'), last_fieldname)
self.assertEquals(frappe.db.get_value("Custom Field",
"Event-test_add_custom_field_via_customize_form", 'insert_after'), last_fieldname)
frappe.delete_doc("Custom Field", "User-test_add_custom_field_via_customize_form")
self.assertEqual(frappe.db.get_value("Custom Field",
"User-test_add_custom_field_via_customize_form"), None)
frappe.delete_doc("Custom Field", "Event-test_add_custom_field_via_customize_form")
self.assertEquals(frappe.db.get_value("Custom Field",
"Event-test_add_custom_field_via_customize_form"), None)
def test_save_customization_remove_field(self):
d = self.get_customize_form("User")
d = self.get_customize_form("Event")
custom_field = d.get("fields", {"fieldname": "test_custom_field"})[0]
d.get("fields").remove(custom_field)
d.run_method("save_customization")
@ -148,24 +137,24 @@ class TestCustomizeForm(unittest.TestCase):
def test_reset_to_defaults(self):
d = frappe.get_doc("Customize Form")
d.doc_type = "User"
d.doc_type = "Event"
d.run_method('reset_to_defaults')
self.assertEqual(d.get("fields", {"fieldname": "location"})[0].in_list_view, 0)
self.assertEquals(d.get("fields", {"fieldname": "repeat_this_event"})[0].in_list_view, 0)
frappe.local.test_objects["Property Setter"] = []
make_test_records_for_doctype("Property Setter")
def test_set_allow_on_submit(self):
d = self.get_customize_form("User")
d.get("fields", {"fieldname": "first_name"})[0].allow_on_submit = 1
d = self.get_customize_form("Event")
d.get("fields", {"fieldname": "subject"})[0].allow_on_submit = 1
d.get("fields", {"fieldname": "test_custom_field"})[0].allow_on_submit = 1
d.run_method("save_customization")
d = self.get_customize_form("User")
d = self.get_customize_form("Event")
# don't allow for standard fields
self.assertEqual(d.get("fields", {"fieldname": "first_name"})[0].allow_on_submit or 0, 0)
self.assertEquals(d.get("fields", {"fieldname": "subject"})[0].allow_on_submit or 0, 0)
# allow for custom field
self.assertEqual(d.get("fields", {"fieldname": "test_custom_field"})[0].allow_on_submit, 1)
@ -193,4 +182,12 @@ class TestCustomizeForm(unittest.TestCase):
# undo
df.default = None
d.run_method("save_customization")
d.run_method("save_customization")
def test_core_doctype_customization(self):
d = self.get_customize_form('User')
e = self.get_customize_form('Custom Field')
# core doctype is invalid, hence no attributes are set
self.assertEquals(d.get("fields"), [])
self.assertEquals(e.get("fields"), [])

View file

@ -12,6 +12,9 @@ display_fieldtypes = ('Section Break', 'Column Break', 'HTML', 'Button', 'Image'
default_fields = ('doctype','name','owner','creation','modified','modified_by',
'parent','parentfield','parenttype','idx','docstatus')
optional_fields = ("_user_tags", "_comments", "_assign", "_liked_by", "_seen")
core_doctypes_list = ('DocType', 'DocField', 'DocPerm', 'User', 'Role', 'Has Role',
'Page', 'Module Def', 'Print Format', 'Report', 'Customize Form',
'Customize Form Field', 'Property Setter', 'Custom Field', 'Custom Script')
def copytables(srctype, src, srcfield, tartype, tar, tarfield, srcfields, tarfields=[]):
if not tarfields:

View file

@ -504,6 +504,9 @@ frappe.ui.form.Layout = Class.extend({
} else if(expression.substr(0,5)=='eval:') {
try {
out = eval(expression.substr(5));
if(parent && parent.istable && expression.includes('is_submittable')) {
out = true;
}
} catch(e) {
frappe.throw(__('Invalid "depends_on" expression'));
}

View file

@ -971,9 +971,13 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
items.push({
label: __('Customize'),
action: () => frappe.set_route('Form', 'Customize Form', {
doc_type: doctype
}),
action: () => {
if(this.meta && !this.meta.custom) {
frappe.set_route('Form', 'Customize Form', {
doc_type: doctype
});
}
},
standard: true
});
}

View file

@ -13,6 +13,10 @@ $.extend(frappe.model, {
'_user_tags', '_comments', '_assign', '_liked_by', 'docstatus',
'parent', 'parenttype', 'parentfield', 'idx'],
core_doctypes_list: ['DocType', 'DocField', 'DocPerm', 'User', 'Role', 'Has Role',
'Page', 'Module Def', 'Print Format', 'Report', 'Customize Form',
'Customize Form Field', 'Property Setter', 'Custom Field', 'Custom Script'],
std_fields: [
{fieldname:'name', fieldtype:'Link', label:__('ID')},
{fieldname:'owner', fieldtype:'Link', label:__('Created By'), options: 'User'},

View file

@ -109,7 +109,6 @@ frappe.ui.toolbar.Toolbar = Class.extend({
$("#input-help").on("keydown", function (e) {
if(e.which == 13) {
var keywords = $(this).val();
$(this).val("");
}
});

View file

@ -14,6 +14,11 @@ def get_pdf(html, options=None, output = None):
html, options = prepare_options(html, options)
fname = os.path.join("/tmp", "frappe-pdf-{0}.pdf".format(frappe.generate_hash()))
options.update({
"disable-javascript": "",
"disable-local-file-access": "",
})
try:
pdfkit.from_string(html, fname, options=options or {})
if output: