diff --git a/frappe/auth.py b/frappe/auth.py index c3b00970e8..468f45c53e 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -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")) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 2be07cadd2..74d7fa1654 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -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] diff --git a/frappe/core/doctype/server_script/server_script_utils.py b/frappe/core/doctype/server_script/server_script_utils.py index e327401331..0399dea106 100644 --- a/frappe/core/doctype/server_script/server_script_utils.py +++ b/frappe/core/doctype/server_script/server_script_utils.py @@ -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 diff --git a/frappe/core/utils.py b/frappe/core/utils.py index e4c349da93..55767ffe34 100644 --- a/frappe/core/utils.py +++ b/frappe/core/utils.py @@ -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 \ No newline at end of file + return found diff --git a/frappe/database/database.py b/frappe/database/database.py index 1e6a85236e..f5055571ff 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -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))) diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index 4efcaa558b..f58c14d363 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -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 '{0}'.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 '{0}'.format(title) - @frappe.whitelist() def mark_as_seen(docname): if docname: diff --git a/frappe/desk/doctype/notification_log/test_notification_log.py b/frappe/desk/doctype/notification_log/test_notification_log.py index 9431336aad..fe7d56c081 100644 --- a/frappe/desk/doctype/notification_log/test_notification_log.py +++ b/frappe/desk/doctype/notification_log/test_notification_log.py @@ -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 diff --git a/frappe/desk/listview.py b/frappe/desk/listview.py index 3dc795191a..fd0833eb51 100644 --- a/frappe/desk/listview.py +++ b/frappe/desk/listview.py @@ -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, diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 29fa44d208..b7feee82f4 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -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""" diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 6fc09e19c8..78d2c462e1 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -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 diff --git a/frappe/patches.txt b/frappe/patches.txt index e107449dea..d4aaec5bfc 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -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 diff --git a/frappe/patches/v12_0/copy_to_parent_for_tags.py b/frappe/patches/v12_0/copy_to_parent_for_tags.py new file mode 100644 index 0000000000..1135f9076b --- /dev/null +++ b/frappe/patches/v12_0/copy_to_parent_for_tags.py @@ -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") \ No newline at end of file diff --git a/frappe/public/js/frappe/form/controls/autocomplete.js b/frappe/public/js/frappe/form/controls/autocomplete.js index d4a21fc32c..026cdbec25 100644 --- a/frappe/public/js/frappe/form/controls/autocomplete.js +++ b/frappe/public/js/frappe/form/controls/autocomplete.js @@ -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 { diff --git a/frappe/public/js/frappe/form/controls/base_input.js b/frappe/public/js/frappe/form/controls/base_input.js index 365ff364d5..8a8ac271c7 100644 --- a/frappe/public/js/frappe/form/controls/base_input.js +++ b/frappe/public/js/frappe/form/controls/base_input.js @@ -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)); } } -}); \ No newline at end of file +}); diff --git a/frappe/public/js/frappe/form/sidebar/share.js b/frappe/public/js/frappe/form/sidebar/share.js index 3cb5257e78..79d6d28945 100644 --- a/frappe/public/js/frappe/form/sidebar/share.js +++ b/frappe/public/js/frappe/form/sidebar/share.js @@ -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) { diff --git a/frappe/public/js/frappe/ui/filters/filter.js b/frappe/public/js/frappe/ui/filters/filter.js index 7923d57ad4..5fda43375d 100644 --- a/frappe/public/js/frappe/ui/filters/filter.js +++ b/frappe/public/js/frappe/ui/filters/filter.js @@ -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) { diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js index 034ecd7af6..36d2891928 100644 --- a/frappe/public/js/frappe/ui/notifications/notifications.js +++ b/frappe/public/js/frappe/ui/notifications/notifications.js @@ -299,7 +299,7 @@ frappe.ui.Notifications = class Notifications {
- + `; diff --git a/frappe/share.py b/frappe/share.py index 3875870949..03311bed4a 100644 --- a/frappe/share.py +++ b/frappe/share.py @@ -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 diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.py b/frappe/social/doctype/energy_point_rule/energy_point_rule.py index 1386fc7cbf..38995887f0 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.py +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.py @@ -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() diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index 18c19cbf3e..f4855965f0 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -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 """