Merge branch 'develop' into bench_version_branch_commit

This commit is contained in:
barredterra 2021-07-05 20:26:08 +02:00
commit 93e5398d5d
36 changed files with 135 additions and 103 deletions

View file

@ -105,3 +105,5 @@ jobs:
- name: UI Tests
run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --headless --parallel --ci-build-id $GITHUB_RUN_ID
env:
CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb

View file

@ -4,13 +4,10 @@
# the repo. Unless a later match takes precedence,
* @frappe/frappe-review-team
website/ @prssanna
web_form/ @prssanna
templates/ @surajshetty3416
www/ @surajshetty3416
integrations/ @leela
patches/ @surajshetty3416
dashboard/ @prssanna
email/ @leela
event_streaming/ @ruchamahabal
data_import* @netchampfaris

View file

@ -661,7 +661,7 @@ def run_ui_tests(context, app, headless=False, parallel=True, ci_build_id=None):
frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 --no-lockfile")
# run for headless mode
run_or_open = 'run --browser firefox --record --key 4a48f41c-11b3-425b-aa88-c58048fa69eb' if headless else 'open'
run_or_open = 'run --browser firefox --record' if headless else 'open'
command = '{site_env} {password_env} {cypress} {run_or_open}'
formatted_command = command.format(site_env=site_env, password_env=password_env, cypress=cypress_path, run_or_open=run_or_open)

View file

@ -85,8 +85,6 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
if attachments:
add_attachments(comm.name, attachments)
frappe.db.commit()
if cint(send_email):
if not comm.get_outgoing_email_account():
frappe.throw(msg=OUTGOING_EMAIL_ACCOUNT_MISSING, exc=frappe.OutgoingEmailError)

View file

@ -25,7 +25,6 @@ def get_event_conditions(doctype, filters=None):
@frappe.whitelist()
def get_events(doctype, start, end, field_map, filters=None, fields=None):
field_map = frappe._dict(json.loads(field_map))
fields = frappe.parse_json(fields)
@ -36,8 +35,7 @@ def get_events(doctype, start, end, field_map, filters=None, fields=None):
"color": d.fieldname
})
if filters:
filters = json.loads(filters or '')
filters = json.loads(filters) if filters else []
if not fields:
fields = [field_map.start, field_map.end, field_map.title, 'name']
@ -52,5 +50,5 @@ def get_events(doctype, start, end, field_map, filters=None, fields=None):
[doctype, start_date, '<=', end],
[doctype, end_date, '>=', start],
]
fields = list({field for field in fields if field})
return frappe.get_list(doctype, fields=fields, filters=filters)

View file

@ -368,6 +368,7 @@ def get_desktop_page(page):
'allow_customization': not wspace.doc.disable_user_customization
}
except DoesNotExistError:
frappe.log_error(frappe.get_traceback())
return {}
@frappe.whitelist()

View file

@ -43,19 +43,19 @@ class Workspace(Document):
def get_link_groups(self):
cards = []
current_card = {
current_card = frappe._dict({
"label": "Link",
"type": "Card Break",
"icon": None,
"hidden": False,
}
})
card_links = []
for link in self.links:
link = link.as_dict()
if link.type == "Card Break":
if card_links and (not current_card.only_for or current_card.only_for == frappe.get_system_settings('country')):
if card_links and (not current_card.only_for or current_card.only_for == frappe.get_system_settings('country')):
current_card['links'] = card_links
cards.append(current_card)

View file

