Merge branch 'develop' of https://github.com/frappe/frappe into pref_global_search
This commit is contained in:
commit
00df2ddbad
20 changed files with 94 additions and 54 deletions
|
|
@ -219,6 +219,10 @@ class LoginManager:
|
|||
if not self.user:
|
||||
return
|
||||
|
||||
from frappe.core.doctype.user.user import STANDARD_USERS
|
||||
if self.user in STANDARD_USERS:
|
||||
return False
|
||||
|
||||
reset_pwd_after_days = cint(frappe.db.get_single_value("System Settings",
|
||||
"force_user_to_reset_password"))
|
||||
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ def parse_email(communication, email_strings):
|
|||
a doctype and docname ie in the format `admin+doctype+docname@example.com`,
|
||||
the email is parsed and doctype and docname is extracted and timeline link is added.
|
||||
"""
|
||||
if not frappe.get_list("Email Account", filters={"enable_automatic_linking": 1}):
|
||||
if not frappe.get_all("Email Account", filters={"enable_automatic_linking": 1}):
|
||||
return
|
||||
|
||||
delimiter = "+"
|
||||
|
|
@ -406,7 +406,7 @@ def get_email_without_link(email):
|
|||
returns email address without doctype links
|
||||
returns admin@example.com for email admin+doctype+docname@example.com
|
||||
"""
|
||||
if not frappe.get_list("Email Account", filters={"enable_automatic_linking": 1}):
|
||||
if not frappe.get_all("Email Account", filters={"enable_automatic_linking": 1}):
|
||||
return email
|
||||
|
||||
email_id = email.split("@")[0].split("+")[0]
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ def run_server_script_for_doc_event(doc, event):
|
|||
if frappe.flags.in_install:
|
||||
return
|
||||
|
||||
if frappe.flags.in_migrate:
|
||||
return
|
||||
|
||||
scripts = get_server_script_map().get(doc.doctype, {}).get(EVENT_MAP[event], None)
|
||||
if scripts:
|
||||
# run all scripts for this doctype + event
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ def find(list_of_dict, match_function):
|
|||
Usage:
|
||||
list_of_dict = [{'name': 'Suraj'}, {'name': 'Aditya'}]
|
||||
|
||||
required_dict = find(list_of_dict, lamda d: d['name'] == 'Aditya')
|
||||
required_dict = find(list_of_dict, lambda d: d['name'] == 'Aditya')
|
||||
'''
|
||||
|
||||
for entry in list_of_dict:
|
||||
|
|
@ -60,10 +60,10 @@ def find_all(list_of_dict, match_function):
|
|||
{'color': 'blue', 'shape': 'triangle'}
|
||||
]
|
||||
|
||||
red_shapes = find_all(colored_shapes, lamda d: d['color'] == 'red')
|
||||
red_shapes = find_all(colored_shapes, lambda d: d['color'] == 'red')
|
||||
'''
|
||||
found = []
|
||||
for entry in list_of_dict:
|
||||
if match_function(entry):
|
||||
found.append(entry)
|
||||
return found
|
||||
return found
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ class Database(object):
|
|||
self.log_touched_tables(query)
|
||||
|
||||
if debug:
|
||||
frappe.errprint(self._cursor.mogrify(query, values))
|
||||
time_end = time()
|
||||
frappe.errprint(("Execution time: {0} sec").format(round(time_end - time_start, 2)))
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,16 @@ def get_permission_query_conditions(for_user):
|
|||
|
||||
return '''(`tabNotification Log`.for_user = '{user}')'''.format(user=for_user)
|
||||
|
||||
def get_title(doctype, docname, title_field=None):
|
||||
if not title_field:
|
||||
title_field = frappe.get_meta(doctype).get_title_field()
|
||||
title = docname if title_field == "name" else \
|
||||
frappe.db.get_value(doctype, docname, title_field)
|
||||
return title
|
||||
|
||||
def get_title_html(title):
|
||||
return '<b class="subject-title">{0}</b>'.format(title)
|
||||
|
||||
def enqueue_create_notification(users, doc):
|
||||
'''
|
||||
During installation of new site, enqueue_create_notification tries to connect to Redis.
|
||||
|
|
@ -101,16 +111,6 @@ def get_email_header(doc):
|
|||
}[doc.type or 'Default']
|
||||
|
||||
|
||||
def get_title(doctype, docname, title_field=None):
|
||||
if not title_field:
|
||||
title_field = frappe.get_meta(doctype).get_title_field()
|
||||
title = docname if title_field == "name" else \
|
||||
frappe.db.get_value(doctype, docname, title_field)
|
||||
return title
|
||||
|
||||
def get_title_html(title):
|
||||
return '<b class="subject-title">{0}</b>'.format(title)
|
||||
|
||||
@frappe.whitelist()
|
||||
def mark_as_seen(docname):
|
||||
if docname:
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class TestNotificationLog(unittest.TestCase):
|
|||
todo = get_todo()
|
||||
user = get_user()
|
||||
|
||||
frappe.share.add('ToDo', todo.name, user)
|
||||
frappe.share.add('ToDo', todo.name, user, notify=1)
|
||||
log_type = frappe.db.get_value('Notification Log', {
|
||||
'document_type': 'ToDo',
|
||||
'document_name': todo.name
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ def get_group_by_count(doctype, current_filters, field):
|
|||
order by
|
||||
count desc
|
||||
limit 50""".format(subquery_condition = subquery_condition), as_dict=True)
|
||||
else :
|
||||
else:
|
||||
return frappe.db.get_list(doctype,
|
||||
filters=current_filters,
|
||||
group_by=field,
|
||||
|
|
|
|||
|
|
@ -284,10 +284,14 @@ class DatabaseQuery(object):
|
|||
def set_field_tables(self):
|
||||
'''If there are more than one table, the fieldname must not be ambiguous.
|
||||
If the fieldname is not explicitly mentioned, set the default table'''
|
||||
def _in_standard_sql_methods(field):
|
||||
methods = ('count(', 'avg(', 'sum(')
|
||||
return field.lower().startswith(methods)
|
||||
|
||||
if len(self.tables) > 1:
|
||||
for i, f in enumerate(self.fields):
|
||||
if '.' not in f:
|
||||
self.fields[i] = '{0}.{1}'.format(self.tables[0], f)
|
||||
for idx, field in enumerate(self.fields):
|
||||
if '.' not in field and not _in_standard_sql_methods(field):
|
||||
self.fields[idx] = '{0}.{1}'.format(self.tables[0], field)
|
||||
|
||||
def set_optional_columns(self):
|
||||
"""Removes optional columns like `_user_tags`, `_comments` etc. if not in table"""
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ def set_new_name(doc):
|
|||
|
||||
# if the autoname option is 'field:' and no name was derived, we need to
|
||||
# notify
|
||||
if autoname.startswith('field:') and not doc.name:
|
||||
if autoname.startswith("field:") and not doc.name:
|
||||
fieldname = autoname[6:]
|
||||
frappe.throw(_("{0} is required").format(doc.meta.get_label(fieldname)))
|
||||
|
||||
# at this point, we fall back to name generation with the hash option
|
||||
if not doc.name or autoname == 'hash':
|
||||
doc.name = make_autoname('hash', doc.doctype)
|
||||
if not doc.name or autoname == "hash":
|
||||
doc.name = make_autoname("hash", doc.doctype)
|
||||
|
||||
doc.name = validate_name(
|
||||
doc.doctype,
|
||||
|
|
@ -65,15 +65,15 @@ def set_name_from_naming_options(autoname, doc):
|
|||
|
||||
_autoname = autoname.lower()
|
||||
|
||||
if _autoname.startswith('field:'):
|
||||
if _autoname.startswith("field:"):
|
||||
doc.name = _field_autoname(autoname, doc)
|
||||
elif _autoname.startswith('naming_series:'):
|
||||
elif _autoname.startswith("naming_series:"):
|
||||
set_name_by_naming_series(doc)
|
||||
elif _autoname.startswith('prompt'):
|
||||
elif _autoname.startswith("prompt"):
|
||||
_prompt_autoname(autoname, doc)
|
||||
elif _autoname.startswith('format:'):
|
||||
elif _autoname.startswith("format:"):
|
||||
doc.name = _format_autoname(autoname, doc)
|
||||
elif '#' in autoname:
|
||||
elif "#" in autoname:
|
||||
doc.name = make_autoname(autoname, doc=doc)
|
||||
|
||||
def set_name_by_naming_series(doc):
|
||||
|
|
@ -84,9 +84,9 @@ def set_name_by_naming_series(doc):
|
|||
if not doc.naming_series:
|
||||
frappe.throw(frappe._("Naming Series mandatory"))
|
||||
|
||||
doc.name = make_autoname(doc.naming_series+'.#####', '', doc)
|
||||
doc.name = make_autoname(doc.naming_series+".#####", "", doc)
|
||||
|
||||
def make_autoname(key='', doctype='', doc=''):
|
||||
def make_autoname(key="", doctype="", doc=""):
|
||||
"""
|
||||
Creates an autoname from the given key:
|
||||
|
||||
|
|
@ -199,12 +199,12 @@ def get_default_naming_series(doctype):
|
|||
|
||||
def validate_name(doctype, name, case=None, merge=False):
|
||||
if not name:
|
||||
return 'No Name Specified for %s' % doctype
|
||||
if name.startswith('New '+doctype):
|
||||
frappe.throw(_('There were some errors setting the name, please contact the administrator'), frappe.NameError)
|
||||
if case == 'Title Case':
|
||||
frappe.throw(_("No Name Specified for {0}").format(doctype))
|
||||
if name.startswith("New "+doctype):
|
||||
frappe.throw(_("There were some errors setting the name, please contact the administrator"), frappe.NameError)
|
||||
if case == "Title Case":
|
||||
name = name.title()
|
||||
if case == 'UPPER CASE':
|
||||
if case == "UPPER CASE":
|
||||
name = name.upper()
|
||||
name = name.strip()
|
||||
|
||||
|
|
@ -219,13 +219,13 @@ def validate_name(doctype, name, case=None, merge=False):
|
|||
return name
|
||||
|
||||
|
||||
def append_number_if_name_exists(doctype, value, fieldname='name', separator='-', filters=None):
|
||||
def append_number_if_name_exists(doctype, value, fieldname="name", separator="-", filters=None):
|
||||
if not filters:
|
||||
filters = dict()
|
||||
filters.update({fieldname: value})
|
||||
exists = frappe.db.exists(doctype, filters)
|
||||
|
||||
regex = '^{value}{separator}\d+$'.format(value=re.escape(value), separator=separator)
|
||||
regex = "^{value}{separator}\d+$".format(value=re.escape(value), separator=separator)
|
||||
|
||||
if exists:
|
||||
last = frappe.db.sql("""SELECT `{fieldname}` FROM `tab{doctype}`
|
||||
|
|
@ -251,10 +251,10 @@ def _set_amended_name(doc):
|
|||
am_id = 1
|
||||
am_prefix = doc.amended_from
|
||||
if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"):
|
||||
am_id = cint(doc.amended_from.split('-')[-1]) + 1
|
||||
am_prefix = '-'.join(doc.amended_from.split('-')[:-1]) # except the last hyphen
|
||||
am_id = cint(doc.amended_from.split("-")[-1]) + 1
|
||||
am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen
|
||||
|
||||
doc.name = am_prefix + '-' + str(am_id)
|
||||
doc.name = am_prefix + "-" + str(am_id)
|
||||
return doc.name
|
||||
|
||||
|
||||
|
|
@ -264,7 +264,7 @@ def _field_autoname(autoname, doc, skip_slicing=None):
|
|||
`autoname` field starts with 'field:'
|
||||
"""
|
||||
fieldname = autoname if skip_slicing else autoname[6:]
|
||||
name = (cstr(doc.get(fieldname)) or '').strip()
|
||||
name = (cstr(doc.get(fieldname)) or "").strip()
|
||||
return name
|
||||
|
||||
|
||||
|
|
@ -285,7 +285,7 @@ def _format_autoname(autoname, doc):
|
|||
Example pattern: 'format:LOG-{MM}-{fieldname1}-{fieldname2}-{#####}'
|
||||
"""
|
||||
|
||||
first_colon_index = autoname.find(':')
|
||||
first_colon_index = autoname.find(":")
|
||||
autoname_value = autoname[first_colon_index + 1:]
|
||||
|
||||
def get_param_value_for_match(match):
|
||||
|
|
@ -295,6 +295,6 @@ def _format_autoname(autoname, doc):
|
|||
return parse_naming_series([trimmed_param], doc=doc)
|
||||
|
||||
# Replace braced params with their parsed value
|
||||
name = re.sub(r'(\{[\w | #]+\})', get_param_value_for_match, autoname_value)
|
||||
name = re.sub(r"(\{[\w | #]+\})", get_param_value_for_match, autoname_value)
|
||||
|
||||
return name
|
||||
|
|
|
|||
|
|
@ -256,3 +256,4 @@ frappe.patches.v12_0.update_global_search
|
|||
execute:frappe.reload_doc('desk', 'doctype', 'notification_settings')
|
||||
frappe.patches.v12_0.setup_tags
|
||||
frappe.patches.v12_0.update_auto_repeat_status_and_not_submittable
|
||||
frappe.patches.v12_0.copy_to_parent_for_tags
|
||||
|
|
|
|||
6
frappe/patches/v12_0/copy_to_parent_for_tags.py
Normal file
6
frappe/patches/v12_0/copy_to_parent_for_tags.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
|
||||
frappe.db.sql("UPDATE `tabTag Link` SET parenttype=document_type")
|
||||
frappe.db.sql("UPDATE `tabTag Link` SET parent=document_name")
|
||||
|
|
@ -101,6 +101,9 @@ frappe.ui.form.ControlAutocomplete = frappe.ui.form.ControlData.extend({
|
|||
|
||||
validate(value) {
|
||||
let valid_values = this.awesomplete._list.map(d => d.value);
|
||||
if (!valid_values.length) {
|
||||
return value;
|
||||
}
|
||||
if (valid_values.includes(value)) {
|
||||
return value;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -119,9 +119,12 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
|
|||
} else {
|
||||
value = this.value || value;
|
||||
}
|
||||
this.disp_area && $(this.disp_area)
|
||||
.html(frappe.format(value, this.df, {no_icon:true, inline:true},
|
||||
this.doc || (this.frm && this.frm.doc)));
|
||||
if (this.df.fieldtype === 'Data') {
|
||||
value = frappe.utils.escape_html(value);
|
||||
}
|
||||
let doc = this.doc || (this.frm && this.frm.doc);
|
||||
let display_value = frappe.format(value, this.df, { no_icon: true, inline: true }, doc);
|
||||
this.disp_area && $(this.disp_area).html(display_value);
|
||||
},
|
||||
|
||||
bind_change_event: function() {
|
||||
|
|
@ -184,4 +187,4 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
|
|||
$(this.disp_area).toggleClass("bold", !!(this.df.bold || this.df.reqd));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ frappe.ui.form.Share = Class.extend({
|
|||
read: $(d.body).find(".add-share-read").prop("checked") ? 1 : 0,
|
||||
write: $(d.body).find(".add-share-write").prop("checked") ? 1 : 0,
|
||||
share: $(d.body).find(".add-share-share").prop("checked") ? 1 : 0,
|
||||
notify: 1,
|
||||
},
|
||||
btn: this,
|
||||
callback: function(r) {
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ frappe.ui.Filter = class {
|
|||
if (doctype === "Tag Link" || fieldname === "_user_tags") {
|
||||
original_docfield = {fieldname: "tag", fieldtype: "Data", label: "Tags", parent: "Tag Link"};
|
||||
doctype = "Tag Link";
|
||||
condition = "=";
|
||||
}
|
||||
|
||||
if(!original_docfield) {
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ frappe.ui.Notifications = class Notifications {
|
|||
<div class="notification-timestamp text-muted">
|
||||
${timestamp}
|
||||
</div>
|
||||
<span class="mark-read text-muted" data-action="explicitly_mark_as_seen">
|
||||
<span class="mark-read text-muted hidden-xs" data-action="explicitly_mark_as_seen">
|
||||
${__('Mark as Read')}
|
||||
</span>
|
||||
</a>`;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from frappe.desk.doctype.notification_log.notification_log import enqueue_create
|
|||
from frappe.utils import cint
|
||||
|
||||
@frappe.whitelist()
|
||||
def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=None):
|
||||
def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=None, notify=0):
|
||||
"""Share the given document with a user."""
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
|
@ -42,7 +42,7 @@ def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=No
|
|||
})
|
||||
|
||||
doc.save(ignore_permissions=True)
|
||||
notify_assignment(user, doctype, name, everyone)
|
||||
notify_assignment(user, doctype, name, everyone, notify=notify)
|
||||
|
||||
follow_document(doctype, name, user)
|
||||
|
||||
|
|
@ -147,9 +147,10 @@ def check_share_permission(doctype, name):
|
|||
if not frappe.has_permission(doctype, ptype="share", doc=name):
|
||||
frappe.throw(_("No permission to {0} {1} {2}".format("share", doctype, name)), frappe.PermissionError)
|
||||
|
||||
def notify_assignment(shared_by, doctype, doc_name, everyone):
|
||||
def notify_assignment(shared_by, doctype, doc_name, everyone, notify=0):
|
||||
|
||||
if not (shared_by and doctype and doc_name) or everyone: return
|
||||
if not (shared_by and doctype and doc_name) or everyone or not notify:
|
||||
return
|
||||
|
||||
from frappe.utils import get_fullname
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,10 @@ class EnergyPointRule(Document):
|
|||
def process_energy_points(doc, state):
|
||||
if (frappe.flags.in_patch
|
||||
or frappe.flags.in_install
|
||||
or not is_energy_point_enabled()):
|
||||
or frappe.flags.in_migrate):
|
||||
return
|
||||
|
||||
if not is_energy_point_enabled():
|
||||
return
|
||||
|
||||
old_doc = doc.get_doc_before_save()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import frappe, unittest
|
|||
|
||||
from frappe.model.db_query import DatabaseQuery
|
||||
from frappe.desk.reportview import get_filters_cond
|
||||
|
||||
from frappe.permissions import add_user_permission, clear_user_permissions_for_doctype
|
||||
|
||||
test_dependencies = ['User', 'Blog Post']
|
||||
|
|
@ -332,6 +333,16 @@ class TestReportview(unittest.TestCase):
|
|||
self.assertTrue({'name': 'Prepared Report'} in res)
|
||||
self.assertFalse({'name': 'Property Setter'} in res)
|
||||
|
||||
def test_set_field_tables(self):
|
||||
# Tests _in_standard_sql_methods method in test_set_field_tables
|
||||
# The following query will break if the above method is broken
|
||||
data = frappe.db.get_list("Web Form",
|
||||
filters=[['Web Form Field', 'reqd', '=', 1]],
|
||||
group_by='amount_field',
|
||||
fields=['count(*) as count', '`amount_field` as name'],
|
||||
order_by='count desc',
|
||||
limit=50,
|
||||
)
|
||||
|
||||
def create_event(subject="_Test Event", starts_on=None):
|
||||
""" create a test event """
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue