Sfoglia il codice sorgente

Merge branch 'hotfix'

version-14
Nabin Hait 7 anni fa
parent
commit
cc09d3f97b
6 ha cambiato i file con 111 aggiunte e 25 eliminazioni
  1. +1
    -1
      frappe/__init__.py
  2. +2
    -0
      frappe/core/doctype/user/user.py
  3. +33
    -0
      frappe/public/js/frappe/misc/common.js
  4. +4
    -2
      frappe/templates/includes/login/login.js
  5. +70
    -22
      frappe/utils/global_search.py
  6. +1
    -0
      frappe/website/js/website.js

+ 1
- 1
frappe/__init__.py Vedi File

@@ -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__ = '9.2.23'
__version__ = '9.2.24'
__title__ = "Frappe Framework" __title__ = "Frappe Framework"


local = Local() local = Local()


+ 2
- 0
frappe/core/doctype/user/user.py Vedi File

@@ -769,6 +769,8 @@ def reset_password(user):


try: try:
user = frappe.get_doc("User", user) user = frappe.get_doc("User", user)
if not user.enabled:
return 'disabled'
user.validate_reset_password() user.validate_reset_password()
user.reset_password(send_email=True) user.reset_password(send_email=True)




+ 33
- 0
frappe/public/js/frappe/misc/common.js Vedi File

@@ -255,3 +255,36 @@ frappe.is_mobile = function() {
return $(document).width() < 768; return $(document).width() < 768;
} }


frappe.utils.xss_sanitise = function (string, options) {
// Reference - https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
let sanitised = string; // un-sanitised string.
const DEFAULT_OPTIONS = {
strategies: ['html', 'js'] // use all strategies.
}
const HTML_ESCAPE_MAP = {
'&': '&amp',
'<': '&lt',
'>': '&gt',
'"': '&quot',
"'": '&#x27',
'/': '&#x2F'
};
const REGEX_SCRIPT = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi; // used in jQuery 1.7.2 src/ajax.js Line 14
options = Object.assign({ }, DEFAULT_OPTIONS, options); // don't deep copy, immutable beauty.
// Rule 1
if ( options.strategies.includes('html') ) {
// By far, the best thing that has ever happened to JS - Object.keys
Object.keys(HTML_ESCAPE_MAP).map((char, escape) => {
const regex = new RegExp(char, "g");
sanitised = sanitised.replace(regex, escape);
});
}
// Rule 3 - TODO: Check event handlers?
if ( options.strategies.includes('js') ) {
sanitised = sanitised.replace(REGEX_SCRIPT, "");
}

return sanitised;
}

+ 4
- 2
frappe/templates/includes/login/login.js Vedi File

