From e3a543b9f4f95952a08e38a357de90ea9ce9bb6a Mon Sep 17 00:00:00 2001 From: Dhia' Alhaq Shalabi Date: Sun, 17 Sep 2023 15:55:27 +0300 Subject: [PATCH 01/18] fix: permission check for the child table in search link - Using the parent doctype by passing the reference doctype (cherry picked from commit 4c415587a6b2675ecc1f37fd5548db611d5a4f61) --- frappe/desk/search.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 453990f0912..e0f7d4d8f7d 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -209,6 +209,7 @@ def search_widget( and has_permission( doctype, ptype="select" if frappe.only_has_select_perm(doctype) else "read", + parent_doctype=reference_doctype, ) ) ) From 6ebd4a9a4807ad09a8e6acc28e08eaf8daf3a1df Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 16 Jan 2024 18:22:54 +0530 Subject: [PATCH 02/18] refactor: Simpler perm check --- frappe/core/doctype/communication/communication.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 80814219307..6efa8615b46 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -407,8 +407,7 @@ def has_permission(doc, ptype, user): return if doc.reference_doctype and doc.reference_name: - if frappe.has_permission(doc.reference_doctype, ptype="read", doc=doc.reference_name): - return True + return frappe.has_permission(doc.reference_doctype, ptype="read", doc=doc.reference_name) def get_permission_query_conditions_for_communication(user): From 58b3dd15f45856192c7e235a61316e9e7bb98c9e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:26:03 +0100 Subject: [PATCH 03/18] refactor: remove redundant API Key from Google Picker (cherry picked from commit c17f014ef47e8944a3e13d68818963bbdb31b984) # Conflicts: # frappe/integrations/doctype/google_settings/google_settings.py --- .../google_settings/google_settings.json | 6 +++--- .../google_settings/google_settings.py | 20 ++++++++++++++++++- .../google_settings/test_google_settings.py | 1 - .../js/integrations/google_drive_picker.js | 4 +--- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/google_settings.json b/frappe/integrations/doctype/google_settings/google_settings.json index 6f25fa4bf67..ee48fde97ba 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.json +++ b/frappe/integrations/doctype/google_settings/google_settings.json @@ -39,8 +39,7 @@ "description": "The browser API key obtained from the Google Cloud Console under \n\"APIs & Services\" > \"Credentials\"\n", "fieldname": "api_key", "fieldtype": "Data", - "label": "API Key", - "mandatory_depends_on": "google_drive_picker_enabled" + "label": "API Key" }, { "depends_on": "enable", @@ -76,7 +75,7 @@ ], "issingle": 1, "links": [], - "modified": "2021-06-29 18:26:07.094851", + "modified": "2024-01-16 13:19:22.365362", "modified_by": "Administrator", "module": "Integrations", "name": "Google Settings", @@ -96,5 +95,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "ASC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/frappe/integrations/doctype/google_settings/google_settings.py b/frappe/integrations/doctype/google_settings/google_settings.py index e464e0d0901..90d062cc5fa 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.py +++ b/frappe/integrations/doctype/google_settings/google_settings.py @@ -6,6 +6,25 @@ class GoogleSettings(Document): +<<<<<<< HEAD +======= + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + api_key: DF.Data | None + app_id: DF.Data | None + client_id: DF.Data | None + client_secret: DF.Password | None + enable: DF.Check + google_drive_picker_enabled: DF.Check + # end: auto-generated types + +>>>>>>> c17f014ef4 (refactor: remove redundant API Key from Google Picker) pass @@ -19,6 +38,5 @@ def get_file_picker_settings(): return { "enabled": True, "appId": google_settings.app_id, - "developerKey": google_settings.api_key, "clientId": google_settings.client_id, } diff --git a/frappe/integrations/doctype/google_settings/test_google_settings.py b/frappe/integrations/doctype/google_settings/test_google_settings.py index d4bb8307792..4b705a67f1c 100644 --- a/frappe/integrations/doctype/google_settings/test_google_settings.py +++ b/frappe/integrations/doctype/google_settings/test_google_settings.py @@ -40,4 +40,3 @@ def test_picker_enabled(self): self.assertEqual(True, settings.get("enabled", False)) self.assertEqual("test_client_id", settings.get("clientId", "")) self.assertEqual("test_app_id", settings.get("appId", "")) - self.assertEqual("test_api_key", settings.get("developerKey", "")) diff --git a/frappe/public/js/integrations/google_drive_picker.js b/frappe/public/js/integrations/google_drive_picker.js index 4564e98de96..ba556514fa4 100644 --- a/frappe/public/js/integrations/google_drive_picker.js +++ b/frappe/public/js/integrations/google_drive_picker.js @@ -1,12 +1,11 @@ /* global gapi:false, google:false */ export default class GoogleDrivePicker { - constructor({ pickerCallback, enabled, appId, developerKey, clientId } = {}) { + constructor({ pickerCallback, enabled, appId, clientId } = {}) { this.scope = "https://www.googleapis.com/auth/drive.file"; this.pickerApiLoaded = false; this.enabled = enabled; this.appId = appId; this.pickerCallback = pickerCallback; - this.developerKey = developerKey; this.clientId = clientId; } @@ -45,7 +44,6 @@ export default class GoogleDrivePicker { createPicker(access_token) { this.view = new google.picker.View(google.picker.ViewId.DOCS); this.picker = new google.picker.PickerBuilder() - .setDeveloperKey(this.developerKey) .setAppId(this.appId) .setOAuthToken(access_token) .addView(this.view) From 43536a63e4d02109ac784bd262fd9aab2012f70d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:13:06 +0100 Subject: [PATCH 04/18] chore: resolve merge conflicts --- .../google_settings/google_settings.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/google_settings.py b/frappe/integrations/doctype/google_settings/google_settings.py index 90d062cc5fa..6b44a53098f 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.py +++ b/frappe/integrations/doctype/google_settings/google_settings.py @@ -6,25 +6,6 @@ class GoogleSettings(Document): -<<<<<<< HEAD -======= - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - api_key: DF.Data | None - app_id: DF.Data | None - client_id: DF.Data | None - client_secret: DF.Password | None - enable: DF.Check - google_drive_picker_enabled: DF.Check - # end: auto-generated types - ->>>>>>> c17f014ef4 (refactor: remove redundant API Key from Google Picker) pass From 60a848f58c40a9597fe3761d2e06ff35e63e8a70 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 16 Jan 2024 19:06:05 +0530 Subject: [PATCH 05/18] fix: simultenous sessions ux (#24387) * fix: set 2 as simultaneous_sessions by default * fix: Correct offset for simultaneous_sessions * refactor: use freeze_time instead of patching * chore: misleading docstring * test: set lower simultaneous_sessions for test (cherry picked from commit 484049c5ae76cfecf6737f6c799e76af27993028) --- frappe/core/doctype/user/user.json | 2 +- frappe/database/database.py | 2 +- frappe/sessions.py | 3 ++- frappe/tests/test_auth.py | 7 ++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 7b56f98ffed..3461f50dd50 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -458,7 +458,7 @@ "read_only": 1 }, { - "default": "1", + "default": "2", "fieldname": "simultaneous_sessions", "fieldtype": "Int", "label": "Simultaneous Sessions" diff --git a/frappe/database/database.py b/frappe/database/database.py index 982807cf596..cd45eead2fa 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -757,7 +757,7 @@ def set_single_value( Example: # Update the `deny_multiple_sessions` field in System Settings DocType. - company = frappe.db.set_single_value("System Settings", "deny_multiple_sessions", True) + frappe.db.set_single_value("System Settings", "deny_multiple_sessions", True) """ return self.set_value(doctype, doctype, fieldname, value, *args, **kwargs) diff --git a/frappe/sessions.py b/frappe/sessions.py index cac934eef60..53783f9872a 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -67,13 +67,14 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None): offset = 0 if user == frappe.session.user: simultaneous_sessions = frappe.db.get_value("User", user, "simultaneous_sessions") or 1 - offset = simultaneous_sessions - 1 + offset = simultaneous_sessions session = frappe.qb.DocType("Sessions") session_id = frappe.qb.from_(session).where( (session.user == user) & (session.device.isin(device)) ) if keep_current: + offset = max(0, offset - 1) session_id = session_id.where(session.sid != frappe.session.sid) query = ( diff --git a/frappe/tests/test_auth.py b/frappe/tests/test_auth.py index 47a092cb54f..d039835fc21 100644 --- a/frappe/tests/test_auth.py +++ b/frappe/tests/test_auth.py @@ -22,6 +22,7 @@ def add_user(email, password, username=None, mobile_no=None): dict(doctype="User", email=email, first_name=first_name, username=username, mobile_no=mobile_no) ).insert() user.new_password = password + user.simultaneous_sessions = 1 user.add_roles("System Manager") frappe.db.commit() @@ -212,12 +213,12 @@ def test_session_expires(self): seconds_elapsed = expiry_in * step / 100 time_now = add_to_date(session_created, seconds=seconds_elapsed, as_string=True) - with patch("frappe.utils.now", return_value=time_now): + with self.freeze_time(time_now): data = s.get_session_data_from_db() self.assertEqual(data.user, "Administrator") # 1% higher should immediately expire - time_now = add_to_date(session_created, seconds=expiry_in * 1.01, as_string=True) - with patch("frappe.utils.now", return_value=time_now): + time_of_expiry = add_to_date(session_created, seconds=expiry_in * 1.01, as_string=True) + with self.freeze_time(time_of_expiry): self.assertIn(sid, get_expired_sessions()) self.assertFalse(s.get_session_data_from_db()) From 32cf89b2f8fefc7472039b7c12a683e61d496ddb Mon Sep 17 00:00:00 2001 From: "Anthony C. Emmanuel" Date: Wed, 17 Jan 2024 06:11:36 +0000 Subject: [PATCH 06/18] chore: typo (#24406) --- frappe/modules/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index de0b023332c..425ea69a026 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -156,7 +156,7 @@ def _insert(data): if not frappe.db.exists("DocType", doctype): print(_("DocType {0} does not exist.").format(doctype)) - print(_("Skipping fixture syncing for doctyoe {0} from file {1} ").format(doctype, filename)) + print(_("Skipping fixture syncing for doctype {0} from file {1} ").format(doctype, filename)) return if data["custom_fields"]: From 607c3d336f6cd906df25c6e60331dc3e8e3bce2a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:42:30 +0530 Subject: [PATCH 07/18] feat(list): Add hide_name_filter setting (#24402) (#24403) Co-authored-by: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> (cherry picked from commit 7123f50912e536db00e493bc5fc0bf5c02d35b12) Co-authored-by: Corentin Flr <10946971+cogk@users.noreply.github.com> --- frappe/public/js/frappe/list/base_list.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index d6d5fb2a42e..3a4d6c13304 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -733,15 +733,17 @@ class FilterArea { this.standard_filters_wrapper = this.list_view.page.page_form.find( ".standard-filter-section" ); - let fields = [ - { + let fields = []; + + if (!this.list_view.settings.hide_name_filter) { + fields.push({ fieldtype: "Data", label: "ID", condition: "like", fieldname: "name", onchange: () => this.refresh_list_view(), - }, - ]; + }); + } if (this.list_view.custom_filter_configs) { this.list_view.custom_filter_configs.forEach((config) => { From 160817f6377bde1d4ac139a886cf4b2bfadd9385 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:47:29 +0530 Subject: [PATCH 08/18] fix: add short timeout to gravatar (#24414) (#24417) closes https://github.com/frappe/frappe/issues/24377 (cherry picked from commit afef4b8f3012c5e4821d250516874c1401dcb78a) Co-authored-by: Ankush Menat --- frappe/utils/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 84d055e3dde..4a3a578b292 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -263,12 +263,12 @@ def has_gravatar(email): gravatar_url = f"https://secure.gravatar.com/avatar/{hexdigest}?d=404&s=200" try: - res = requests.get(gravatar_url) + res = requests.get(gravatar_url, timeout=5) if res.status_code == 200: return gravatar_url else: return "" - except requests.exceptions.ConnectionError: + except requests.exceptions.RequestException: return "" From b4ab195fa96351fae77b257d17b28a5c973671f6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:47:54 +0530 Subject: [PATCH 09/18] fix: ignore unique fieldname validation during migrate (#24413) (#24415) This can be addressed later instead of failing a migrate (cherry picked from commit ea08418d7dbbb9a2b9306cd53c876e4f2e7dd286) Co-authored-by: Ankush Menat --- frappe/core/doctype/doctype/doctype.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index af71df42d3b..528e834656c 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -4,13 +4,10 @@ import copy import json import os - -# imports - standard imports import re import shutil from typing import TYPE_CHECKING, Union -# imports - module imports import frappe from frappe import _ from frappe.cache_manager import clear_controller_cache, clear_user_cache @@ -1468,7 +1465,6 @@ def check_no_of_ratings(docfield): check_illegal_characters(d.fieldname) check_invalid_fieldnames(meta.get("name"), d.fieldname) - check_unique_fieldname(meta.get("name"), d.fieldname) check_fieldname_length(d.fieldname) check_hidden_and_mandatory(meta.get("name"), d) check_unique_and_text(meta.get("name"), d) @@ -1478,6 +1474,7 @@ def check_no_of_ratings(docfield): validate_data_field_type(d) if not frappe.flags.in_migrate: + check_unique_fieldname(meta.get("name"), d.fieldname) check_link_table_options(meta.get("name"), d) check_illegal_mandatory(meta.get("name"), d) check_dynamic_link_options(d) From 8c8c93df53fd9ba92c1cfc05cbe5da13591b512d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:17:10 +0530 Subject: [PATCH 10/18] fix: avoid mutating list while iterating over it (backport #24438) (#24439) * fix: avoid mutating list while iterating over it (#24438) (cherry picked from commit ee6743c26b5d8794486ca7b91c5ac28900fa60a0) # Conflicts: # frappe/utils/response.py * chore: conflicts --------- Co-authored-by: Ankush Menat --- frappe/commands/utils.py | 2 +- frappe/core/doctype/comment/comment.py | 2 +- frappe/desk/doctype/desktop_icon/desktop_icon.py | 2 +- frappe/desk/doctype/kanban_board/kanban_board.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 33da595b6f3..407501a6d83 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -635,7 +635,7 @@ def console(context, autoreload=False): all_apps = frappe.get_installed_apps() failed_to_import = [] - for app in all_apps: + for app in list(all_apps): try: locals()[app] = __import__(app) except ModuleNotFoundError: diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index 9cad86ece13..423f2272cb3 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -53,7 +53,7 @@ def notify_change(self, action): def remove_comment_from_cache(self): _comments = get_comments_from_parent(self) - for c in _comments: + for c in list(_comments): if c.get("name") == self.name: _comments.remove(c) diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.py b/frappe/desk/doctype/desktop_icon/desktop_icon.py index 63fa12b8fb7..96b5034f1bc 100644 --- a/frappe/desk/doctype/desktop_icon/desktop_icon.py +++ b/frappe/desk/doctype/desktop_icon/desktop_icon.py @@ -243,7 +243,7 @@ def set_desktop_icons(visible_list, ignore_duplicate=True): frappe.db.sql("update `tabDesktop Icon` set blocked=0, hidden=1 where standard=1") # set as visible if present, or add icon - for module_name in visible_list: + for module_name in list(visible_list): name = frappe.db.get_value("Desktop Icon", {"module_name": module_name}) if name: frappe.db.set_value("Desktop Icon", name, "hidden", 0) diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index e3257e25bec..2ee4ad6581a 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -220,7 +220,7 @@ def update_column_order(board_name, order): new_columns = [] for col in order: - for column in old_columns: + for column in list(old_columns): if col == column.column_name: new_columns.append(column) old_columns.remove(column) From ff9dc7a7bd844bb2b6a58a990ec147ca167f8600 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 18 Jan 2024 16:10:20 +0530 Subject: [PATCH 11/18] fix: skip strict user perms for single doctypes (cherry picked from commit f74939eb0c78ec8b32dfd96d3d3b8ac0b612f46d) # Conflicts: # frappe/permissions.py --- frappe/permissions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/permissions.py b/frappe/permissions.py index 355a85ca5ad..d14c07c3dfd 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -300,7 +300,16 @@ def has_user_permission(doc, user=None): if get_role_permissions("User Permission", user=user).get("write"): return True +<<<<<<< HEAD apply_strict_user_permissions = frappe.get_system_settings("apply_strict_user_permissions") +======= + # don't apply strict user permissions for single doctypes since they contain empty link fields + apply_strict_user_permissions = ( + False if doc.meta.issingle else frappe.get_system_settings("apply_strict_user_permissions") + ) + if apply_strict_user_permissions: + debug and _debug_log("Strict user permissions will be applied") +>>>>>>> f74939eb0c (fix: skip strict user perms for single doctypes) doctype = doc.get("doctype") docname = doc.get("name") From be6fd485ed7399e7fce3be60fe7bf606fa6fb4d3 Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:22:05 +0530 Subject: [PATCH 12/18] chore: resolve conflicts --- frappe/permissions.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/frappe/permissions.py b/frappe/permissions.py index d14c07c3dfd..180dda91dce 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -300,16 +300,10 @@ def has_user_permission(doc, user=None): if get_role_permissions("User Permission", user=user).get("write"): return True -<<<<<<< HEAD - apply_strict_user_permissions = frappe.get_system_settings("apply_strict_user_permissions") -======= # don't apply strict user permissions for single doctypes since they contain empty link fields apply_strict_user_permissions = ( False if doc.meta.issingle else frappe.get_system_settings("apply_strict_user_permissions") ) - if apply_strict_user_permissions: - debug and _debug_log("Strict user permissions will be applied") ->>>>>>> f74939eb0c (fix: skip strict user perms for single doctypes) doctype = doc.get("doctype") docname = doc.get("name") From cdb488e4ce20e16ab6b864c1d12bd221548ba83f Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:04:35 +0530 Subject: [PATCH 13/18] fix: Handle bad URL (#24456) (cherry picked from commit d1f308aad3f431784528205a4a1c626a52bdfa2e) --- frappe/public/js/frappe/router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index c2e44496627..89e3a8b93ca 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -250,7 +250,7 @@ frappe.router = { standard_route = ["Tree", doctype_route.doctype]; } else { let new_route = this.list_views_route[_route.toLowerCase()]; - let re_route = route[2].toLowerCase() !== new_route.toLowerCase(); + let re_route = route[2].toLowerCase() !== new_route?.toLowerCase(); if (re_route) { /** From 2d5eb6274e6c31741488e18f7e473254009426cd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 19 Jan 2024 15:49:02 +0530 Subject: [PATCH 14/18] fix: Handle invalid descendant filter (cherry picked from commit 0f4a1d8f10416c7c761b9a0cef112c791ff55934) --- frappe/model/db_query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index a6009b64028..2f3b87ace08 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -756,7 +756,7 @@ def prepare_filter_condition(self, f): ref_doctype = field.options if field else f.doctype lft, rgt = "", "" if f.value: - lft, rgt = frappe.db.get_value(ref_doctype, f.value, ["lft", "rgt"]) + lft, rgt = frappe.db.get_value(ref_doctype, f.value, ["lft", "rgt"]) or (0, 0) # Get descendants elements of a DocType with a tree structure if f.operator.lower() in ("descendants of", "not descendants of"): From d72ccf8b9862a419f1c6baa441e3a809984b3c3f Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Mon, 22 Jan 2024 13:53:41 +0530 Subject: [PATCH 15/18] fix(translate): drop logger for now This partially reverts bd3f89bf322aa68ef3d85d3bc216ef7e7fd11ba6 A few custom app installations are breaking because of them seemingly writing to `$BENCH_DIR/apps/logs/frappe.log` instead of `$BENCH_DIR/logs/frappe.log` Until we can figure out why that's happening, reverting this change. Signed-off-by: Akhil Narang --- frappe/translate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/translate.py b/frappe/translate.py index 278f9731e3f..f8d5cea4638 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -296,7 +296,6 @@ def _merge_translations(): except Exception: # People mistakenly call translation function on global variables # where locals are not initalized, translations dont make much sense there - frappe.logger().error("Unable to load translations", exc_info=True) return {} From 742919f88e796f2d9b40f76f2b55aec9ded34925 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:13:34 +0100 Subject: [PATCH 16/18] fix: Google Drive translatability (cherry picked from commit bb7bb41621263fea7d7446c98b728110c438bc17) --- frappe/integrations/doctype/google_drive/google_drive.js | 7 ++++--- frappe/integrations/doctype/google_drive/google_drive.py | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frappe/integrations/doctype/google_drive/google_drive.js b/frappe/integrations/doctype/google_drive/google_drive.js index 208c1e5e1a4..ec5833cf594 100644 --- a/frappe/integrations/doctype/google_drive/google_drive.js +++ b/frappe/integrations/doctype/google_drive/google_drive.js @@ -13,13 +13,14 @@ frappe.ui.form.on("Google Drive", { frappe.realtime.on("upload_to_google_drive", (data) => { if (data.progress) { + const progress_title = __("Uploading to Google Drive"); frm.dashboard.show_progress( - "Uploading to Google Drive", + progress_title, (data.progress / data.total) * 100, - __("{0}", [data.message]) + data.message ); if (data.progress === data.total) { - frm.dashboard.hide_progress("Uploading to Google Drive"); + frm.dashboard.hide_progress(progress_title); } } }); diff --git a/frappe/integrations/doctype/google_drive/google_drive.py b/frappe/integrations/doctype/google_drive/google_drive.py index fbb970de463..f9cd1143a79 100644 --- a/frappe/integrations/doctype/google_drive/google_drive.py +++ b/frappe/integrations/doctype/google_drive/google_drive.py @@ -153,7 +153,7 @@ def upload_system_backup_to_google_drive(): validate_file_size() if frappe.flags.create_new_backup: - set_progress(1, "Backing up Data.") + set_progress(1, _("Backing up Data.")) backup = new_backup() file_urls = [] file_urls.append(backup.backup_path_db) @@ -179,12 +179,12 @@ def upload_system_backup_to_google_drive(): frappe.throw(_("Google Drive - Could not locate - {0}").format(e)) try: - set_progress(2, "Uploading backup to Google Drive.") + set_progress(2, _("Uploading backup to Google Drive.")) google_drive.files().create(body=file_metadata, media_body=media, fields="id").execute() except HttpError as e: send_email(False, "Google Drive", "Google Drive", "email", error_status=e) - set_progress(3, "Uploading successful.") + set_progress(3, _("Uploading successful.")) frappe.db.set_single_value("Google Drive", "last_backup_on", frappe.utils.now_datetime()) send_email(True, "Google Drive", "Google Drive", "email") return _("Google Drive Backup Successful.") From 397cf49def609b557d143d995a36faf1e03613aa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:17:45 +0530 Subject: [PATCH 17/18] fix: select label `null` breaks form rendereding (#24427) (#24429) (cherry picked from commit c7f1789dafd58a0e4c6c6369741e77c5b35c7269) Co-authored-by: Ankush Menat --- frappe/public/js/frappe/form/controls/select.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/select.js b/frappe/public/js/frappe/form/controls/select.js index de96cd902c9..9ce73168a10 100644 --- a/frappe/public/js/frappe/form/controls/select.js +++ b/frappe/public/js/frappe/form/controls/select.js @@ -110,7 +110,7 @@ frappe.ui.form.add_options = function (input, options_list, sort) { let options = options_list.map((raw_option) => parse_option(raw_option)); if (sort) { - options = options.sort((a, b) => a.label.localeCompare(b.label)); + options = options.sort((a, b) => cstr(a.label).localeCompare(cstr(b.label))); } options From 59089466d1231f59de67ca9693ae204f6b295211 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:31:45 +0530 Subject: [PATCH 18/18] fix: improve email reply (#24490) (#24503) form_timeline.js - check if frappe.session.user_email is available in email_account for the user - if reply_all and sender is not same as last email sender, set (last email recipient - current sender) to cc communication.js - if frappe.session.user_email is available in email_account for the user, set sender by default to frappe.session.user_email - added on_change on sender to re-evaluate recipients. - check if sender is same as last email sender - if yes, set recipient to last email recipient - if no, set recipient to last email sender - add all other recipients to cc (cherry picked from commit c6d3035c7d44e5825c382f25250b9d247c03f312) Co-authored-by: Maharshi Patel <39730881+maharshivpatel@users.noreply.github.com> --- .../js/frappe/form/footer/form_timeline.js | 29 +++++++++++-- .../public/js/frappe/views/communication.js | 42 +++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index 7e8f3f57e57..13fb2f87fdf 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -527,11 +527,34 @@ class FormTimeline extends BaseTimeline { title: communication_doc ? __("Reply") : null, last_email: communication_doc, subject: communication_doc && communication_doc.subject, + reply_all: reply_all, }; - if (communication_doc && reply_all) { - args.cc = communication_doc.cc; - args.bcc = communication_doc.bcc; + const email_accounts = frappe.boot.email_accounts + .filter((account) => { + return ( + !["All Accounts", "Sent", "Spam", "Trash"].includes(account.email_account) && + account.enable_outgoing + ); + }) + .map((e) => e.email_id); + + if (communication_doc && args.is_a_reply) { + args.cc = ""; + if ( + email_accounts.includes(frappe.session.user_email) && + communication_doc.sender != frappe.session.user_email + ) { + // add recipients to cc if replying sender is different from last email + const recipients = communication_doc.recipients.split(",").map((r) => r.trim()); + args.cc = + recipients.filter((r) => r != frappe.session.user_email).join(", ") + ", "; + } + if (reply_all) { + // if reply_all then add cc and bcc as well. + args.cc += communication_doc.cc; + args.bcc = communication_doc.bcc; + } } if (this.frm.doctype === "Communication") { diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 6351456922b..08b06cfd5b6 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -172,10 +172,15 @@ frappe.views.CommunicationComposer = class { reqd: 1, fieldname: "sender", options: this.user_email_accounts, + onchange: () => { + this.setup_recipients_if_reply(); + }, }); //Preselect email senders if there is only one if (this.user_email_accounts.length == 1) { this["sender"] = this.user_email_accounts; + } else if (this.user_email_accounts.includes(frappe.session.user_email)) { + this["sender"] = frappe.session.user_email; } } @@ -226,6 +231,43 @@ frappe.views.CommunicationComposer = class { }); } + setup_recipients_if_reply() { + if (!this.is_a_reply || !this.last_email) return; + let sender = this.dialog.get_value("sender"); + if (!sender) return; + const fields = { + recipients: this.dialog.fields_dict.recipients, + cc: this.dialog.fields_dict.cc, + bcc: this.dialog.fields_dict.bcc, + }; + // If same user replies to their own email, set recipients to last email recipients + if (this.last_email.sender == sender) { + fields.recipients.set_value(this.last_email.recipients); + if (this.reply_all) { + fields.cc.set_value(this.last_email.cc); + fields.bcc.set_value(this.last_email.bcc); + } + } else { + fields.recipients.set_value(this.last_email.sender); + if (this.reply_all) { + // if sending reply add ( last email's recipients - sender's email_id ) to cc. + const recipients = this.last_email.recipients.split(",").map((r) => r.trim()); + if (!this.cc) { + this.cc = ""; + } + const cc_array = this.cc.split(",").map((r) => r.trim()); + if (this.cc && !this.cc.endsWith(", ")) { + this.cc += ", "; + } + this.cc += recipients + .filter((r) => !cc_array.includes(r) && r != sender) + .join(", "); + this.cc = this.cc.replace(sender + ", ", ""); + fields.cc.set_value(this.cc); + } + } + } + setup_subject_and_recipients() { this.subject = this.subject || "";