Merge branch 'version-12-hotfix' into version-12

This commit is contained in:
Sahil Khan 2019-08-02 14:26:27 +05:30
commit 0de729ff3d
21 changed files with 112 additions and 36 deletions

View file

@ -23,7 +23,7 @@ if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
__version__ = '12.0.3'
__version__ = '12.0.4'
__title__ = "Frappe Framework"
local = Local()

View file

@ -115,9 +115,12 @@ def set_maintenance_mode(context, state, site=None):
@click.command('doctor') #Passing context always gets a site and if there is no use site it breaks
@click.option('--site', help='site name')
def doctor(site=None):
@pass_context
def doctor(context, site=None):
"Get diagnostic info about background workers"
from frappe.utils.doctor import doctor as _doctor
if not site:
site = get_site(context)
return _doctor(site=site)
@click.command('show-pending-jobs')

View file

@ -13,6 +13,12 @@ frappe.pages['background_jobs'].on_page_load = function(wrapper) {
frappe.pages['background_jobs'].on_page_show = function(wrapper) {
frappe.pages.background_jobs.refresh_jobs();
frappe.call({
method: 'frappe.core.page.background_jobs.background_jobs.get_scheduler_status',
callback: function(r) {
frappe.pages.background_jobs.page.set_indicator(...r.message);
}
});
}
frappe.pages.background_jobs.refresh_jobs = function() {

View file

@ -7,6 +7,8 @@ import frappe
from rq import Queue, Worker
from frappe.utils.background_jobs import get_redis_conn
from frappe.utils import format_datetime, cint
from frappe.utils.scheduler import is_scheduler_inactive
from frappe import _
colors = {
'queued': 'orange',
@ -49,3 +51,9 @@ def get_info(show_failed=False):
for j in q.get_jobs()[:10]: add_job(j, q.name)
return jobs
@frappe.whitelist()
def get_scheduler_status():
if is_scheduler_inactive():
return [_("Inactive"), "red"]
return [_("Active"), "green"]

View file

@ -336,7 +336,7 @@ def get_desktop_settings():
if category in user_saved_modules_by_category:
user_modules = user_saved_modules_by_category[category]
user_modules_by_category[category] = [apply_user_saved_links(modules_by_name[m]) \
for m in user_modules]
for m in user_modules if modules_by_name.get(m)]
else:
user_modules_by_category[category] = [apply_user_saved_links(m) \
for m in all_modules if m.get('category') == category]

View file

@ -68,6 +68,7 @@ def get_form_params():
# queries must always be server side
data.query = None
data.strict = None
return data

View file

@ -153,7 +153,8 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0,
order_by=order_by,
ignore_permissions=ignore_permissions,
reference_doctype=reference_doctype,
as_list=not as_dict)
as_list=not as_dict,
strict=False)
if doctype in UNTRANSLATED_DOCTYPES:
values = tuple([v for v in list(values) if re.search(txt+".*", (_(v.name) if as_dict else _(v[0])), re.IGNORECASE)])

View file

@ -36,7 +36,7 @@ class DatabaseQuery(object):
ignore_permissions=False, user=None, with_comment_count=False,
join='left join', distinct=False, start=None, page_length=None, limit=None,
ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False,
update=None, add_total_row=None, user_settings=None, reference_doctype=None, return_query=False):
update=None, add_total_row=None, user_settings=None, reference_doctype=None, return_query=False, strict=True):
if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user):
frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(self.doctype))
raise frappe.PermissionError(self.doctype)
@ -80,6 +80,7 @@ class DatabaseQuery(object):
self.update = update
self.user_settings_fields = copy.deepcopy(self.fields)
self.return_query = return_query
self.strict = strict
# for contextual user permission check
# to determine which user permission is applicable on link field of specific doctype
@ -115,8 +116,12 @@ class DatabaseQuery(object):
args.fields = 'distinct ' + args.fields
args.order_by = '' # TODO: recheck for alternative
query = """select %(fields)s from %(tables)s %(conditions)s
%(group_by)s %(order_by)s %(limit)s""" % args
query = """select %(fields)s
from %(tables)s
%(conditions)s
%(group_by)s
%(order_by)s
%(limit)s""" % args
if self.return_query:
return query
@ -240,6 +245,12 @@ class DatabaseQuery(object):
_is_query(field)
if self.strict:
if re.compile(r".*/\*.*").match(field):
frappe.throw(_('Illegal SQL Query'))
if re.compile(r".*\s(union).*\s").match(field.lower()):
frappe.throw(_('Illegal SQL Query'))
def extract_tables(self):
"""extract tables from fields"""
@ -688,6 +699,8 @@ class DatabaseQuery(object):
if 'select' in _lower and ' from ' in _lower:
frappe.throw(_('Cannot use sub-query in order by'))
if re.compile(r".*[^a-z0-9-_ ,`'\"\.\(\)].*").match(_lower):
frappe.throw(_('Illegal SQL Query'))
for field in parameters.split(","):
if "." in field and field.strip().startswith("`tab"):
@ -755,6 +768,7 @@ def get_list(doctype, *args, **kwargs):
kwargs.pop('cmd', None)
kwargs.pop('ignore_permissions', None)
kwargs.pop('data', None)
kwargs.pop('strict', None)
# If doctype is child table
if frappe.is_table(doctype):

