Skip to content

Commit

Permalink
Merge pull request #24489 from frappe/version-14-hotfix
Browse files Browse the repository at this point in the history
chore: release v14
  • Loading branch information
ankush committed Jan 24, 2024
2 parents fb865c7 + 2aeb9a6 commit 2fe852c
Show file tree
Hide file tree
Showing 26 changed files with 109 additions and 41 deletions.
2 changes: 1 addition & 1 deletion frappe/commands/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion frappe/core/doctype/comment/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
3 changes: 1 addition & 2 deletions frappe/core/doctype/communication/communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion frappe/core/doctype/user/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@
"read_only": 1
},
{
"default": "1",
"default": "2",
"fieldname": "simultaneous_sessions",
"fieldtype": "Int",
"label": "Simultaneous Sessions"
Expand Down
2 changes: 1 addition & 1 deletion frappe/database/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion frappe/desk/doctype/desktop_icon/desktop_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion frappe/desk/doctype/kanban_board/kanban_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions frappe/desk/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
)
)
Expand Down
7 changes: 4 additions & 3 deletions frappe/integrations/doctype/google_drive/google_drive.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
});
Expand Down
6 changes: 3 additions & 3 deletions frappe/integrations/doctype/google_drive/google_drive.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@
"description": "The browser API key obtained from the Google Cloud Console under <a href=\"https://console.cloud.google.com/apis/credentials\">\n\"APIs &amp; Services\" &gt; \"Credentials\"\n</a>",
"fieldname": "api_key",
"fieldtype": "Data",
"label": "API Key",
"mandatory_depends_on": "google_drive_picker_enabled"
"label": "API Key"
},
{
"depends_on": "enable",
Expand Down Expand Up @@ -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",
Expand All @@ -96,5 +95,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,5 @@ def get_file_picker_settings():
return {
"enabled": True,
"appId": google_settings.app_id,
"developerKey": google_settings.api_key,
"clientId": google_settings.client_id,
}
Original file line number Diff line number Diff line change
Expand Up @@ -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", ""))
2 changes: 1 addition & 1 deletion frappe/model/db_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"):
Expand Down
2 changes: 1 addition & 1 deletion frappe/modules/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]:
Expand Down
5 changes: 4 additions & 1 deletion frappe/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,10 @@ def has_user_permission(doc, user=None):
if get_role_permissions("User Permission", user=user).get("write"):
return True

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")
)

doctype = doc.get("doctype")
docname = doc.get("name")
Expand Down
2 changes: 1 addition & 1 deletion frappe/public/js/frappe/form/controls/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 26 additions & 3 deletions frappe/public/js/frappe/form/footer/form_timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
10 changes: 6 additions & 4 deletions frappe/public/js/frappe/list/base_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion frappe/public/js/frappe/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
/**
Expand Down
42 changes: 42 additions & 0 deletions frappe/public/js/frappe/views/communication.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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 || "";

Expand Down
4 changes: 1 addition & 3 deletions frappe/public/js/integrations/google_drive_picker.js
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion frappe/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand Down
7 changes: 4 additions & 3 deletions frappe/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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())
1 change: 0 additions & 1 deletion frappe/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}


Expand Down

0 comments on commit 2fe852c

Please sign in to comment.