Merge branch 'develop' into dib-fixes-2020-07-22
This commit is contained in:
commit
48634e131a
48 changed files with 288 additions and 251 deletions
|
|
@ -231,8 +231,7 @@ def get_site_config(sites_path=None, site_path=None):
|
|||
if os.path.exists(site_config):
|
||||
config.update(get_file_json(site_config))
|
||||
elif local.site and not local.flags.new_site:
|
||||
print("Site {0} does not exist".format(local.site))
|
||||
sys.exit(1)
|
||||
raise IncorrectSitePath("{0} does not exist".format(local.site))
|
||||
|
||||
return _dict(config)
|
||||
|
||||
|
|
@ -436,12 +435,8 @@ def get_roles(username=None):
|
|||
"""Returns roles of current user."""
|
||||
if not local.session:
|
||||
return ["Guest"]
|
||||
|
||||
if username:
|
||||
import frappe.permissions
|
||||
return frappe.permissions.get_roles(username)
|
||||
else:
|
||||
return get_user().get_roles()
|
||||
import frappe.permissions
|
||||
return frappe.permissions.get_roles(username or local.session.user)
|
||||
|
||||
def get_request_header(key, default=None):
|
||||
"""Return HTTP request header.
|
||||
|
|
@ -1559,10 +1554,10 @@ def get_doctype_app(doctype):
|
|||
|
||||
loggers = {}
|
||||
log_level = None
|
||||
def logger(module=None, with_more_info=False):
|
||||
def logger(module=None, with_more_info=False, allow_site=True, filter=None, max_size=100_000, file_count=20):
|
||||
'''Returns a python logger that uses StreamHandler'''
|
||||
from frappe.utils.logger import get_logger
|
||||
return get_logger(module=module, with_more_info=with_more_info)
|
||||
return get_logger(module=module, with_more_info=with_more_info, allow_site=allow_site, filter=filter, max_size=max_size, file_count=file_count)
|
||||
|
||||
def log_error(message=None, title=_("Error")):
|
||||
'''Log error to Error Log'''
|
||||
|
|
@ -1707,3 +1702,7 @@ def mock(type, size=1, locale='en'):
|
|||
|
||||
from frappe.chat.util import squashify
|
||||
return squashify(results)
|
||||
|
||||
def validate_and_sanitize_search_inputs(fn):
|
||||
from frappe.desk.search import validate_and_sanitize_search_inputs as func
|
||||
return func(fn)
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ def application(request):
|
|||
frappe.monitor.stop(response)
|
||||
frappe.recorder.dump()
|
||||
|
||||
frappe.logger("frappe.web").info({
|
||||
frappe.logger("frappe.web", allow_site=frappe.local.site).info({
|
||||
"site": get_site_name(request.host),
|
||||
"remote_addr": getattr(request, "remote_addr", "NOTFOUND"),
|
||||
"base_url": getattr(request, "base_url", "NOTFOUND"),
|
||||
|
|
@ -256,9 +256,11 @@ def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=No
|
|||
'SERVER_NAME': 'localhost:8000'
|
||||
}
|
||||
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.propagate = False
|
||||
|
||||
in_test_env = os.environ.get('CI')
|
||||
if in_test_env:
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.setLevel(logging.ERROR)
|
||||
|
||||
run_simple('0.0.0.0', int(port), application,
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ class CookieManager:
|
|||
self.set_cookie("country", frappe.session.session_country)
|
||||
|
||||
def set_cookie(self, key, value, expires=None, secure=False, httponly=False, samesite="Lax"):
|
||||
if not secure:
|
||||
if not secure and hasattr(frappe.local, 'request'):
|
||||
secure = frappe.local.request.scheme == "https"
|
||||
self.cookies[key] = {
|
||||
"value": value,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{
|
||||
"hidden": 0,
|
||||
"label": "Tools",
|
||||
"links": "[\n {\n \"description\": \"Documents assigned to you and by you.\",\n \"label\": \"To Do\",\n \"name\": \"ToDo\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Event and other calendars.\",\n \"label\": \"Calendar\",\n \"link\": \"List/Event/Calendar\",\n \"name\": \"Event\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Private and public Notes.\",\n \"label\": \"Note\",\n \"name\": \"Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Files\",\n \"name\": \"File\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Video\",\n \"name\": \"Video\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Activity log of all users.\",\n \"label\": \"Activity\",\n \"name\": \"activity\",\n \"type\": \"page\"\n }\n]"
|
||||
"links": "[\n {\n \"description\": \"Documents assigned to you and by you.\",\n \"label\": \"To Do\",\n \"name\": \"ToDo\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Event and other calendars.\",\n \"label\": \"Calendar\",\n \"link\": \"List/Event/Calendar\",\n \"name\": \"Event\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Private and public Notes.\",\n \"label\": \"Note\",\n \"name\": \"Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Files\",\n \"name\": \"File\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Activity log of all users.\",\n \"label\": \"Activity\",\n \"name\": \"activity\",\n \"type\": \"page\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
|
|
@ -29,10 +29,11 @@
|
|||
"docstatus": 0,
|
||||
"doctype": "Desk Page",
|
||||
"extends_another_page": 0,
|
||||
"hide_custom": 0,
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Tools",
|
||||
"modified": "2020-04-20 18:21:14.152537",
|
||||
"modified": "2020-07-21 19:32:18.480700",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Automation",
|
||||
"name": "Tools",
|
||||
|
|
|
|||
|
|
@ -374,6 +374,7 @@ def make_auto_repeat(doctype, docname, frequency = 'Daily', start_date = None, e
|
|||
|
||||
# method for reference_doctype filter
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_auto_repeat_doctypes(doctype, txt, searchfield, start, page_len, filters):
|
||||
res = frappe.db.get_all('Property Setter', {
|
||||
'property': 'allow_auto_repeat',
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ def delete_contact_and_address(doctype, docname):
|
|||
doc.delete()
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def filter_dynamic_link_doctypes(doctype, txt, searchfield, start, page_len, filters):
|
||||
if not txt: txt = ""
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ def get_company_address(company):
|
|||
return ret
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def address_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
from frappe.desk.reportview import get_match_cond
|
||||
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ def update_contact(doc, method):
|
|||
contact.save(ignore_permissions=True)
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def contact_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
from frappe.desk.reportview import get_match_cond
|
||||
|
||||
|
|
|
|||
|
|
@ -960,6 +960,9 @@ class Column:
|
|||
if not self.df:
|
||||
return
|
||||
|
||||
if self.skip_import:
|
||||
return
|
||||
|
||||
if self.df.fieldtype == "Link":
|
||||
# find all values that dont exist
|
||||
values = list(set([cstr(v) for v in self.column_values[1:] if v]))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
frappe.ui.form.on('Report', {
|
||||
refresh: function(frm) {
|
||||
if (frm.doc.is_standard && !frappe.boot.developer_mode) {
|
||||
if (frm.doc.is_standard === "Yes" && !frappe.boot.developer_mode) {
|
||||
// make the document read-only
|
||||
frm.set_read_only();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -812,6 +812,7 @@ def reset_password(user):
|
|||
return 'not found'
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def user_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
from frappe.desk.reportview import get_match_cond
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,8 @@ def user_permission_exists(user, allow, for_value, applicable_for=None):
|
|||
|
||||
return has_same_user_permission
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_applicable_for_doctype_list(doctype, txt, searchfield, start, page_len, filters):
|
||||
linked_doctypes_map = get_linked_doctypes(doctype, True)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestVideo(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Video', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:title",
|
||||
"creation": "2018-10-17 05:47:13.087395",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"provider",
|
||||
"url",
|
||||
"column_break_4",
|
||||
"publish_date",
|
||||
"duration",
|
||||
"section_break_7",
|
||||
"description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Title",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "provider",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Provider",
|
||||
"options": "YouTube\nVimeo",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "url",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "URL",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "publish_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Publish Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "duration",
|
||||
"fieldtype": "Data",
|
||||
"label": "Duration"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-04-22 12:09:49.057403",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Video",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Video(Document):
|
||||
pass
|
||||
|
|
@ -41,6 +41,8 @@ def get_columns_and_fields(doctype):
|
|||
|
||||
return columns, fields
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def query_doctypes(doctype, txt, searchfield, start, page_len, filters):
|
||||
user = filters.get("user")
|
||||
user_perms = frappe.utils.user.UserPermissions(user)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ def create_plan():
|
|||
'connector_name': 'Local Connector',
|
||||
'connector_type': 'Frappe',
|
||||
# connect to same host.
|
||||
'hostname': frappe.conf.host_name,
|
||||
'hostname': frappe.conf.host_name or frappe.utils.get_site_url(frappe.local.site),
|
||||
'username': 'Administrator',
|
||||
'password': 'admin'
|
||||
'password': frappe.conf.get("admin_password") or 'admin'
|
||||
}).insert(ignore_if_duplicate=True)
|
||||
|
|
|
|||
|
|
@ -221,6 +221,8 @@ class Workspace:
|
|||
incomplete_dependencies = [d for d in item.dependencies if not _doctype_contains_a_record(d)]
|
||||
if len(incomplete_dependencies):
|
||||
item.incomplete_dependencies = incomplete_dependencies
|
||||
else:
|
||||
item.incomplete_dependencies = ""
|
||||
|
||||
if item.onboard:
|
||||
# Mark Spotlights for initial
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
frm.chart_filters = null;
|
||||
|
||||
if (!frappe.boot.developer_mode && frm.doc.is_standard) {
|
||||
frm.set_df_property('chart_options_section', 'hidden', 1);
|
||||
frm.disable_form();
|
||||
}
|
||||
|
||||
|
|
@ -57,11 +56,6 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
if (frm.doc.report_name) {
|
||||
frm.trigger('set_chart_report_filters');
|
||||
}
|
||||
|
||||
if (!frappe.boot.developer_mode) {
|
||||
frm.set_df_property("custom_options", "hidden", 1);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
is_standard: function(frm) {
|
||||
|
|
|
|||
|
|
@ -28,15 +28,28 @@ def get_permission_query_conditions(user):
|
|||
if "System Manager" in roles:
|
||||
return None
|
||||
|
||||
allowed_doctypes = ['"%s"' % doctype for doctype in frappe.permissions.get_doctypes_with_read()]
|
||||
allowed_reports = ['"%s"' % key if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()]
|
||||
doctype_condition = False
|
||||
report_condition = False
|
||||
|
||||
allowed_doctypes = [frappe.db.escape(doctype) for doctype in frappe.permissions.get_doctypes_with_read()]
|
||||
allowed_reports = [frappe.db.escape(key) if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()]
|
||||
|
||||
if allowed_doctypes:
|
||||
doctype_condition = '`tabDashboard Chart`.`document_type` in ({allowed_doctypes})'.format(
|
||||
allowed_doctypes=','.join(allowed_doctypes))
|
||||
if allowed_reports:
|
||||
report_condition = '`tabDashboard Chart`.`report_name` in ({allowed_reports})'.format(
|
||||
allowed_reports=','.join(allowed_reports))
|
||||
|
||||
return '''
|
||||
`tabDashboard Chart`.`document_type` in ({allowed_doctypes})
|
||||
or `tabDashboard Chart`.`report_name` in ({allowed_reports})
|
||||
(`tabDashboard Chart`.`chart_type` in ('Count', 'Sum', 'Average')
|
||||
and {doctype_condition})
|
||||
or
|
||||
(`tabDashboard Chart`.`chart_type` = 'Report'
|
||||
and {report_condition})
|
||||
'''.format(
|
||||
allowed_doctypes=','.join(allowed_doctypes),
|
||||
allowed_reports=','.join(allowed_reports)
|
||||
doctype_condition=doctype_condition,
|
||||
report_condition=report_condition
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -130,7 +143,7 @@ def add_chart_to_dashboard(args):
|
|||
dashboard_link = frappe.new_doc('Dashboard Chart Link')
|
||||
dashboard_link.chart = args.chart_name or args.name
|
||||
|
||||
if args.set_standard:
|
||||
if args.set_standard and dashboard.is_standard:
|
||||
chart = frappe.get_doc('Dashboard Chart', dashboard_link.chart)
|
||||
chart.is_standard = 1
|
||||
chart.module = dashboard.module
|
||||
|
|
@ -344,6 +357,8 @@ def get_year_ending(date):
|
|||
# last day of this month
|
||||
return add_to_date(date, days=-1)
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_charts_for_user(doctype, txt, searchfield, start, page_len, filters):
|
||||
or_filters = {'owner': frappe.session.user, 'is_public': 1}
|
||||
return frappe.db.get_list('Dashboard Chart',
|
||||
|
|
|
|||
|
|
@ -5,14 +5,23 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils.data import validate_json_string
|
||||
from frappe.modules.export_file import export_to_files
|
||||
from frappe.model.document import Document
|
||||
|
||||
class DeskPage(Document):
|
||||
def validate(self):
|
||||
self.validate_cards_json()
|
||||
if (self.is_standard and not frappe.conf.developer_mode and not disable_saving_as_standard()):
|
||||
frappe.throw(_("You need to be in developer mode to edit this document"))
|
||||
|
||||
def validate_cards_json(self):
|
||||
for card in self.cards:
|
||||
try:
|
||||
validate_json_string(card.links)
|
||||
except frappe.ValidationError:
|
||||
frappe.throw(_("Invalid JSON in card links for {0}").format(frappe.bold(card.label)))
|
||||
|
||||
def on_update(self):
|
||||
if disable_saving_as_standard():
|
||||
return
|
||||
|
|
|
|||
|
|
@ -32,13 +32,17 @@ def get_permission_query_conditions(user=None):
|
|||
if "System Manager" in roles:
|
||||
return None
|
||||
|
||||
allowed_doctypes = ['"%s"' % doctype for doctype in frappe.permissions.get_doctypes_with_read()]
|
||||
doctype_condition = False
|
||||
|
||||
allowed_doctypes = [frappe.db.escape(doctype) for doctype in frappe.permissions.get_doctypes_with_read()]
|
||||
|
||||
if allowed_doctypes:
|
||||
doctype_condition = '`tabNumber Card`.`document_type` in ({allowed_doctypes})'.format(
|
||||
allowed_doctypes=','.join(allowed_doctypes))
|
||||
|
||||
return '''
|
||||
`tabNumber Card`.`document_type` in ({allowed_doctypes})
|
||||
'''.format(
|
||||
allowed_doctypes=','.join(allowed_doctypes)
|
||||
)
|
||||
{doctype_condition}
|
||||
'''.format(doctype_condition=doctype_condition)
|
||||
|
||||
def has_permission(doc, ptype, user):
|
||||
roles = frappe.get_roles(user)
|
||||
|
|
@ -124,11 +128,16 @@ def create_number_card(args):
|
|||
doc.insert(ignore_permissions=True)
|
||||
return doc
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters):
|
||||
meta = frappe.get_meta(doctype)
|
||||
searchfields = meta.get_search_fields()
|
||||
search_conditions = []
|
||||
|
||||
if not frappe.db.exists('DocType', doctype):
|
||||
return
|
||||
|
||||
if txt:
|
||||
for field in searchfields:
|
||||
search_conditions.append('`tab{doctype}`.`{field}` like %(txt)s'.format(field=field, doctype=doctype, txt=txt))
|
||||
|
|
@ -172,7 +181,7 @@ def add_card_to_dashboard(args):
|
|||
dashboard_link = frappe.new_doc('Number Card Link')
|
||||
dashboard_link.card = args.name
|
||||
|
||||
if args.set_standard:
|
||||
if args.set_standard and dashboard.is_standard:
|
||||
card = frappe.get_doc('Number Card', dashboard_link.card)
|
||||
card.is_standard = 1
|
||||
card.module = dashboard.module
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from frappe.handler import is_whitelisted
|
|||
from frappe import _
|
||||
from six import string_types
|
||||
import re
|
||||
import wrapt
|
||||
|
||||
UNTRANSLATED_DOCTYPES = ["DocType", "Role"]
|
||||
|
||||
|
|
@ -206,3 +207,15 @@ def scrub_custom_query(query, key, txt):
|
|||
if '%s' in query:
|
||||
query = query.replace('%s', ((txt or '') + '%'))
|
||||
return query
|
||||
|
||||
@wrapt.decorator
|
||||
def validate_and_sanitize_search_inputs(fn, instance, args, kwargs):
|
||||
kwargs.update(dict(zip(fn.__code__.co_varnames, args)))
|
||||
sanitize_searchfield(kwargs['searchfield'])
|
||||
kwargs['start'] = cint(kwargs['start'])
|
||||
kwargs['page_len'] = cint(kwargs['page_len'])
|
||||
|
||||
if kwargs['doctype'] and not frappe.db.exists('DocType', kwargs['doctype']):
|
||||
return []
|
||||
|
||||
return fn(**kwargs)
|
||||
|
|
@ -57,6 +57,8 @@ def relink(name, reference_doctype=None, reference_name=None):
|
|||
communication_type = "Communication" and
|
||||
name = %s""", (reference_doctype, reference_name, name))
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_communication_doctype(doctype, txt, searchfield, start, page_len, filters):
|
||||
user_perms = frappe.utils.user.UserPermissions(frappe.session.user)
|
||||
user_perms.build_permissions()
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ class EmailAccount(Document):
|
|||
email_server = None
|
||||
|
||||
if frappe.local.flags.in_test:
|
||||
incoming_mails = test_mails
|
||||
incoming_mails = test_mails or []
|
||||
else:
|
||||
email_sync_rule = self.build_email_sync_rule()
|
||||
|
||||
|
|
|
|||
|
|
@ -1307,6 +1307,16 @@ class Document(BaseDocument):
|
|||
users = set([assignment.owner for assignment in assignments])
|
||||
return users
|
||||
|
||||
def add_tag(self, tag):
|
||||
"""Add a Tag to this document"""
|
||||
from frappe.desk.doctype.tag.tag import DocTags
|
||||
DocTags(self.doctype).add(self.name, tag)
|
||||
|
||||
def get_tags(self):
|
||||
"""Return a list of Tags attached to this document"""
|
||||
from frappe.desk.doctype.tag.tag import DocTags
|
||||
return DocTags(self.doctype).get_tags(self.name).split(",")[1:]
|
||||
|
||||
def execute_action(doctype, name, action, **kwargs):
|
||||
"""Execute an action on a document (called by background worker)"""
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
|
|
|
|||
|
|
@ -296,3 +296,4 @@ frappe.patches.v13_0.update_duration_options
|
|||
frappe.patches.v13_0.replace_old_data_import # 2020-06-24
|
||||
frappe.patches.v13_0.create_custom_dashboards_cards_and_charts
|
||||
frappe.patches.v13_0.rename_is_custom_field_in_dashboard_chart
|
||||
frappe.patches.v13_0.generate_theme_files_in_public_folder
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
themes = frappe.db.get_all(
|
||||
"Website Theme", filters={"theme_url": ("not like", "/files/website_theme/%")}
|
||||
)
|
||||
for theme in themes:
|
||||
doc = frappe.get_doc("Website Theme", theme.name)
|
||||
doc.generate_bootstrap_theme()
|
||||
doc.save()
|
||||
|
|
@ -3,7 +3,7 @@ frappe.ui.form.ControlMultiSelectList = frappe.ui.form.ControlData.extend({
|
|||
let template = `
|
||||
<div class="multiselect-list dropdown">
|
||||
<div class="form-control cursor-pointer dropdown-toggle input-sm" data-toggle="dropdown" tabindex=0>
|
||||
<span class="status-text ellipsis"></span>
|
||||
<div class="status-text ellipsis"></div>
|
||||
</div>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-input-wrapper">
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ frappe.ui.form.on = frappe.ui.form.on_change = function(doctype, fieldname, hand
|
|||
|
||||
let _handler = (...args) => {
|
||||
try {
|
||||
handler(...args);
|
||||
return handler(...args);
|
||||
} catch (error) {
|
||||
console.error(handler);
|
||||
throw error;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ frappe.breadcrumbs = {
|
|||
|
||||
preferred: {
|
||||
"File": "",
|
||||
"Video": "",
|
||||
"Dashboard": "Customization",
|
||||
"Dashboard Chart": "Customization",
|
||||
"Dashboard Chart Source": "Customization"
|
||||
|
|
|
|||
|
|
@ -122,7 +122,8 @@ function shorten_number(number, country) {
|
|||
const number_system = get_number_system(country);
|
||||
let x = Math.abs(Math.round(number));
|
||||
for (const map of number_system) {
|
||||
if (x >= map.divisor) {
|
||||
const condition = map.condition ? map.condition(x) : x >= map.divisor;
|
||||
if (condition) {
|
||||
return Math.round(number/map.divisor) + ' ' + map.symbol;
|
||||
}
|
||||
}
|
||||
|
|
@ -152,9 +153,14 @@ function get_number_system(country) {
|
|||
{
|
||||
divisor: 1.0e+6,
|
||||
symbol: 'M'
|
||||
},
|
||||
{
|
||||
divisor: 1.0e+3,
|
||||
symbol: 'K',
|
||||
condition: (num) => num.toFixed().length > 5
|
||||
}]
|
||||
};
|
||||
return number_system_map[country];
|
||||
}
|
||||
|
||||
export { generate_route, generate_grid, build_summary_item, shorten_number };
|
||||
export { generate_route, generate_grid, build_summary_item, shorten_number };
|
||||
|
|
|
|||
|
|
@ -174,18 +174,12 @@ class ShortcutDialog extends WidgetDialog {
|
|||
onchange: () => {
|
||||
if (this.dialog.get_value("type") == "DocType") {
|
||||
let doctype = this.dialog.get_value("link_to");
|
||||
|
||||
doctype &&
|
||||
frappe.db
|
||||
.get_value("DocType", doctype, "issingle")
|
||||
.then((res) => {
|
||||
if (res.message && res.message.issingle) {
|
||||
this.hide_filters();
|
||||
} else {
|
||||
this.setup_filter(doctype);
|
||||
this.show_filters();
|
||||
}
|
||||
});
|
||||
if (doctype && frappe.boot.single_types.includes(doctype)) {
|
||||
this.hide_filters();
|
||||
} else if (doctype) {
|
||||
this.setup_filter(doctype);
|
||||
this.show_filters();
|
||||
}
|
||||
} else {
|
||||
this.hide_filters();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
{%- set res = frappe.utils.get_thumbnail_base64_for_image(src) if src else false -%}
|
||||
{%- if res and res['base64'].startswith('data:') -%}
|
||||
<img src="{{ res['base64'] }}" class="image-with-blur {{ resolve_class(class) }}"
|
||||
data-src="{{ src or '' }}" alt="{{ alt or '' }}"
|
||||
width="{{ res['width'] }}" height="{{ res['height'] }}"
|
||||
style="width: {{ res['width'] }}px; height: {{ res['height'] }}px;" />
|
||||
alt="{{ alt or '' }}" width="{{ res['width'] }}" height="{{ res['height'] }}" data-src="{{ src or '' }}" />
|
||||
{%- else -%}
|
||||
<img src="{{ src or '' }}" class="{{ resolve_class(class) }}" alt="{{ alt or '' }}" />
|
||||
{%- endif -%}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class TestScheduler(TestCase):
|
|||
|
||||
# 2nd job not loaded
|
||||
self.assertFalse(job.enqueue())
|
||||
job.delete()
|
||||
frappe.db.sql('DELETE FROM `tabScheduled Job Log` WHERE `scheduled_job_type`=%s', job.name)
|
||||
|
||||
def test_is_dormant(self):
|
||||
self.assertTrue(is_dormant(check_time= get_datetime('2100-01-01 00:00:00')))
|
||||
|
|
|
|||
|
|
@ -50,3 +50,31 @@ class TestSearch(unittest.TestCase):
|
|||
|
||||
def tearDown(self):
|
||||
frappe.local.lang = 'en'
|
||||
|
||||
def test_validate_and_sanitize_search_inputs(self):
|
||||
|
||||
# should raise error if searchfield is injectable
|
||||
self.assertRaises(frappe.DataError,
|
||||
get_data, *('User', 'Random', 'select * from tabSessions) --', '1', '10', dict()))
|
||||
|
||||
# page_len and start should be converted to int
|
||||
self.assertListEqual(get_data('User', 'Random', 'email', 'name or (select * from tabSessions)', '10', dict()),
|
||||
['User', 'Random', 'email', 0, 10, {}])
|
||||
self.assertListEqual(get_data('User', 'Random', 'email', page_len='2', start='10', filters=dict()),
|
||||
['User', 'Random', 'email', 10, 2, {}])
|
||||
|
||||
# DocType can be passed as None which should be accepted
|
||||
self.assertListEqual(get_data(None, 'Random', 'email', '2', '10', dict()),
|
||||
[None, 'Random', 'email', 2, 10, {}])
|
||||
|
||||
# return empty string if passed doctype is invalid
|
||||
self.assertListEqual(get_data("Random DocType", 'Random', 'email', '2', '10', dict()), [])
|
||||
|
||||
# should not fail if function is called via frappe.call with extra arguments
|
||||
args = ("Random DocType", 'Random', 'email', '2', '10', dict())
|
||||
kwargs = {'as_dict': False}
|
||||
self.assertListEqual(frappe.call('frappe.tests.test_search.get_data', *args, **kwargs), [])
|
||||
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_data(doctype, txt, searchfield, start, page_len, filters):
|
||||
return [doctype, txt, searchfield, start, page_len, filters]
|
||||
|
|
@ -119,7 +119,7 @@ def get_dict(fortype, name=None):
|
|||
messages += frappe.db.sql("select 'Role:', name from tabRole")
|
||||
messages += frappe.db.sql("select 'Module:', name from `tabModule Def`")
|
||||
|
||||
message_dict = make_dict_from_messages(messages)
|
||||
message_dict = make_dict_from_messages(messages, load_user_translation=False)
|
||||
message_dict.update(get_dict_from_hooks(fortype, name))
|
||||
# remove untranslated
|
||||
message_dict = {k:v for k, v in iteritems(message_dict) if k!=v}
|
||||
|
|
@ -127,6 +127,7 @@ def get_dict(fortype, name=None):
|
|||
cache.hset("translation_assets", frappe.local.lang, translation_assets, shared=True)
|
||||
|
||||
translation_map = translation_assets[asset_key]
|
||||
|
||||
if fortype == "boot":
|
||||
translation_map.update(get_user_translations(frappe.local.lang))
|
||||
|
||||
|
|
@ -144,14 +145,17 @@ def get_dict_from_hooks(fortype, name):
|
|||
|
||||
return translated_dict
|
||||
|
||||
def make_dict_from_messages(messages, full_dict=None):
|
||||
def make_dict_from_messages(messages, full_dict=None, load_user_translation=True):
|
||||
"""Returns translated messages as a dict in Language specified in `frappe.local.lang`
|
||||
|
||||
:param messages: List of untranslated messages
|
||||
"""
|
||||
out = {}
|
||||
if full_dict==None:
|
||||
full_dict = get_full_dict(frappe.local.lang)
|
||||
if load_user_translation:
|
||||
full_dict = get_full_dict(frappe.local.lang)
|
||||
else:
|
||||
full_dict = load_lang(frappe.local.lang)
|
||||
|
||||
for m in messages:
|
||||
if m[1] in full_dict:
|
||||
|
|
@ -189,11 +193,9 @@ def get_full_dict(lang):
|
|||
try:
|
||||
# get user specific transaltion data
|
||||
user_translations = get_user_translations(lang)
|
||||
except Exception:
|
||||
user_translations = None
|
||||
|
||||
if user_translations:
|
||||
frappe.local.lang_full_dict.update(user_translations)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return frappe.local.lang_full_dict
|
||||
|
||||
|
|
|
|||
|
|
@ -118,8 +118,9 @@ def get_versions():
|
|||
def get_app_branch(app):
|
||||
'''Returns branch of an app'''
|
||||
try:
|
||||
null_stream = open(os.devnull, 'wb')
|
||||
result = subprocess.check_output('cd ../apps/{0} && git rev-parse --abbrev-ref HEAD'.format(app),
|
||||
shell=True)
|
||||
shell=True, stdin=null_stream, stderr=null_stream)
|
||||
result = safe_decode(result)
|
||||
result = result.strip()
|
||||
return result
|
||||
|
|
@ -128,8 +129,9 @@ def get_app_branch(app):
|
|||
|
||||
def get_app_last_commit_ref(app):
|
||||
try:
|
||||
null_stream = open(os.devnull, 'wb')
|
||||
result = subprocess.check_output('cd ../apps/{0} && git rev-parse HEAD --short 7'.format(app),
|
||||
shell=True)
|
||||
shell=True, stdin=null_stream, stderr=null_stream)
|
||||
result = safe_decode(result)
|
||||
result = result.strip()
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import frappe
|
|||
from dateutil.parser._parser import ParserError
|
||||
import subprocess
|
||||
import operator
|
||||
import json
|
||||
import re, datetime, math, time
|
||||
import babel.dates
|
||||
from babel.core import UnknownLocaleError
|
||||
|
|
@ -1236,8 +1237,6 @@ def is_subset(list_a, list_b):
|
|||
def generate_hash(*args, **kwargs):
|
||||
return frappe.generate_hash(*args, **kwargs)
|
||||
|
||||
|
||||
|
||||
def guess_date_format(date_string):
|
||||
DATE_FORMATS = [
|
||||
r"%d-%m-%Y",
|
||||
|
|
@ -1310,3 +1309,9 @@ def guess_date_format(date_string):
|
|||
|
||||
if date_format and time_format:
|
||||
return (date_format + ' ' + time_format).strip()
|
||||
|
||||
def validate_json_string(string):
|
||||
try:
|
||||
json.loads(string)
|
||||
except (TypeError, ValueError):
|
||||
raise frappe.ValidationError
|
||||
|
|
@ -11,56 +11,86 @@ from six import text_type
|
|||
|
||||
# imports - module imports
|
||||
import frappe
|
||||
from frappe.utils import get_sites
|
||||
|
||||
|
||||
default_log_level = logging.DEBUG
|
||||
site = getattr(frappe.local, 'site', None)
|
||||
|
||||
|
||||
def get_logger(module, with_more_info=False):
|
||||
global site
|
||||
if module in frappe.loggers:
|
||||
return frappe.loggers[module]
|
||||
def get_logger(module=None, with_more_info=False, allow_site=True, filter=None, max_size=100_000, file_count=20):
|
||||
"""Application Logger for your given module
|
||||
|
||||
Args:
|
||||
module (str, optional): Name of your logger and consequently your log file. Defaults to None.
|
||||
with_more_info (bool, optional): Will log the form dict using the SiteContextFilter. Defaults to False.
|
||||
allow_site ((str, bool), optional): Pass site name to explicitly log under it's logs. If True and unspecified, guesses which site the logs would be saved under. Defaults to True.
|
||||
filter (function, optional): Add a filter function for your logger. Defaults to None.
|
||||
max_size (int, optional): Max file size of each log file in bytes. Defaults to 100_000.
|
||||
file_count (int, optional): Max count of log files to be retained via Log Rotation. Defaults to 20.
|
||||
|
||||
Returns:
|
||||
<class 'logging.Logger'>: Returns a Python logger object with Site and Bench level logging capabilities.
|
||||
"""
|
||||
|
||||
if allow_site is True:
|
||||
site = getattr(frappe.local, "site", None)
|
||||
elif allow_site in get_sites():
|
||||
site = allow_site
|
||||
else:
|
||||
site = False
|
||||
|
||||
logger_name = "{0}-{1}".format(module, site or "all")
|
||||
|
||||
try:
|
||||
return frappe.loggers[logger_name]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if not module:
|
||||
module = "frappe"
|
||||
with_more_info = True
|
||||
|
||||
logfile = module + '.log'
|
||||
site = getattr(frappe.local, 'site', None)
|
||||
LOG_FILENAME = os.path.join('..', 'logs', logfile)
|
||||
logfile = module + ".log"
|
||||
log_filename = os.path.join("..", "logs", logfile)
|
||||
|
||||
logger = logging.getLogger(module)
|
||||
logger = logging.getLogger(logger_name)
|
||||
logger.setLevel(frappe.log_level or default_log_level)
|
||||
logger.propagate = False
|
||||
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s %(message)s')
|
||||
handler = RotatingFileHandler(LOG_FILENAME, maxBytes=100_000, backupCount=20)
|
||||
formatter = logging.Formatter("%(asctime)s %(levelname)s {0} %(message)s".format(module))
|
||||
handler = RotatingFileHandler(log_filename, maxBytes=max_size, backupCount=file_count)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
#
|
||||
|
||||
if site:
|
||||
SITELOG_FILENAME = os.path.join(site, 'logs', logfile)
|
||||
site_handler = RotatingFileHandler(SITELOG_FILENAME, maxBytes=100_000, backupCount=20)
|
||||
sitelog_filename = os.path.join(site, "logs", logfile)
|
||||
site_handler = RotatingFileHandler(sitelog_filename, maxBytes=max_size, backupCount=file_count)
|
||||
site_handler.setFormatter(formatter)
|
||||
logger.addHandler(site_handler)
|
||||
|
||||
if with_more_info:
|
||||
handler.addFilter(SiteContextFilter())
|
||||
|
||||
handler.setFormatter(formatter)
|
||||
if filter:
|
||||
logger.addFilter(filter)
|
||||
|
||||
frappe.loggers[module] = logger
|
||||
frappe.loggers[logger_name] = logger
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
class SiteContextFilter(logging.Filter):
|
||||
"""This is a filter which injects request information (if available) into the log."""
|
||||
|
||||
def filter(self, record):
|
||||
if "Form Dict" not in text_type(record.msg):
|
||||
record.msg = text_type(record.msg) + "\nSite: {0}\nForm Dict: {1}".format(site, getattr(frappe.local, 'form_dict', None))
|
||||
site = getattr(frappe.local, "site", None)
|
||||
form_dict = getattr(frappe.local, "form_dict", None)
|
||||
record.msg = text_type(record.msg) + "\nSite: {0}\nForm Dict: {1}".format(site, form_dict)
|
||||
return True
|
||||
|
||||
|
||||
def set_log_level(level):
|
||||
'''Use this method to set log level to something other than the default DEBUG'''
|
||||
frappe.log_level = getattr(logging, (level or '').upper(), None) or default_log_level
|
||||
"""Use this method to set log level to something other than the default DEBUG"""
|
||||
frappe.log_level = getattr(logging, (level or "").upper(), None) or default_log_level
|
||||
frappe.loggers = {}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ def get_safe_globals():
|
|||
render_template=frappe.render_template,
|
||||
msgprint=frappe.msgprint,
|
||||
throw=frappe.throw,
|
||||
sendmail = frappe.sendmail,
|
||||
|
||||
user=user,
|
||||
get_fullname=frappe.utils.get_fullname,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"route_to_success_link",
|
||||
"allow_edit",
|
||||
"allow_multiple",
|
||||
"apply_document_permissions",
|
||||
"show_in_grid",
|
||||
"allow_delete",
|
||||
"allow_print",
|
||||
|
|
@ -346,14 +347,20 @@
|
|||
"fieldname": "custom_css_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Custom CSS"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "apply_document_permissions",
|
||||
"fieldtype": "Check",
|
||||
"label": "Apply Document Permissions"
|
||||
}
|
||||
],
|
||||
"has_web_view": 1,
|
||||
"icon": "icon-edit",
|
||||
"is_published_field": "published",
|
||||
"links": [],
|
||||
"modified": "2019-12-24 14:15:43.497431",
|
||||
"modified_by": "faris@erpnext.com",
|
||||
"modified": "2020-06-30 21:49:18.237443",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Web Form",
|
||||
"owner": "Administrator",
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ def get_context(context):
|
|||
if frappe.session.user == "Guest" and frappe.form_dict.name:
|
||||
frappe.throw(_("You need to be logged in to access this {0}.").format(self.doc_type), frappe.PermissionError)
|
||||
|
||||
if frappe.form_dict.name and not has_web_form_permission(self.doc_type, frappe.form_dict.name):
|
||||
if frappe.form_dict.name and not self.has_web_form_permission(self.doc_type, frappe.form_dict.name):
|
||||
frappe.throw(_("You don't have the permissions to access this document"), frappe.PermissionError)
|
||||
|
||||
self.reset_field_parent()
|
||||
|
|
@ -343,6 +343,27 @@ def get_context(context):
|
|||
frappe.throw(_('Mandatory Information missing:') + '<br><br>'
|
||||
+ '<br>'.join(['{0} ({1})'.format(d.label, d.fieldtype) for d in missing]))
|
||||
|
||||
def has_web_form_permission(self, doctype, name, ptype='read'):
|
||||
if frappe.session.user=="Guest":
|
||||
return False
|
||||
|
||||
if self.apply_document_permissions:
|
||||
return frappe.get_doc(doctype, name).has_permission()
|
||||
|
||||
# owner matches
|
||||
elif frappe.db.get_value(doctype, name, "owner")==frappe.session.user:
|
||||
return True
|
||||
|
||||
elif frappe.has_website_permission(name, ptype=ptype, doctype=doctype):
|
||||
return True
|
||||
|
||||
elif check_webform_perm(doctype, name):
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def accept(web_form, data, docname=None, for_payment=False):
|
||||
|
|
@ -391,7 +412,7 @@ def accept(web_form, data, docname=None, for_payment=False):
|
|||
doc.run_method('validate_payment')
|
||||
|
||||
if doc.name:
|
||||
if has_web_form_permission(doc.doctype, doc.name, "write"):
|
||||
if web_form.has_web_form_permission(doc.doctype, doc.name, "write"):
|
||||
doc.save(ignore_permissions=True)
|
||||
else:
|
||||
# only if permissions are present
|
||||
|
|
@ -478,24 +499,6 @@ def delete_multiple(web_form_name, docnames):
|
|||
raise frappe.PermissionError("You do not have permisssion to delete " + ", ".join(restricted_docnames))
|
||||
|
||||
|
||||
def has_web_form_permission(doctype, name, ptype='read'):
|
||||
if frappe.session.user=="Guest":
|
||||
return False
|
||||
|
||||
# owner matches
|
||||
elif frappe.db.get_value(doctype, name, "owner")==frappe.session.user:
|
||||
return True
|
||||
|
||||
elif frappe.has_website_permission(name, ptype=ptype, doctype=doctype):
|
||||
return True
|
||||
|
||||
elif check_webform_perm(doctype, name):
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def check_webform_perm(doctype, name):
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
if hasattr(doc, "has_webform_permission"):
|
||||
|
|
@ -532,7 +535,7 @@ def get_form_data(doctype, docname=None, web_form_name=None):
|
|||
|
||||
if docname:
|
||||
doc = frappe.get_doc(doctype, docname)
|
||||
if has_web_form_permission(doctype, docname, ptype='read'):
|
||||
if web_form.has_web_form_permission(doctype, docname, ptype='read'):
|
||||
out.doc = doc
|
||||
else:
|
||||
frappe.throw(_("Not permitted"), frappe.PermissionError)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import frappe
|
|||
import unittest
|
||||
from frappe.utils import random_string
|
||||
from frappe.model.workflow import apply_workflow, WorkflowTransitionError, WorkflowPermissionError, get_common_transition_actions
|
||||
from frappe.test_runner import make_test_records
|
||||
|
||||
make_test_records("User")
|
||||
|
||||
class TestWorkflow(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -78,7 +81,7 @@ class TestWorkflow(unittest.TestCase):
|
|||
frappe.set_user('test2@example.com')
|
||||
|
||||
doc = self.test_default_condition()
|
||||
workflow_actions = frappe.get_all('Workflow Action', fields=['status'])
|
||||
workflow_actions = frappe.get_all('Workflow Action', fields=['status', 'reference_name'])
|
||||
self.assertEqual(len(workflow_actions), 1)
|
||||
|
||||
# test if status of workflow actions are updated on approval
|
||||
|
|
@ -102,6 +105,9 @@ class TestWorkflow(unittest.TestCase):
|
|||
todo.reload()
|
||||
self.assertEqual(todo.docstatus, 1)
|
||||
|
||||
self.workflow.states[1].doc_status = 0
|
||||
self.workflow.save()
|
||||
|
||||
def test_if_workflow_set_on_action(self):
|
||||
self.workflow.states[1].doc_status = 1
|
||||
self.workflow.save()
|
||||
|
|
@ -111,12 +117,17 @@ class TestWorkflow(unittest.TestCase):
|
|||
self.assertEqual(todo.docstatus, 1)
|
||||
self.assertEqual(todo.workflow_state, 'Approved')
|
||||
|
||||
self.workflow.states[1].doc_status = 0
|
||||
self.workflow.save()
|
||||
|
||||
def create_todo_workflow():
|
||||
if frappe.db.exists('Workflow', 'Test ToDo'):
|
||||
return frappe.get_doc('Workflow', 'Test ToDo').save(ignore_permissions=True)
|
||||
else:
|
||||
frappe.get_doc(dict(doctype='Role',
|
||||
role_name='Test Approver')).insert(ignore_if_duplicate=True)
|
||||
frappe.db.commit()
|
||||
frappe.cache().hdel('roles', frappe.session.user)
|
||||
workflow = frappe.new_doc('Workflow')
|
||||
workflow.workflow_name = 'Test ToDo'
|
||||
workflow.document_type = 'ToDo'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||
|
||||
import frappe
|
||||
from frappe.model.document import get_controller
|
||||
from frappe.utils import get_request_site_address, get_datetime, nowdate
|
||||
from frappe.utils import get_datetime, nowdate, get_url
|
||||
from frappe.website.router import get_pages, get_all_page_context_from_doctypes
|
||||
from six import iteritems
|
||||
from six.moves.urllib.parse import quote, urljoin
|
||||
|
|
@ -25,13 +25,13 @@ def get_context(context):
|
|||
for route, page in iteritems(get_pages()):
|
||||
if page.sitemap:
|
||||
links.append({
|
||||
"loc": urljoin(host, quote(page.name.encode("utf-8"))),
|
||||
"loc": get_url(quote(page.name.encode("utf-8"))),
|
||||
"lastmod": nowdate()
|
||||
})
|
||||
|
||||
for route, data in iteritems(get_public_pages_from_doctypes()):
|
||||
links.append({
|
||||
"loc": urljoin(host, quote((route or "").encode("utf-8"))),
|
||||
"loc": get_url(quote((route or "").encode("utf-8"))),
|
||||
"lastmod": get_datetime(data.get("modified")).strftime("%Y-%m-%d")
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -69,4 +69,5 @@ Whoosh==2.7.4
|
|||
xlrd==1.2.0
|
||||
zxcvbn-python==4.4.24
|
||||
pycryptodome==3.9.8
|
||||
paytmchecksum==1.7.0
|
||||
paytmchecksum==1.7.0
|
||||
wrapt==1.10.11
|
||||
Loading…
Add table
Reference in a new issue