@@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json | |||||
from .exceptions import * | from .exceptions import * | ||||
from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template | from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template | ||||
__version__ = '8.7.5' | |||||
__version__ = '8.7.6' | |||||
__title__ = "Frappe Framework" | __title__ = "Frappe Framework" | ||||
local = Local() | local = Local() | ||||
@@ -447,7 +447,7 @@ def _set_limits(context, site, limits): | |||||
frappe.connect() | frappe.connect() | ||||
new_limits = {} | new_limits = {} | ||||
for limit, value in limits: | for limit, value in limits: | ||||
if limit not in ('emails', 'space', 'users', 'email_group', | |||||
if limit not in ('daily_emails', 'emails', 'space', 'users', 'email_group', | |||||
'expiry', 'support_email', 'support_chat', 'upgrade_url'): | 'expiry', 'support_email', 'support_chat', 'upgrade_url'): | ||||
frappe.throw(_('Invalid limit {0}').format(limit)) | frappe.throw(_('Invalid limit {0}').format(limit)) | ||||
@@ -27,7 +27,7 @@ $.extend(frappe.desktop, { | |||||
render: function() { | render: function() { | ||||
var me = this; | var me = this; | ||||
frappe.utils.set_title("Desktop"); | |||||
frappe.utils.set_title(__("Desktop")); | |||||
var template = frappe.list_desktop ? "desktop_list_view" : "desktop_icon_grid"; | var template = frappe.list_desktop ? "desktop_list_view" : "desktop_icon_grid"; | ||||
@@ -353,8 +353,7 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with | |||||
if isinstance(f[1], string_types) and f[1][0] == '!': | if isinstance(f[1], string_types) and f[1][0] == '!': | ||||
flt.append([doctype, f[0], '!=', f[1][1:]]) | flt.append([doctype, f[0], '!=', f[1][1:]]) | ||||
else: | else: | ||||
value = frappe.db.escape(f[1]) if isinstance(f[1], string_types) else f[1] | |||||
flt.append([doctype, f[0], '=', value]) | |||||
flt.append([doctype, f[0], '=', f[1]]) | |||||
query = DatabaseQuery(doctype) | query = DatabaseQuery(doctype) | ||||
query.filters = flt | query.filters = flt | ||||
@@ -95,7 +95,7 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content= | |||||
and add_unsubscribe_link==1) | and add_unsubscribe_link==1) | ||||
unsubscribe_link = None | unsubscribe_link = None | ||||
if should_append_unsubscribe or True: | |||||
if should_append_unsubscribe: | |||||
unsubscribe_link = get_unsubscribe_message(unsubscribe_message, expose_recipients) | unsubscribe_link = get_unsubscribe_message(unsubscribe_message, expose_recipients) | ||||
email_text_context += unsubscribe_link.text | email_text_context += unsubscribe_link.text | ||||
@@ -218,9 +218,18 @@ def check_email_limit(recipients): | |||||
or frappe.flags.in_test): | or frappe.flags.in_test): | ||||
monthly_email_limit = frappe.conf.get('limits', {}).get('emails') | monthly_email_limit = frappe.conf.get('limits', {}).get('emails') | ||||
daily_email_limit = frappe.conf.get('limits', {}).get('daily_emails') | |||||
if frappe.flags.in_test: | if frappe.flags.in_test: | ||||
monthly_email_limit = 500 | monthly_email_limit = 500 | ||||
daily_email_limit = 50 | |||||
if daily_email_limit: | |||||
# get count of sent mails in last 24 hours | |||||
today = get_emails_sent_today() | |||||
if (today + len(recipients)) > daily_email_limit: | |||||
throw(_("Cannot send this email. You have crossed the sending limit of {0} emails for this day.").format(daily_email_limit), | |||||
EmailLimitCrossedError) | |||||
if not monthly_email_limit: | if not monthly_email_limit: | ||||
return | return | ||||
@@ -236,6 +245,10 @@ def get_emails_sent_this_month(): | |||||
return frappe.db.sql("""select count(name) from `tabEmail Queue` where | return frappe.db.sql("""select count(name) from `tabEmail Queue` where | ||||
status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0] | status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0] | ||||
def get_emails_sent_today(): | |||||
return frappe.db.sql("""select count(name) from `tabEmail Queue` where | |||||
status='Sent' and creation>DATE_SUB(NOW(), INTERVAL 24 HOUR)""")[0][0] | |||||
def get_unsubscribe_message(unsubscribe_message, expose_recipients): | def get_unsubscribe_message(unsubscribe_message, expose_recipients): | ||||
if unsubscribe_message: | if unsubscribe_message: | ||||
unsubscribe_html = '''<a href="<!--unsubscribe url-->" | unsubscribe_html = '''<a href="<!--unsubscribe url-->" | ||||
@@ -169,7 +169,7 @@ def check_permission_and_not_submitted(doc): | |||||
# check if submitted | # check if submitted | ||||
if doc.docstatus == 1: | if doc.docstatus == 1: | ||||
frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted.").format(doc.doctype, doc.name), | |||||
frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted.").format(_(doc.doctype), doc.name), | |||||
raise_exception=True) | raise_exception=True) | ||||
def check_if_doc_is_linked(doc, method="Delete"): | def check_if_doc_is_linked(doc, method="Delete"): | ||||
@@ -190,3 +190,4 @@ frappe.patches.v8_1.update_format_options_in_auto_email_report | |||||
frappe.patches.v8_1.delete_custom_docperm_if_doctype_not_exists | frappe.patches.v8_1.delete_custom_docperm_if_doctype_not_exists | ||||
frappe.patches.v8_5.delete_email_group_member_with_invalid_emails | frappe.patches.v8_5.delete_email_group_member_with_invalid_emails | ||||
frappe.patches.v8_x.update_user_permission | frappe.patches.v8_x.update_user_permission | ||||
frappe.patches.v8_5.patch_event_colors |
@@ -0,0 +1,21 @@ | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
def execute(): | |||||
colors = ['red', 'green', 'blue', 'yellow', 'skyblue', 'orange'] | |||||
hex_colors = ['#ffc4c4', '#cef6d1', '#d2d2ff', '#fffacd', '#d2f1ff', '#ffd2c2'] | |||||
def get_hex_for_color(color): | |||||
index = colors.index(color) | |||||
return hex_colors[index] | |||||
query = ''' | |||||
update tabEvent | |||||
set color='{hex}' | |||||
where color='{color}' | |||||
''' | |||||
for color in colors: | |||||
frappe.db.sql(query.format(color=color, hex=get_hex_for_color(color))) | |||||
frappe.db.commit() |
@@ -93,9 +93,9 @@ frappe.Application = Class.extend({ | |||||
var dialog = frappe.msgprint({ | var dialog = frappe.msgprint({ | ||||
message:__("The application has been updated to a new version, please refresh this page"), | message:__("The application has been updated to a new version, please refresh this page"), | ||||
indicator: 'green', | indicator: 'green', | ||||
title: 'Version Updated' | |||||
title: __('Version Updated') | |||||
}); | }); | ||||
dialog.set_primary_action("Refresh", function() { | |||||
dialog.set_primary_action(__("Refresh"), function() { | |||||
location.reload(true); | location.reload(true); | ||||
}); | }); | ||||
dialog.get_close_btn().toggle(false); | dialog.get_close_btn().toggle(false); | ||||
@@ -211,10 +211,51 @@ frappe.ui.get_upload_dialog = function(opts){ | |||||
title: __('Upload Attachment'), | title: __('Upload Attachment'), | ||||
no_focus: true, | no_focus: true, | ||||
fields: [ | fields: [ | ||||
{"fieldtype": "Section Break"}, | |||||
{"fieldtype": "Link" , "fieldname": "file" , "label": __("Select uploaded file"), "options": "File"}, | |||||
{"hidden": !opts.args.doctype || !frappe.boot.gsuite_enabled,"fieldtype": "Section Break", "label": __("GSuite Document")}, | |||||
{"fieldtype": "Link" ,"fieldname": "gs_template" ,"label": __("Select template"), "options": "GSuite Templates", reqd : false, filters: {'related_doctype': opts.args.doctype}}, | |||||
{ | |||||
"fieldtype": "Section Break" | |||||
}, | |||||
{ | |||||
"fieldtype": "Link" , | |||||
"fieldname": "file" , | |||||
"label": __("Select uploaded file"), | |||||
"options": "File", | |||||
onchange: function() { | |||||
frappe.call({ | |||||
'method': 'frappe.client.get_value', | |||||
'args': { | |||||
'doctype': 'File', | |||||
'fieldname': ['file_url','file_name','is_private'], | |||||
'filters': { | |||||
'name': dialog.get_value("file") | |||||
} | |||||
}, | |||||
callback: function(r){ | |||||
if(!r.message) { | |||||
dialog.$wrapper.find('[name="file_url"]').val(""); | |||||
return; | |||||
} | |||||
dialog.$wrapper.find('[name="file_url"]').val(r.message.file_url); | |||||
dialog.$wrapper.find('.private-file input').prop('checked', r.message.is_private); | |||||
opts.args.filename = r.message.file_name; | |||||
} | |||||
}); | |||||
} | |||||
}, | |||||
{ | |||||
"hidden": !opts.args.doctype || !frappe.boot.gsuite_enabled, | |||||
"fieldtype": "Section Break", | |||||
"label": __("GSuite Document"), | |||||
}, | |||||
{ | |||||
"fieldtype": "Link" , | |||||
"fieldname": "gs_template" , | |||||
"label": __("Select template"), | |||||
"options": "GSuite Templates", | |||||
"reqd" : false, | |||||
"filters": { | |||||
'related_doctype': opts.args.doctype | |||||
} | |||||
}, | |||||
], | ], | ||||
}); | }); | ||||
var btn = dialog.set_primary_action(__("Attach")); | var btn = dialog.set_primary_action(__("Attach")); | ||||
@@ -229,24 +270,6 @@ frappe.ui.get_upload_dialog = function(opts){ | |||||
opts.args.gs_template = fd.gs_template.get_value(); | opts.args.gs_template = fd.gs_template.get_value(); | ||||
}); | }); | ||||
$(fd.file.input).change(function() { | |||||
frappe.call({ | |||||
'method': 'frappe.client.get_value', | |||||
'args': { | |||||
'doctype': 'File', | |||||
'fieldname': ['file_url','file_name','is_private'], | |||||
'filters': { | |||||
'name': dialog.get_value("file") | |||||
} | |||||
}, | |||||
callback: function(r){ | |||||
if(!r.message) return; | |||||
dialog.$wrapper.find('[name="file_url"]').val(r.message.file_url); | |||||
dialog.$wrapper.find('.private-file input').prop('checked', r.message.is_private); | |||||
opts.args.filename = r.message.file_name; | |||||
} | |||||
}); | |||||
}); | |||||
frappe.upload.make({ | frappe.upload.make({ | ||||
parent: upload_area, | parent: upload_area, | ||||
args: opts.args, | args: opts.args, | ||||
@@ -229,8 +229,8 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
var options = {}; | var options = {}; | ||||
options[search_field] = ["like", "%" + txt + "%"]; | options[search_field] = ["like", "%" + txt + "%"]; | ||||
this.options.push({ | this.options.push({ | ||||
label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]), | |||||
value: __('Find {0} in {1}', [txt, route[1]]), | |||||
label: __('Find {0} in {1}', [txt.bold(), __(route[1]).bold()]), | |||||
value: __('Find {0} in {1}', [txt, __(route[1])]), | |||||
route_options: options, | route_options: options, | ||||
onclick: function() { | onclick: function() { | ||||
cur_list.refresh(); | cur_list.refresh(); | ||||