@@ -17,8 +17,8 @@ login.bind_events = function() {
event.preventDefault(); event.preventDefault();
var args = {}; var args = {};
args.cmd = "login"; args.cmd = "login";
args.usr = ($("#login_email").val() || "").trim();
args.pwd = $("#login_password").val();
args.usr = frappe.utils.xss_sanitise(($("#login_email").val() || "").trim());
args.pwd = frappe.utils.xss_sanitise($("#login_password").val());
args.device = "desktop"; args.device = "desktop";
if(!args.usr || !args.pwd) { if(!args.usr || !args.pwd) {
frappe.msgprint("{{ _("Both login and password required") }}"); frappe.msgprint("{{ _("Both login and password required") }}");
@@ -184,6 +184,8 @@ login.login_handlers = (function() {
login.set_indicator("{{ _("Not a valid user") }}", 'red'); login.set_indicator("{{ _("Not a valid user") }}", 'red');
} else if (data.message=='not allowed') { } else if (data.message=='not allowed') {
login.set_indicator("{{ _("Not Allowed") }}", 'red'); login.set_indicator("{{ _("Not Allowed") }}", 'red');
} else if (data.message=='disabled') {
login.set_indicator("{{ _("Not Allowed: Disabled User") }}", 'red');
} else { } else {
login.set_indicator("{{ _("Instructions Emailed") }}", 'green'); login.set_indicator("{{ _("Instructions Emailed") }}", 'green');
} }


+ 70
- 22
frappe/utils/global_search.py Vedi File

@@ -10,8 +10,12 @@ from frappe.utils import cint, strip_html_tags
from frappe.model.base_document import get_controller from frappe.model.base_document import get_controller
from six import text_type from six import text_type



def setup_global_search_table(): def setup_global_search_table():
'''Creates __global_seach table'''
"""
Creates __global_seach table
:return:
"""
if not '__global_search' in frappe.db.get_tables(): if not '__global_search' in frappe.db.get_tables():
frappe.db.sql('''create table __global_search( frappe.db.sql('''create table __global_search(
doctype varchar(100), doctype varchar(100),
@@ -26,12 +30,21 @@ def setup_global_search_table():
ENGINE=MyISAM ENGINE=MyISAM
CHARACTER SET=utf8mb4''') CHARACTER SET=utf8mb4''')



def reset(): def reset():
'''Deletes all data in __global_search'''
"""
Deletes all data in __global_search
:return:
"""
frappe.db.sql('delete from __global_search') frappe.db.sql('delete from __global_search')



def get_doctypes_with_global_search(with_child_tables=True): def get_doctypes_with_global_search(with_child_tables=True):
'''Return doctypes with global search fields'''
"""
Return doctypes with global search fields
:param with_child_tables:
:return:
"""
def _get(): def _get():
global_search_doctypes = [] global_search_doctypes = []
filters = {} filters = {}
@@ -43,17 +56,25 @@ def get_doctypes_with_global_search(with_child_tables=True):
global_search_doctypes.append(d) global_search_doctypes.append(d)


installed_apps = frappe.get_installed_apps() installed_apps = frappe.get_installed_apps()
module_app = frappe.local.module_app

doctypes = [
d.name for d in global_search_doctypes
if module_app.get(frappe.scrub(d.module))
and module_app[frappe.scrub(d.module)] in installed_apps
]


doctypes = [d.name for d in global_search_doctypes
if frappe.local.module_app[frappe.scrub(d.module)] in installed_apps]
return doctypes return doctypes


return frappe.cache().get_value('doctypes_with_global_search', _get) return frappe.cache().get_value('doctypes_with_global_search', _get)



def rebuild_for_doctype(doctype): def rebuild_for_doctype(doctype):
'''Rebuild entries of doctype's documents in __global_search on change of
searchable fields
:param doctype: Doctype '''
"""
Rebuild entries of doctype's documents in __global_search on change of
searchable fields
:param doctype: Doctype
"""


def _get_filters(): def _get_filters():
filters = frappe._dict({ "docstatus": ["!=", 2] }) filters = frappe._dict({ "docstatus": ["!=", 2] })
@@ -127,6 +148,7 @@ def rebuild_for_doctype(doctype):
if all_contents: if all_contents:
insert_values_for_multiple_docs(all_contents) insert_values_for_multiple_docs(all_contents)



def delete_global_search_records_for_doctype(doctype): def delete_global_search_records_for_doctype(doctype):
frappe.db.sql(''' frappe.db.sql('''
delete delete
@@ -134,6 +156,7 @@ def delete_global_search_records_for_doctype(doctype):
where where
doctype = %s''', doctype, as_dict=True) doctype = %s''', doctype, as_dict=True)



def get_selected_fields(meta, global_search_fields): def get_selected_fields(meta, global_search_fields):
fieldnames = [df.fieldname for df in global_search_fields] fieldnames = [df.fieldname for df in global_search_fields]
if meta.istable==1: if meta.istable==1:
@@ -146,6 +169,7 @@ def get_selected_fields(meta, global_search_fields):


return fieldnames return fieldnames



def get_children_data(doctype, meta): def get_children_data(doctype, meta):
""" """
Get all records from all the child tables of a doctype Get all records from all the child tables of a doctype
@@ -182,6 +206,7 @@ def get_children_data(doctype, meta):


return all_children, child_search_fields return all_children, child_search_fields



def insert_values_for_multiple_docs(all_contents): def insert_values_for_multiple_docs(all_contents):
values = [] values = []
for content in all_contents: for content in all_contents:
@@ -201,9 +226,11 @@ def insert_values_for_multiple_docs(all_contents):




def update_global_search(doc): def update_global_search(doc):
'''Add values marked with `in_global_search` to
`frappe.flags.update_global_search` from given doc
:param doc: Document to be added to global search'''
"""
Add values marked with `in_global_search` to
`frappe.flags.update_global_search` from given doc
:param doc: Document to be added to global search
"""


if doc.docstatus > 1 or (doc.meta.has_field("enabled") and not doc.get("enabled")) \ if doc.docstatus > 1 or (doc.meta.has_field("enabled") and not doc.get("enabled")) \
or doc.get("disabled"): or doc.get("disabled"):
@@ -235,6 +262,7 @@ def update_global_search(doc):
published=published, title=doc.get_title(), route=doc.get('route'))) published=published, title=doc.get_title(), route=doc.get('route')))
enqueue_global_search() enqueue_global_search()



