diff --git a/frappe/commands/site.py b/frappe/commands/site.py old mode 100644 new mode 100755 index 85a8414318..7382012c4b --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -353,7 +353,7 @@ def set_limit(context, site, limit, value): else: limit += '_limit' # Space can be float, while other should be integers - val = float(value) if limit == 'space_limit' else int(value) + value = float(value) if limit == 'space_limit' else int(value) set_limits({limit : value}) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 29c19a2d59..7699baae1d 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -360,9 +360,6 @@ def validate_space_limit(file_size): frappe_limits = get_limits() - if not frappe_limits: - return - if not frappe_limits.has_key('space_limit'): return diff --git a/frappe/core/page/usage_info/usage_info.html b/frappe/core/page/usage_info/usage_info.html index f43424974b..3f2293c5c9 100644 --- a/frappe/core/page/usage_info/usage_info.html +++ b/frappe/core/page/usage_info/usage_info.html @@ -1,6 +1,10 @@ -
-

Users

+
+ + {% if user_limit %} {% var users_percent = ((users / user_limit) * 100); %} +

Users

+ +
@@ -23,6 +27,70 @@
+ {% endif %} + + + + {% if expiry %} +

Expiration

+ {% var days_remaining = total_days - used_days %} + {% var date_percent = (( used_days / total_days) * 100); %} + +
+
+
+
+ + + + + + + + + + + + + + + + +
Creation DateExpiry DateDays Remaining
{%= creation %}{%= expiry %}{%= days_remaining %}
+
+ {% endif %} + + + {% if email_limit %} +

Emails

+ + {% var email_percent = (( bulk_count / email_limit ) * 100); %} + {% var emails_remaining = (email_limit - bulk_count) %} + +
+
+
+
+ + + + + + + + + + + + + + + + +
Emails UsedTotal EmailsEmails Left
{%= bulk_count %}{%= email_limit %}{%= emails_remaining %}
+
+ {% endif %} +

Disk Space