View file

@ -7,7 +7,7 @@ def execute():
filters={'is_folder': 0})
for file in files:
file_url = file.file_url
file_url = file.file_url or ""
if file.is_private:
if not file_url.startswith('/private/files/'):
generate_file(file.name)
@ -32,4 +32,4 @@ def generate_file(file_name):
except IOError:
pass
except Exception as e:
print(e)
print(e)

View file

@ -30,7 +30,9 @@ export default {
}
},
mounted() {
this.setup_sortable();
if (!frappe.utils.is_mobile()) {
this.setup_sortable();
}
},
methods: {
setup_sortable() {

View file

@ -354,7 +354,7 @@ frappe.views.FileView.grid_view = frappe.get_user_settings('File').grid_view ||
function redirect_to_home_if_invalid_route() {
const route = frappe.get_route();
if (route[2] !== 'Home') {
if (route[2] === 'List') {
// if the user somehow redirects to List/File/List
// redirect back to Home
frappe.set_route('List', 'File', 'Home');

View file

@ -98,6 +98,9 @@ frappe.views.Page = Class.extend({
this.wrapper.innerHTML = this.pagedoc.content;
frappe.dom.eval(this.pagedoc.__script || this.pagedoc.script || '');
frappe.dom.set_style(this.pagedoc.style || '');
// set breadcrumbs
frappe.breadcrumbs.add(this.pagedoc.module || null);
}
this.trigger_page_event('on_page_load');

View file

@ -714,9 +714,10 @@ def get_url(uri=None, full_address=False):
return uri
if not host_name:
if hasattr(frappe.local, "request") and frappe.local.request and frappe.local.request.host:
protocol = 'https://' if 'https' == frappe.get_request_header('X-Forwarded-Proto', "") else 'http://'
host_name = protocol + frappe.local.request.host
request_host_name = get_host_name_from_request()
if request_host_name:
host_name = request_host_name
elif frappe.local.site:
protocol = 'http://'
@ -753,6 +754,11 @@ def get_url(uri=None, full_address=False):
return url
def get_host_name_from_request():
if hasattr(frappe.local, "request") and frappe.local.request and frappe.local.request.host:
protocol = 'https://' if 'https' == frappe.get_request_header('X-Forwarded-Proto', "") else 'http://'
return protocol + frappe.local.request.host
def url_contains_port(url):
parts = url.split(':')
return len(parts) > 2

View file

@ -3,7 +3,7 @@ import frappe.utils
from collections import defaultdict
from rq import Worker, Connection
from frappe.utils.background_jobs import get_redis_conn, get_queue, get_queue_list
from frappe.utils.scheduler import is_scheduler_disabled
from frappe.utils.scheduler import is_scheduler_disabled, is_scheduler_inactive
from six import iteritems
@ -107,8 +107,19 @@ def doctor(site=None):
for s in sites:
frappe.init(s)
frappe.connect()
if is_scheduler_disabled():
print("Scheduler disabled for", s)
if frappe.local.conf.maintenance_mode:
print("Maintenance mode on for", s)
if frappe.local.conf.pause_scheduler:
print("Scheduler paused for", s)
if is_scheduler_inactive():
print("Scheduler inactive for", s)
frappe.destroy()
# TODO improve this

View file

@ -58,7 +58,7 @@ def sanitize_html(html, linkify=False):
return html
tags = (acceptable_elements + svg_elements + mathml_elements
+ ["html", "head", "meta", "link", "body", "iframe", "style", "o:p"])
+ ["html", "head", "meta", "link", "body", "style", "o:p"])
attributes = {"*": acceptable_attributes, 'svg': svg_attributes}
styles = bleach_whitelist.all_styles
strip_comments = False

View file

@ -6,10 +6,11 @@ def get_jenv():
import frappe
if not getattr(frappe.local, 'jenv', None):
from jinja2 import Environment, DebugUndefined
from jinja2 import DebugUndefined
from jinja2.sandbox import SandboxedEnvironment
# frappe will be loaded last, so app templates will get precedence
jenv = Environment(loader = get_jloader(),
jenv = SandboxedEnvironment(loader = get_jloader(),
undefined=DebugUndefined)
set_filters(jenv)

View file

@ -79,14 +79,8 @@ def enqueue_events_for_site(site, queued_jobs):
try:
frappe.init(site=site)
if frappe.local.conf.maintenance_mode:
return
if frappe.local.conf.pause_scheduler:
return
frappe.connect()
if is_scheduler_disabled():
if is_scheduler_inactive():
return
enqueue_events(site=site, queued_jobs=queued_jobs)
@ -226,6 +220,18 @@ def get_enabled_scheduler_events():
return ["all", "hourly", "hourly_long", "daily", "daily_long",
"weekly", "weekly_long", "monthly", "monthly_long", "cron"]
def is_scheduler_inactive():
if frappe.local.conf.maintenance_mode:
return True
if frappe.local.conf.pause_scheduler:
return True
if is_scheduler_disabled():
return True
return False
def is_scheduler_disabled():
if frappe.conf.disable_scheduler:
return True

View file

@ -12,7 +12,9 @@ from frappe.utils.verified_command import get_signed_params
class PersonalDataDownloadRequest(Document):
def after_insert(self):
personal_data = get_user_data(self.user)
self.generate_file_and_send_mail(personal_data)
frappe.enqueue_doc(self.doctype, self.name, 'generate_file_and_send_mail',
queue='short', personal_data=personal_data, now=frappe.flags.in_test)
def generate_file_and_send_mail(self, personal_data):
"""generate the file link for download"""

View file

@ -21,18 +21,25 @@ class TestRequestPersonalData(unittest.TestCase):
def test_file_and_email_creation(self):
frappe.set_user('test_privacy@example.com')
download_request = frappe.get_doc({"doctype": 'Personal Data Download Request', 'user': 'test_privacy@example.com'})
download_request = frappe.get_doc({
"doctype": 'Personal Data Download Request',
'user': 'test_privacy@example.com'
})
download_request.save(ignore_permissions=True)
frappe.set_user('Administrator')
f = frappe.get_all('File',
{'attached_to_doctype':'Personal Data Download Request', 'attached_to_name': download_request.name},
['*'])
self.assertEqual(len(f), 1)
file_count = frappe.db.count('File', {
'attached_to_doctype':'Personal Data Download Request',
'attached_to_name': download_request.name
})
email_queue = frappe.db.sql("""SELECT *
FROM `tabEmail Queue`
ORDER BY `creation` DESC""", as_dict=True)
self.assertEqual(file_count, 1)
email_queue = frappe.get_all('Email Queue',
fields=['message'],
order_by="creation DESC",
limit=1)
self.assertTrue("Subject: Download Your Data" in email_queue[0].message)
frappe.db.sql("delete from `tabEmail Queue`")

View file

@ -87,7 +87,7 @@
<span class="indicator blue" data-text="{{ _("Forgot Password") }}"></span></div>
<input type="email" id="forgot_email"
class="form-control" placeholder="{{ _('Email address') }}" required autofocus>
<button class="btn btn-sm btn-primary btn-block btn-forgot" type="submit">{{ _("Send Password") }}</button>
<button class="btn btn-sm btn-primary btn-block btn-forgot" type="submit">{{ _("Reset Password") }}</button>
</form>
</div>
<div class='form-footer'>

View file

@ -15,7 +15,12 @@ base_template_path = "templates/www/sitemap.xml"
def get_context(context):
"""generate the sitemap XML"""
host = get_request_site_address()
# the site might be accessible from multiple host_names
# for e.g gadgets.erpnext.com and gadgetsinternational.com
# so it should be picked from the request
host = frappe.utils.get_host_name_from_request()
links = []
for route, page in iteritems(get_pages()):
if page.sitemap: