Merge branch 'develop' of https://github.com/frappe/frappe into custom_append_to

This commit is contained in:
Himanshu Warekar 2020-01-16 13:22:31 +05:30
commit 092dcde236
20 changed files with 124 additions and 44 deletions

View file

@ -91,5 +91,6 @@ install:
- bench build --app frappe
after_script:
- pip install coverage==4.5.4
- pip install python-coveralls
- coveralls -b apps/frappe -d ../../sites/.coverage

View file

@ -11,7 +11,7 @@
<tbody>
{% for j in jobs %}
<tr>
<td><span class="indicator {{ j.color }}" title="{{ j.get_status() }}">{{ j.queue.split(".").slice(-1)[0] }}</span></td>
<td><span class="indicator {{ j.color }}" title="{{ j.status }}">{{ j.queue.split(".").slice(-1)[0] }}</span></td>
<td style="overflow: auto;">
<div>
{{ frappe.utils.encode_tags(j.job_name) }}

View file

@ -17,6 +17,14 @@ frappe.ui.form.on("Customize Form", {
};
});
frm.set_query("default_print_format", function() {
return {
filters: {
'print_format_type': ['!=', 'JS']
}
}
});
$(frm.wrapper).on("grid-row-render", function(e, grid_row) {
if(grid_row.doc && grid_row.doc.fieldtype=="Section Break") {
$(grid_row.row).css({"font-weight": "bold"});

View file

@ -114,9 +114,8 @@ frappe.ui.form.on('Dashboard Chart', {
} else {
// standard filters
if (frm.doc.document_type) {
// allow all link and select fields as filters
frm.chart_filters = [];
frappe.model.with_doctype(frm.doc.document_type, () => {
frm.chart_filters = [];
frappe.get_meta(frm.doc.document_type).fields.map(df => {
if (['Link', 'Select'].includes(df.fieldtype)) {
let _df = copy_dict(df);
@ -131,8 +130,8 @@ frappe.ui.form.on('Dashboard Chart', {
frm.chart_filters.push(_df);
}
frm.trigger('render_filters_table');
});
frm.trigger('render_filters_table');
});
}
}
@ -158,7 +157,7 @@ frappe.ui.form.on('Dashboard Chart', {
let filters = JSON.parse(frm.doc.filters_json || '{}');
var filters_set = false;
fields.map( f => {
fields.map(f => {
if (filters[f.fieldname]) {
const filter_row = $(`<tr><td>${f.label}</td><td>${filters[f.fieldname] || ""}</td></tr>`);
table.find('tbody').append(filter_row);

View file

@ -229,7 +229,7 @@ def get_prepared_report_result(report, filters, dn="", user=None):
"status": "Completed",
"filters": json.dumps(filters),
"owner": user,
"report_name": report.custom_report or report.report_name
"report_name": report.get('custom_report') or report.get('report_name')
},
order_by = 'creation desc'
)

View file

@ -76,7 +76,7 @@ class StripeSettings(Document):
def create_charge_on_stripe(self):
import stripe
try:
charge = stripe.Charge.create(amount=cint(flt(self.data.amount)*100), currency=self.data.currency, source=self.data.stripe_token_id, description=self.data.description)
charge = stripe.Charge.create(amount=cint(flt(self.data.amount)*100), currency=self.data.currency, source=self.data.stripe_token_id, description=self.data.description, receipt_email=self.data.payer_email)
if charge.captured == True:
self.integration_request.db_set('status', 'Completed', update_modified=False)

View file

@ -66,6 +66,10 @@ frappe.ui.form.on('Webhook', {
webhook_doctype: (frm) => {
frappe.webhook.set_fieldname_select(frm);
},
enable_security: (frm) => {
frm.toggle_reqd('webhook_secret', frm.doc.enable_security);
}
});

View file

@ -19,6 +19,9 @@
"request_url",
"cb_webhook",
"request_structure",
"sb_security",
"enable_security",
"webhook_secret",
"sb_webhook_headers",
"webhook_headers",
"sb_webhook_data",
@ -127,10 +130,27 @@
"fieldtype": "Select",
"label": "Naming Series",
"options": "\nHOOK-.####"
},
{
"fieldname": "sb_security",
"fieldtype": "Section Break",
"label": "Webhook Security"
},
{
"default": "0",
"fieldname": "enable_security",
"fieldtype": "Check",
"label": "Enable Security"
},
{
"depends_on": "eval:doc.enable_security == 1",
"fieldname": "webhook_secret",
"fieldtype": "Password",
"label": "Webhook Secret"
}
],
"links": [],
"modified": "2020-01-06 02:51:07.997566",
"modified": "2020-01-13 01:53:04.459968",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Webhook",

View file

@ -4,7 +4,10 @@
from __future__ import unicode_literals
import base64
import datetime
import hashlib
import hmac
import json
from time import sleep
@ -16,6 +19,8 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils.jinja import validate_template
WEBHOOK_SECRET_HEADER = "X-Frappe-Webhook-Signature"
class Webhook(Document):
def validate(self):
@ -94,10 +99,23 @@ def enqueue_webhook(doc, webhook):
def get_webhook_headers(doc, webhook):
headers = {}
if webhook.enable_security:
data = get_webhook_data(doc, webhook)
signature = base64.b64encode(
hmac.new(
webhook.get_password("webhook_secret").encode("utf8"),
json.dumps(data).encode("utf8"),
hashlib.sha256
).digest()
)
headers[WEBHOOK_SECRET_HEADER] = signature
if webhook.webhook_headers:
for h in webhook.webhook_headers:
if h.get("key") and h.get("value"):
headers[h.get("key")] = h.get("value")
return headers

View file

@ -31,6 +31,10 @@ def login_via_office365(code, state):
def login_via_salesforce(code, state):
login_via_oauth2("salesforce", code, state, decoder=decoder_compat)
@frappe.whitelist(allow_guest=True)
def login_via_fairlogin(code, state):
login_via_oauth2("fairlogin", code, state, decoder=decoder_compat)
@frappe.whitelist(allow_guest=True)
def custom(code, state):
"""

View file

@ -3,7 +3,7 @@ import frappe
def execute():
if frappe.db.table_exists('Prepared Report'):
frappe.reload_doc("core", "doctype", "prepared_doctype")
frappe.reload_doc("core", "doctype", "prepared_report")
prepared_reports = frappe.get_all("Prepared Report")
for report in prepared_reports:
frappe.delete_doc("Prepared Report", report.name)

View file

@ -481,7 +481,7 @@ frappe.Application = Class.extend({
// Iterate over changelog
var change_log_dialog = frappe.msgprint({
message: frappe.render_template("change_log", {"change_log": change_log}),
title: __("Updated To New Version 🎉"),
title: __("Updated To A New Version 🎉"),
wide: true,
scroll: true
});

View file

@ -1,4 +1,6 @@
frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlCode.extend({
frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({
horizontal: false,
make_wrapper() {
// Create the elements for map area
this._super();

View file

@ -371,6 +371,10 @@ frappe.ui.form.Timeline = class Timeline {
c.sender = c.sender.split("<")[1].split(">")[0];
}
if (!c.doctype && ['Comment', 'Communication'].includes(c.communication_type)) {
c.doctype = c.communication_type;
}
c.user_info = frappe.user_info(c.sender);
c["delete"] = "";

View file

@ -65,26 +65,22 @@ export default class Grid {
<div class="small form-clickable-section grid-footer">
<div class="row">
<div class="col-sm-5 grid-buttons">
<button type="reset"
class="btn btn-xs btn-danger grid-remove-rows hidden"
<button class="btn btn-xs btn-danger grid-remove-rows hidden"
style="margin-right: 4px;"
data-action="delete_rows">
${__("Delete")}
</button>
<button type="reset"
class="btn btn-xs btn-danger grid-remove-all-rows hidden"
<button class="btn btn-xs btn-danger grid-remove-all-rows hidden"
style="margin-right: 4px;"
data-action="delete_all_rows">
${__("Delete All")}
</button>
<button type="reset"
class="grid-add-multiple-rows btn btn-xs btn-default hidden"
<button class="grid-add-multiple-rows btn btn-xs btn-default hidden"
style="margin-right: 4px;">
${__("Add Multiple")}</a>
</button>
<!-- hack to allow firefox include this in tabs -->
<button type="reset"
class="btn btn-xs btn-default grid-add-row">
<button class="btn btn-xs btn-default grid-add-row">
${__("Add Row")}
</button>
</div>

View file

@ -267,19 +267,24 @@ frappe.ui.form.Toolbar = Class.extend({
});
}
if(frappe.user_roles.includes("System Manager") && me.frm.meta.issingle === 0) {
this.page.add_menu_item(__("Customize"), function() {
if (frappe.user_roles.includes("System Manager") && me.frm.meta.issingle === 0) {
let is_doctype_form = me.frm.doctype === 'DocType';
let doctype = is_doctype_form ? me.frm.docname : me.frm.doctype;
let is_doctype_custom = is_doctype_form ? me.frm.doc.custom : false;
if (me.frm.meta && me.frm.meta.custom) {
frappe.set_route('Form', 'DocType', me.frm.doctype);
} else {
frappe.set_route('Form', 'Customize Form', {
doc_type: me.frm.doctype
});
}
}, true);
if (doctype != 'DocType' && !is_doctype_custom) {
this.page.add_menu_item(__("Customize"), function() {
if (me.frm.meta && me.frm.meta.custom) {
frappe.set_route('Form', 'DocType', doctype);
} else {
frappe.set_route('Form', 'Customize Form', {
doc_type: doctype
});
}
}, true);
}
if (frappe.boot.developer_mode===1) {
if (frappe.boot.developer_mode===1 && !is_doctype_form) {
// edit doctype
this.page.add_menu_item(__("Edit DocType"), function() {
frappe.set_route('Form', 'DocType', me.frm.doctype);

View file

@ -81,7 +81,11 @@ frappe.ui.GroupBy = class {
}
apply_settings(settings) {
this.groupby_select.val(settings.group_by);
// Extract fieldname from `tabdoctype`.`fieldname`
let group_by_fieldname = settings.group_by.split('.')[1].replace('`', '');
this.groupby_select.val(group_by_fieldname);
this.aggregate_function_select.val(settings.aggregate_function);
this.show_hide_aggregate_on();
this.aggregate_on_select.val(settings.aggregate_on);

View file

@ -86,7 +86,11 @@ export default class WebForm extends frappe.ui.FieldGroup {
}
setup_delete_button() {
this.add_button_to_header("Delete", "danger", () => this.delete());
this.add_button_to_header(
'<i class="fa fa-trash" aria-hidden="true"></i>',
"light",
() => this.delete()
);
}
setup_print_button() {

View file

@ -713,6 +713,7 @@ li.user-progress {
height: 60px;
width: 60px;
background-color: #fafbfc;
overflow: hidden;
.flex-text {
display: flex;

View file

@ -41,7 +41,7 @@ def main(app=None, module=None, doctype=None, verbose=False, tests=(),
xmloutput_fh = None
if junit_xml_output:
xmloutput_fh = open(junit_xml_output, 'w')
xmloutput_fh = open(junit_xml_output, 'wb')
unittest_runner = xmlrunner_wrapper(xmloutput_fh)
else:
unittest_runner = unittest.TextTestRunner
@ -68,11 +68,11 @@ def main(app=None, module=None, doctype=None, verbose=False, tests=(),
frappe.get_attr(fn)()
if doctype:
ret = run_tests_for_doctype(doctype, verbose, tests, force, profile)
ret = run_tests_for_doctype(doctype, verbose, tests, force, profile, junit_xml_output=junit_xml_output)
elif module:
ret = run_tests_for_module(module, verbose, tests, profile)
ret = run_tests_for_module(module, verbose, tests, profile, junit_xml_output=junit_xml_output)
else:
ret = run_all_tests(app, verbose, profile, ui_tests, failfast=failfast)
ret = run_all_tests(app, verbose, profile, ui_tests, failfast=failfast, junit_xml_output=junit_xml_output)
if frappe.db: frappe.db.commit()
@ -109,7 +109,7 @@ class TimeLoggingTestResult(unittest.TextTestResult):
super(TimeLoggingTestResult, self).addSuccess(test)
def run_all_tests(app=None, verbose=False, profile=False, ui_tests=False, failfast=False):
def run_all_tests(app=None, verbose=False, profile=False, ui_tests=False, failfast=False, junit_xml_output=False):
import os
apps = [app] if app else frappe.get_installed_apps()
@ -130,11 +130,16 @@ def run_all_tests(app=None, verbose=False, profile=False, ui_tests=False, failfa
_add_test(app, path, filename, verbose,
test_suite, ui_tests)
if junit_xml_output:
runner = unittest_runner(verbosity=1+(verbose and 1 or 0), failfast=failfast)
else:
runner = unittest_runner(resultclass=TimeLoggingTestResult, verbosity=1+(verbose and 1 or 0), failfast=failfast)
if profile:
pr = cProfile.Profile()
pr.enable()
out = unittest_runner(resultclass=TimeLoggingTestResult, verbosity=1+(verbose and 1 or 0), failfast=failfast).run(test_suite)
out = runner.run(test_suite)
if profile:
pr.disable()
@ -145,7 +150,7 @@ def run_all_tests(app=None, verbose=False, profile=False, ui_tests=False, failfa
return out
def run_tests_for_doctype(doctypes, verbose=False, tests=(), force=False, profile=False):
def run_tests_for_doctype(doctypes, verbose=False, tests=(), force=False, profile=False, junit_xml_output=False):
modules = []
if not isinstance(doctypes, (list, tuple)):
doctypes = [doctypes]
@ -163,17 +168,17 @@ def run_tests_for_doctype(doctypes, verbose=False, tests=(), force=False, profil
make_test_records(doctype, verbose=verbose, force=force)
modules.append(importlib.import_module(test_module))
return _run_unittest(modules, verbose=verbose, tests=tests, profile=profile)
return _run_unittest(modules, verbose=verbose, tests=tests, profile=profile, junit_xml_output=junit_xml_output)
def run_tests_for_module(module, verbose=False, tests=(), profile=False):
def run_tests_for_module(module, verbose=False, tests=(), profile=False, junit_xml_output=False):
module = importlib.import_module(module)
if hasattr(module, "test_dependencies"):
for doctype in module.test_dependencies:
make_test_records(doctype, verbose=verbose)
return _run_unittest(module, verbose=verbose, tests=tests, profile=profile)
return _run_unittest(module, verbose=verbose, tests=tests, profile=profile, junit_xml_output=junit_xml_output)
def _run_unittest(modules, verbose=False, tests=(), profile=False):
def _run_unittest(modules, verbose=False, tests=(), profile=False, junit_xml_output=False):
test_suite = unittest.TestSuite()
if not isinstance(modules, (list, tuple)):
@ -189,13 +194,18 @@ def _run_unittest(modules, verbose=False, tests=(), profile=False):
else:
test_suite.addTest(module_test_cases)
if junit_xml_output:
runner = unittest_runner(verbosity=1+(verbose and 1 or 0))
else:
runner = unittest_runner(resultclass=TimeLoggingTestResult, verbosity=1+(verbose and 1 or 0))
if profile:
pr = cProfile.Profile()
pr.enable()
frappe.flags.tests_verbose = verbose
out = unittest_runner(verbosity=1+(verbose and 1 or 0)).run(test_suite)
out = runner.run(test_suite)
if profile: