Merge branch 'master' into develop
This commit is contained in:
commit
7fe50c885a
21 changed files with 229 additions and 83 deletions
|
|
@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json
|
|||
from .exceptions import *
|
||||
from .utils.jinja import get_jenv, get_template, render_template
|
||||
|
||||
__version__ = '7.2.28'
|
||||
__version__ = '7.2.29'
|
||||
__title__ = "Frappe Framework"
|
||||
|
||||
local = Local()
|
||||
|
|
|
|||
|
|
@ -235,8 +235,17 @@ class User(Document):
|
|||
|
||||
link = self.reset_password()
|
||||
|
||||
self.send_login_mail(_("Verify Your Account"), "templates/emails/new_user.html",
|
||||
{"link": link, "site_url": get_url()})
|
||||
app_title = [t for t in frappe.get_hooks('app_title') if t != 'Frappe Framework']
|
||||
if app_title:
|
||||
subject = _("Welcome to {0}").format(app_title[0])
|
||||
else:
|
||||
subject = _("Complete Registration")
|
||||
|
||||
self.send_login_mail(subject, "templates/emails/new_user.html",
|
||||
dict(
|
||||
link=link,
|
||||
site_url=get_url(),
|
||||
))
|
||||
|
||||
|
||||
def send_login_mail(self, subject, template, add_args, now=None):
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ def get_std_fields_list(meta, key):
|
|||
def build_for_autosuggest(res):
|
||||
results = []
|
||||
for r in res:
|
||||
out = {"value": r[0], "description": ", ".join(unique(cstr(d) for d in r)[1:])}
|
||||
out = {"value": r[0], "description": ", ".join(unique(cstr(d) for d in r if d)[1:])}
|
||||
results.append(out)
|
||||
return results
|
||||
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ class DatabaseQuery(object):
|
|||
can_be_null = True
|
||||
|
||||
# prepare in condition
|
||||
if f.operator in ('in', 'not in'):
|
||||
if f.operator.lower() in ('in', 'not in'):
|
||||
values = f.value
|
||||
if not isinstance(values, (list, tuple)):
|
||||
values = values.split(",")
|
||||
|
|
@ -296,7 +296,7 @@ class DatabaseQuery(object):
|
|||
if df and df.fieldtype in ("Check", "Float", "Int", "Currency", "Percent"):
|
||||
can_be_null = False
|
||||
|
||||
if f.operator=='Between' and \
|
||||
if f.operator.lower() == 'between' and \
|
||||
(f.fieldname in ('creation', 'modified') or (df and (df.fieldtype=="Date" or df.fieldtype=="Datetime"))):
|
||||
value = "'%s' AND '%s'" % (
|
||||
get_datetime(f.value[0]).strftime("%Y-%m-%d %H:%M:%S.%f"),
|
||||
|
|
@ -315,12 +315,12 @@ class DatabaseQuery(object):
|
|||
value = get_time(f.value).strftime("%H:%M:%S.%f")
|
||||
fallback = "'00:00:00'"
|
||||
|
||||
elif f.operator in ("like", "not like") or (isinstance(f.value, basestring) and
|
||||
elif f.operator.lower() in ("like", "not like") or (isinstance(f.value, basestring) and
|
||||
(not df or df.fieldtype not in ["Float", "Int", "Currency", "Percent", "Check"])):
|
||||
value = "" if f.value==None else f.value
|
||||
fallback = '""'
|
||||
|
||||
if f.operator in ("like", "not like") and isinstance(value, basestring):
|
||||
if f.operator.lower() in ("like", "not like") and isinstance(value, basestring):
|
||||
# because "like" uses backslash (\) for escaping
|
||||
value = value.replace("\\", "\\\\").replace("%", "%%")
|
||||
|
||||
|
|
@ -329,12 +329,12 @@ class DatabaseQuery(object):
|
|||
fallback = 0
|
||||
|
||||
# put it inside double quotes
|
||||
if isinstance(value, basestring) and not f.operator=='Between':
|
||||
if isinstance(value, basestring) and not f.operator.lower() == 'between':
|
||||
value = '"{0}"'.format(frappe.db.escape(value, percent=False))
|
||||
|
||||
if (self.ignore_ifnull
|
||||
or not can_be_null
|
||||
or (f.value and f.operator in ('=', 'like'))
|
||||
or (f.value and f.operator.lower() in ('=', 'like'))
|
||||
or 'ifnull(' in column_name.lower()):
|
||||
condition = '{column_name} {operator} {value}'.format(
|
||||
column_name=column_name, operator=f.operator,
|
||||
|
|
|
|||
|
|
@ -112,9 +112,6 @@ a.badge-hover:focus .badge,
|
|||
a.badge-hover:active .badge {
|
||||
background-color: #D8DFE5;
|
||||
}
|
||||
.msgprint {
|
||||
margin: 15px 0px;
|
||||
}
|
||||
.msgprint pre {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,9 +112,6 @@ a.badge-hover:focus .badge,
|
|||
a.badge-hover:active .badge {
|
||||
background-color: #D8DFE5;
|
||||
}
|
||||
.msgprint {
|
||||
margin: 15px 0px;
|
||||
}
|
||||
.msgprint pre {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
@ -503,6 +500,7 @@ fieldset[disabled] .form-control {
|
|||
color: #fff;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
/* on small screens, show only icons on top */
|
||||
@media (max-width: 767px) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
border: 1px solid #d1d8dd;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.form-grid.error {
|
||||
border-color: #ff5858;
|
||||
}
|
||||
.grid-heading-row {
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
background-color: #F7FAFC;
|
||||
|
|
@ -55,7 +58,13 @@
|
|||
padding: 10px 15px;
|
||||
max-height: 200px;
|
||||
border-right: 1px solid #d1d8dd;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.grid-static-col.bold {
|
||||
font-weight: bold;
|
||||
background-color: #fffdf4;
|
||||
}
|
||||
.validated-form .grid-static-col.error {
|
||||
background-color: #FFDCDC;
|
||||
}
|
||||
.grid-static-col input[type="checkbox"] {
|
||||
margin-left: -16px !important;
|
||||
|
|
|
|||
|
|
@ -112,9 +112,6 @@ a.badge-hover:focus .badge,
|
|||
a.badge-hover:active .badge {
|
||||
background-color: #D8DFE5;
|
||||
}
|
||||
.msgprint {
|
||||
margin: 15px 0px;
|
||||
}
|
||||
.msgprint pre {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -401,10 +401,10 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
|
|||
},
|
||||
set_bold: function() {
|
||||
if(this.$input) {
|
||||
this.$input.toggleClass("bold", !!this.df.bold);
|
||||
this.$input.toggleClass("bold", !!(this.df.bold || this.df.reqd));
|
||||
}
|
||||
if(this.disp_area) {
|
||||
$(this.disp_area).toggleClass("bold", !!this.df.bold);
|
||||
$(this.disp_area).toggleClass("bold", !!(this.df.bold || this.df.reqd));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ frappe.ui.form.Grid = Class.extend({
|
|||
.appendTo(this.parent)
|
||||
.attr("data-fieldname", this.df.fieldname);
|
||||
|
||||
this.form_grid = this.wrapper.find('.form-grid');
|
||||
|
||||
this.wrapper.find(".grid-add-row").click(function() {
|
||||
me.add_new_row(null, null, true);
|
||||
me.set_focus_on_row();
|
||||
|
|
@ -206,6 +208,9 @@ frappe.ui.form.Grid = Class.extend({
|
|||
frappe.utils.scroll_to(_scroll_y);
|
||||
}
|
||||
|
||||
// red if mandatory
|
||||
this.form_grid.toggleClass('error', !!(this.df.reqd && !(data && data.length)));
|
||||
|
||||
this.refresh_remove_rows_button();
|
||||
},
|
||||
setup_toolbar: function() {
|
||||
|
|
@ -821,6 +826,15 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
this.refresh_field(df.fieldname, txt);
|
||||
}
|
||||
|
||||
// background color for cellz
|
||||
if(this.doc) {
|
||||
if(df.reqd && !txt) {
|
||||
column.addClass('error');
|
||||
}
|
||||
if (df.reqd || df.bold) {
|
||||
column.addClass('bold');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
|
@ -1085,8 +1099,9 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
}
|
||||
},
|
||||
refresh_field: function(fieldname, txt) {
|
||||
var df = this.grid.get_docfield(fieldname);
|
||||
if(txt===undefined) {
|
||||
var txt = frappe.format(this.doc[fieldname], this.grid.get_docfield(fieldname),
|
||||
var txt = frappe.format(this.doc[fieldname], df,
|
||||
null, this.frm.doc);
|
||||
}
|
||||
|
||||
|
|
@ -1094,6 +1109,9 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
var column = this.columns[fieldname];
|
||||
if(column) {
|
||||
column.static_area.html(txt || "");
|
||||
if(df.reqd) {
|
||||
column.toggleClass('error', !!(txt===null || txt===''));
|
||||
}
|
||||
}
|
||||
|
||||
// reset field value
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
|
|||
|
||||
var save = function() {
|
||||
check_name(function() {
|
||||
$(frm.wrapper).addClass('validated-form');
|
||||
if(check_mandatory()) {
|
||||
_call({
|
||||
method: "frappe.desk.form.save.savedocs",
|
||||
|
|
@ -132,10 +133,21 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
|
|||
|
||||
}
|
||||
});
|
||||
if(error_fields.length)
|
||||
msgprint(__('Mandatory fields required in {0}', [(doc.parenttype
|
||||
? (__(frappe.meta.docfield_map[doc.parenttype][doc.parentfield].label) + ' ('+ __("Table") + ')')
|
||||
: __(doc.doctype))]) + '<br> <ul><li>' + error_fields.join('</li><li>') + "</ul>");
|
||||
if(error_fields.length) {
|
||||
if(doc.parenttype) {
|
||||
var message = __('Mandatory fields required in table {0}, Row {1}',
|
||||
[__(frappe.meta.docfield_map[doc.parenttype][doc.parentfield].label).bold(), doc.idx]);
|
||||
} else {
|
||||
var message = __('Mandatory fields required in {0}', [__(doc.doctype)]);
|
||||
|
||||
}
|
||||
message = message + '<br><br><ul><li>' + error_fields.join('</li><li>') + "</ul>";
|
||||
msgprint({
|
||||
message: message,
|
||||
indicator: 'red',
|
||||
title: __('Missing Fields')
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return !has_errors;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@
|
|||
<span class="open-notification hidden"
|
||||
title="{{ __("Open {0}", [__(doctype)])}}"></span>
|
||||
{% if !internal_links[doctype] %}
|
||||
<button class="btn btn-new btn-default btn-xs pull-right hidden"
|
||||
data-doctype="{{ doctype }}">{{ __("New") }}</button>
|
||||
<button class="btn btn-new btn-default btn-xs hidden"
|
||||
data-doctype="{{ doctype }}">
|
||||
<i class="octicon octicon-plus" style="font-size: 12px;"></i></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% } %}
|
||||
|
|
|
|||
|
|
@ -457,6 +457,7 @@ _f.Frm.prototype.refresh = function(docname) {
|
|||
}
|
||||
|
||||
if(is_a_different_doc) {
|
||||
$(this.wrapper).removeClass('validated-form')
|
||||
if(this.show_print_first && this.doc.docstatus===1) {
|
||||
// show print view
|
||||
this.print_doc();
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ a.badge-hover& {
|
|||
}
|
||||
|
||||
.msgprint {
|
||||
margin: 15px 0px;
|
||||
// margin: 15px 0px;
|
||||
// text-align: center;
|
||||
|
||||
pre {
|
||||
|
|
|
|||
|
|
@ -324,6 +324,7 @@ textarea.form-control {
|
|||
color:#fff;
|
||||
border-radius:10px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* on small screens, show only icons on top */
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.form-grid.error {
|
||||
border-color: @indicator-red;
|
||||
}
|
||||
|
||||
.grid-heading-row {
|
||||
border-bottom: 1px solid @border-color;
|
||||
background-color: @panel-bg;
|
||||
|
|
@ -69,7 +73,15 @@
|
|||
padding: 10px 15px;
|
||||
max-height: 200px;
|
||||
border-right: 1px solid @border-color;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.grid-static-col.bold {
|
||||
font-weight: bold;
|
||||
background-color: @extra-light-yellow;
|
||||
}
|
||||
|
||||
.validated-form .grid-static-col.error {
|
||||
background-color: @label-danger-bg;
|
||||
}
|
||||
|
||||
.grid-static-col {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
<h3>{{ title }}</h3>
|
||||
{% if title %}<h3>{{ title }}</h3>{% endif %}
|
||||
<p>{{_("Dear")}} {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p>
|
||||
<p>{{_("A new account has been created for you at {0}").format(site_url)}}.</p>
|
||||
<p>{{_("Your login id is")}}: <b>{{ user }}</b>
|
||||
<p>{{_("Click on the link below to complete your registration and set a new password")}}.</p>
|
||||
<p><b><a href="{{ link }}">{{_("Complete Registration")}}</a></b></p>
|
||||
|
||||
<p style="margin: 30px 0px;">
|
||||
<a href="{{ link }}" rel="nofollow" style="padding: 8px 20px; background-color: #7575ff; color: #fff; border-radius: 4px; text-decoration: none; line-height: 1; border-bottom: 3px solid rgba(0, 0, 0, 0.2); font-size: 14px; font-weight: 200;">{{ _("Complete Registration") }}</a>
|
||||
</p>
|
||||
|
||||
{% if user_fullname != "Administrator" %}
|
||||
<br>
|
||||
<p>{{_("Thank you")}},<br>
|
||||
{{ user_fullname }}</p>
|
||||
|
||||
{% endif %}
|
||||
<br>
|
||||
<p style="font-size: 85%;">{{_("You can also copy-paste this link in your browser")}} <a href="{{ link }}">{{ link }}</a></p>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@
|
|||
from __future__ import unicode_literals
|
||||
from werkzeug.test import Client
|
||||
import os, re, urllib, sys, json, md5, requests, traceback
|
||||
import bleach, bleach_whitelist
|
||||
from html5lib.sanitizer import HTMLSanitizer
|
||||
from markdown2 import markdown as _markdown
|
||||
from .html_utils import sanitize_html
|
||||
|
||||
import frappe
|
||||
from frappe.utils.identicon import Identicon
|
||||
|
|
@ -441,51 +440,6 @@ def watch(path, handler=None, debug=True):
|
|||
observer.stop()
|
||||
observer.join()
|
||||
|
||||
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
|
||||
|
||||
Does not sanitize JSON, as it could lead to future problems
|
||||
"""
|
||||
if not isinstance(html, basestring):
|
||||
return html
|
||||
|
||||
elif is_json(html):
|
||||
return html
|
||||
|
||||
tags = (HTMLSanitizer.acceptable_elements + HTMLSanitizer.svg_elements
|
||||
+ ["html", "head", "meta", "link", "body", "iframe", "style", "o:p"])
|
||||
attributes = {"*": HTMLSanitizer.acceptable_attributes, "svg": HTMLSanitizer.svg_attributes}
|
||||
styles = bleach_whitelist.all_styles
|
||||
strip_comments = False
|
||||
|
||||
# retuns html with escaped tags, escaped orphan >, <, etc.
|
||||
escaped_html = bleach.clean(html, tags=tags, attributes=attributes, styles=styles, strip_comments=strip_comments)
|
||||
|
||||
if linkify:
|
||||
# based on bleach.clean
|
||||
class s(bleach.BleachSanitizer):
|
||||
allowed_elements = tags
|
||||
allowed_attributes = attributes
|
||||
allowed_css_properties = styles
|
||||
strip_disallowed_elements = False
|
||||
strip_html_comments = strip_comments
|
||||
|
||||
escaped_html = bleach.linkify(escaped_html, tokenizer=s)
|
||||
|
||||
return escaped_html
|
||||
|
||||
def is_json(text):
|
||||
try:
|
||||
json.loads(text)
|
||||
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
||||
def markdown(text, sanitize=True, linkify=True):
|
||||
html = _markdown(text)
|
||||
|
||||
|
|
|
|||
|
|
@ -714,8 +714,8 @@ def get_filter(doctype, f):
|
|||
# if operator is missing
|
||||
f.operator = "="
|
||||
|
||||
valid_operators = ("=", "!=", ">", "<", ">=", "<=", "like", "not like", "in", "not in", "Between")
|
||||
if f.operator not in valid_operators:
|
||||
valid_operators = ("=", "!=", ">", "<", ">=", "<=", "like", "not like", "in", "not in", "between")
|
||||
if f.operator.lower() not in valid_operators:
|
||||
frappe.throw("Operator must be one of {0}".format(", ".join(valid_operators)))
|
||||
|
||||
|
||||
|
|
|
|||
131
frappe/utils/html_utils.py
Normal file
131
frappe/utils/html_utils.py
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import json
|
||||
import bleach, bleach_whitelist
|
||||
|
||||
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
|
||||
|
||||
Does not sanitize JSON, as it could lead to future problems
|
||||
"""
|
||||
if not isinstance(html, basestring):
|
||||
return html
|
||||
|
||||
elif is_json(html):
|
||||
return html
|
||||
|
||||
tags = (acceptable_elements + svg_elements + mathml_elements
|
||||
+ ["html", "head", "meta", "link", "body", "iframe", "style", "o:p"])
|
||||
attributes = {"*": acceptable_attributes, 'svg': svg_attributes}
|
||||
styles = bleach_whitelist.all_styles
|
||||
strip_comments = False
|
||||
|
||||
# retuns html with escaped tags, escaped orphan >, <, etc.
|
||||
escaped_html = bleach.clean(html, tags=tags, attributes=attributes, styles=styles, strip_comments=strip_comments)
|
||||
|
||||
if linkify:
|
||||
escaped_html = bleach.linkify(escaped_html, callbacks=[])
|
||||
|
||||
return escaped_html
|
||||
|
||||
|
||||
def is_json(text):
|
||||
try:
|
||||
json.loads(text)
|
||||
except ValueError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# adapted from https://raw.githubusercontent.com/html5lib/html5lib-python/4aa79f113e7486c7ec5d15a6e1777bfe546d3259/html5lib/sanitizer.py
|
||||
|
||||
acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area',
|
||||
'article', 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button',
|
||||
'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
|
||||
'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
|
||||
'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
|
||||
'figcaption', 'figure', 'footer', 'font', 'form', 'header', 'h1',
|
||||
'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input', 'ins',
|
||||
'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
|
||||
'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
|
||||
'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select',
|
||||
'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong',
|
||||
'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
|
||||
'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video']
|
||||
|
||||
mathml_elements = ['maction', 'math', 'merror', 'mfrac', 'mi',
|
||||
'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom',
|
||||
'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle', 'msub',
|
||||
'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder',
|
||||
'munderover', 'none']
|
||||
|
||||
svg_elements = ['a', 'animate', 'animateColor', 'animateMotion',
|
||||
'animateTransform', 'clipPath', 'circle', 'defs', 'desc', 'ellipse',
|
||||
'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
|
||||
'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph',
|
||||
'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect',
|
||||
'set', 'stop', 'svg', 'switch', 'text', 'title', 'tspan', 'use']
|
||||
|
||||
acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
|
||||
'action', 'align', 'alt', 'autocomplete', 'autofocus', 'axis',
|
||||
'background', 'balance', 'bgcolor', 'bgproperties', 'border',
|
||||
'bordercolor', 'bordercolordark', 'bordercolorlight', 'bottompadding',
|
||||
'cellpadding', 'cellspacing', 'ch', 'challenge', 'char', 'charoff',
|
||||
'choff', 'charset', 'checked', 'cite', 'class', 'clear', 'color',
|
||||
'cols', 'colspan', 'compact', 'contenteditable', 'controls', 'coords',
|
||||
'data', 'datafld', 'datapagesize', 'datasrc', 'datetime', 'default',
|
||||
'delay', 'dir', 'disabled', 'draggable', 'dynsrc', 'enctype', 'end',
|
||||
'face', 'for', 'form', 'frame', 'galleryimg', 'gutter', 'headers',
|
||||
'height', 'hidefocus', 'hidden', 'high', 'href', 'hreflang', 'hspace',
|
||||
'icon', 'id', 'inputmode', 'ismap', 'keytype', 'label', 'leftspacing',
|
||||
'lang', 'list', 'longdesc', 'loop', 'loopcount', 'loopend',
|
||||
'loopstart', 'low', 'lowsrc', 'max', 'maxlength', 'media', 'method',
|
||||
'min', 'multiple', 'name', 'nohref', 'noshade', 'nowrap', 'open',
|
||||
'optimum', 'pattern', 'ping', 'point-size', 'poster', 'pqg', 'preload',
|
||||
'prompt', 'radiogroup', 'readonly', 'rel', 'repeat-max', 'repeat-min',
|
||||
'replace', 'required', 'rev', 'rightspacing', 'rows', 'rowspan',
|
||||
'rules', 'scope', 'selected', 'shape', 'size', 'span', 'src', 'start',
|
||||
'step', 'style', 'summary', 'suppress', 'tabindex', 'target',
|
||||
'template', 'title', 'toppadding', 'type', 'unselectable', 'usemap',
|
||||
'urn', 'valign', 'value', 'variable', 'volume', 'vspace', 'vrml',
|
||||
'width', 'wrap', 'xml:lang']
|
||||
|
||||
mathml_attributes = ['actiontype', 'align', 'columnalign', 'columnalign',
|
||||
'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'depth',
|
||||
'display', 'displaystyle', 'equalcolumns', 'equalrows', 'fence',
|
||||
'fontstyle', 'fontweight', 'frame', 'height', 'linethickness', 'lspace',
|
||||
'mathbackground', 'mathcolor', 'mathvariant', 'mathvariant', 'maxsize',
|
||||
'minsize', 'other', 'rowalign', 'rowalign', 'rowalign', 'rowlines',
|
||||
'rowspacing', 'rowspan', 'rspace', 'scriptlevel', 'selection',
|
||||
'separator', 'stretchy', 'width', 'width', 'xlink:href', 'xlink:show',
|
||||
'xlink:type', 'xmlns', 'xmlns:xlink']
|
||||
|
||||
svg_attributes = ['accent-height', 'accumulate', 'additive', 'alphabetic',
|
||||
'arabic-form', 'ascent', 'attributeName', 'attributeType',
|
||||
'baseProfile', 'bbox', 'begin', 'by', 'calcMode', 'cap-height',
|
||||
'class', 'clip-path', 'color', 'color-rendering', 'content', 'cx',
|
||||
'cy', 'd', 'dx', 'dy', 'descent', 'display', 'dur', 'end', 'fill',
|
||||
'fill-opacity', 'fill-rule', 'font-family', 'font-size',
|
||||
'font-stretch', 'font-style', 'font-variant', 'font-weight', 'from',
|
||||
'fx', 'fy', 'g1', 'g2', 'glyph-name', 'gradientUnits', 'hanging',
|
||||
'height', 'horiz-adv-x', 'horiz-origin-x', 'id', 'ideographic', 'k',
|
||||
'keyPoints', 'keySplines', 'keyTimes', 'lang', 'marker-end',
|
||||
'marker-mid', 'marker-start', 'markerHeight', 'markerUnits',
|
||||
'markerWidth', 'mathematical', 'max', 'min', 'name', 'offset',
|
||||
'opacity', 'orient', 'origin', 'overline-position',
|
||||
'overline-thickness', 'panose-1', 'path', 'pathLength', 'points',
|
||||
'preserveAspectRatio', 'r', 'refX', 'refY', 'repeatCount',
|
||||
'repeatDur', 'requiredExtensions', 'requiredFeatures', 'restart',
|
||||
'rotate', 'rx', 'ry', 'slope', 'stemh', 'stemv', 'stop-color',
|
||||
'stop-opacity', 'strikethrough-position', 'strikethrough-thickness',
|
||||
'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap',
|
||||
'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity',
|
||||
'stroke-width', 'systemLanguage', 'target', 'text-anchor', 'to',
|
||||
'transform', 'type', 'u1', 'u2', 'underline-position',
|
||||
'underline-thickness', 'unicode', 'unicode-range', 'units-per-em',
|
||||
'values', 'version', 'viewBox', 'visibility', 'width', 'widths', 'x',
|
||||
'x-height', 'x1', 'x2', 'xlink:actuate', 'xlink:arcrole',
|
||||
'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type',
|
||||
'xml:base', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'y',
|
||||
'y1', 'y2', 'zoomAndPan']
|
||||
|
||||
|
|
@ -261,6 +261,7 @@ def add_system_manager(email, first_name=None, last_name=None, send_welcome_emai
|
|||
"user_type": "System User",
|
||||
"send_welcome_email": 1 if send_welcome_email else 0
|
||||
})
|
||||
|
||||
user.insert()
|
||||
|
||||
# add roles
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue