diff --git a/.mergify.yml b/.mergify.yml
index d810898eee..b145834cc4 100644
--- a/.mergify.yml
+++ b/.mergify.yml
@@ -1,7 +1,7 @@
pull_request_rules:
- name: Automatic merge on CI success and review
conditions:
- - status-success=Codacy/PR Quality Review
+ - status-success=Sider
- status-success=Semantic Pull Request
- status-success=Travis CI - Pull Request
- status-success=security/snyk - package.json (frappe)
@@ -14,7 +14,7 @@ pull_request_rules:
method: merge
- name: Automatic squash on CI success and review
conditions:
- - status-success=Codacy/PR Quality Review
+ - status-success=Sider
- status-success=Semantic Pull Request
- status-success=Travis CI - Pull Request
- status-success=security/snyk - package.json (frappe)
diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py
index 0c5b5f94b4..78f452db21 100644
--- a/frappe/cache_manager.py
+++ b/frappe/cache_manager.py
@@ -15,7 +15,8 @@ global_cache_keys = ("app_hooks", "installed_apps",
"app_modules", "module_app", "system_settings",
'scheduler_events', 'time_zone', 'webhooks', 'active_domains',
'active_modules', 'assignment_rule', 'server_script_map', 'wkhtmltopdf_version',
- 'domain_restricted_doctypes', 'domain_restricted_pages', 'information_schema:counts')
+ 'domain_restricted_doctypes', 'domain_restricted_pages', 'information_schema:counts',
+ 'sitemap_routes')
user_cache_keys = ("bootinfo", "user_recent", "roles", "user_doc", "lang",
"defaults", "user_permissions", "home_page", "linked_with",
diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json
index 9b04ebb7ad..4614dd09c4 100644
--- a/frappe/core/doctype/docfield/docfield.json
+++ b/frappe/core/doctype/docfield/docfield.json
@@ -102,6 +102,7 @@
},
{
"default": "0",
+ "depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)",
"fieldname": "reqd",
"fieldtype": "Check",
"in_list_view": 1,
@@ -452,7 +453,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-03-16 14:49:49.672099",
+ "modified": "2020-04-15 02:26:03.310781",
"modified_by": "Administrator",
"module": "Core",
"name": "DocField",
diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py
index f970f51419..f7c9cbe28a 100644
--- a/frappe/core/doctype/doctype/doctype.py
+++ b/frappe/core/doctype/doctype/doctype.py
@@ -206,7 +206,7 @@ class DocType(Document):
if d.fieldtype:
if (not getattr(d, "fieldname", None)):
if d.label:
- d.fieldname = d.label.strip().lower().replace(' ','_')
+ d.fieldname = d.label.strip().lower().replace(' ','_').strip('?')
if d.fieldname in restricted:
d.fieldname = d.fieldname + '1'
if d.fieldtype=='Section Break':
@@ -914,7 +914,7 @@ def validate_fields(meta):
if not d.permlevel: d.permlevel = 0
if d.fieldtype not in table_fields: d.allow_bulk_edit = 0
if not d.fieldname:
- d.fieldname = d.fieldname.lower()
+ d.fieldname = d.fieldname.lower().strip('?')
check_illegal_characters(d.fieldname)
check_invalid_fieldnames(meta.get("name"), d.fieldname)
diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py
index 6633884bb3..7f763ea9fc 100755
--- a/frappe/core/doctype/file/file.py
+++ b/frappe/core/doctype/file/file.py
@@ -517,7 +517,7 @@ class File(Document):
delete_file(self.thumbnail_url)
def is_downloadable(self):
- return self.is_private and has_permission(self, 'read')
+ return has_permission(self, 'read')
def get_extension(self):
'''returns split filename and extension'''
@@ -712,7 +712,11 @@ def remove_all(dt, dn, from_delete=False):
def has_permission(doc, ptype=None, user=None):
- permission = True
+ has_access = False
+ user = user or frappe.session.user
+
+ if not doc.is_private or doc.owner == user or user == 'Administrator':
+ has_access = True
if doc.attached_to_doctype and doc.attached_to_name:
attached_to_doctype = doc.attached_to_doctype
@@ -722,20 +726,20 @@ def has_permission(doc, ptype=None, user=None):
ref_doc = frappe.get_doc(attached_to_doctype, attached_to_name)
if ptype in ['write', 'create', 'delete']:
- permission = ref_doc.has_permission('write')
+ has_access = ref_doc.has_permission('write')
- if ptype == 'delete' and permission == False:
+ if ptype == 'delete' and not has_access:
frappe.throw(_("Cannot delete file as it belongs to {0} {1} for which you do not have permissions").format(
doc.attached_to_doctype, doc.attached_to_name),
frappe.PermissionError)
else:
- permission = ref_doc.has_permission('read')
+ has_access = ref_doc.has_permission('read')
except frappe.DoesNotExistError:
# if parent doc is not created before file is created
- # we cannot check its permission so allow the file
- permission = True
+ # we cannot check its permission so we will use file's permission
+ pass
- return permission
+ return has_access
def remove_file_by_url(file_url, doctype=None, name=None):
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index 68848d26f6..ab005bd428 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -208,9 +208,11 @@ class CustomizeForm(Document):
self.validate_fieldtype_change(df, meta_df[0].get(property), df.get(property))
elif property == "allow_on_submit" and df.get(property):
- frappe.msgprint(_("Row {0}: Not allowed to enable Allow on Submit for standard fields")\
- .format(df.idx))
- continue
+ if not frappe.db.get_value("DocField",
+ {"parent": self.doc_type, "fieldname": df.fieldname}, "allow_on_submit"):
+ frappe.msgprint(_("Row {0}: Not allowed to enable Allow on Submit for standard fields")\
+ .format(df.idx))
+ continue
elif property == "reqd" and \
((frappe.db.get_value("DocField",
diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json
index 34778a76e9..350d159541 100644
--- a/frappe/custom/doctype/customize_form_field/customize_form_field.json
+++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json
@@ -93,6 +93,7 @@
},
{
"default": "0",
+ "depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)",
"fieldname": "reqd",
"fieldtype": "Check",
"label": "Mandatory",
@@ -385,7 +386,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-07 14:53:40.619043",
+ "modified": "2020-04-15 02:26:59.673750",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form Field",
diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py
index 1a110c46d9..f4a15930c4 100644
--- a/frappe/desk/desktop.py
+++ b/frappe/desk/desktop.py
@@ -278,9 +278,13 @@ def get_table_with_counts():
def get_custom_reports_and_doctypes(module):
return [
_dict({
- "label": "Custom",
- "links": get_custom_doctype_list(module) + get_custom_report_list(module)
- })
+ "label": _("Custom Documents"),
+ "links": get_custom_doctype_list(module)
+ }),
+ _dict({
+ "label": _("Custom Reports"),
+ "links": get_custom_report_list(module)
+ }),
]
def get_custom_doctype_list(module):
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
index 9bb12a4ec8..6102be61ce 100644
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -367,8 +367,11 @@ def scrub_user_tags(tagcount):
return rlist
# used in building query in queries.py
-def get_match_cond(doctype):
- cond = DatabaseQuery(doctype).build_match_conditions()
+def get_match_cond(doctype, as_condition=True):
+ cond = DatabaseQuery(doctype).build_match_conditions(as_condition=as_condition)
+ if not as_condition:
+ return cond
+
return ((' and ' + cond) if cond else "").replace("%", "%%")
def build_match_conditions(doctype, user=None, as_condition=True):
diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py
index f99536f9a8..d58b35040e 100644
--- a/frappe/email/__init__.py
+++ b/frappe/email/__init__.py
@@ -65,7 +65,7 @@ def get_communication_doctype(doctype, txt, searchfield, start, page_len, filter
com_doctypes = []
if len(txt)<2:
- for name in ["Customer", "Supplier"]:
+ for name in frappe.get_hooks("communication_doctypes"):
try:
module = load_doctype_module(name, suffix='_dashboard')
if hasattr(module, 'get_data'):
diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py
index 6a3dd89873..8c011ade65 100644
--- a/frappe/email/doctype/notification/notification.py
+++ b/frappe/email/doctype/notification/notification.py
@@ -335,4 +335,4 @@ def evaluate_alert(doc, alert, event):
frappe.utils.get_link_to_form('Error Log', error_log.name)))
def get_context(doc):
- return {"doc": doc, "nowdate": nowdate, "frappe.utils": frappe.utils}
+ return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=frappe.utils)}
diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py
index 13b2b61bef..9ab1ef7799 100644
--- a/frappe/model/base_document.py
+++ b/frappe/model/base_document.py
@@ -506,9 +506,13 @@ class BaseDocument(object):
fetch_from_fieldname = _df.fetch_from.split('.')[-1]
value = values[fetch_from_fieldname]
if _df.fieldtype == 'Small Text' or _df.fieldtype == 'Text' or _df.fieldtype == 'Data':
- fetch_from_df = frappe.get_meta(doctype).get_field(fetch_from_fieldname)
- fetch_from_ft = fetch_from_df.get('fieldtype')
+ if fetch_from_fieldname in default_fields:
+ from frappe.model.meta import get_default_df
+ fetch_from_df = get_default_df(fetch_from_fieldname)
+ else:
+ fetch_from_df = frappe.get_meta(doctype).get_field(fetch_from_fieldname)
+ fetch_from_ft = fetch_from_df.get('fieldtype')
if fetch_from_ft == 'Text Editor' and value:
value = unescape_html(strip_html(value))
setattr(self, _df.fieldname, value)
diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js
index 810de89874..8b1c09ac93 100644
--- a/frappe/public/js/frappe/chat.js
+++ b/frappe/public/js/frappe/chat.js
@@ -1566,7 +1566,11 @@ class extends Component {
const alert = // TODO: ellipses content
`
- ${frappe.user.first_name(r.user)}: ${r.content}
+
+
+
+
+ ${frappe.user.first_name(r.user)}: ${r.content}
`
frappe.show_alert(alert, 15, {
@@ -1575,6 +1579,11 @@ class extends Component {
this.base.firstChild._component.toggle()
}.bind(this, r)
})
+ frappe.notify(`${frappe.user.first_name(r.user)}`, {
+ body: r.content,
+ icon: frappe.user.image(r.user),
+ tag: r.user
+ })
}
if ( r.room === state.room.name ) {
diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js
index b55c822ba6..e714418375 100644
--- a/frappe/public/js/frappe/form/form.js
+++ b/frappe/public/js/frappe/form/form.js
@@ -1052,7 +1052,7 @@ frappe.ui.form.Form = class FrappeForm {
}
is_dirty() {
- return this.doc.__unsaved;
+ return !!this.doc.__unsaved;
}
is_new() {
diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js
index fbc35634f4..d40b3ed341 100644
--- a/frappe/public/js/frappe/form/save.js
+++ b/frappe/public/js/frappe/form/save.js
@@ -21,7 +21,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
remove_empty_rows();
$(frm.wrapper).addClass('validated-form');
- if (check_mandatory()) {
+ if (frm.is_dirty() && check_mandatory()) {
_call({
method: "frappe.desk.form.save.savedocs",
args: { doc: frm.doc, action: action },
@@ -36,6 +36,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
freeze_message: freeze_message
});
} else {
+ frappe.show_alert({message: __("Document not updated"), indicator: "yellow"});
$(btn).prop("disabled", false);
}
};
diff --git a/frappe/public/js/frappe/ui/group_by/group_by.js b/frappe/public/js/frappe/ui/group_by/group_by.js
index 6936f25c18..dc81bbdf20 100644
--- a/frappe/public/js/frappe/ui/group_by/group_by.js
+++ b/frappe/public/js/frappe/ui/group_by/group_by.js
@@ -92,7 +92,6 @@ frappe.ui.GroupBy = class {
}
apply_settings(settings) {
-
if (!settings.group_by.startsWith('`tab')) {
settings.group_by = '`tab' + this.doctype + '`.`' + settings.group_by + '`';
}
diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js
index a2b03f180e..0f27e97178 100644
--- a/frappe/public/js/frappe/utils/utils.js
+++ b/frappe/public/js/frappe/utils/utils.js
@@ -675,7 +675,9 @@ Object.assign(frappe.utils, {
return __(frappe.utils.to_title_case(route[0], true));
},
report_column_total: function(values, column, type) {
- if (values.length > 0) {
+ if (column.column.disable_total) {
+ return '';
+ } else if (values.length > 0) {
if (column.column.fieldtype == "Percent" || type === "mean") {
return values.reduce((a, b) => a + flt(b)) / values.length;
} else if (column.column.fieldtype == "Int") {
diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js
index b479c4c101..43540f449d 100644
--- a/frappe/public/js/frappe/views/reports/report_view.js
+++ b/frappe/public/js/frappe/views/reports/report_view.js
@@ -837,6 +837,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
fieldtype: 'MultiCheck',
columns: 2,
options: columns[this.doctype]
+ .filter(df => {
+ return !df.hidden;
+ })
.map(df => ({
label: __(df.label),
value: df.fieldname,
@@ -858,6 +861,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
fieldtype: 'MultiCheck',
columns: 2,
options: columns[cdt]
+ .filter(df => {
+ return !df.hidden;
+ })
.map(df => ({
label: __(df.label),
value: df.fieldname,
diff --git a/frappe/public/js/frappe/web_form/webform_script.js b/frappe/public/js/frappe/web_form/webform_script.js
index 7bf7162101..688c0938c4 100644
--- a/frappe/public/js/frappe/web_form/webform_script.js
+++ b/frappe/public/js/frappe/web_form/webform_script.js
@@ -2,13 +2,13 @@ import WebFormList from './web_form_list'
import WebForm from './web_form'
frappe.ready(function() {
+ let query_params = frappe.utils.get_query_params();
let wrapper = $(".web-form-wrapper");
- let is_list = parseInt(wrapper.data('is-list'));
+ let is_list = parseInt(wrapper.data('is-list')) || query_params.is_list;
let webform_doctype = wrapper.data('web-form-doctype');
let webform_name = wrapper.data('web-form');
let login_required = parseInt(wrapper.data('login-required'));
let allow_delete = parseInt(wrapper.data('allow-delete'));
- let query_params = frappe.utils.get_query_params();
let doc_name = query_params.name || '';
let is_new = query_params.new;
@@ -38,7 +38,7 @@ frappe.ready(function() {
settings: {
allow_delete
}
- })
+ });
}
function show_form() {
diff --git a/frappe/templates/base.html b/frappe/templates/base.html
index 1c5f286442..2a241c4843 100644
--- a/frappe/templates/base.html
+++ b/frappe/templates/base.html
@@ -62,7 +62,11 @@
{%- endblock -%}
{%- block navbar -%}
- {% include "templates/includes/navbar/navbar.html" %}
+ {%- if navbar_content -%}
+ {{ navbar_content }}
+ {%- else -%}
+ {% include "templates/includes/navbar/navbar.html" %}
+ {%- endif -%}
{%- endblock -%}
{% block content %}
@@ -70,7 +74,11 @@
{% endblock %}
{%- block footer -%}
- {% include "templates/includes/footer/footer.html" %}
+ {%- if footer_content -%}
+ {{ footer_content }}
+ {%- else -%}
+ {% include "templates/includes/footer/footer.html" %}
+ {%- endif -%}
{%- endblock -%}
{% block base_scripts %}
diff --git a/frappe/templates/web.html b/frappe/templates/web.html
index e61672124a..d2d38a6320 100644
--- a/frappe/templates/web.html
+++ b/frappe/templates/web.html
@@ -13,7 +13,7 @@
{% block page_container %}
-
+