def enqueue_global_search(): def enqueue_global_search():
if frappe.flags.update_global_search: if frappe.flags.update_global_search:
try: try:
@@ -246,21 +274,32 @@ def enqueue_global_search():


frappe.flags.update_global_search = [] frappe.flags.update_global_search = []



def get_formatted_value(value, field): def get_formatted_value(value, field):
'''Prepare field from raw data'''
"""
Prepare field from raw data
:param value:
:param field:
:return:
"""


from six.moves.html_parser import HTMLParser from six.moves.html_parser import HTMLParser


if(getattr(field, 'fieldtype', None) in ["Text", "Text Editor"]):
if getattr(field, 'fieldtype', None) in ["Text", "Text Editor"]:
h = HTMLParser() h = HTMLParser()
value = h.unescape(value) value = h.unescape(value)
value = (re.subn(r'<[\s]*(script|style).*?</\1>(?s)', '', text_type(value))[0]) value = (re.subn(r'<[\s]*(script|style).*?</\1>(?s)', '', text_type(value))[0])
value = ' '.join(value.split()) value = ' '.join(value.split())
return field.label + " : " + strip_html_tags(text_type(value)) return field.label + " : " + strip_html_tags(text_type(value))



def sync_global_search(flags=None): def sync_global_search(flags=None):
'''Add values from `flags` (frappe.flags.update_global_search) to __global_search.
This is called internally at the end of the request.'''
"""
Add values from `flags` (frappe.flags.update_global_search) to __global_search.
This is called internally at the end of the request.
:param flags:
:return:
"""


if not flags: if not flags:
flags = frappe.flags.update_global_search flags = frappe.flags.update_global_search
@@ -278,10 +317,13 @@ def sync_global_search(flags=None):


frappe.flags.update_global_search = [] frappe.flags.update_global_search = []



def delete_for_document(doc): def delete_for_document(doc):
'''Delete the __global_search entry of a document that has
been deleted
:param doc: Deleted document'''
"""
Delete the __global_search entry of a document that has
been deleted
:param doc: Deleted document
"""


frappe.db.sql(''' frappe.db.sql('''
delete delete
@@ -290,13 +332,16 @@ def delete_for_document(doc):
doctype = %s and doctype = %s and
name = %s''', (doc.doctype, doc.name), as_dict=True) name = %s''', (doc.doctype, doc.name), as_dict=True)



@frappe.whitelist() @frappe.whitelist()
def search(text, start=0, limit=20, doctype=""): def search(text, start=0, limit=20, doctype=""):
'''Search for given text in __global_search
"""
Search for given text in __global_search
:param text: phrase to be searched :param text: phrase to be searched
:param start: start results at, default 0 :param start: start results at, default 0
:param limit: number of results to return, default 20 :param limit: number of results to return, default 20
:return: Array of result objects'''
:return: Array of result objects
"""


text = "+" + text + "*" text = "+" + text + "*"
if not doctype: if not doctype:
@@ -328,13 +373,16 @@ def search(text, start=0, limit=20, doctype=""):


return results return results



@frappe.whitelist(allow_guest=True) @frappe.whitelist(allow_guest=True)
def web_search(text, start=0, limit=20): def web_search(text, start=0, limit=20):
'''Search for given text in __global_search where published = 1
"""
Search for given text in __global_search where published = 1
:param text: phrase to be searched :param text: phrase to be searched
:param start: start results at, default 0 :param start: start results at, default 0
:param limit: number of results to return, default 20 :param limit: number of results to return, default 20
:return: Array of result objects'''
:return: Array of result objects
"""


text = "+" + text + "*" text = "+" + text + "*"
results = frappe.db.sql(''' results = frappe.db.sql('''


+ 1
- 0
frappe/website/js/website.js Vedi File

@@ -185,6 +185,7 @@ $.extend(frappe, {
if($.isArray(html)) { if($.isArray(html)) {
html = html.join("<hr>") html = html.join("<hr>")
} }

return frappe.get_modal(title || "Message", html).modal("show"); return frappe.get_modal(title || "Message", html).modal("show");
}, },
send_message: function(opts, btn) { send_message: function(opts, btn) {


Caricamento…
Annulla
Salva