@@ -1227,4 +1227,4 @@ def logger(module=None, with_more_info=True): | |||
return get_logger(module or __name__, with_more_info=with_more_info) | |||
def get_desk_link(doctype, name): | |||
return '<a href="#Form/{0}/{1}" style="font-weight: bold;">{2} {1}</a>'.format(doctype, name, _(doctype)) | |||
return '<a href="#Form/{0}/{1}" style="font-weight: bold;">{2} {1}</a>'.format(doctype, name, _(doctype)) |
@@ -12,6 +12,7 @@ import frappe.desk.desk_page | |||
from frappe.desk.form.load import get_meta_bundle | |||
from frappe.utils.change_log import get_versions | |||
from frappe.translate import get_lang_dict | |||
from frappe.limits import get_limits, get_expiry_message | |||
def get_bootinfo(): | |||
"""build and return boot info""" | |||
@@ -65,6 +66,10 @@ def get_bootinfo(): | |||
bootinfo.calendars = sorted(frappe.get_hooks("calendars")) | |||
bootinfo.lang_dict = get_lang_dict() | |||
# limits | |||
bootinfo.limits = get_limits() | |||
bootinfo.expiry_message = get_expiry_message() | |||
return bootinfo | |||
def load_conf_settings(bootinfo): | |||
@@ -4,8 +4,7 @@ import hashlib, os | |||
import frappe | |||
from frappe.commands import pass_context, get_site | |||
from frappe.commands.scheduler import _is_scheduler_enabled | |||
from frappe.commands import get_site | |||
from frappe.limits import set_limits, get_limits | |||
from frappe.limits import update_limits, get_limits | |||
from frappe.installer import update_site_config | |||
@click.command('new-site') | |||
@@ -345,18 +344,19 @@ def set_limit(context, site, limit, value): | |||
site = get_site(context) | |||
with frappe.init_site(site): | |||
if limit == 'expiry': | |||
if limit=='expiry': | |||
try: | |||
datetime.datetime.strptime(value, '%Y-%m-%d') | |||
except ValueError: | |||
raise ValueError("Incorrect data format, should be YYYY-MM-DD") | |||
else: | |||
limit += '_limit' | |||
# Space can be float, while other should be integers | |||
value = float(value) if limit == 'space_limit' else int(value) | |||
set_limits({limit : value}) | |||
elif limit=='space': | |||
value = float(value) | |||
else: | |||
value = int(value) | |||
update_limits({ limit : value }) | |||
@click.command('clear-limit') | |||
@click.option('--site', help='site name') | |||
@@ -369,14 +369,11 @@ def clear_limit(context, site, limit): | |||
site = get_site(context) | |||
with frappe.init_site(site): | |||
if not limit == 'expiry': | |||
limit += '_limit' | |||
_clear_limit(limit) | |||
# Remove limits from the site_config, if it's empty | |||
cur_limits = get_limits() | |||
if not cur_limits: | |||
limits = get_limits() | |||
if not limits: | |||
update_site_config('limits', 'None', validate=False) | |||
@@ -11,7 +11,7 @@ naming for same name files: file.gif, file-1.gif, file-2.gif etc | |||
import frappe | |||
import json | |||
import urllib | |||
import os, subprocess | |||
import os | |||
import requests | |||
import requests.exceptions | |||
import StringIO | |||
@@ -20,8 +20,7 @@ import mimetypes, imghdr | |||
from frappe.utils.file_manager import delete_file_data_content, get_content_hash, get_random_filename | |||
from frappe import _ | |||
from frappe.utils.nestedset import NestedSet | |||
from frappe.limits import get_limits, set_limits | |||
from frappe.utils import strip, get_url, get_files_path, flt | |||
from frappe.utils import strip, get_files_path | |||
from PIL import Image, ImageOps | |||
class FolderNotEmpty(frappe.ValidationError): pass | |||
@@ -352,63 +351,3 @@ def check_file_permission(file_url): | |||
raise frappe.PermissionError | |||
def validate_space_limit(file_size): | |||
"""Stop from writing file if max space limit is reached""" | |||
from frappe.installer import update_site_config | |||
from frappe.utils import cint | |||
from frappe.utils.file_manager import MaxFileSizeReachedError | |||
frappe_limits = get_limits() | |||
if not frappe_limits.has_key('space_limit'): | |||
return | |||
# In Gigabytes | |||
space_limit = flt(flt(frappe_limits['space_limit']) * 1024, 2) | |||
# in Kilobytes | |||
used_space = flt(frappe_limits['files_size']) + flt(frappe_limits['backup_size']) + flt(frappe_limits['database_size']) | |||
file_size = file_size / (1024.0**2) | |||
# Stop from attaching file | |||
if flt(used_space + file_size, 2) > space_limit: | |||
frappe.throw(_("You have exceeded the max space of {0} for your plan. {1} or {2}.").format( | |||
"<b>{0}MB</b>".format(cint(space_limit)) if (space_limit < 1024) else "<b>{0}GB</b>".format(frappe_limits['space_limit']), | |||
'<a href="#usage-info">{0}</a>'.format(_("Click here to check your usage")), | |||
'<a href="#upgrade">{0}</a>'.format(_("upgrade to a higher plan")), | |||
), MaxFileSizeReachedError) | |||
# update files size in frappe subscription | |||
new_files_size = flt(frappe_limits['files_size']) + file_size | |||
set_limits({'files_size': file_size}) | |||
def update_sizes(): | |||
from frappe.installer import update_site_config | |||
# public files | |||
files_path = frappe.get_site_path("public", "files") | |||
files_size = flt(subprocess.check_output(['du', '-ms', files_path]).split()[0]) | |||
# private files | |||
files_path = frappe.get_site_path("private", "files") | |||
if os.path.exists(files_path): | |||
files_size += flt(subprocess.check_output(['du', '-ms', files_path]).split()[0]) | |||
# backups | |||
backup_path = frappe.get_site_path("private", "backups") | |||
backup_size = subprocess.check_output(['du', '-ms', backup_path]).split()[0] | |||
database_size = get_database_size() | |||
set_limits({'files_size': files_size, | |||
'backup_size': backup_size, | |||
'database_size': database_size}) | |||
def get_database_size(): | |||
db_name = frappe.conf.db_name | |||
# This query will get the database size in MB | |||
db_size = frappe.db.sql(''' | |||
SELECT table_schema "database_name", sum( data_length + index_length ) / 1024 / 1024 "database_size" | |||
FROM information_schema.TABLES WHERE table_schema = %s GROUP BY table_schema''', db_name, as_dict=True) | |||
return db_size[0].get('database_size') |
@@ -98,10 +98,17 @@ class TestFile(unittest.TestCase): | |||
def test_file_upload_limit(self): | |||
from frappe.utils.file_manager import MaxFileSizeReachedError | |||
from frappe.limits import set_limits, clear_limit | |||
from frappe.limits import update_limits, clear_limit | |||
from frappe import _dict | |||
set_limits({'space_limit': 1, 'files_size': (1024 * 1024), 'database_size': 0, 'backup_size': 0}) | |||
update_limits({ | |||
'space': 1, | |||
'space_usage': { | |||
'files_size': (1024 * 1024), | |||
'database_size': 0, | |||
'backup_size': 0 | |||
} | |||
}) | |||
# Rebuild the frappe.local.conf to take up the changes from site_config | |||
frappe.local.conf = _dict(frappe.get_site_config()) | |||
@@ -110,5 +117,5 @@ class TestFile(unittest.TestCase): | |||
'This files test for max space usage', "", "", self.get_folder("Test Folder 2", "Home").name) | |||
# Scrub the site_config and rebuild frappe.local.conf | |||
clear_limit("space_limit") | |||
frappe.local.conf = _dict(frappe.get_site_config()) | |||
clear_limit("space") | |||
frappe.local.conf = _dict(frappe.get_site_config()) |
@@ -8,7 +8,7 @@ import requests | |||
from frappe.model.delete_doc import delete_doc | |||
from frappe.utils.data import today, add_to_date | |||
from frappe import _dict | |||
from frappe.limits import SiteExpiredError, set_limits, clear_limit | |||
from frappe.limits import SiteExpiredError, update_limits, clear_limit | |||
from frappe.utils import get_url | |||
from frappe.installer import update_site_config | |||
@@ -82,7 +82,7 @@ class TestUser(unittest.TestCase): | |||
def test_user_limit_for_site(self): | |||
from frappe.core.doctype.user.user import get_total_users | |||
set_limits({'user_limit': get_total_users()}) | |||
update_limits({'users': get_total_users()}) | |||
# reload site config | |||
from frappe import _dict | |||
@@ -99,10 +99,10 @@ class TestUser(unittest.TestCase): | |||
frappe.delete_doc('User', 'test_max_users@example.com') | |||
# Clear the user limit | |||
clear_limit('user_limit') | |||
clear_limit('users') | |||
def test_site_expiry(self): | |||
set_limits({'expiry': add_to_date(today(), days=-1)}) | |||
update_limits({'expiry': add_to_date(today(), days=-1)}) | |||
frappe.local.conf = _dict(frappe.get_site_config()) | |||
frappe.db.commit() | |||
@@ -3,6 +3,7 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe.model.document import Document | |||
from frappe.utils import cint, has_gravatar, format_datetime, now_datetime, get_formatted_email | |||
from frappe import throw, msgprint, _ | |||
from frappe.utils.password import update_password as _update_password | |||
@@ -11,10 +12,11 @@ from frappe.utils.user import get_system_managers | |||
import frappe.permissions | |||
import frappe.share | |||
import re | |||
from frappe.limits import get_limits | |||
STANDARD_USERS = ("Guest", "Administrator") | |||
from frappe.model.document import Document | |||
class MaxUsersReachedError(frappe.ValidationError): pass | |||
class User(Document): | |||
__new_password = None | |||
@@ -54,6 +56,7 @@ class User(Document): | |||
self.remove_all_roles_for_guest() | |||
self.validate_username() | |||
self.remove_disabled_roles() | |||
self.validate_user_limit() | |||
if self.language == "Loading...": | |||
self.language = None | |||
@@ -383,6 +386,34 @@ class User(Document): | |||
"""Returns list of modules blocked for that user""" | |||
return [d.module for d in self.block_modules] if self.block_modules else [] | |||
def validate_user_limit(self): | |||
''' | |||
Validate if user limit has been reached for System Users | |||
Checked in 'Validate' event as we don't want welcome email sent if max users are exceeded. | |||
''' | |||
if self.user_type == "Website User": | |||
return | |||
if not self.enabled: | |||
# don't validate max users when saving a disabled user | |||
return | |||
limits = get_limits() | |||
if not limits.users: | |||
# no limits defined | |||
return | |||
total_users = get_total_users() | |||
if self.is_new(): | |||
# get_total_users gets existing users in database | |||
# a new record isn't inserted yet, so adding 1 | |||
total_users += 1 | |||
if total_users > limits.users: | |||
frappe.throw(_("Sorry. You have reached the maximum user limit for your subscription. You can either disable an existing user or buy a higher subscription plan."), | |||
MaxUsersReachedError) | |||
@frappe.whitelist() | |||
def get_timezones(): | |||
import pytz | |||
@@ -6,32 +6,24 @@ frappe.pages['usage-info'].on_page_load = function(wrapper) { | |||
}); | |||
frappe.call({ | |||
method: "frappe.limits.get_usage_data", | |||
method: "frappe.limits.get_usage_info", | |||
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; | |||
if(typeof doc.space_limit !== "undefined") | |||
{ | |||
doc.max = flt(doc.space_limit * 1024); | |||
var usage_info = r.message; | |||
if (!usage_info) { | |||
// nothing to show | |||
// TODO improve this | |||
return; | |||
} | |||
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); | |||
var btn_text = doc.user_limit == 1 ? __("Upgrade") : __("Renew / Upgrade"); | |||
var btn_text = usage_info.limits.users == 1 ? __("Upgrade") : __("Renew / Upgrade"); | |||
if(doc.limits_upgrade_link) { | |||
page.set_primary_action(btn_text, function() { | |||
frappe.set_route("upgrade"); | |||
}); | |||
} | |||
if(usage_info.upgrade_link) { | |||
page.set_primary_action(btn_text, function() { | |||
window.open(usage_info.upgrade_link); | |||
}); | |||
} | |||
} | |||
}); | |||
@@ -421,7 +421,7 @@ function load_frappe_slides() { | |||
}, | |||
frappe.wiz.user= { | |||
frappe.wiz.user = { | |||
app_name: "frappe", | |||
title: __("The First User: You"), | |||
icon: "icon-user", | |||
@@ -465,5 +465,8 @@ frappe.wiz.on("before_load", function() { | |||
// add welcome slide | |||
frappe.wiz.add_slide(frappe.wiz.welcome); | |||
frappe.wiz.add_slide(frappe.wiz.region); | |||
frappe.wiz.add_slide(frappe.wiz.user); | |||
if (!(frappe.boot.limits && frappe.boot.limits.users===1)) { | |||
frappe.wiz.add_slide(frappe.wiz.user); | |||
} | |||
}); |
@@ -163,10 +163,7 @@ def prettify_args(args): | |||
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'): | |||
if not frappe.local.conf.setup_wizard_exception_email: | |||
return | |||
pretty_args = prettify_args(args) | |||
@@ -210,12 +207,8 @@ def email_setup_wizard_exception(traceback, args): | |||
headers=frappe.local.request.headers, | |||
accept_languages=", ".join(frappe.local.request.accept_languages.values())) | |||
frappe.sendmail(recipients=frappe_limits.get('setup_wizard_exception_email'), | |||
frappe.sendmail(recipients=frappe.local.conf.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()}) |
@@ -4,12 +4,20 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe import _ | |||
from frappe.model.document import Document | |||
from frappe.email.queue import send_one | |||
from frappe.limits import get_limits | |||
class EmailQueue(Document): | |||
pass | |||
def on_trash(self): | |||
self.prevent_email_queue_delete() | |||
def prevent_email_queue_delete(self): | |||
'''If email limit is set, don't allow users to delete Email Queue record''' | |||
if get_limits().emails and frappe.session.user != 'Administrator': | |||
frappe.throw(_('Only Administrator can delete Email Queue')) | |||
@frappe.whitelist() | |||
def retry_sending(name): | |||
@@ -147,15 +147,18 @@ def check_email_limit(recipients): | |||
or frappe.flags.in_test): | |||
# get count of mails sent this month | |||
this_month = frappe.db.sql("""select count(name) from `tabEmail Queue` where | |||
status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0] | |||
this_month = get_emails_sent_this_month() | |||
monthly_email_limit = frappe.conf.get('monthly_email_limit') or 500 | |||
monthly_email_limit = frappe.conf.get('limits', {}).get('emails') or 500 | |||
if (this_month + len(recipients)) > monthly_email_limit: | |||
throw(_("Cannot send this email. You have crossed the sending limit of {0} emails for this month.").format(monthly_email_limit), | |||
EmailLimitCrossedError) | |||
def get_emails_sent_this_month(): | |||
return frappe.db.sql("""select count(name) from `tabEmail Queue` where | |||
status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0] | |||
def get_unsubscribe_link(reference_doctype, reference_name, | |||
email, recipients, expose_recipients, show_as_cc, | |||
unsubscribe_method, unsubscribe_params, unsubscribe_message): | |||
@@ -334,8 +337,3 @@ def clear_outbox(): | |||
"""Remove mails older than 31 days in Outbox. Called daily via scheduler.""" | |||
frappe.db.sql("""delete from `tabEmail Queue` where | |||
datediff(now(), creation) > 31""") | |||
def prevent_email_queue_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 Email Queue')) |
@@ -14,7 +14,6 @@ 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" | |||
@@ -96,12 +95,6 @@ standard_queries = { | |||
} | |||
doc_events = { | |||
"User": { | |||
"validate": "frappe.utils.user.validate_user_limit" | |||
}, | |||
"Email Queue": { | |||
"on_trash": "frappe.email.queue.prevent_email_queue_delete" | |||
}, | |||
"*": { | |||
"after_insert": "frappe.email.doctype.email_alert.email_alert.trigger_email_alerts", | |||
"validate": "frappe.email.doctype.email_alert.email_alert.trigger_email_alerts", | |||
@@ -141,7 +134,7 @@ scheduler_events = { | |||
"frappe.async.remove_old_task_logs", | |||
"frappe.utils.scheduler.disable_scheduler_on_expiry", | |||
"frappe.utils.scheduler.restrict_scheduler_events_if_dormant", | |||
"frappe.core.doctype.file.file.update_sizes" | |||
"frappe.limits.update_space_usage" | |||
], | |||
"daily_long": [ | |||
@@ -180,5 +173,4 @@ bot_parsers = [ | |||
] | |||
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" | |||
before_write_file = "frappe.limits.validate_space_limit" |
@@ -1,25 +1,34 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe.utils import now_datetime, getdate | |||
from frappe import _ | |||
from frappe.utils import now_datetime, getdate, flt, cint, get_fullname | |||
from frappe.installer import update_site_config | |||
from frappe.utils.data import formatdate | |||
from frappe import _ | |||
from frappe.utils.user import get_enabled_system_users | |||
import os, subprocess, urlparse, urllib | |||
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 check_if_expired(): | |||
"""check if account is expired. If expired, do not allow login""" | |||
if not has_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 {0}. | |||
To extend please send an email to {1}""").format(expires_on, support_email), | |||
SiteExpiredError) | |||
def has_expired(): | |||
if frappe.session.user=="Administrator": | |||
return False | |||
expires_on = get_limits().get("expiry") | |||
expires_on = get_limits().expiry | |||
if not expires_on: | |||
return False | |||
@@ -28,18 +37,6 @@ def has_expired(): | |||
return True | |||
def check_if_expired(): | |||
"""check if account is expired. If expired, do not allow login""" | |||
if not has_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 {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 "" | |||
@@ -67,30 +64,147 @@ def get_expiry_message(): | |||
return message | |||
@frappe.whitelist() | |||
def get_usage_info(): | |||
'''Get data to show for Usage Info''' | |||
# imported here to prevent circular import | |||
from frappe.email.queue import get_emails_sent_this_month | |||
limits = get_limits() | |||
if not (limits and any([limits.users, limits.space, limits.emails, limits.expiry])): | |||
# no limits! | |||
return | |||
limits.space = limits.space * 1024.0 # to MB | |||
if not limits.space_usage: | |||
# hack! to show some progress | |||
limits.space_usage = { | |||
'database_size': 26, | |||
'files_size': 1, | |||
'backup_size': 1, | |||
'total': 28 | |||
} | |||
usage_info = frappe._dict({ | |||
'limits': limits, | |||
'enabled_users': get_enabled_system_users(), | |||
'emails_sent': get_emails_sent_this_month(), | |||
'space_usage': limits.space_usage['total'], | |||
}) | |||
if limits.expiry: | |||
usage_info['days_to_expiry'] = (getdate(limits.expiry) - getdate()).days | |||
if limits.upgrade_link: | |||
usage_info['upgrade_link'] = get_upgrade_link(limits.upgrade_link) | |||
return usage_info | |||
def get_upgrade_link(upgrade_link): | |||
parts = urlparse.urlsplit(upgrade_link) | |||
params = dict(urlparse.parse_qsl(parts.query)) | |||
params.update({ | |||
'site': frappe.local.site, | |||
'email': frappe.session.user, | |||
'fullname': get_fullname() | |||
}) | |||
parts.query = urllib.urlencode(params) | |||
url = urlparse.urlunparse(parts) | |||
return url | |||
def get_limits(): | |||
return frappe.get_conf().get("limits") or {} | |||
''' | |||
"limits": { | |||
"users": 1, | |||
"space": 0.5, # in GB | |||
"emails": 1000 # per month | |||
"expiry": "2099-12-31" | |||
} | |||
''' | |||
return frappe._dict(frappe.local.conf.limits or {}) | |||
def update_limits(key, value): | |||
'''Add/Update limit in site_config''' | |||
limits = get_limits() | |||
if isinstance(key, dict): | |||
limits.update(key) | |||
else: | |||
limits[key] = value | |||
@frappe.whitelist() | |||
def get_usage_data(): | |||
update_site_config("limits", limits, validate=False) | |||
frappe.conf.limits = limits | |||
def clear_limit(key): | |||
'''Remove a limit option from site_config''' | |||
limits = get_limits() | |||
day = frappe.utils.add_months(frappe.utils.today(), -1) | |||
limits["emails_sent"] = frappe.db.count("Email Queue", filters={'creation': ['>', day]}) | |||
return limits | |||
if key in limits: | |||
del limits[key] | |||
update_site_config("limits", limits, validate=False) | |||
frappe.conf.limits = limits | |||
def validate_space_limit(file_size): | |||
"""Stop from writing file if max space limit is reached""" | |||
from frappe.utils.file_manager import MaxFileSizeReachedError | |||
limits = get_limits() | |||
if not limits.space: | |||
return | |||
# to MB | |||
space_limit = flt(limits.space * 1024.0, 2) | |||
# in MB | |||
usage = frappe._dict(limits.space_usage or {}) | |||
if not usage: | |||
# first time | |||
usage = frappe._dict(update_space_usage()) | |||
file_size = file_size / (1024.0 ** 2) | |||
if flt(usage.total + file_size, 2) > space_limit: | |||
# Stop from attaching file | |||
frappe.throw(_("You have exceeded the max space of {0} for your plan. {1}.").format( | |||
"<b>{0}MB</b>".format(cint(space_limit)) if (space_limit < 1024) else "<b>{0}GB</b>".format(limits.space), | |||
'<a href="#usage-info">{0}</a>'.format(_("Click here to check your usage or upgrade to a higher plan"))), | |||
MaxFileSizeReachedError) | |||
# update files size in frappe subscription | |||
usage.files_size = flt(usage.files_size) + file_size | |||
update_limits({ 'space_usage': usage }) | |||
def update_space_usage(): | |||
# public and private files | |||
files_size = get_folder_size(frappe.get_site_path("public", "files")) | |||
files_size += get_folder_size(frappe.get_site_path("private", "files")) | |||
backup_size = get_folder_size(frappe.get_site_path("private", "backups")) | |||
database_size = get_database_size() | |||
usage = { | |||
'files_size': files_size, | |||
'backup_size': backup_size, | |||
'database_size': database_size, | |||
'total': flt(flt(files_size) + flt(backup_size) + flt(database_size), 2) | |||
} | |||
update_limits({ 'space_usage': usage }) | |||
def set_limits(limits): | |||
# Add/Update current config options in site_config | |||
frappe_limits = get_limits() or {} | |||
for key in limits.keys(): | |||
frappe_limits[key] = limits[key] | |||
return usage | |||
update_site_config("limits", frappe_limits, validate=False) | |||
def get_folder_size(path): | |||
'''Returns folder size in MB if it exists''' | |||
if os.path.exists(path): | |||
return flt(subprocess.check_output(['du', '-ms', path]).split()[0]) | |||
def get_database_size(): | |||
'''Returns approximate database size in MB''' | |||
db_name = frappe.conf.db_name | |||
def clear_limit(limit): | |||
frappe_limits = get_limits() | |||
if limit in frappe_limits: | |||
del frappe_limits[limit] | |||
# This query will get the database size in MB | |||
db_size = frappe.db.sql(''' | |||
SELECT table_schema "database_name", sum( data_length + index_length ) / 1024 / 1024 "database_size" | |||
FROM information_schema.TABLES WHERE table_schema = %s GROUP BY table_schema''', db_name, as_dict=True) | |||
update_site_config("limits", frappe_limits, validate=False) | |||
return db_size[0].get('database_size') |
@@ -1,18 +0,0 @@ | |||
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); | |||
} | |||
} | |||
}); |
@@ -5,51 +5,56 @@ $(document).on("toolbar_setup", function() { | |||
var help_links = []; | |||
var support_link = "#upgrade"; | |||
var chat_link = "#upgrade"; | |||
frappe_limits = frappe.boot.frappe_limits | |||
var limits = frappe.boot.limits; | |||
if(frappe.boot.expiry_message) { | |||
frappe.msgprint(frappe.boot.expiry_message) | |||
} | |||
if(frappe_limits.support_email || frappe_limits.support_chat) { | |||
if(limits.support_email || limits.support_chat) { | |||
help_links.push('<li class="divider"></li>'); | |||
} | |||
if(frappe_limits.support_email) { | |||
support_link = 'mailto:'+frappe.boot.frappe_limits.support_email; | |||
if(limits.support_email) { | |||
support_link = 'mailto:'+frappe.boot.limits.support_email; | |||
help_links.push('<li><a href="'+support_link+'">' + frappe._('Email Support') + '</a></li>'); | |||
} | |||
if(frappe_limits.support_chat) { | |||
chat_link = frappe_limits.support_chat; | |||
help_links.push('<li><a href="'+chat_link+'" target="_blank">' + frappe._('Chat Support') + '</a></li>'); | |||
} | |||
if(limits.support_chat) { | |||
help_links.push('<li><a href="'+limits.support_chat+'" target="_blank">' + frappe._('Chat Support') + '</a></li>'); | |||
} | |||
$(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('<li><a href="#usage-info">' + frappe._('Usage Info') + '</a></li>'); | |||
help_links.push('<li class="divider"></li>'); | |||
if(limits.space || limits.users || limits.expiry || limits.emails) { | |||
help_links = []; | |||
help_links.push('<li><a href="#usage-info">' + frappe._('Usage Info') + '</a></li>'); | |||
help_links.push('<li class="divider"></li>'); | |||
$(help_links.join("\n")).insertBefore($("#toolbar-user").find("li:first")); | |||
} | |||
$(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 = '<ul class="list-unstyled sidebar-menu">\ | |||
<li class="usage-stats">\ | |||
<a href="#usage-info" class="text-muted">{{ fs.total_used }}MB ({{ fs.total_used_percent }}%) used</a></li>\ | |||
</ul>'; | |||
fs.sidebar_usage_html = frappe.render(template, {fs:fs}, "form_sidebar_usage"); | |||
var limits = frappe.boot.limits; | |||
if (!limits.usage) { limits.usage = {}; } | |||
var usage = limits.usage; | |||
if(!usage.sidebar_usage_html) { | |||
if (limits.space) { | |||
usage.total_used = flt(usage.database_size) + flt(usage.backup_size) + flt(usage.files_size); | |||
usage.total_used_percent = cint(usage.total_used / flt(limits.space * 1024) * 100); | |||
var template = '<ul class="list-unstyled sidebar-menu">\ | |||
<li class="usage-stats">\ | |||
<a href="#usage-info" class="text-muted">{{ usage.total_used }}MB ({{ usage.total_used_percent }}%) used</a></li>\ | |||
</ul>'; | |||
usage.sidebar_usage_html = frappe.render(template, { 'usage': usage }, "form_sidebar_usage"); | |||
} else { | |||
usage.sidebar_usage_html = ''; | |||
} | |||
} | |||
return fs.sidebar_usage_html; | |||
return usage.sidebar_usage_html; | |||
} |
@@ -7,7 +7,7 @@ from frappe.utils.scheduler import (enqueue_applicable_events, restrict_schedule | |||
from frappe import _dict | |||
from frappe.utils.background_jobs import enqueue | |||
from frappe.utils import now_datetime, today, add_days, add_to_date | |||
from frappe.limits import set_limits, clear_limit | |||
from frappe.limits import update_limits, clear_limit | |||
import frappe | |||
import json, time | |||
@@ -56,7 +56,7 @@ class TestScheduler(TestCase): | |||
def test_restrict_scheduler_events(self): | |||
frappe.set_user("Administrator") | |||
user = frappe.get_doc("User", "Administrator") | |||
dormant_date = add_days(today(), -5) | |||
dormant_date = add_days(today(), -5) | |||
user.last_active = dormant_date | |||
user.save() | |||
@@ -69,7 +69,7 @@ class TestScheduler(TestCase): | |||
def test_disable_scheduler_on_expiry(self): | |||
set_limits({'expiry': add_to_date(today(), days=-1)}) | |||
update_limits({'expiry': add_to_date(today(), days=-1)}) | |||
frappe.local.conf = _dict(frappe.get_site_config()) | |||
user = frappe.new_doc('User') | |||
@@ -8,8 +8,6 @@ from frappe import _dict | |||
import frappe.share | |||
from frappe import _ | |||
class MaxUsersReachedError(frappe.ValidationError): pass | |||
class UserPermissions: | |||
""" | |||
A user permission object can be accessed as `frappe.get_user()` | |||
@@ -307,36 +305,3 @@ def get_users(): | |||
}) | |||
return users | |||
def validate_user_limit(doc, method): | |||
""" | |||
This is called using validate hook, because welcome email is sent in on_update. | |||
We don't want welcome email sent if max users are exceeded. | |||
""" | |||
from frappe.limits import get_limits | |||
from frappe.core.doctype.user.user import get_total_users | |||
frappe_limits = get_limits() | |||
if doc.user_type == "Website User": | |||
return | |||
if not doc.enabled: | |||
# don't validate max users when saving a disabled user | |||
return | |||
user_limit = frappe_limits.get("user_limit") if frappe_limits else None | |||
if not user_limit: | |||
return | |||
total_users = get_total_users() | |||
if doc.is_new(): | |||
# get_total_users gets existing users in database | |||
# a new record isn't inserted yet, so adding 1 | |||
total_users += 1 | |||
if total_users > user_limit: | |||
frappe.throw(_("Sorry. You have reached the maximum user limit for your subscription. You can either disable an existing user or buy a higher subscription plan."), | |||
MaxUsersReachedError) |
@@ -56,7 +56,7 @@ def render(path, http_status_code=None): | |||
except frappe.Redirect, e: | |||
return build_response(path, "", 301, { | |||
"Location": frappe.flags.redirect_location, | |||
"Location": frappe.flags.redirect_location or (frappe.local.response or {}).get('location'), | |||
"Cache-Control": "no-store, no-cache, must-revalidate" | |||
}) | |||