{% var database_percent = ((database_size / max) * 100); %} @@ -61,11 +129,15 @@ Total {%= total %} MB + {% if max %} Available {%= flt(max - total, 2) %} MB + {% endif %} + +
diff --git a/frappe/core/page/usage_info/usage_info.js b/frappe/core/page/usage_info/usage_info.js index 689ee9a703..f72f8bfce4 100644 --- a/frappe/core/page/usage_info/usage_info.js +++ b/frappe/core/page/usage_info/usage_info.js @@ -6,18 +6,29 @@ frappe.pages['usage-info'].on_page_load = function(wrapper) { }); frappe.call({ - method: "frappe.limits.get_limits", - callback: function(doc) { - doc = doc.message; - if(!doc.database_size) doc.database_size = 26; - if(!doc.files_size) doc.files_size = 1; - if(!doc.backup_size) doc.backup_size = 1; + method: "frappe.limits.get_limits", + callback: function(r) { + var doc = r.message; + if(!doc.database_size) doc.database_size = 26; + if(!doc.files_size) doc.files_size = 1; + if(!doc.backup_size) doc.backup_size = 1; - doc.max = flt(doc.space_limit * 1024); - doc.total = (doc.database_size + doc.files_size + doc.backup_size); - doc.users = keys(frappe.boot.user_info).length - 2; + doc.max = flt(doc.space_limit * 1024); + doc.total = (doc.database_size + doc.files_size + doc.backup_size); + doc.users = keys(frappe.boot.user_info).length - 2; + doc.today = frappe.datetime.get_today() + doc.total_days = frappe.datetime.get_day_diff(doc.expiry, doc.creation) + doc.used_days = frappe.datetime.get_day_diff(doc.today, doc.creation) - $(frappe.render_template("usage_info", doc)).appendTo(page.main); + $(frappe.render_template("usage_info", doc)).appendTo(page.main); + + var btn_text = doc.user_limit == 1 ? __("Upgrade") : __("Renew / Upgrade"); + + if(doc.limits_upgrade_link) { + page.set_primary_action(btn_text, function() { + frappe.set_route("upgrade"); + }); + } } }); diff --git a/frappe/desk/page/setup_wizard/setup_wizard.json b/frappe/desk/page/setup_wizard/setup_wizard.json index a2bee8df20..2072d8f79f 100644 --- a/frappe/desk/page/setup_wizard/setup_wizard.json +++ b/frappe/desk/page/setup_wizard/setup_wizard.json @@ -1,15 +1,15 @@ { - "content": null, - "creation": "2013-10-04 13:49:33", - "docstatus": 0, - "doctype": "Page", - "idx": 1, - "modified": "2016-06-10 12:05:31.701566", - "modified_by": "Administrator", - "module": "Desk", - "name": "setup-wizard", - "owner": "Administrator", - "page_name": "setup-wizard", + "content": null, + "creation": "2013-10-04 13:49:33", + "docstatus": 0, + "doctype": "Page", + "idx": 1, + "modified": "2016-06-15 13:07:32.651564", + "modified_by": "Administrator", + "module": "Desk", + "name": "setup-wizard", + "owner": "Administrator", + "page_name": "setup-wizard", "roles": [ { "role": "Administrator" diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py old mode 100644 new mode 100755 index 71486d26fa..419648a0ef --- a/frappe/desk/page/setup_wizard/setup_wizard.py +++ b/frappe/desk/page/setup_wizard/setup_wizard.py @@ -10,6 +10,7 @@ from frappe.translate import (set_default_language, get_dict, from frappe.geo.country_info import get_country_info from frappe.utils.file_manager import save_file from frappe.utils.password import update_password +from werkzeug.useragents import UserAgent @frappe.whitelist() def setup_complete(args): @@ -146,3 +147,75 @@ def load_languages(): "default_language": get_language_from_code(frappe.local.lang), "languages": sorted(get_lang_dict().keys()) } + + +def prettify_args(args): + # remove attachments + for key, val in args.items(): + if isinstance(val, basestring) and "data:image" in val: + filename = val.split("data:image", 1)[0].strip(", ") + size = round((len(val) * 3 / 4) / 1048576.0, 2) + args[key] = "Image Attached: '{0}' of size {1} MB".format(filename, size) + + pretty_args = [] + for key in sorted(args): + pretty_args.append("{} = {}".format(key, args[key])) + return pretty_args + +def email_setup_wizard_exception(traceback, args): + from frappe.limits import get_limits + frappe_limits = get_limits() + + if not frappe_limits.get('setup_wizard_exception_email'): + return + + pretty_args = prettify_args(args) + + if frappe.local.request: + user_agent = UserAgent(frappe.local.request.headers.get('User-Agent', '')) + + else: + user_agent = frappe._dict() + + message = """ +#### Basic Information + +- **Site:** {site} +- **User:** {user} +- **Browser:** {user_agent.platform} {user_agent.browser} version: {user_agent.version} language: {user_agent.language} +- **Browser Languages**: `{accept_languages}` + +--- + +#### Traceback + +
{traceback}
+ +--- + +#### Setup Wizard Arguments + +
{args}
+ +--- + +#### Request Headers + +
{headers}
""".format( + site=frappe.local.site, + traceback=traceback, + args="\n".join(pretty_args), + user=frappe.session.user, + user_agent=user_agent, + headers=frappe.local.request.headers, + accept_languages=", ".join(frappe.local.request.accept_languages.values())) + + frappe.sendmail(recipients=frappe_limits.get('setup_wizard_exception_email'), + sender=frappe.session.user, + subject="Exception in Setup Wizard - {}".format(frappe.local.site), + message=message) + + +def set_setup_complete(*args): + from frappe.limits import set_limits + set_limits({'setup_complete' : 1 , 'creation': frappe.utils.today()}) diff --git a/frappe/email/bulk.py b/frappe/email/bulk.py old mode 100644 new mode 100755 index 5d8d411f86..48885237a4 --- a/frappe/email/bulk.py +++ b/frappe/email/bulk.py @@ -334,3 +334,8 @@ def clear_outbox(): """Remove mails older than 31 days in Outbox. Called daily via scheduler.""" frappe.db.sql("""delete from `tabBulk Email` where datediff(now(), creation) > 31""") + +def prevent_bulk_email_delete(doc, method): + from frappe.limits import get_limits + if frappe.session.user != 'Administrator' and get_limits().get('block_bulk_email_delete'): + frappe.throw(_('Only Administrator can delete Bulk Email')) \ No newline at end of file diff --git a/frappe/hooks.py b/frappe/hooks.py index 197ad77395..cd300d813c 100755 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -14,6 +14,11 @@ app_email = "info@frappe.io" before_install = "frappe.utils.install.before_install" after_install = "frappe.utils.install.after_install" +extend_bootinfo = "frappe.limits.load_limits" + +page_js = { + "setup-wizard": "public/js/frappe/setup_wizard.js" +} # website app_include_js = [ @@ -23,7 +28,8 @@ app_include_js = [ "assets/js/list.min.js", "assets/js/form.min.js", "assets/js/report.min.js", - "assets/js/d3.min.js" + "assets/js/d3.min.js", + "assets/frappe/js/frappe/toolbar.js" ] app_include_css = [ "assets/css/desk.min.css", @@ -64,7 +70,7 @@ calendars = ["Event"] on_session_creation = [ "frappe.core.doctype.communication.feed.login_feed", "frappe.core.doctype.user.user.notifify_admin_access_to_system_manager", - "frappe.limits.check_if_expired", #Unsure of where to move + "frappe.limits.check_if_expired", "frappe.utils.scheduler.reset_enabled_scheduler_events", ] @@ -93,6 +99,9 @@ doc_events = { "User": { "validate": "frappe.utils.user.validate_user_limit" }, + "Bulk Email": { + "on_trash": "frappe.email.bulk.prevent_bulk_email_delete" + }, "*": { "after_insert": "frappe.email.doctype.email_alert.email_alert.trigger_email_alerts", "validate": "frappe.email.doctype.email_alert.email_alert.trigger_email_alerts", @@ -170,4 +179,6 @@ bot_parsers = [ 'frappe.utils.bot.CountBot' ] +setup_wizard_exception = "frappe.desk.page.setup_wizard.setup_wizard.email_setup_wizard_exception" +setup_wizard_success = "frappe.desk.page.setup_wizard.setup_wizard.set_setup_complete" before_write_file = "frappe.core.doctype.file.file.validate_space_limit" diff --git a/frappe/limits.py b/frappe/limits.py index 053bcac880..9f671902fa 100755 --- a/frappe/limits.py +++ b/frappe/limits.py @@ -1,25 +1,24 @@ from __future__ import unicode_literals import frappe -import subprocess, os -from frappe.model.document import Document -from frappe.core.doctype.user.user import get_total_users -from frappe.utils import flt, cint, now_datetime, getdate, get_site_path +from frappe.utils import now_datetime, getdate from frappe.installer import update_site_config -from frappe.utils.file_manager import MaxFileSizeReachedError from frappe.utils.data import formatdate from frappe import _ class SiteExpiredError(frappe.ValidationError): pass +EXPIRY_WARNING_DAYS = 10 + +def load_limits(bootinfo): + bootinfo["frappe_limits"] = get_limits() + bootinfo["expiry_message"] = get_expiry_message() + def has_expired(): if frappe.session.user=="Administrator": return False - if not get_limits(): - return False - expires_on = get_limits().get("expiry") if not expires_on: return False @@ -35,14 +34,46 @@ def check_if_expired(): return # if expired, stop user from logging in expires_on = formatdate(get_limits().get("expiry")) + support_email = get_limits().get("support_email") or _("your provider") - frappe.throw("""Your subscription expired on {}. - To extend please drop a mail at support@erpnext.com""".format(expires_on), + frappe.throw(_("""Your subscription expired on {0}. + To extend please send an email to {1}""").format(expires_on, support_email), SiteExpiredError) +def get_expiry_message(): + if "System Manager" not in frappe.get_roles(): + return "" + + if not get_limits().get("expiry"): + return "" + + expires_on = getdate(get_limits().get("expiry")) + today = now_datetime().date() + + message = "" + if today > expires_on: + message = _("Your subscription has expired") + else: + days_to_expiry = (expires_on - today).days + + if days_to_expiry == 0: + message = _("Your subscription will expire today") + + elif days_to_expiry == 1: + message = _("Your subscription will expire tomorrow") + + elif days_to_expiry <= EXPIRY_WARNING_DAYS: + message = _("Your subscription will expire on") + " " + formatdate(expires_on) + + return message + + @frappe.whitelist() def get_limits(): - return frappe.get_conf().get("limits") + limits = frappe.get_conf().get("limits") or {} + day = frappe.utils.add_months(frappe.utils.today(), -1) + limits["bulk_count"] = frappe.db.count("Bulk Email", filters={'creation': ['>', day]}) + return limits def set_limits(limits): @@ -55,7 +86,7 @@ def set_limits(limits): def clear_limit(limit): - frappe_limits = get_limits() or {} + frappe_limits = get_limits() if limit in frappe_limits: del frappe_limits[limit] diff --git a/frappe/public/build.json b/frappe/public/build.json old mode 100644 new mode 100755 diff --git a/frappe/public/js/frappe/setup_wizard.js b/frappe/public/js/frappe/setup_wizard.js new file mode 100755 index 0000000000..66d6795c9b --- /dev/null +++ b/frappe/public/js/frappe/setup_wizard.js @@ -0,0 +1,18 @@ +frappe.wiz.on("after_load", function() { + if (frappe.boot.frappe_limits.user_limit===1) { + // remove users slide + var users_slide; + for (var i in frappe.wiz.wizard.slides) { + var slide = frappe.wiz.wizard.slides[i]; + if (slide.title === frappe.wiz.user.title) { + users_slide = i; + break; + } + } + + if (users_slide >= 0) { + frappe.wiz.wizard.slides.splice(users_slide, 1); + } + + } +}); diff --git a/frappe/public/js/frappe/toolbar.js b/frappe/public/js/frappe/toolbar.js new file mode 100755 index 0000000000..d034052563 --- /dev/null +++ b/frappe/public/js/frappe/toolbar.js @@ -0,0 +1,55 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. +// For license information, please see license.txt + +$(document).on("toolbar_setup", function() { + var help_links = []; + var support_link = "#upgrade"; + var chat_link = "#upgrade"; + frappe_limits = frappe.boot.frappe_limits + + if(frappe.boot.expiry_message) { + frappe.msgprint(frappe.boot.expiry_message) + } + + if(frappe_limits.support_email || frappe_limits.support_chat) { + help_links.push('
  • '); + } + + if(frappe_limits.support_email) { + support_link = 'mailto:'+frappe.boot.frappe_limits.support_email; + help_links.push('
  • ' + frappe._('Email Support') + '
  • '); + } + + if(frappe_limits.support_chat) { + chat_link = frappe_limits.support_chat; + help_links.push('
  • ' + frappe._('Chat Support') + '
  • '); + } + + + $(help_links.join("\n")).insertBefore($("#toolbar-user").find(".divider:last")); + + if(frappe_limits.space_limit || frappe_limits.user_limit || frappe_limits.expiry || frappe_limits.email_limit) + { + help_links = []; + help_links.push('
  • ' + frappe._('Usage Info') + '
  • '); + help_links.push('
  • '); + } + + $(help_links.join("\n")).insertBefore($("#toolbar-user").find("li:first")); +}); + +frappe.get_form_sidebar_extension = function() { + var fs = frappe.boot.frappe_limits; + if(!fs.sidebar_usage_html) { + fs.total_used = flt(fs.database_size + fs.backup_size + fs.files_size); + fs.total_used_percent = cint(fs.total_used / flt(fs.max_space * 1024) * 100); + + var template = ''; + fs.sidebar_usage_html = frappe.render(template, {fs:fs}, "form_sidebar_usage"); + } + + return fs.sidebar_usage_html; +}