Merge branch 'develop' of https://github.com/frappe/frappe into rebrand-ui-fixes

This commit is contained in:
prssanna 2021-02-08 11:45:27 +05:30
commit 96f0bab661
25 changed files with 104 additions and 69 deletions

View file

@ -1,8 +1,14 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
"""
globals attached to frappe module
+ some utility functions that should probably be moved
Frappe - Low Code Open Source Framework in Python and JS
Frappe, pronounced fra-pay, is a full stack, batteries-included, web
framework written in Python and Javascript with MariaDB as the database.
It is the framework which powers ERPNext. It is pretty generic and can
be used to build database driven apps.
Read the documentation: https://frappeframework.com/docs
"""
from __future__ import unicode_literals, print_function

View file

@ -15,6 +15,8 @@ from frappe.model.document import Document
from frappe.core.doctype.communication.email import make
from frappe.utils.background_jobs import get_jobs
from frappe.automation.doctype.assignment_rule.assignment_rule import get_repeated
from frappe.contacts.doctype.contact.contact import get_contacts_linked_from
from frappe.contacts.doctype.contact.contact import get_contacts_linking_to
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
week_map = {'Monday': 0, 'Tuesday': 1, 'Wednesday': 2, 'Thursday': 3, 'Friday': 4, 'Saturday': 5, 'Sunday': 6}
@ -328,13 +330,8 @@ class AutoRepeat(Document):
def fetch_linked_contacts(self):
if self.reference_doctype and self.reference_document:
res = frappe.db.get_all('Contact',
fields=['email_id'],
filters=[
['Dynamic Link', 'link_doctype', '=', self.reference_doctype],
['Dynamic Link', 'link_name', '=', self.reference_document]
])
res = get_contacts_linking_to(self.reference_doctype, self.reference_document, fields=['email_id'])
res += get_contacts_linked_from(self.reference_doctype, self.reference_document, fields=['email_id'])
email_ids = list(set([d.email_id for d in res]))
if not email_ids:
frappe.msgprint(_('No contacts linked to document'), alert=True)

View file

@ -256,3 +256,27 @@ def get_contact_with_phone_number(number):
def get_contact_name(email_id):
contact = frappe.get_list("Contact Email", filters={"email_id": email_id}, fields=["parent"], limit=1)
return contact[0].parent if contact else None
def get_contacts_linking_to(doctype, docname, fields=None):
"""Return a list of contacts containing a link to the given document."""
return frappe.get_list('Contact', fields=fields, filters=[
['Dynamic Link', 'link_doctype', '=', doctype],
['Dynamic Link', 'link_name', '=', docname]
])
def get_contacts_linked_from(doctype, docname, fields=None):
"""Return a list of contacts that are contained in (linked from) the given document."""
link_fields = frappe.get_meta(doctype).get('fields', {
'fieldtype': 'Link',
'options': 'Contact'
})
if not link_fields:
return []
contact_names = frappe.get_value(doctype, docname, fieldname=[f.fieldname for f in link_fields])
if not contact_names:
return []
return frappe.get_list('Contact', fields=fields, filters={
'name': ('in', contact_names)
})

View file

@ -656,7 +656,7 @@ class DocType(Document):
flags = {"flags": re.ASCII} if six.PY3 else {}
# a DocType name should not start or end with an empty space
if re.match("^[ \t\n\r]+|[ \t\n\r]+$", name, **flags):
if re.search("^[ \t\n\r]+|[ \t\n\r]+$", name, **flags):
frappe.throw(_("DocType's name should not start or end with whitespace"), frappe.NameError)
# a DocType's name should not start with a number or underscore

View file

@ -302,7 +302,7 @@
"no_copy": 1
},
{
"default": "0",
"default": "1",
"fieldname": "logout_all_sessions",
"fieldtype": "Check",
"label": "Logout From All Devices After Changing Password"
@ -669,7 +669,7 @@
}
],
"max_attachments": 5,
"modified": "2021-01-02 11:21:50.507786",
"modified": "2021-02-01 16:11:06.037543",
"modified_by": "Administrator",
"module": "Core",
"name": "User",

View file

@ -562,6 +562,10 @@ def get_perm_info(role):
@frappe.whitelist(allow_guest=True)
def update_password(new_password, logout_all_sessions=0, key=None, old_password=None):
#validate key to avoid key input like ['like', '%'], '', ['in', ['']]
if key and not isinstance(key, str):
frappe.throw(_('Invalid key type'))
result = test_password_strength(new_password, key, old_password)
feedback = result.get("feedback", None)

View file

@ -86,7 +86,7 @@ def get_result(doc, filters, to_date=None):
filters = frappe.parse_json(filters)
if not filters:
filters = []
filters = []
if to_date:
filters.append([doc.document_type, 'creation', '<', to_date])
@ -107,9 +107,13 @@ def get_percentage_difference(doc, filters, result):
return
previous_result = calculate_previous_result(doc, filters)
difference = (result - previous_result)/100.0
return difference
if previous_result == 0:
return None
else:
if result == previous_result:
return 0
else:
return ((result/previous_result)-1)*100.0
def calculate_previous_result(doc, filters):
@ -197,4 +201,4 @@ def add_card_to_dashboard(args):
card.save()
dashboard.append('cards', dashboard_link)
dashboard.save()
dashboard.save()