@ -83,11 +83,15 @@ class BaseDocument(object):
@property
def meta(self):
if not hasattr(self, "_meta"):
if not getattr(self, "_meta", None):
self._meta = frappe.get_meta(self.doctype)
return self._meta
def __getstate__(self):
self._meta = None
return self.__dict__
def update(self, d):
""" Update multiple fields of a doctype using a dictionary of key-value pairs.

View file

@ -113,22 +113,20 @@ frappe.ui.form.PrintView = class {
},
).$input;
this.letterhead_selector = this.add_sidebar_item(
this.letterhead_selector_df = this.add_sidebar_item(
{
fieldtype: 'Select',
fieldtype: 'Autocomplete',
fieldname: 'letterhead',
label: __('Select Letterhead'),
options: [
this.get_default_option_for_select(__('Select Letterhead')),
__('No Letterhead')
],
placeholder: __('Select Letterhead'),
options: [__('No Letterhead')],
change: () => this.preview(),
default: this.print_settings.with_letterhead
? __('No Letterhead')
: __('Select Letterhead')
},
).$input;
);
this.letterhead_selector = this.letterhead_selector_df.$input;
this.sidebar_dynamic_section = $(
`<div class="dynamic-settings"></div>`
).appendTo(this.sidebar);
@ -336,23 +334,19 @@ frappe.ui.form.PrintView = class {
}
set_letterhead_options() {
let letterhead_options = [
this.get_default_option_for_select(__('Select Letterhead')),
__('No Letterhead')
];
let letterhead_options = [__('No Letterhead')];
let default_letterhead;
let doc_letterhead = this.frm.doc.letter_head;
return frappe.db
.get_list('Letter Head', { fields: ['name', 'is_default'] })
.get_list('Letter Head', { fields: ['name', 'is_default'], limit: 0 })
.then((letterheads) => {
this.letterhead_selector.empty();
letterheads.map((letterhead) => {
if (letterhead.is_default) default_letterhead = letterhead.name;
return letterhead_options.push(letterhead.name);
});
this.letterhead_selector.add_options(letterhead_options);
this.letterhead_selector_df.set_data(letterhead_options);
let selected_letterhead = doc_letterhead || default_letterhead;
if (selected_letterhead)
this.letterhead_selector.val(selected_letterhead);

View file

@ -10,7 +10,7 @@ frappe.db = {
if (!args.fields) {
args.fields = ['name'];
}
if (!args.limit) {
if (!('limit' in args)) {
args.limit = 20;
}
return new Promise ((resolve) => {

View file

@ -992,7 +992,7 @@ frappe.ui.form.Form = class FrappeForm {
}
frappe.re_route[frappe.router.get_sub_path()] = `${encodeURIComponent(frappe.router.slug(this.doctype))}/${encodeURIComponent(name)}`;
frappe.set_route('Form', this.doctype, name);
!frappe._from_link && frappe.set_route('Form', this.doctype, name);
}
// ACTIONS

View file

@ -543,7 +543,7 @@ frappe.ui.form.Layout = class Layout {
} else if (expression.substr(0, 5)=='eval:') {
try {
out = frappe.utils.eval(expression.substr(5), { doc });
out = frappe.utils.eval(expression.substr(5), { doc, parent });
if (parent && parent.istable && expression.includes('is_submittable')) {
out = true;
}

View file

@ -11,7 +11,7 @@ frappe.ui.form.Attachments = class Attachments {
this.parent.find(".add-attachment-btn").click(function() {
me.new_attachment();
});
this.add_attachment_wrapper = this.parent.find(".add_attachment").parent();
this.add_attachment_wrapper = this.parent.find(".add-attachment-btn");
this.attachments_label = this.parent.find(".attachments-label");
}
max_reached(raise_exception=false) {
@ -39,7 +39,7 @@ frappe.ui.form.Attachments = class Attachments {
this.parent.find(".attachment-row").remove();
var max_reached = this.max_reached();
this.add_attachment_wrapper.toggleClass("hide", !max_reached);
this.add_attachment_wrapper.toggle(!max_reached);
// add attachment objects
var attachments = this.get_attachments();

View file

@ -5,7 +5,7 @@
{% } %}
<div class="col-md-4">
<div class="form-link-title">
<span>{{ transactions[i].label }}<span>
<span>{{ __(transactions[i].label) }}<span>
</div>
{% for (let j=0; j < transactions[i].items.length; j++) { %}
{% let doctype = transactions[i].items[j]; %}

View file

@ -5,7 +5,7 @@
{% } %}
<div class="col-md-4">
<div class="form-link-title">
<span>{{ reports[i].label }}</span>
<span>{{ __(reports[i].label) }}</span>
</div>
{% for (let j=0; j < reports[i].items.length; j++) { %}
{% let report = reports[i].items[j]; %}

View file

@ -116,10 +116,7 @@ frappe.ui.Capture = class {
})
.catch(err => {
if (this.options.error) {
const alert = `<span class="indicator red"/> ${
frappe.ui.Capture.ERR_MESSAGE
}`;
frappe.show_alert(alert, 3);
frappe.show_alert(frappe.ui.Capture.ERR_MESSAGE, 3);
}
throw err;

View file

@ -109,7 +109,7 @@ frappe.views.CalendarView = class CalendarView extends frappe.views.ListView {
frappe.views.Calendar = class Calendar {
constructor(options) {
$.extend(this, options);
this.field_map = {
this.field_map = this.field_map || {
"id": "name",
"start": "start",
"end": "end",

View file

@ -145,7 +145,7 @@ frappe.views.CommunicationComposer = class {
);
});
if (email_accounts.length > 1) {
if (email_accounts.length) {
fields.unshift({
label: __("From"),
fieldtype: "Select",
@ -728,7 +728,7 @@ frappe.views.CommunicationComposer = class {
const SALUTATION_END_COMMENT = "<!-- salutation-ends -->";
if (this.real_name && !message.includes(SALUTATION_END_COMMENT)) {
this.message = `
<p>${__('Dear')} ${this.real_name},</p>
<p>${__('Dear {0},', [this.real_name], 'Salutation in new email')},</p>
${SALUTATION_END_COMMENT}<br>
${message}
`;

View file

@ -64,6 +64,7 @@ export default class LinksWidget extends Widget {
const opts = {
name: item.link_to,
type: item.link_type,
doctype: item.doctype,
is_query_report: item.is_query_report
};

View file

@ -24,7 +24,7 @@
--blue-100: #D3E9FC;
--blue-50 : #F0F8FE;
--cyan-900: #006464;
--cyan-900: #006464;
--cyan-800: #007272;
--cyan-700: #008b8b;
--cyan-600: #02c5c5;
@ -179,6 +179,10 @@
--text-on-pink: var(--pink-500);
--text-on-cyan: var(--cyan-600);
--disabled-control-bg: var(--gray-50);
--control-bg: var(--gray-100);
--control-bg-on-gray: var(--gray-200);
--awesomplete-hover-bg: var(--control-bg);
// Other Colors
@ -208,5 +212,4 @@
--checkbox-right-margin: var(--margin-xs);
--checkbox-size: 14px;
--checkbox-focus-shadow: 0 0 0 2px var(--gray-300);
}

View file

@ -15,6 +15,10 @@
&--time-current-hours, &--time-current-minutes, &--time-current-seconds {
font-family: inherit;
&:after {
color: var(--text-color);
background-color: var(--fg-hover-color);
}
}
&--day-name {
@ -47,10 +51,13 @@
background: fade(#0089FF, 10%);
}
&.-focus- {
background-color: var(--fg-hover-color);
}
&.-selected-.-focus- {
background: fade(#0089FF, 90%);
}
}
&--time, &--buttons {
@ -67,8 +74,20 @@
&--time-row:first-child {
margin: 0;
}
&--pointer {
background: var(--fg-color);
border-top-right-radius: 2px;
border: 1px var(--border-color);
border-style: solid solid hidden hidden;
}
&--button {
color: var(--brand-color);
&:hover {
color: var(--text-color);
background-color: var(--fg-hover-color);
}
}
}
.datepicker--button {
color: var(--brand-color);
}

View file

@ -35,13 +35,14 @@
.ql-container.ql-snow {
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
overflow: hidden;
}
.ql-snow {
.ql-editor {
min-height: 400px;
max-height: 600px;
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
.ql-stroke {
stroke: var(--icon-stroke);

View file

@ -291,10 +291,18 @@ var verify_token = function (event) {
}
var request_otp = function (r) {
const otp_form_template = '{{ _("Verification") }}<input type="text" id="login_token" autocomplete="off" class="form-control" placeholder={{ _("Verification Code") }} required="" autofocus="">{{ _("Verify") }}';
$('.login-content').empty();
$('.login-content:visible').append(
$('<div>').attr({ 'id': 'twofactor_div' }).html(otp_form_template)
`<div id="twofactor_div">
<form class="form-verify">
<div class="page-card-head">
<span class="indicator blue" data-text="Verification">{{ _("Verification") }}</span>
</div>
<div id="otp_div"></div>
<input type="text" id="login_token" autocomplete="off" class="form-control" placeholder={{ _("Verification Code") }} required="" autofocus="">
<button class="btn btn-sm btn-primary btn-block mt-3" id="verify_token">{{ _("Verify") }}</button>
</form>
</div>`
);
// add event handler for submit button
verify_token();

View file

@ -1,20 +1,9 @@
<!-- base template for testing -->
<html>
<head>
{%- block style %}
{% if colocated_css -%}
<style>{{ colocated_css }}</style>
{%- endif %}
{%- endblock -%}
</head>
<body>
{% include "templates/includes/breadcrumbs.html" %}
<h1>This is for testing</h1>
{% block content %}{% endblock %}
{%- block script %}
{% if colocated_js -%}
<script>{{ colocated_js }}</script>
{%- endif %}
{%- endblock %}
</body>
</html>

View file

@ -0,0 +1,20 @@
<!-- base template for testing -->
<html>
<head>
{%- block style %}
{% if colocated_css -%}
<style>{{ colocated_css }}</style>
{%- endif %}
{%- endblock -%}
</head>
<body>
{% include "templates/includes/breadcrumbs.html" %}
<h1>This is for testing</h1>
{% block content %}{% endblock %}
{%- block script %}
{% if colocated_js -%}
<script>{{ colocated_js }}</script>
{%- endif %}
{%- endblock %}
</body>
</html>

View file

@ -244,14 +244,22 @@ class TestWebsite(unittest.TestCase):
self.assertIn("<script>console.log('test data');</script>", content)
self.assertIn("background-color: var(--bg-color);", content)
def test_raw_assets_are_loaded(self):
content = get_response_content('/_test/assets/js_asset.min.js')
# minified js files should not be passed through jinja renderer
self.assertEqual("//{% if title %} {{title}} {% endif %}\nconsole.log('in');", content)
content = get_response_content('/_test/assets/css_asset.css')
self.assertEqual("""body{color:red}""", content)
def test_breadcrumbs(self):
content = get_response_content('/_test/_test_folder/_test_page')
self.assertIn('<span itemprop="name">Test TOC</span>', content)
self.assertIn('<span itemprop="name">Test Folder</span>', content)
self.assertIn('<span itemprop="name"> Test Page</span>', content)
content = get_response_content('/_test/_test_folder/index')
self.assertIn('<span itemprop="name"> Test</span>', content)
self.assertIn('<span itemprop="name">Test TOC</span>', content)
self.assertIn('<span itemprop="name">Test Folder</span>', content)
def test_get_context_without_context_object(self):
content = get_response_content('/_test/_test_no_context')

View file

@ -70,7 +70,7 @@ class TemplatePage(BaseTemplatePage):
self.set_pymodule()
self.update_context()
self.setup_template()
self.setup_template_source()
self.load_colocated_files()
self.set_properties_from_source()
self.post_process_context()
@ -118,7 +118,7 @@ class TemplatePage(BaseTemplatePage):
if os.path.exists(os.path.join(self.app_path, self.pymodule_path)):
self.pymodule_name = self.app + "." + self.pymodule_path.replace(os.path.sep, ".")[:-3]
def setup_template(self):
def setup_template_source(self):
'''Setup template source, frontmatter and markdown conversion'''
self.source = self.get_raw_template()
self.extract_frontmatter()
@ -126,7 +126,6 @@ class TemplatePage(BaseTemplatePage):
def update_context(self):
self.set_page_properties()
self.set_properties_from_source()
self.context.build_version = frappe.utils.get_build_version()
if self.pymodule_name:
@ -150,8 +149,7 @@ class TemplatePage(BaseTemplatePage):
def set_page_properties(self):
self.context.base_template = self.context.base_template \
or get_base_template(self.path) \
or 'templates/web.html'
or get_base_template(self.path)
self.context.basepath = self.basepath
self.context.basename = self.basename
self.context.name = self.name
@ -203,13 +201,10 @@ class TemplatePage(BaseTemplatePage):
frappe.errprint(frappe.utils.get_traceback())
def render_template(self):
if self.source:
if self.template_path.endswith('min.js'):
html = self.source # static
else:
html = frappe.render_template(self.source, self.context)
elif self.template_path:
if self.path.endswith('min.js'):
html = self.get_raw_template() # static
else:
html = frappe.get_template(self.template_path).render(self.context)
return html

View file

@ -37,7 +37,7 @@ class PathResolver():
endpoint = resolve_path(self.path)
custom_renderers = self.get_custom_page_renderers()
renderers = custom_renderers + [StaticPage, WebFormPage, ListPage, DocumentPage, TemplatePage, PrintPage, NotFoundPage]
renderers = custom_renderers + [StaticPage, WebFormPage, DocumentPage, TemplatePage, ListPage, PrintPage, NotFoundPage]
for renderer in renderers:
renderer_instance = renderer(endpoint, 200)

View file

@ -270,7 +270,7 @@ def get_doctypes_with_web_view():
doctypes_with_web_view = frappe.get_all('DocType', fields=['name', 'module'],
filters=dict(has_web_view=1))
module_app_map = frappe.local.module_app
doctypes += [d.name for d in doctypes_with_web_view if module_app_map[frappe.scrub(d.module)] in installed_apps]
doctypes += [d.name for d in doctypes_with_web_view if module_app_map.get(frappe.scrub(d.module)) in installed_apps]
return doctypes
return frappe.cache().get_value('doctypes_with_web_view', _get)

View file

@ -1,3 +1,4 @@
{% extends base_template_path %}
{% block content %}
{% include "templates/includes/web_sidebar.html" %}
<p>Test content</p>

View file

@ -1,3 +1,3 @@
def get_context(context):
context.base_template_path = 'frappe/templates/test/_test_base.html'
context.base_template_path = 'frappe/templates/test/_test_base_breadcrumbs.html'
context.add_breadcrumbs = 1

View file

@ -1,9 +1,9 @@
---
title: Test TOC
title: Test Folder
add_breadcrumbs: 1
show_sidebar: 1
base_template: templates/web.html
---
# Index
{index}

View file

View file

@ -0,0 +1 @@
body{color:red}

View file

@ -0,0 +1,2 @@
//{% if title %} {{title}} {% endif %}
console.log('in');

View file

@ -1381,12 +1381,12 @@ component-bind@1.0.0:
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=
component-emitter@1.2.1, component-emitter@^1.2.0:
component-emitter@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
component-emitter@~1.3.0:
component-emitter@^1.2.0, component-emitter@~1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
@ -1713,28 +1713,14 @@ debug@2.6.9:
dependencies:
ms "2.0.0"
debug@^3.0.1:
debug@^3.0.1, debug@^3.1.0, debug@^3.2.6:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
debug@^3.1.0, debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
debug@^4.1.1, debug@~4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
debug@^4.2.0, debug@^4.3.1:
debug@^4.1.1, debug@^4.2.0, debug@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
@ -1748,6 +1734,13 @@ debug@~3.1.0:
dependencies:
ms "2.0.0"
debug@~4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
decamelize@^1.0.0, decamelize@^1.1.2, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@ -6425,11 +6418,11 @@ socket.io-client@2.4.0:
to-array "0.1.4"
socket.io-parser@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f"
integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==
version "3.3.2"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6"
integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==
dependencies:
component-emitter "1.2.1"
component-emitter "~1.3.0"
debug "~3.1.0"
isarray "2.0.1"