- {{ __("Tags") }}
+ {{ __("Tags") }}
{{ var | pprint | e }}
+{%- endif -%}
+{%- endmacro %}
diff --git a/frappe/tests/test_db_update.py b/frappe/tests/test_db_update.py
new file mode 100644
index 0000000000..34378de3af
--- /dev/null
+++ b/frappe/tests/test_db_update.py
@@ -0,0 +1,70 @@
+import unittest
+import frappe
+
+from frappe.core.utils import find
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+
+
+class TestDBUpdate(unittest.TestCase):
+ def test_db_update(self):
+ doctype = 'User'
+ frappe.reload_doctype('User', force=True)
+ frappe.model.meta.trim_tables('User')
+ make_property_setter(doctype, 'bio', 'fieldtype', 'Text', 'Data')
+ make_property_setter(doctype, 'enabled', 'default', '1', 'Int')
+
+ frappe.db.updatedb(doctype)
+
+ field_defs = get_field_defs(doctype)
+ table_columns = frappe.db.get_table_columns_description('tab{}'.format(doctype))
+
+ self.assertEqual(len(field_defs), len(table_columns))
+
+ for field_def in field_defs:
+ fieldname = field_def.get('fieldname')
+ table_column = find(table_columns, lambda d: d.get('name') == fieldname)
+
+ fieldtype = get_fieldtype_from_def(field_def)
+
+ fallback_default = '0' if field_def.get('fieldtype') in frappe.model.numeric_fieldtypes else 'NULL'
+ default = field_def.default if field_def.default is not None else fallback_default
+
+ self.assertEqual(fieldtype, table_column.type)
+ self.assertIn(table_column.default or 'NULL', [default, "'{}'".format(default)])
+
+def get_fieldtype_from_def(field_def):
+ fieldtuple = frappe.db.type_map.get(field_def.fieldtype, ('', 0))
+ fieldtype = fieldtuple[0]
+ if fieldtype in ('varchar', 'datetime', 'int'):
+ fieldtype += '({})'.format(field_def.length or fieldtuple[1])
+ return fieldtype
+
+def get_field_defs(doctype):
+ meta = frappe.get_meta(doctype, cached=False)
+ field_defs = meta.get_fieldnames_with_value(True)
+ field_defs += get_other_fields_meta(meta)
+ return field_defs
+
+def get_other_fields_meta(meta):
+ default_fields_map = {
+ 'name': ('Data', 0),
+ 'owner': ('Data', 0),
+ 'parent': ('Data', 0),
+ 'parentfield': ('Data', 0),
+ 'modified_by': ('Data', 0),
+ 'parenttype': ('Data', 0),
+ 'creation': ('Datetime', 0),
+ 'modified': ('Datetime', 0),
+ 'idx': ('Int', 8),
+ 'docstatus': ('Check', 0)
+ }
+
+ optional_fields = frappe.db.OPTIONAL_COLUMNS
+ if meta.track_seen:
+ optional_fields.append('_seen')
+
+ optional_fields_map = {field: ('Text', 0) for field in optional_fields}
+ fields = dict(default_fields_map, **optional_fields_map)
+ field_map = [frappe._dict({'fieldname': field, 'fieldtype': _type, 'length': _length}) for field, (_type, _length) in fields.items()]
+
+ return field_map
\ No newline at end of file
diff --git a/frappe/tests/test_permissions.py b/frappe/tests/test_permissions.py
index 15597aef39..7e9416f68a 100644
--- a/frappe/tests/test_permissions.py
+++ b/frappe/tests/test_permissions.py
@@ -314,6 +314,8 @@ class TestPermissions(unittest.TestCase):
frappe.set_user('Administrator')
frappe.db.sql('DELETE FROM `tabContact`')
+ frappe.db.sql('DELETE FROM `tabContact Email`')
+ frappe.db.sql('DELETE FROM `tabContact Phone`')
reset('Salutation')
reset('Contact')
diff --git a/frappe/utils/html_utils.py b/frappe/utils/html_utils.py
index 67a01296c1..3de647894b 100644
--- a/frappe/utils/html_utils.py
+++ b/frappe/utils/html_utils.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
import frappe
-import json, re
-import bleach, bleach_whitelist.bleach_whitelist as bleach_whitelist
+import json
+import re
+import bleach
+import bleach_whitelist.bleach_whitelist as bleach_whitelist
from six import string_types
from bs4 import BeautifulSoup
@@ -47,7 +49,7 @@ def clean_script_and_style(html):
def sanitize_html(html, linkify=False):
"""
Sanitize HTML tags, attributes and style to prevent XSS attacks
- Based on bleach clean, bleach whitelist and HTML5lib's Sanitizer defaults
+ Based on bleach clean, bleach whitelist and html5lib's Sanitizer defaults
Does not sanitize JSON, as it could lead to future problems
"""
diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py
index c69dc430cf..58275a5c6c 100644
--- a/frappe/utils/pdf.py
+++ b/frappe/utils/pdf.py
@@ -16,6 +16,7 @@ import frappe
from frappe import _
from frappe.utils import get_wkhtmltopdf_version, scrub_urls
+
PDF_CONTENT_ERRORS = ["ContentNotFoundError", "ContentOperationNotPermittedError",
"UnknownContentError", "RemoteHostClosedError"]
@@ -127,7 +128,7 @@ def read_options_from_html(html):
toggle_visible_pdf(soup)
# use regex instead of soup-parser
- for attr in ("margin-top", "margin-bottom", "margin-left", "margin-right", "page-size", "header-spacing"):
+ for attr in ("margin-top", "margin-bottom", "margin-left", "margin-right", "page-size", "header-spacing", "orientation"):
try:
pattern = re.compile(r"(\.print-format)([\S|\s][^}]*?)(" + str(attr) + r":)(.+)(mm;)")
match = pattern.findall(html)
diff --git a/frappe/website/context.py b/frappe/website/context.py
index 20762f7454..dcef22af43 100644
--- a/frappe/website/context.py
+++ b/frappe/website/context.py
@@ -31,6 +31,11 @@ def get_context(path, args=None):
if hasattr(frappe.local, 'response') and frappe.local.response.get('context'):
context.update(frappe.local.response.context)
+ # to be able to inspect the context in development
+ # Use the macro "inspect" from macros.html
+ if frappe.conf.developer_mode:
+ context._context_dict = context
+
return context
def update_controller_context(context, controller):
diff --git a/frappe/website/router.py b/frappe/website/router.py
index 7cf9b88f23..bce40c7713 100644
--- a/frappe/website/router.py
+++ b/frappe/website/router.py
@@ -237,7 +237,7 @@ def setup_source(page_info):
source = jenv.loader.get_source(jenv, page_info.template)[0]
html = ''
- if page_info.template.endswith('.md'):
+ if page_info.template.endswith(('.md', '.html')):
# extract frontmatter block if exists
try:
# values will be used to update page_info
@@ -248,10 +248,11 @@ def setup_source(page_info):
except Exception as e:
pass
- source = frappe.utils.md_to_html(source)
+ if page_info.template.endswith('.md'):
+ source = frappe.utils.md_to_html(source)
- if not page_info.show_sidebar:
- source = '
' + source + '
'
+ if not page_info.show_sidebar:
+ source = '
' + source + '
'
# if only content
if page_info.template.endswith('.html') or page_info.template.endswith('.md'):
diff --git a/frappe/website/utils.py b/frappe/website/utils.py
index 3aa4930c30..495c36c92a 100644
--- a/frappe/website/utils.py
+++ b/frappe/website/utils.py
@@ -89,7 +89,7 @@ def is_signup_enabled():
def cleanup_page_name(title):
"""make page name from title"""
if not title:
- return title
+ return ''
name = title.lower()
name = re.sub('[~!@#$%^&*+()<>,."\'\?]', '', name)
@@ -287,7 +287,9 @@ def extract_title(source, path):
if not title and "
" in source:
# extract title from h1
match = re.findall('([^<]*)', source)
- title = match[0].strip()[:300]
+ title_content = match[0].strip()[:300]
+ if '{{' not in title_content:
+ title = title_content
if not title:
# make title from name
diff --git a/requirements.txt b/requirements.txt
index 931f6b528d..55a4910056 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
Babel==2.6.0
beautifulsoup4==4.8.2
bleach-whitelist==0.0.10
-bleach==2.1.4
+bleach==3.1.2
boto3==1.10.18
braintree==3.57.1
chardet==3.0.4
@@ -23,8 +23,10 @@ google-auth==1.7.1
googlemaps==3.1.1
gunicorn==19.10.0
html2text==2016.9.19
+html5lib==1.0.1
ipython==5.9.0
Jinja2==2.10.3
+ldap3==2.7
markdown2==2.3.8
maxminddb-geolite2==2018.703
ndg-httpsclient==0.5.1
diff --git a/yarn.lock b/yarn.lock
index 6de2bd96b2..1b3d0a2176 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -392,9 +392,9 @@ ace-builds@^1.4.8:
integrity sha512-8ZVAxwyCGAxQX8mOp9imSXH0hoSPkGfy8igJy+WO/7axL30saRhKgg1XPACSmxxPA7nfHVwM+ShWXT+vKsNuFg==
acorn@^5.2.1:
- version "5.7.3"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
- integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
+ version "5.7.4"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
+ integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
acorn@^6.1.1:
version "6.1.1"