View file

@ -297,8 +297,9 @@ def inline_style_in_html(html):
for app in apps:
path = 'assets/{0}/css/email.css'.format(app)
if os.path.exists(os.path.abspath(path)):
css_files.append(path)
css_files.append(path)
css_files = [css_file for css_file in css_files if os.path.exists(os.path.abspath(css_file))]
p = Premailer(html=html, external_styles=css_files, strip_important=False)

View file

@ -2729,11 +2729,11 @@
},
"Zimbabwe": {
"code": "zw",
"currency": "ZWD",
"currency_fraction": "Thebe",
"currency": "ZWL",
"currency_fraction": "Cent",
"currency_fraction_units": 100,
"currency_name": "Zimbabwe Dollar",
"currency_symbol": "P",
"currency_symbol": "ZWL$",
"number_format": "# ###.##",
"timezones": [
"Africa/Harare"

View file

@ -58,6 +58,11 @@ website_route_rules = [
{"from_route": "/kb/<category>", "to_route": "Help Article"},
{"from_route": "/newsletters", "to_route": "Newsletter"},
{"from_route": "/profile", "to_route": "me"},
{"from_route": "/app/<path:app_path>", "to_route": "app"},
]
website_redirects = [
{"source": r"/desk(.*)", "target": r"/app\1"},
]
base_template = "templates/base.html"

View file

@ -61,7 +61,7 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe
for module_name in frappe.local.app_modules.get(app_name) or []:
folder = os.path.dirname(frappe.get_module(app_name + "." + module_name).__file__)
get_doc_files(files, folder, force, sync_everything, verbose=verbose)
get_doc_files(files, folder)
l = len(files)
if l:
@ -77,7 +77,7 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe
# print each progress bar on new line
print()
def get_doc_files(files, start_path, force=0, sync_everything = False, verbose=False):
def get_doc_files(files, start_path):
"""walk and sync all doctypes and pages"""
# load in sequence - warning for devs

View file

@ -209,7 +209,7 @@ frappe.patches.v9_1.resave_domain_settings
frappe.patches.v9_1.revert_domain_settings
frappe.patches.v9_1.move_feed_to_activity_log
execute:frappe.delete_doc('Page', 'data-import-tool', ignore_missing=True)
frappe.patches.v10_0.reload_countries_and_currencies # 14-10-2020
frappe.patches.v10_0.reload_countries_and_currencies # 2021-02-03
frappe.patches.v10_0.refactor_social_login_keys
frappe.patches.v10_0.enable_chat_by_default_within_system_settings
frappe.patches.v10_0.remove_custom_field_for_disabled_domain

View file

@ -15,7 +15,7 @@ def execute():
rename_doc('DocType', 'Desk Shortcut', 'Workspace Shortcut', ignore_if_exists=True)
rename_doc('DocType', 'Desk Link', 'Workspace Link', ignore_if_exists=True)
frappe.reload_doc('desk', 'doctype', 'workspace')
frappe.reload_doc('desk', 'doctype', 'workspace_link')
frappe.reload_doc('desk', 'doctype', 'workspace_chart')
frappe.reload_doc('desk', 'doctype', 'workspace_shortcut')
frappe.reload_doc('desk', 'doctype', 'workspace', force=True)
frappe.reload_doc('desk', 'doctype', 'workspace_link', force=True)
frappe.reload_doc('desk', 'doctype', 'workspace_chart', force=True)
frappe.reload_doc('desk', 'doctype', 'workspace_shortcut', force=True)

View file

@ -1305,8 +1305,6 @@ class {
this.set_wrapper(selector ? selector : "body")
this.set_options(options)
// Load Emojis.
frappe.chat.emoji()
}
/**

View file

@ -127,13 +127,6 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
let display_value = frappe.format(value, this.df, { no_icon: true, inline: true }, doc);
this.disp_area && $(this.disp_area).html(display_value);
},
bind_change_event: function() {
var me = this;
this.$input && this.$input.on("change", this.change || function(e) {
me.parse_validate_and_set_in_model(me.get_input_value(), e);
});
},
set_label: function(label) {
if(label) this.df.label = label;

View file

@ -3,6 +3,7 @@ frappe.provide('frappe.phone_call');
frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
html_element: "input",
input_type: "text",
trigger_change_on_input_event: true,
make_input: function() {
if(this.$input) return;
@ -22,8 +23,20 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
this.has_input = true;
this.bind_change_event();
this.setup_autoname_check();
// somehow this event does not bubble up to document
// after v7, if you can debug, remove this
},
bind_change_event: function() {
const change_handler = e => {
if (this.change) this.change(e);
else {
let value = this.get_input_value();
this.parse_validate_and_set_in_model(value, e);
}
};
this.$input.on("change", change_handler);
if (this.trigger_change_on_input_event) {
// debounce to avoid repeated validations on value change
this.$input.on("input", frappe.utils.debounce(change_handler, 500));
}
},
setup_autoname_check: function() {
if (!this.df.parent) return;

View file

@ -1,5 +1,5 @@
frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({
trigger_change_on_input_event: false,
make_input: function() {
this._super();
this.make_picker();

View file

@ -9,6 +9,7 @@ import Awesomplete from 'awesomplete';
frappe.ui.form.recent_link_validations = {};
frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
trigger_change_on_input_event: false,
make_input: function() {
var me = this;
// line-height: 1 is for Mozilla 51, shows extra padding otherwise

View file

@ -564,13 +564,8 @@ frappe.ui.form.Form = class FrappeForm {
let me = this;
return new Promise((resolve, reject) => {
btn && $(btn).prop("disabled", true);
$(document.activeElement).blur();
frappe.ui.form.close_grid_form();
// let any pending js process finish
setTimeout(function() {
me.validate_and_save(save_action, callback, btn, on_error, resolve, reject);
}, 100);
me.validate_and_save(save_action, callback, btn, on_error, resolve, reject);
}).then(() => {
me.show_success_action();
}).catch((e) => {

View file

@ -152,9 +152,13 @@ frappe.ui.form.LinkSelector = Class.extend({
d = me.target.add_new_row();
},
() => frappe.timeout(0.1),
() => frappe.model.set_value(d.doctype, d.name, me.fieldname, value),
() => frappe.timeout(0.5),
() => frappe.model.set_value(d.doctype, d.name, me.qty_fieldname, data.qty),
() => {
let args = {};
args[me.fieldname] = value;
args[me.qty_fieldname] = data.qty;
return frappe.model.set_value(d.doctype, d.name, args);
},
() => frappe.show_alert(__("Added {0} ({1})", [value, data.qty]))
]);
}

View file

@ -362,9 +362,7 @@ $.extend(frappe.model, {
);
} else if (!opts.source_name && opts.frm) {
opts.source_name = opts.frm.doc.name;
// Allow opening a mapped doc without a source document name
} else if (!opts.frm) {
} else if (!opts.frm && !opts.source_name) {
opts.source_name = null;
}

View file

@ -191,9 +191,9 @@ window.strip = function (s, chars) {
window.lstrip = function lstrip(s, chars) {
if (!chars) chars = ['\n', '\t', ' '];
// strip left
var first_char = s.substr(0, 1);
let first_char = s.substr(0, 1);
while (in_list(chars, first_char)) {
var s = s.substr(1);
s = s.substr(1);
first_char = s.substr(0, 1);
}
return s;
@ -201,9 +201,9 @@ window.lstrip = function lstrip(s, chars) {
window.rstrip = function (s, chars) {
if (!chars) chars = ['\n', '\t', ' '];
var last_char = s.substr(s.length - 1);
let last_char = s.substr(s.length - 1);
while (in_list(chars, last_char)) {
let s = s.substr(0, s.length - 1);
s = s.substr(0, s.length - 1);
last_char = s.substr(s.length - 1);
}
return s;

View file

@ -231,9 +231,7 @@ export default class NumberCardWidget extends Widget {
let color_class = '';
return this.get_percentage_stats().then(() => {
if (this.percentage_stat == undefined) return;
if (this.percentage_stat == 0) {
if (this.percentage_stat == 0 || this.percentage_stat == undefined) {
color_class = 'grey-stat';
} else if (this.percentage_stat > 0) {
caret_html =
@ -258,6 +256,7 @@ export default class NumberCardWidget extends Widget {
const stats_qualifier = stats_qualifier_map[this.card_doc.stats_time_interval];
let get_stat = () => {
if (this.percentage_stat == undefined) return NaN;
const parts = this.percentage_stat.split(' ');
const symbol = parts[1] || '';
return Math.abs(parts[0]) + ' ' + symbol;

View file

@ -252,13 +252,6 @@ def resolve_path(path):
if path != "index":
path = resolve_from_map(path)
if path.startswith("app"):
path = "app"
# to keep backward compatibility
if path.startswith("desk"):
path = "app"
return path
def resolve_from_map(path):

View file

@ -1,7 +1,7 @@
Babel==2.6.0
beautifulsoup4==4.8.2
bleach-whitelist==0.0.10
bleach==3.1.4
bleach==3.3.0
boto3==1.10.18
braintree==3.57.1
chardet==3.0.4
@ -27,7 +27,7 @@ html5lib==1.0.1
ipython==7.14.0
Jinja2==2.11.3
ldap3==2.7
markdown2==2.3.9
markdown2==2.4.0
maxminddb-geolite2==2018.703
ndg-httpsclient==0.5.1
num2words==0.5.10