@@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json | |||||
from .exceptions import * | from .exceptions import * | ||||
from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template | from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template | ||||
__version__ = '8.6.5' | |||||
__version__ = '8.6.6' | |||||
__title__ = "Frappe Framework" | __title__ = "Frappe Framework" | ||||
local = Local() | local = Local() | ||||
@@ -8,13 +8,14 @@ import unittest | |||||
# test_records = frappe.get_test_records('DocType') | # test_records = frappe.get_test_records('DocType') | ||||
class TestDocType(unittest.TestCase): | class TestDocType(unittest.TestCase): | ||||
def new_doctype(self, name): | |||||
def new_doctype(self, name, unique=0): | |||||
return frappe.get_doc({ | return frappe.get_doc({ | ||||
"doctype": "DocType", | "doctype": "DocType", | ||||
"module": "Core", | "module": "Core", | ||||
"custom": 1, | "custom": 1, | ||||
"fields": [{"label": "Some Field", "fieldname": "some_fieldname", "fieldtype": "Data"}], | |||||
"fields": [{"label": "Some Field", "fieldname": "some_fieldname", "fieldtype": "Data", "unique": unique}], | |||||
"permissions": [{"role": "System Manager", "read": 1}], | "permissions": [{"role": "System Manager", "read": 1}], | ||||
"name": name | "name": name | ||||
}) | }) | ||||
@@ -28,4 +29,28 @@ class TestDocType(unittest.TestCase): | |||||
frappe.delete_doc("DocType", name) | frappe.delete_doc("DocType", name) | ||||
doc = self.new_doctype(name).insert() | doc = self.new_doctype(name).insert() | ||||
doc.delete() | |||||
doc.delete() | |||||
def test_doctype_unique_constraint_dropped(self): | |||||
if frappe.db.exists("DocType", "With_Unique"): | |||||
frappe.delete_doc("DocType", "With_Unique") | |||||
dt = self.new_doctype("With_Unique", unique=1) | |||||
dt.insert() | |||||
doc1 = frappe.new_doc("With_Unique") | |||||
doc2 = frappe.new_doc("With_Unique") | |||||
doc1.some_fieldname = "Something" | |||||
doc1.name = "one" | |||||
doc2.some_fieldname = "Something" | |||||
doc2.name = "two" | |||||
doc1.insert() | |||||
self.assertRaises(frappe.UniqueValidationError, doc2.insert) | |||||
dt.fields[0].unique = 0 | |||||
dt.save() | |||||
doc2.insert() | |||||
doc1.delete() | |||||
doc2.delete() |
@@ -1,17 +1,24 @@ | |||||
{ | { | ||||
"accept_payment": 0, | |||||
"allow_comments": 0, | "allow_comments": 0, | ||||
"allow_delete": 0, | "allow_delete": 0, | ||||
"allow_edit": 1, | "allow_edit": 1, | ||||
"allow_incomplete": 0, | |||||
"allow_multiple": 0, | "allow_multiple": 0, | ||||
"allow_print": 0, | |||||
"amount": 0.0, | |||||
"amount_based_on_field": 0, | |||||
"breadcrumbs": "[{\"title\": _(\"My Account\"), \"route\": \"me\"}]", | "breadcrumbs": "[{\"title\": _(\"My Account\"), \"route\": \"me\"}]", | ||||
"creation": "2016-09-19 05:16:59.242754", | "creation": "2016-09-19 05:16:59.242754", | ||||
"doc_type": "User", | "doc_type": "User", | ||||
"docstatus": 0, | "docstatus": 0, | ||||
"doctype": "Web Form", | "doctype": "Web Form", | ||||
"idx": 0, | "idx": 0, | ||||
"introduction_text": "", | |||||
"is_standard": 1, | "is_standard": 1, | ||||
"login_required": 1, | "login_required": 1, | ||||
"modified": "2016-09-24 04:31:41.920694", | |||||
"max_attachment_size": 0, | |||||
"modified": "2017-07-24 12:14:04.039284", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "edit-profile", | "name": "edit-profile", | ||||
@@ -29,6 +36,8 @@ | |||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"hidden": 0, | "hidden": 0, | ||||
"label": "First Name", | "label": "First Name", | ||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"read_only": 0, | "read_only": 0, | ||||
"reqd": 1 | "reqd": 1 | ||||
}, | }, | ||||
@@ -37,6 +46,8 @@ | |||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"hidden": 0, | "hidden": 0, | ||||
"label": "Middle Name (Optional)", | "label": "Middle Name (Optional)", | ||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"read_only": 0, | "read_only": 0, | ||||
"reqd": 0 | "reqd": 0 | ||||
}, | }, | ||||
@@ -45,6 +56,8 @@ | |||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"hidden": 0, | "hidden": 0, | ||||
"label": "Last Name", | "label": "Last Name", | ||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"read_only": 0, | "read_only": 0, | ||||
"reqd": 0 | "reqd": 0 | ||||
}, | }, | ||||
@@ -54,6 +67,8 @@ | |||||
"fieldtype": "Attach", | "fieldtype": "Attach", | ||||
"hidden": 0, | "hidden": 0, | ||||
"label": "User Image", | "label": "User Image", | ||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"read_only": 0, | "read_only": 0, | ||||
"reqd": 0 | "reqd": 0 | ||||
}, | }, | ||||
@@ -61,6 +76,8 @@ | |||||
"fieldtype": "Section Break", | "fieldtype": "Section Break", | ||||
"hidden": 0, | "hidden": 0, | ||||
"label": "More Information", | "label": "More Information", | ||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"read_only": 0, | "read_only": 0, | ||||
"reqd": 0 | "reqd": 0 | ||||
}, | }, | ||||
@@ -69,6 +86,18 @@ | |||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"hidden": 0, | "hidden": 0, | ||||
"label": "Phone", | "label": "Phone", | ||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"read_only": 0, | |||||
"reqd": 0 | |||||
}, | |||||
{ | |||||
"fieldname": "mobile_no", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"label": "Mobile Number", | |||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"read_only": 0, | "read_only": 0, | ||||
"reqd": 0 | "reqd": 0 | ||||
}, | }, | ||||
@@ -78,6 +107,8 @@ | |||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"hidden": 0, | "hidden": 0, | ||||
"label": "Language", | "label": "Language", | ||||
"max_length": 0, | |||||
"max_value": 0, | |||||
"options": "Language", | "options": "Language", | ||||
"read_only": 0, | "read_only": 0, | ||||
"reqd": 0 | "reqd": 0 | ||||
@@ -2,25 +2,6 @@ | |||||
margin-top: 30px; | margin-top: 30px; | ||||
} | } | ||||
.setup-wizard-brand { | |||||
margin: 30px; | |||||
text-align: center; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center | |||||
} | |||||
.setup-wizard-brand .brand-icon { | |||||
width: 36px; | |||||
height: 36px; | |||||
} | |||||
.setup-wizard-brand .brand-name { | |||||
font-size: 20px; | |||||
margin-left: 8px; | |||||
color: #36414C; | |||||
} | |||||
.setup-wizard-slide { | .setup-wizard-slide { | ||||
padding-left: 0px; | padding-left: 0px; | ||||
padding-right: 0px; | padding-right: 0px; | ||||
@@ -59,14 +40,6 @@ | |||||
font-weight: 500; | font-weight: 500; | ||||
} | } | ||||
.setup-wizard-slide .has-error .control-label { | |||||
color: #ffa00a; | |||||
} | |||||
.setup-wizard-slide .has-error .form-control{ | |||||
border-color: #ffa00a; | |||||
} | |||||
.setup-wizard-slide .form-control.bold { | .setup-wizard-slide .form-control.bold { | ||||
background-color: #fff; | background-color: #fff; | ||||
} | } | ||||
@@ -113,8 +86,7 @@ | |||||
.setup-wizard-slide .frappe-control[data-fieldtype="Attach Image"] { | .setup-wizard-slide .frappe-control[data-fieldtype="Attach Image"] { | ||||
width: 140px; | width: 140px; | ||||
height: 180px; /*depends on presence of heading*/ | height: 180px; /*depends on presence of heading*/ | ||||
text-align: center; | |||||
margin-left: calc((100% - 140px)/2); | |||||
margin-top: 20px; | |||||
} | } | ||||
.setup-wizard-slide .frappe-control[data-fieldtype="Attach Image"] .form-group, | .setup-wizard-slide .frappe-control[data-fieldtype="Attach Image"] .form-group, | ||||
@@ -78,7 +78,6 @@ frappe.setup.Wizard = Class.extend({ | |||||
</div>', {html:html})) | </div>', {html:html})) | ||||
}, | }, | ||||
show_working: function() { | show_working: function() { | ||||
$('header').find('.setup-wizard-brand').hide(); | |||||
this.hide_current_slide(); | this.hide_current_slide(); | ||||
frappe.set_route(this.page_name); | frappe.set_route(this.page_name); | ||||
this.current_slide = {"$wrapper": this.get_message(this.working_html()).appendTo(this.parent)}; | this.current_slide = {"$wrapper": this.get_message(this.working_html()).appendTo(this.parent)}; | ||||
@@ -506,7 +505,7 @@ var frappe_slides = [ | |||||
icon: "fa fa-user", | icon: "fa fa-user", | ||||
fields: [ | fields: [ | ||||
{ "fieldtype":"Attach Image", "fieldname":"attach_user_image", | { "fieldtype":"Attach Image", "fieldname":"attach_user_image", | ||||
label: __("Attach Your Picture"), is_private: 0}, | |||||
label: __("Attach Your Picture"), is_private: 0, align: 'center'}, | |||||
{ "fieldname": "full_name", "label": __("Full Name"), "fieldtype": "Data", | { "fieldname": "full_name", "label": __("Full Name"), "fieldtype": "Data", | ||||
reqd:1}, | reqd:1}, | ||||
{ "fieldname": "email", "label": __("Email Address") + ' (' + __("Will be your login ID") + ')', | { "fieldname": "email", "label": __("Email Address") + ' (' + __("Will be your login ID") + ')', | ||||
@@ -721,12 +720,4 @@ var utils = { | |||||
frappe.setup.on("before_load", function() { | frappe.setup.on("before_load", function() { | ||||
// load slides | // load slides | ||||
frappe_slides.map(frappe.setup.add_slide); | frappe_slides.map(frappe.setup.add_slide); | ||||
// set header image | |||||
let $icon = $('header .setup-wizard-brand'); | |||||
if($icon.length === 0) { | |||||
$('header').append(`<div class="setup-wizard-brand""> | |||||
<img src="/assets/frappe/images/frappe-bird-grey.svg" | |||||
class="brand-icon frappe-icon" style="width:36px;"></div>`); | |||||
} | |||||
}); | }); |
@@ -47,6 +47,13 @@ def remove_tag(tag, dt, dn): | |||||
"removes tag from the record" | "removes tag from the record" | ||||
DocTags(dt).remove(dn, tag) | DocTags(dt).remove(dn, tag) | ||||
@frappe.whitelist() | |||||
def get_tagged_docs(doctype, tag): | |||||
frappe.has_permission(doctype, throw=True) | |||||
return frappe.db.sql("""SELECT name | |||||
FROM `tab{0}` | |||||
WHERE _user_tags LIKE '%{1}%'""".format(doctype, tag)) | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def get_tags(doctype, txt, cat_tags): | def get_tags(doctype, txt, cat_tags): | ||||
@@ -69,15 +69,15 @@ def add_subscribers(name, email_list): | |||||
count = 0 | count = 0 | ||||
for email in email_list: | for email in email_list: | ||||
email = email.strip() | email = email.strip() | ||||
valid = validate_email_add(email, False) | |||||
parsed_email = validate_email_add(email, False) | |||||
if valid: | |||||
if parsed_email: | |||||
if not frappe.db.get_value("Email Group Member", | if not frappe.db.get_value("Email Group Member", | ||||
{"email_group": name, "email": email}): | |||||
{"email_group": name, "email": parsed_email}): | |||||
frappe.get_doc({ | frappe.get_doc({ | ||||
"doctype": "Email Group Member", | "doctype": "Email Group Member", | ||||
"email_group": name, | "email_group": name, | ||||
"email": email | |||||
"email": parsed_email | |||||
}).insert(ignore_permissions = frappe.flags.ignore_permissions) | }).insert(ignore_permissions = frappe.flags.ignore_permissions) | ||||
count += 1 | count += 1 | ||||
@@ -86,7 +86,7 @@ def backup_to_dropbox(): | |||||
access_token = generate_oauth2_access_token_from_oauth1_token(dropbox_settings) | access_token = generate_oauth2_access_token_from_oauth1_token(dropbox_settings) | ||||
if not access_token.get('oauth2_token'): | if not access_token.get('oauth2_token'): | ||||
return | |||||
return 'Failed backup upload', 'No Access Token exists! Please generate the access token for Dropbox.' | |||||
dropbox_settings['access_token'] = access_token['oauth2_token'] | dropbox_settings['access_token'] = access_token['oauth2_token'] | ||||
set_dropbox_access_token(access_token['oauth2_token']) | set_dropbox_access_token(access_token['oauth2_token']) | ||||
@@ -0,0 +1,16 @@ | |||||
[ | |||||
{ | |||||
"app_name": "_Test OAuth Client", | |||||
"client_id": "test_client_id", | |||||
"client_secret": "test_client_secret", | |||||
"default_redirect_uri": "http://localhost", | |||||
"docstatus": 0, | |||||
"doctype": "OAuth Client", | |||||
"grant_type": "Authorization Code", | |||||
"name": "test_client_id", | |||||
"redirect_uris": "http://localhost", | |||||
"response_type": "Code", | |||||
"scopes": "all openid", | |||||
"skip_authorization": 0 | |||||
} | |||||
] |
@@ -291,7 +291,7 @@ class DatabaseQuery(object): | |||||
# prepare in condition | # prepare in condition | ||||
if f.operator.lower() in ('in', 'not in'): | if f.operator.lower() in ('in', 'not in'): | ||||
values = f.value | |||||
values = f.value or '' | |||||
if not isinstance(values, (list, tuple)): | if not isinstance(values, (list, tuple)): | ||||
values = values.split(",") | values = values.split(",") | ||||
@@ -314,7 +314,7 @@ class DbTable: | |||||
# if index key exists | # if index key exists | ||||
if frappe.db.sql("""show index from `{0}` | if frappe.db.sql("""show index from `{0}` | ||||
where key_name=%s | where key_name=%s | ||||
and Non_unique=%s""".format(self.name), (col.fieldname, 0 if col.unique else 1)): | |||||
and Non_unique=%s""".format(self.name), (col.fieldname, col.unique)): | |||||
query.append("drop index `{}`".format(col.fieldname)) | query.append("drop index `{}`".format(col.fieldname)) | ||||
for col in self.set_default: | for col in self.set_default: | ||||
@@ -187,4 +187,5 @@ frappe.patches.v8_0.update_gender_and_salutation | |||||
execute:frappe.db.sql('update tabReport set module="Desk" where name="ToDo"') | execute:frappe.db.sql('update tabReport set module="Desk" where name="ToDo"') | ||||
frappe.patches.v8_1.enable_allow_error_traceback_in_system_settings | frappe.patches.v8_1.enable_allow_error_traceback_in_system_settings | ||||
frappe.patches.v8_1.update_format_options_in_auto_email_report | frappe.patches.v8_1.update_format_options_in_auto_email_report | ||||
frappe.patches.v8_1.delete_custom_docperm_if_doctype_not_exists | |||||
frappe.patches.v8_1.delete_custom_docperm_if_doctype_not_exists | |||||
frappe.patches.v8_5.delete_email_group_member_with_invalid_emails |
@@ -0,0 +1,20 @@ | |||||
# Copyright (c) 2017, Frappe and Contributors | |||||
# License: GNU General Public License v3. See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
from frappe.utils import validate_email_add | |||||
def execute(): | |||||
''' update/delete the email group member with the wrong email ''' | |||||
email_group_members = frappe.get_all("Email Group Member", fields=["name", "email"]) | |||||
for member in email_group_members: | |||||
validated_email = validate_email_add(member.email) | |||||
if (validated_email==member.email): | |||||
pass | |||||
else: | |||||
try: | |||||
frappe.db.set_value("Email Group Member", member.name, "email", validated_email) | |||||
except Exception: | |||||
frappe.delete_doc(doctype="Email Group Member", name=member.name, force=1, ignore_permissions=True) |
@@ -287,6 +287,8 @@ h6.uppercase, | |||||
border-radius: 3px; | border-radius: 3px; | ||||
margin-left: -7px; | margin-left: -7px; | ||||
position: relative; | position: relative; | ||||
max-width: calc(100% - 50px); | |||||
padding-right: 0px; | |||||
overflow: visible; | overflow: visible; | ||||
} | } | ||||
.timeline-item.user-content .avatar-medium { | .timeline-item.user-content .avatar-medium { | ||||
@@ -294,6 +296,11 @@ h6.uppercase, | |||||
height: 45px; | height: 45px; | ||||
width: 45px; | width: 45px; | ||||
} | } | ||||
.timeline-item.user-content .action-btns { | |||||
position: absolute; | |||||
right: 0; | |||||
padding: 5px 15px 2px 5px; | |||||
} | |||||
.timeline-item.user-content .comment-header { | .timeline-item.user-content .comment-header { | ||||
background-color: #fafbfc; | background-color: #fafbfc; | ||||
padding: 10px 15px 10px 13px; | padding: 10px 15px 10px 13px; | ||||
@@ -301,12 +308,19 @@ h6.uppercase, | |||||
color: #8D99A6; | color: #8D99A6; | ||||
border-bottom: 1px solid #EBEFF2; | border-bottom: 1px solid #EBEFF2; | ||||
} | } | ||||
.timeline-item.user-content .comment-header.links-active { | |||||
padding-right: 60px; | |||||
} | |||||
.timeline-item.user-content .comment-header .commented-on-small { | |||||
display: none; | |||||
} | |||||
.timeline-item.user-content .comment-header .octicon-heart { | .timeline-item.user-content .comment-header .octicon-heart { | ||||
color: #ff5858; | color: #ff5858; | ||||
cursor: pointer; | cursor: pointer; | ||||
} | } | ||||
.timeline-item.user-content .reply { | .timeline-item.user-content .reply { | ||||
padding: 15px; | padding: 15px; | ||||
overflow: auto; | |||||
} | } | ||||
.timeline-item.user-content .reply > div > p:first-child { | .timeline-item.user-content .reply > div > p:first-child { | ||||
margin-top: 0px; | margin-top: 0px; | ||||
@@ -317,11 +331,13 @@ h6.uppercase, | |||||
.timeline-item.user-content .reply hr { | .timeline-item.user-content .reply hr { | ||||
margin: 10px 0px; | margin: 10px 0px; | ||||
} | } | ||||
.timeline-item.user-content .close-btn-container { | |||||
padding: 4px 10px 2px 5px; | |||||
.timeline-item.user-content .close-btn-container .close { | |||||
color: inherit; | |||||
opacity: 1; | |||||
padding: 0 0 0 10px; | |||||
} | } | ||||
.timeline-item.user-content .edit-btn-container { | .timeline-item.user-content .edit-btn-container { | ||||
padding: 4px 5px; | |||||
padding: 0; | |||||
} | } | ||||
.timeline-item.user-content .edit-btn-container .edit { | .timeline-item.user-content .edit-btn-container .edit { | ||||
color: inherit; | color: inherit; | ||||
@@ -515,6 +531,14 @@ h6.uppercase, | |||||
padding: 0px; | padding: 0px; | ||||
margin: 0px; | margin: 0px; | ||||
} | } | ||||
.flex-justify-center { | |||||
display: flex; | |||||
justify-content: center; | |||||
} | |||||
.flex-justify-end { | |||||
display: flex; | |||||
justify-content: flex-end; | |||||
} | |||||
.hide-control { | .hide-control { | ||||
display: none !important; | display: none !important; | ||||
} | } | ||||
@@ -192,6 +192,9 @@ body { | |||||
} | } | ||||
} | } | ||||
@media (max-width: 767px) { | @media (max-width: 767px) { | ||||
.toggle-sidebar { | |||||
margin-right: 0; | |||||
} | |||||
body[data-route^="Form"] .page-title .title-text { | body[data-route^="Form"] .page-title .title-text { | ||||
font-size: 16px; | font-size: 16px; | ||||
width: calc(100% - 30px); | width: calc(100% - 30px); | ||||
@@ -331,4 +334,64 @@ body { | |||||
body[data-route^="Form"] .page-head .sub-heading { | body[data-route^="Form"] .page-head .sub-heading { | ||||
right: 90px; | right: 90px; | ||||
} | } | ||||
.timeline::before { | |||||
content: none; | |||||
} | |||||
.timeline .timeline-new-email { | |||||
margin: 20px 0; | |||||
padding-left: 15px; | |||||
} | |||||
.timeline .timeline-new-email::before { | |||||
content: none; | |||||
} | |||||
.timeline .timeline-item.user-content { | |||||
margin: 20px 15px; | |||||
} | |||||
.timeline .timeline-item.user-content .media-body { | |||||
margin-left: 0; | |||||
max-width: 100%; | |||||
overflow: hidden; | |||||
} | |||||
.timeline .timeline-item.user-content .media-body:before { | |||||
content: none; | |||||
} | |||||
.timeline .timeline-item.user-content .action-btns { | |||||
padding: 5px 10px 2px 5px; | |||||
} | |||||
.timeline .timeline-item.user-content .comment-header { | |||||
padding: 7px 10px; | |||||
} | |||||
.timeline .timeline-item.user-content .comment-header .links-active { | |||||
padding-right: 10px; | |||||
} | |||||
.timeline .timeline-item.user-content .avatar-medium { | |||||
margin-right: 10px; | |||||
} | |||||
.timeline .timeline-item.user-content .reply { | |||||
padding: 10px; | |||||
} | |||||
.timeline .timeline-item.user-content .commented-on-small { | |||||
display: inline-block; | |||||
} | |||||
.timeline .timeline-item.user-content .commented-on-small { | |||||
display: inline-block; | |||||
} | |||||
.timeline .timeline-item.notification-content { | |||||
padding-left: 15px; | |||||
margin: 20px 0; | |||||
} | |||||
.timeline .timeline-item.notification-content::before { | |||||
content: none; | |||||
} | |||||
.timeline .timeline-item.notification-content .small { | |||||
padding-left: 0; | |||||
} | |||||
.timeline .timeline-item .delivery-status-indicator { | |||||
float: left; | |||||
margin: 0 5px 0 0; | |||||
} | |||||
.timeline .asset-details { | |||||
line-height: 24px; | |||||
/*Height of avtar image -36px to align text center vertically*/ | |||||
} | |||||
} | } |
@@ -318,7 +318,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ | |||||
} else { | } else { | ||||
$(me.input_area).toggle(false); | $(me.input_area).toggle(false); | ||||
if (me.disp_area) { | if (me.disp_area) { | ||||
me.set_disp_area(); | |||||
me.set_disp_area(me.value); | |||||
$(me.disp_area).toggle(true); | $(me.disp_area).toggle(true); | ||||
} | } | ||||
} | } | ||||
@@ -332,8 +332,7 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ | |||||
} | } | ||||
}, | }, | ||||
set_disp_area: function() { | |||||
let value = this.get_input_value(); | |||||
set_disp_area: function(value) { | |||||
if(in_list(["Currency", "Int", "Float"], this.df.fieldtype) | if(in_list(["Currency", "Int", "Float"], this.df.fieldtype) | ||||
&& (this.value === 0 || value === 0)) { | && (this.value === 0 || value === 0)) { | ||||
// to set the 0 value in readonly for currency, int, float field | // to set the 0 value in readonly for currency, int, float field | ||||
@@ -449,7 +448,7 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({ | |||||
this.last_value = this.value; | this.last_value = this.value; | ||||
this.value = value; | this.value = value; | ||||
this.set_formatted_input(value); | this.set_formatted_input(value); | ||||
this.set_disp_area(); | |||||
this.set_disp_area(value); | |||||
this.set_mandatory && this.set_mandatory(value); | this.set_mandatory && this.set_mandatory(value); | ||||
}, | }, | ||||
set_formatted_input: function(value) { | set_formatted_input: function(value) { | ||||
@@ -752,29 +751,38 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({ | |||||
if(!$.fn.datepicker.language[lang]) { | if(!$.fn.datepicker.language[lang]) { | ||||
lang = 'en'; | lang = 'en'; | ||||
} | } | ||||
this.today_text = __("Today"); | |||||
this.datepicker_options = { | this.datepicker_options = { | ||||
language: lang, | language: lang, | ||||
autoClose: true, | autoClose: true, | ||||
todayButton: new Date(), | |||||
todayButton: frappe.datetime.now_date(true), | |||||
dateFormat: (frappe.boot.sysdefaults.date_format || 'yyyy-mm-dd'), | dateFormat: (frappe.boot.sysdefaults.date_format || 'yyyy-mm-dd'), | ||||
onSelect: function(dateStr) { | |||||
me.$input.trigger('change'); | |||||
startDate: frappe.datetime.now_date(true), | |||||
onSelect: () => { | |||||
this.$input.trigger('change'); | |||||
}, | }, | ||||
onShow: function() { | |||||
$('.datepicker--button:visible').text(__('Today')); | |||||
if(!me.frm) return; | |||||
var window_height = $(window).height(); | |||||
var window_scroll_top = $(window).scrollTop(); | |||||
var el_offset_top = me.$input.offset().top + 280; | |||||
var position = 'top left'; | |||||
if(window_height + window_scroll_top >= el_offset_top) { | |||||
position = 'bottom left'; | |||||
} | |||||
me.datepicker.update('position', position); | |||||
onShow: () => { | |||||
this.datepicker.$datepicker | |||||
.find('.datepicker--button:visible') | |||||
.text(me.today_text); | |||||
this.update_datepicker_position(); | |||||
} | } | ||||
}; | }; | ||||
}, | }, | ||||
update_datepicker_position: function() { | |||||
if(!this.frm) return; | |||||
// show datepicker above or below the input | |||||
// based on scroll position | |||||
var window_height = $(window).height(); | |||||
var window_scroll_top = $(window).scrollTop(); | |||||
var el_offset_top = this.$input.offset().top + 280; | |||||
var position = 'top left'; | |||||
if(window_height + window_scroll_top >= el_offset_top) { | |||||
position = 'bottom left'; | |||||
} | |||||
this.datepicker.update('position', position); | |||||
}, | |||||
set_datepicker: function() { | set_datepicker: function() { | ||||
this.$input.datepicker(this.datepicker_options); | this.$input.datepicker(this.datepicker_options); | ||||
this.datepicker = this.$input.data('datepicker'); | this.datepicker = this.$input.data('datepicker'); | ||||
@@ -814,6 +822,30 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({ | |||||
} | } | ||||
}); | }); | ||||
frappe.ui.form.ControlDatetime = frappe.ui.form.ControlDate.extend({ | |||||
set_date_options: function() { | |||||
this._super(); | |||||
this.today_text = __("Now"); | |||||
$.extend(this.datepicker_options, { | |||||
timepicker: true, | |||||
timeFormat: "hh:ii:ss", | |||||
todayButton: frappe.datetime.now_datetime(true) | |||||
}); | |||||
}, | |||||
set_description: function() { | |||||
const { description } = this.df; | |||||
const { time_zone } = frappe.sys_defaults; | |||||
if (!frappe.datetime.is_timezone_same()) { | |||||
if (!description) { | |||||
this.df.description = time_zone; | |||||
} else if (!description.includes(time_zone)) { | |||||
this.df.description += '<br>' + time_zone; | |||||
} | |||||
} | |||||
this._super(); | |||||
} | |||||
}); | |||||
frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ | frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ | ||||
make_input: function() { | make_input: function() { | ||||
var me = this; | var me = this; | ||||
@@ -823,13 +855,14 @@ frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ | |||||
timepicker: true, | timepicker: true, | ||||
onlyTimepicker: true, | onlyTimepicker: true, | ||||
timeFormat: "hh:ii:ss", | timeFormat: "hh:ii:ss", | ||||
startDate: frappe.datetime.now_time(true), | |||||
onSelect: function() { | onSelect: function() { | ||||
me.$input.trigger('change'); | me.$input.trigger('change'); | ||||
}, | }, | ||||
onShow: function() { | onShow: function() { | ||||
$('.datepicker--button:visible').text(__('Now')); | $('.datepicker--button:visible').text(__('Now')); | ||||
}, | }, | ||||
todayButton: new Date() | |||||
todayButton: frappe.datetime.now_time(true) | |||||
}); | }); | ||||
this.datepicker = this.$input.data('datepicker'); | this.datepicker = this.$input.data('datepicker'); | ||||
this.refresh(); | this.refresh(); | ||||
@@ -840,33 +873,21 @@ frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ | |||||
&& ((this.last_value && this.last_value !== this.value) | && ((this.last_value && this.last_value !== this.value) | ||||
|| (!this.datepicker.selectedDates.length))) { | || (!this.datepicker.selectedDates.length))) { | ||||
this.datepicker.selectDate(moment(value, 'hh:mm:ss')._d); | |||||
var date_obj = frappe.datetime.moment_to_date_obj(moment(value, 'hh:mm:ss')); | |||||
this.datepicker.selectDate(date_obj); | |||||
} | } | ||||
}, | }, | ||||
}); | |||||
frappe.ui.form.ControlDatetime = frappe.ui.form.ControlDate.extend({ | |||||
set_date_options: function() { | |||||
this._super(); | |||||
this.datepicker_options.timepicker = true; | |||||
this.datepicker_options.timeFormat = "hh:ii:ss"; | |||||
this.datepicker_options.onShow = function() { | |||||
$('.datepicker--button:visible').text(__('Now')); | |||||
}; | |||||
}, | |||||
parse: function(value) { | |||||
if(value) { | |||||
// parse and convert | |||||
value = frappe.datetime.convert_to_system_tz(frappe.datetime.user_to_str(value)); | |||||
} | |||||
return value; | |||||
}, | |||||
format_for_input: function(value) { | |||||
if(value) { | |||||
// convert and format | |||||
value = frappe.datetime.str_to_user(frappe.datetime.convert_to_user_tz(value)); | |||||
set_description: function() { | |||||
const { description } = this.df; | |||||
const { time_zone } = frappe.sys_defaults; | |||||
if (!frappe.datetime.is_timezone_same()) { | |||||
if (!description) { | |||||
this.df.description = time_zone; | |||||
} else if (!description.includes(time_zone)) { | |||||
this.df.description += '<br>' + time_zone; | |||||
} | |||||
} | } | ||||
return value || ""; | |||||
this._super(); | |||||
} | } | ||||
}); | }); | ||||
@@ -903,11 +924,12 @@ frappe.ui.form.ControlDateRange = frappe.ui.form.ControlData.extend({ | |||||
this.value = value; | this.value = value; | ||||
} | } | ||||
if (this.value) { | if (this.value) { | ||||
this.$input && this.$input.val(this.format_for_input(this.value[0], this.value[1])); | |||||
let formatted = this.format_for_input(this.value[0], this.value[1]); | |||||
this.$input && this.$input.val(formatted); | |||||
} else { | } else { | ||||
this.$input && this.$input.val(""); | this.$input && this.$input.val(""); | ||||
} | } | ||||
this.set_disp_area(); | |||||
this.set_disp_area(value || ''); | |||||
this.set_mandatory && this.set_mandatory(value); | this.set_mandatory && this.set_mandatory(value); | ||||
}, | }, | ||||
parse: function(value) { | parse: function(value) { | ||||
@@ -984,7 +1006,7 @@ frappe.ui.form.ControlCheck = frappe.ui.form.ControlData.extend({ | |||||
} | } | ||||
this.last_value = value; | this.last_value = value; | ||||
this.set_mandatory(value); | this.set_mandatory(value); | ||||
this.set_disp_area(); | |||||
this.set_disp_area(value); | |||||
} | } | ||||
}); | }); | ||||
@@ -1219,6 +1241,16 @@ frappe.ui.form.ControlAttachImage = frappe.ui.form.ControlAttach.extend({ | |||||
make: function() { | make: function() { | ||||
var me = this; | var me = this; | ||||
this._super(); | this._super(); | ||||
this.container = $('<div class="control-container">').insertAfter($(this.wrapper)); | |||||
$(this.wrapper).detach(); | |||||
this.container.attr('data-fieldtype', this.df.fieldtype).append(this.wrapper); | |||||
if(this.df.align === 'center') { | |||||
this.container.addClass("flex-justify-center"); | |||||
} else if (this.df.align === 'right') { | |||||
this.container.addClass("flex-justify-end"); | |||||
} | |||||
this.img_wrapper = $('<div style="width: 100%; height: calc(100% - 40px); position: relative;">\ | this.img_wrapper = $('<div style="width: 100%; height: calc(100% - 40px); position: relative;">\ | ||||
<div class="missing-image attach-missing-image"><i class="octicon octicon-device-camera"></i></div></div>') | <div class="missing-image attach-missing-image"><i class="octicon octicon-device-camera"></i></div></div>') | ||||
.appendTo(this.wrapper); | .appendTo(this.wrapper); | ||||
@@ -248,14 +248,14 @@ frappe.ui.form.Timeline = Class.extend({ | |||||
c["edit"] = ""; | c["edit"] = ""; | ||||
if(c.communication_type=="Comment" && (c.comment_type || "Comment") === "Comment") { | if(c.communication_type=="Comment" && (c.comment_type || "Comment") === "Comment") { | ||||
if(frappe.model.can_delete("Communication")) { | if(frappe.model.can_delete("Communication")) { | ||||
c["delete"] = '<a class="close" href="#"><i class="octicon octicon-trashcan"></i></a>'; | |||||
c["delete"] = '<a class="close" title="Delete" href="#"><i class="octicon octicon-x"></i></a>'; | |||||
} | } | ||||
if(frappe.user.name == c.sender || (frappe.user.name == 'Administrator')) { | if(frappe.user.name == c.sender || (frappe.user.name == 'Administrator')) { | ||||
c["edit"] = '<a class="edit" href="#"><i class="octicon octicon-pencil"></i></a>'; | |||||
c["edit"] = '<a class="edit" title="Edit" href="#"><i class="octicon octicon-pencil"></i></a>'; | |||||
} | } | ||||
} | } | ||||
c.comment_on_small = comment_when(c.creation, true); | |||||
c.comment_on = comment_when(c.creation); | c.comment_on = comment_when(c.creation); | ||||
if(!c.fullname) { | if(!c.fullname) { | ||||
c.fullname = c.sender_full_name || frappe.user.full_name(c.sender); | c.fullname = c.sender_full_name || frappe.user.full_name(c.sender); | ||||
@@ -360,7 +360,8 @@ frappe.ui.form.Timeline = Class.extend({ | |||||
"Unshared": "octicon octicon-circle-slash", | "Unshared": "octicon octicon-circle-slash", | ||||
"Like": "octicon octicon-heart", | "Like": "octicon octicon-heart", | ||||
"Edit": "octicon octicon-pencil", | "Edit": "octicon octicon-pencil", | ||||
"Relinked": "octicon octicon-check" | |||||
"Relinked": "octicon octicon-check", | |||||
"Reply": "octicon octicon-mail-reply" | |||||
}[c.comment_type || c.communication_medium] | }[c.comment_type || c.communication_medium] | ||||
c.color = { | c.color = { | ||||
@@ -378,7 +379,8 @@ frappe.ui.form.Timeline = Class.extend({ | |||||
"Label": "#2c3e50", | "Label": "#2c3e50", | ||||
"Attachment": "#7f8c8d", | "Attachment": "#7f8c8d", | ||||
"Attachment Removed": "#eee", | "Attachment Removed": "#eee", | ||||
"Relinked": "#16a085" | |||||
"Relinked": "#16a085", | |||||
"Reply": "#8d99a6" | |||||
}[c.comment_type || c.communication_medium]; | }[c.comment_type || c.communication_medium]; | ||||
c.icon_fg = { | c.icon_fg = { | ||||
@@ -1,6 +1,6 @@ | |||||
<div class="media timeline-item {% if (data.user_content) { %} user-content {% } else { %} notification-content {% } %}" data-doctype="{{ data.doctype }}" data-name="{%= data.name %}"> | <div class="media timeline-item {% if (data.user_content) { %} user-content {% } else { %} notification-content {% } %}" data-doctype="{{ data.doctype }}" data-name="{%= data.name %}"> | ||||
{% if (data.user_content) { %} | {% if (data.user_content) { %} | ||||
<span class="pull-left avatar avatar-medium" style="margin-top: 1px"> | |||||
<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px"> | |||||
{% if(data.user_info.image) { %} | {% if(data.user_info.image) { %} | ||||
<div class="avatar-frame" style="background-image: url({%= data.user_info.image %})"></div> | <div class="avatar-frame" style="background-image: url({%= data.user_info.image %})"></div> | ||||
{% } else { %} | {% } else { %} | ||||
@@ -10,88 +10,108 @@ | |||||
</span> | </span> | ||||
{% } %} | {% } %} | ||||
<div class="pull-left media-body" style="max-width: calc(100% - 50px); padding-right: 0px;"> | |||||
<div class="pull-left media-body"> | |||||
<div class="media-content-wrapper"> | <div class="media-content-wrapper"> | ||||
<div class="pull-right close-btn-container"> | |||||
<span class="small text-muted"> | |||||
{%= data.delete %} | |||||
</span> | |||||
</div> | |||||
<div class="pull-right edit-btn-container"> | |||||
<span class="small text-muted"> | |||||
{%= data.edit %} | |||||
</span> | |||||
<div class="action-btns"> | |||||
{% if(data.delete) { %} | |||||
<div class="pull-right hidden-xs close-btn-container"> | |||||
<span class="small text-muted"> | |||||
{%= data.delete %} | |||||
</span> | |||||
</div> | |||||
{% } %} | |||||
{% if(data.edit) { %} | |||||
<div class="pull-right edit-btn-container"> | |||||
<span class="small text-muted"> | |||||
{%= data.edit %} | |||||
</span> | |||||
</div> | |||||
{% } %} | |||||
</div> | </div> | ||||
{% if(data.communication_type==="Communication" | {% if(data.communication_type==="Communication" | ||||
|| data.communication_type==="Feedback" | || data.communication_type==="Feedback" | ||||
|| (data.communication_type==="Comment" | || (data.communication_type==="Comment" | ||||
&& data.comment_type==="Comment")) { %} | && data.comment_type==="Comment")) { %} | ||||
<div class="comment-header small"> | |||||
<i class="{%= data.icon %} fa-fw"></i> | |||||
<span title="{%= data.comment_by %}">{%= data.fullname %}</span> | |||||
<span> | |||||
{% if (data.timeline_doctype===data.frm.doc.doctype | |||||
&& data.timeline_name===data.frm.doc.name) { %} | |||||
– | |||||
<a href="#Form/{%= data.reference_doctype %}/{%= data.reference_name %}" class="text-muted"> | |||||
<strong>{{ __(data.reference_doctype) }}</strong> | |||||
{{ data.reference_name }} | |||||
</a> | |||||
<div class="comment-header clearfix small {% if (data.edit || data.delete) { %} links-active {% } %}"> | |||||
<span class="pull-left avatar avatar-small visible-xs"> | |||||
{% if(data.user_info.image) { %} | |||||
<div class="avatar-frame" style="background-image: url({%= data.user_info.image %})"></div> | |||||
{% } else { %} | |||||
<div class="standard-image" style="background-color: {{ data.user_info.color }}"> | |||||
{{ data.user_info.abbr }}</div> | |||||
{% } %} | {% } %} | ||||
</span> | </span> | ||||
<span class="text-muted" style="font-weight: normal;"> | |||||
– {%= data.comment_on %}</span> | |||||
{% if(in_list(["Communication", "Feedback"], data.communication_type)) { %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
<a href="#Form/{%= data.doctype %}/{%= data.name %}" | |||||
class="text-muted"> | |||||
<div class="asset-details"> | |||||
<span class="author-wrap"> | |||||
<i class="{%= data.icon %} hidden-xs fa-fw"></i> | |||||
<span title="{%= data.comment_by %}">{%= data.fullname %}</span> | |||||
</span> | |||||
<span> | |||||
{% if (data.timeline_doctype===data.frm.doc.doctype | |||||
&& data.timeline_name===data.frm.doc.name) { %} | |||||
– | |||||
<a href="#Form/{%= data.reference_doctype %}/{%= data.reference_name %}" class="text-muted"> | |||||
<strong>{{ __(data.reference_doctype) }}</strong> | |||||
{{ data.reference_name }} | |||||
</a> | |||||
{% } %} | {% } %} | ||||
</span> | |||||
{% if(in_list(["Communication", "Feedback"], data.communication_type)) { %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
<a href="#Form/{%= data.doctype %}/{%= data.name %}" | |||||
class="text-muted"> | |||||
{% } %} | |||||
{% if (data.delivery_status) { | |||||
if (in_list(["Sent", "Opened", "Clicked"], data.delivery_status)) { | |||||
var indicator_class = "green"; | |||||
} else if (data.delivery_status === "Sending") { | |||||
var indicator_class = "orange"; | |||||
} else { | |||||
var indicator_class = "red"; | |||||
} | |||||
%} | |||||
<span class="text-muted">–</span> | |||||
<span class="indicator-right {%= indicator_class %} | |||||
delivery-status-indicator" | |||||
title="{%= data.delivery_status %}"> | |||||
{%= data.delivery_status %}</span> | |||||
{% if (data.delivery_status) { | |||||
if (in_list(["Sent", "Opened", "Clicked"], data.delivery_status)) { | |||||
var indicator_class = "green"; | |||||
} else if (data.delivery_status === "Sending") { | |||||
var indicator_class = "orange"; | |||||
} else { | |||||
var indicator_class = "red"; | |||||
} | |||||
%} | |||||
<span class="text-muted hidden-xs">–</span> | |||||
<span class="indicator-right {%= indicator_class %} | |||||
delivery-status-indicator" | |||||
title="{%= data.delivery_status %}"><span class="hidden-xs"> | |||||
{%= data.delivery_status %}</span></span> | |||||
{% } else { %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
<span class="text-muted">–</span> | |||||
{%= __("Details") %} | |||||
{% } else { %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
<span class="text-muted n-dash">–</span> | |||||
{%= __("Details") %} | |||||
{% } %} | |||||
{% } %} | {% } %} | ||||
{% } %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
</a> | |||||
{% } %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
</a> | |||||
{% } %} | |||||
{% if (data.communication_medium === "Email" | |||||
&& data.sender !== frappe.session.user_email) { %} | |||||
<a class="text-muted reply-link pull-right timeline-content-show" | |||||
data-name="{%= data.name %}">{%= __("Reply") %}</a> | |||||
{% if (data.communication_medium === "Email" | |||||
&& data.sender !== frappe.session.user_email) { %} | |||||
<a class="text-muted reply-link pull-right timeline-content-show" | |||||
data-name="{%= data.name %}" title="{%= __("Reply") %}"><i class="octicon octicon-mail-reply"></i></a> | |||||
{% } %} | |||||
{% } %} | {% } %} | ||||
{% } %} | |||||
<span class="comment-likes" | |||||
data-liked-by=\'{{ JSON.stringify(data._liked_by) }}\'> | |||||
<i class="octicon octicon-heart like-action | |||||
{% if (!data.liked_by_user) { %} | |||||
text-extra-muted not-liked | |||||
{% } %} fa-fw" | |||||
data-doctype="{%= data.doctype %}" | |||||
data-name="{%= data.name %}"></i> | |||||
<span class="likes-count text-muted"> | |||||
{{ (data._liked_by || []).length }}</span> | |||||
</span> | |||||
<span class="text-muted commented-on hidden-xs"> | |||||
– {%= data.comment_on %}</span> | |||||
<span class="text-muted commented-on-small"> | |||||
– {%= data.comment_on_small %}</span> | |||||
<span class="comment-likes hidden-xs" | |||||
data-liked-by=\'{{ JSON.stringify(data._liked_by) }}\'> | |||||
<i class="octicon octicon-heart like-action | |||||
{% if (!data.liked_by_user) { %} | |||||
text-extra-muted not-liked | |||||
{% } %} fa-fw" | |||||
data-doctype="{%= data.doctype %}" | |||||
data-name="{%= data.name %}"></i> | |||||
<span class="likes-count text-muted"> | |||||
{{ (data._liked_by || []).length }}</span> | |||||
</span> | |||||
</div> | |||||
</div> | </div> | ||||
<div class="reply timeline-content-show" style="overflow-x: auto"> | |||||
<div class="reply timeline-content-show"> | |||||
<div class="timeline-item-content"> | <div class="timeline-item-content"> | ||||
{% if data.show_subject %} | {% if data.show_subject %} | ||||
<p class="text-muted small"> | <p class="text-muted small"> | ||||
@@ -143,7 +163,7 @@ | |||||
{% if(data.link_doctype && data.link_name) { %} | {% if(data.link_doctype && data.link_name) { %} | ||||
</a> | </a> | ||||
{% } %} | {% } %} | ||||
<span class="text-muted" style="font-weight: normal;"> | |||||
<span class="text-muted commented-on" style="font-weight: normal;"> | |||||
– {%= data.comment_on %}</span> | – {%= data.comment_on %}</span> | ||||
</div> | </div> | ||||
{% } else { %} | {% } else { %} | ||||
@@ -172,7 +192,7 @@ | |||||
</a> | </a> | ||||
{% } %} | {% } %} | ||||
{% } %} | {% } %} | ||||
<span class="text-muted" style="font-weight: normal;"> | |||||
<span class="text-muted commented-on" style="font-weight: normal;"> | |||||
– {%= data.comment_on %}</span> | – {%= data.comment_on %}</span> | ||||
</div> | </div> | ||||
{% } %} | {% } %} | ||||
@@ -410,7 +410,7 @@ frappe.views.ListRenderer = Class.extend({ | |||||
}, | }, | ||||
get_indicator_html: function (doc) { | get_indicator_html: function (doc) { | ||||
var indicator = frappe.get_indicator(doc, this.doctype); | |||||
var indicator = this.get_indicator_from_doc(doc); | |||||
if (indicator) { | if (indicator) { | ||||
return `<span class='indicator ${indicator[1]} filterable' | return `<span class='indicator ${indicator[1]} filterable' | ||||
data-filter='${indicator[2]}'> | data-filter='${indicator[2]}'> | ||||
@@ -419,15 +419,18 @@ frappe.views.ListRenderer = Class.extend({ | |||||
} | } | ||||
return ''; | return ''; | ||||
}, | }, | ||||
get_indicator_dot: function (doc) { | get_indicator_dot: function (doc) { | ||||
var indicator = frappe.get_indicator(doc, this.doctype); | |||||
var indicator = this.get_indicator_from_doc(doc); | |||||
if (!indicator) { | if (!indicator) { | ||||
return ''; | return ''; | ||||
} | } | ||||
return `<span class='indicator ${indicator[1]}' title='${__(indicator[0])}'></span>`; | return `<span class='indicator ${indicator[1]}' title='${__(indicator[0])}'></span>`; | ||||
}, | }, | ||||
get_indicator_from_doc: function (doc) { | |||||
var workflow = frappe.workflow.workflows[this.doctype]; | |||||
var override = workflow ? workflow['override_status'] : true; | |||||
return frappe.get_indicator(doc, this.doctype, override); | |||||
}, | |||||
prepare_data: function (data) { | prepare_data: function (data) { | ||||
if (data.modified) | if (data.modified) | ||||
this.prepare_when(data, data.modified); | this.prepare_when(data, data.modified); | ||||
@@ -3,8 +3,11 @@ | |||||
frappe.provide('frappe.datetime'); | frappe.provide('frappe.datetime'); | ||||
moment.defaultFormat = "YYYY-MM-DD"; | |||||
moment.defaultDatetimeFormat = "YYYY-MM-DD HH:mm:ss" | |||||
moment.defaultDateFormat = "YYYY-MM-DD"; | |||||
moment.defaultTimeFormat = "HH:mm:ss"; | |||||
moment.defaultDatetimeFormat = moment.defaultDateFormat + " " + moment.defaultTimeFormat; | |||||
moment.defaultFormat = moment.defaultDateFormat; | |||||
frappe.provide("frappe.datetime"); | frappe.provide("frappe.datetime"); | ||||
$.extend(frappe.datetime, { | $.extend(frappe.datetime, { | ||||
@@ -91,8 +94,14 @@ $.extend(frappe.datetime, { | |||||
return frappe.sys_defaults.date_format || "yyyy-mm-dd"; | return frappe.sys_defaults.date_format || "yyyy-mm-dd"; | ||||
}, | }, | ||||
str_to_user: function(val, no_time_str) { | |||||
str_to_user: function(val, only_time = false) { | |||||
if(!val) return ""; | if(!val) return ""; | ||||
if(only_time) { | |||||
return moment(val, moment.defaultTimeFormat) | |||||
.format(moment.defaultTimeFormat); | |||||
} | |||||
var user_fmt = frappe.datetime.get_user_fmt().toUpperCase(); | var user_fmt = frappe.datetime.get_user_fmt().toUpperCase(); | ||||
if(typeof val !== "string" || val.indexOf(" ")===-1) { | if(typeof val !== "string" || val.indexOf(" ")===-1) { | ||||
return moment(val).format(user_fmt); | return moment(val).format(user_fmt); | ||||
@@ -101,15 +110,17 @@ $.extend(frappe.datetime, { | |||||
} | } | ||||
}, | }, | ||||
now_datetime: function() { | |||||
return moment().format("YYYY-MM-DD HH:mm:ss"); | |||||
}, | |||||
get_datetime_as_string: function(d) { | get_datetime_as_string: function(d) { | ||||
return moment(d).format("YYYY-MM-DD HH:mm:ss"); | return moment(d).format("YYYY-MM-DD HH:mm:ss"); | ||||
}, | }, | ||||
user_to_str: function(val, no_time_str) { | |||||
user_to_str: function(val, only_time = false) { | |||||
if(only_time) { | |||||
return moment(val, moment.defaultTimeFormat) | |||||
.format(moment.defaultTimeFormat); | |||||
} | |||||
var user_fmt = frappe.datetime.get_user_fmt().toUpperCase(); | var user_fmt = frappe.datetime.get_user_fmt().toUpperCase(); | ||||
var system_fmt = "YYYY-MM-DD"; | var system_fmt = "YYYY-MM-DD"; | ||||
@@ -136,21 +147,60 @@ $.extend(frappe.datetime, { | |||||
} | } | ||||
}, | }, | ||||
get_today: function() { | |||||
return moment().locale("en").format(); | |||||
now_date: function(as_obj = false) { | |||||
return frappe.datetime._date(moment.defaultDateFormat, as_obj); | |||||
}, | |||||
now_time: function(as_obj = false) { | |||||
return frappe.datetime._date(moment.defaultTimeFormat, as_obj); | |||||
}, | |||||
now_datetime: function(as_obj = false) { | |||||
return frappe.datetime._date(moment.defaultDatetimeFormat, as_obj); | |||||
}, | |||||
_date: function(format, as_obj = false) { | |||||
const { time_zone } = frappe.sys_defaults; | |||||
let date; | |||||
if (time_zone) { | |||||
date = moment.tz(time_zone); | |||||
} else { | |||||
date = moment(); | |||||
} | |||||
if (as_obj) { | |||||
return frappe.datetime.moment_to_date_obj(date); | |||||
} else { | |||||
return date.format(format); | |||||
} | |||||
}, | |||||
moment_to_date_obj: function(moment) { | |||||
const date_obj = new Date(); | |||||
const date_array = moment.toArray(); | |||||
date_obj.setFullYear(date_array[0]); | |||||
date_obj.setMonth(date_array[1]); | |||||
date_obj.setDate(date_array[2]); | |||||
date_obj.setHours(date_array[3]); | |||||
date_obj.setMinutes(date_array[4]); | |||||
date_obj.setSeconds(date_array[5]); | |||||
date_obj.setMilliseconds(date_array[6]); | |||||
return date_obj; | |||||
}, | }, | ||||
nowdate: function() { | nowdate: function() { | ||||
return frappe.datetime.get_today(); | |||||
return frappe.datetime.now_date(); | |||||
}, | }, | ||||
now_time: function() { | |||||
return frappe.datetime.convert_to_system_tz(moment(), false) | |||||
.locale("en").format("HH:mm:ss"); | |||||
get_today: function() { | |||||
return frappe.datetime.now_date(); | |||||
}, | }, | ||||
validate: function(d) { | validate: function(d) { | ||||
return moment(d).isValid(); | |||||
return moment(d, [ | |||||
moment.defaultDateFormat, | |||||
moment.defaultDatetimeFormat, | |||||
moment.defaultTimeFormat | |||||
], true).isValid(); | |||||
}, | }, | ||||
}); | }); | ||||
@@ -142,10 +142,10 @@ frappe.ui.Graph = class Graph { | |||||
show_specific_values() { | show_specific_values() { | ||||
this.specific_values.map(d => { | this.specific_values.map(d => { | ||||
this.specific_y_lines.add(this.snap.g( | this.specific_y_lines.add(this.snap.g( | ||||
this.snap.line(0, 0, this.width - 50, 0).attr({ | |||||
this.snap.line(0, 0, this.width - 70, 0).attr({ | |||||
class: d.line_type === "dashed" ? "dashed": "" | class: d.line_type === "dashed" ? "dashed": "" | ||||
}), | }), | ||||
this.snap.text(this.width - 100, 0, d.name.toUpperCase()).attr({ | |||||
this.snap.text(this.width - 95, 0, d.name.toUpperCase()).attr({ | |||||
dy: ".32em", | dy: ".32em", | ||||
class: "specific-value", | class: "specific-value", | ||||
}) | }) | ||||
@@ -67,11 +67,11 @@ frappe.ui.notifications = { | |||||
add_notification: function(name, value, doc_dt, target = false) { | add_notification: function(name, value, doc_dt, target = false) { | ||||
let label = this.config[name] ? this.config[name].label : name; | let label = this.config[name] ? this.config[name].label : name; | ||||
let $list_item = !target | let $list_item = !target | ||||
? $(`<li><a class="badge-hover" data-doctype="${name}">${label} | |||||
? $(`<li><a class="badge-hover" data-doctype="${name}">${__(label)} | |||||
<span class="badge pull-right">${value}</span> | <span class="badge pull-right">${value}</span> | ||||
</a></li>`) | </a></li>`) | ||||
: $(`<li><a class="progress-small" data-doctype="${doc_dt}" | : $(`<li><a class="progress-small" data-doctype="${doc_dt}" | ||||
data-doc="${name}"><span class="dropdown-item-label">${label}<span> | |||||
data-doc="${name}"><span class="dropdown-item-label">${__(label)}<span> | |||||
<div class="progress-chart"><div class="progress"> | <div class="progress-chart"><div class="progress"> | ||||
<div class="progress-bar" style="width: ${value}%"></div> | <div class="progress-bar" style="width: ${value}%"></div> | ||||
</div></div> | </div></div> | ||||
@@ -376,7 +376,8 @@ h6.uppercase, .h6.uppercase { | |||||
border-radius: 3px; | border-radius: 3px; | ||||
margin-left: -7px; | margin-left: -7px; | ||||
position: relative; | position: relative; | ||||
max-width: calc(~"100% - 50px"); | |||||
padding-right: 0px; | |||||
// to display the triangle beside the box | // to display the triangle beside the box | ||||
overflow: visible; | overflow: visible; | ||||
} | } | ||||
@@ -387,12 +388,24 @@ h6.uppercase, .h6.uppercase { | |||||
width: 45px; | width: 45px; | ||||
} | } | ||||
.action-btns { | |||||
position: absolute; | |||||
right: 0; | |||||
padding: 5px 15px 2px 5px; | |||||
} | |||||
.comment-header { | .comment-header { | ||||
background-color: @light-bg; | background-color: @light-bg; | ||||
padding: 10px 15px 10px 13px; | padding: 10px 15px 10px 13px; | ||||
margin: 0px; | margin: 0px; | ||||
color: @text-muted; | color: @text-muted; | ||||
border-bottom: 1px solid @light-border-color; | border-bottom: 1px solid @light-border-color; | ||||
&.links-active { | |||||
padding-right: 60px; | |||||
} | |||||
.commented-on-small { | |||||
display: none; | |||||
} | |||||
.octicon-heart { | .octicon-heart { | ||||
color: @heart-color; | color: @heart-color; | ||||
@@ -402,6 +415,7 @@ h6.uppercase, .h6.uppercase { | |||||
.reply { | .reply { | ||||
padding: 15px; | padding: 15px; | ||||
overflow: auto; | |||||
& > div > p:first-child { | & > div > p:first-child { | ||||
margin-top: 0px; | margin-top: 0px; | ||||
@@ -417,11 +431,15 @@ h6.uppercase, .h6.uppercase { | |||||
} | } | ||||
.close-btn-container { | .close-btn-container { | ||||
padding: 4px 10px 2px 5px; | |||||
.close { | |||||
color: inherit; | |||||
opacity: 1; | |||||
padding: 0 0 0 10px; | |||||
} | |||||
} | } | ||||
.edit-btn-container { | .edit-btn-container { | ||||
padding: 4px 5px; | |||||
padding: 0; | |||||
.edit { | .edit { | ||||
color: inherit; | color: inherit; | ||||
@@ -650,6 +668,16 @@ h6.uppercase, .h6.uppercase { | |||||
} | } | ||||
} | } | ||||
.flex-justify-center { | |||||
display: flex; | |||||
justify-content: center; | |||||
} | |||||
.flex-justify-end { | |||||
display: flex; | |||||
justify-content: flex-end; | |||||
} | |||||
.hide-control { | .hide-control { | ||||
display: none !important; | display: none !important; | ||||
} | } | ||||
@@ -223,6 +223,9 @@ body { | |||||
} | } | ||||
@media(max-width: 767px) { | @media(max-width: 767px) { | ||||
.toggle-sidebar { | |||||
margin-right: 0; | |||||
} | |||||
body[data-route^="Form"]{ | body[data-route^="Form"]{ | ||||
.page-title { | .page-title { | ||||
.title-text { | .title-text { | ||||
@@ -406,4 +409,67 @@ body { | |||||
right: 90px; | right: 90px; | ||||
} | } | ||||
} | } | ||||
.timeline { | |||||
&::before { | |||||
content: none; | |||||
} | |||||
.timeline-new-email { | |||||
margin: 20px 0; | |||||
padding-left: 15px; | |||||
&::before { | |||||
content: none; | |||||
} | |||||
} | |||||
.timeline-item { | |||||
&.user-content { | |||||
margin: 20px 15px; | |||||
.media-body { | |||||
margin-left: 0; | |||||
max-width: 100%; | |||||
overflow: hidden; | |||||
&:before { | |||||
content: none; | |||||
} | |||||
} | |||||
.action-btns { | |||||
padding: 5px 10px 2px 5px; | |||||
} | |||||
.comment-header{ | |||||
padding: 7px 10px; | |||||
.links-active { | |||||
padding-right: 10px; | |||||
} | |||||
} | |||||
.avatar-medium { | |||||
margin-right: 10px; | |||||
} | |||||
.reply { | |||||
padding: 10px; | |||||
} | |||||
.commented-on-small{ | |||||
display: inline-block; | |||||
} | |||||
.commented-on-small{ | |||||
display: inline-block; | |||||
} | |||||
} | |||||
&.notification-content { | |||||
padding-left: 15px; | |||||
margin: 20px 0; | |||||
&::before { | |||||
content: none; | |||||
} | |||||
.small { | |||||
padding-left: 0; | |||||
} | |||||
} | |||||
.delivery-status-indicator { | |||||
float: left; | |||||
margin: 0 5px 0 0; | |||||
} | |||||
} | |||||
.asset-details { | |||||
line-height: 24px; /*Height of avtar image -36px to align text center vertically*/ | |||||
} | |||||
} | |||||
} | } |
@@ -15,7 +15,7 @@ | |||||
{% endif %} | {% endif %} | ||||
</table> | </table> | ||||
{% if data %} | {% if data %} | ||||
<table class="table table-bordered" cellpadding="0" cellspacing="0" border="0" width="100%"> | |||||
<table class="table table-bordered text-medium" cellpadding="0" cellspacing="0" border="0" width="100%"> | |||||
<thead> | <thead> | ||||
<tr> | <tr> | ||||
{% for col in columns %} | {% for col in columns %} | ||||
@@ -26,8 +26,15 @@ | |||||
background-color: #7575ff; | background-color: #7575ff; | ||||
} | } | ||||
.for-login { | |||||
display: none; | |||||
} | |||||
.for-forgot { | |||||
display: none; | |||||
} | |||||
section { | |||||
.for-signup { | |||||
display: none; | display: none; | ||||
} | } | ||||
@@ -77,7 +77,9 @@ login.route = function() { | |||||
login.reset_sections = function(hide) { | login.reset_sections = function(hide) { | ||||
if(hide || hide===undefined) { | if(hide || hide===undefined) { | ||||
$("section").toggle(false); | |||||
$("section.for-login").toggle(false); | |||||
$("section.for-forgot").toggle(false); | |||||
$("section.for-signup").toggle(false); | |||||
} | } | ||||
$('section .indicator').each(function() { | $('section .indicator').each(function() { | ||||
$(this).removeClass().addClass('indicator').addClass('blue') | $(this).removeClass().addClass('indicator').addClass('blue') | ||||
@@ -31,6 +31,19 @@ class TestReportview(unittest.TestCase): | |||||
self.assertTrue({"name":"DocField"} \ | self.assertTrue({"name":"DocField"} \ | ||||
in DatabaseQuery("DocType").execute(filters={"name": "DocField"})) | in DatabaseQuery("DocType").execute(filters={"name": "DocField"})) | ||||
def test_in_not_in_filters(self): | |||||
self.assertFalse(DatabaseQuery("DocType").execute(filters={"name": ["in", None]})) | |||||
self.assertTrue({"name":"DocType"} \ | |||||
in DatabaseQuery("DocType").execute(filters={"name": ["not in", None]})) | |||||
for result in [{"name":"DocType"}, {"name":"DocField"}]: | |||||
self.assertTrue(result | |||||
in DatabaseQuery("DocType").execute(filters={"name": ["in", 'DocType,DocField']})) | |||||
for result in [{"name":"DocType"}, {"name":"DocField"}]: | |||||
self.assertFalse(result | |||||
in DatabaseQuery("DocType").execute(filters={"name": ["not in", 'DocType,DocField']})) | |||||
def test_or_filters(self): | def test_or_filters(self): | ||||
data = DatabaseQuery("DocField").execute( | data = DatabaseQuery("DocField").execute( | ||||
filters={"parent": "DocType"}, fields=["fieldname", "fieldtype"], | filters={"parent": "DocType"}, fields=["fieldname", "fieldtype"], | ||||
@@ -0,0 +1,31 @@ | |||||
QUnit.module('views'); | |||||
QUnit.test("Test: Setting column colour [Kanban view]", function(assert) { | |||||
assert.expect(3); | |||||
let done = assert.async(); | |||||
function get_column(name, colour) { | |||||
return ('.kanban-column:contains('+name+')>div>div>ul>li>div.'+colour); | |||||
} | |||||
frappe.run_serially([ | |||||
() => frappe.set_route("List", "ToDo", "Kanban", "Kanban test"), | |||||
() => frappe.timeout(1), | |||||
() => assert.deepEqual(["List", "ToDo", "Kanban", "Kanban test"], frappe.get_route(), | |||||
"Kanban view opened successfully."), | |||||
() => { | |||||
// set colour for columns | |||||
$(get_column('High', "red")).click(); | |||||
$(get_column('Medium', "green")).click(); | |||||
$(get_column('Low', "yellow")).click(); | |||||
}, | |||||
() => frappe.timeout(1), | |||||
() => { | |||||
//check if different colours are set | |||||
assert.equal($('.red > span')[0].innerText, 'High', | |||||
"Colour is set for kanban column."); | |||||
assert.equal($('.green > span')[0].innerText, 'Medium', | |||||
"Different colour is set for other column."); | |||||
}, | |||||
() => done() | |||||
]); | |||||
}); |
@@ -0,0 +1,30 @@ | |||||
QUnit.module('views'); | |||||
QUnit.test("Test: Creation [Kanban view]", function(assert) { | |||||
assert.expect(2); | |||||
let done = assert.async(); | |||||
frappe.run_serially([ | |||||
() => frappe.set_route("List", "ToDo", "List"), | |||||
// click kanban in side bar | |||||
() => frappe.click_link('Kanban'), | |||||
() => frappe.click_link('New Kanban Board'), | |||||
() => frappe.timeout(0.5), | |||||
// create new kanban | |||||
() => { | |||||
assert.equal(cur_dialog.title, 'New Kanban Board', | |||||
"Dialog for new kanban opened."); | |||||
cur_dialog.set_value('board_name', 'Kanban test'); | |||||
cur_dialog.set_value('field_name', 'Priority'); | |||||
}, | |||||
() => frappe.timeout(0.5), | |||||
() => cur_dialog.get_primary_btn().click(), | |||||
() => frappe.timeout(1), | |||||
() => frappe.set_route("List", "Kanban Board", "List"), | |||||
() => frappe.timeout(0.5), | |||||
// check in kanban list if new kanban is created | |||||
() => assert.equal(cur_list.data[0].name, 'Kanban test', | |||||
"Added kanban is visible in kanban list."), | |||||
() => done() | |||||
]); | |||||
}); |
@@ -0,0 +1,27 @@ | |||||
QUnit.module('views'); | |||||
QUnit.test("Test: Filters [Kanban view]", function(assert) { | |||||
assert.expect(3); | |||||
let done = assert.async(); | |||||
frappe.run_serially([ | |||||
() => frappe.set_route("List", "ToDo", "Kanban", "Kanban test"), | |||||
() => frappe.timeout(1), | |||||
() => { | |||||
assert.deepEqual(["List", "ToDo", "Kanban", "Kanban test"], frappe.get_route(), | |||||
"Kanban view opened successfully."); | |||||
// set filter values | |||||
return frappe.set_control('priority', 'Low'); | |||||
}, | |||||
() => frappe.timeout(1), | |||||
() => cur_list.page.btn_secondary.click(), | |||||
() => frappe.timeout(1), | |||||
() => { | |||||
assert.equal(cur_list.data[0].priority, 'Low', | |||||
'visible element has low priority'); | |||||
let non_low_items = cur_list.data.filter(d => d.priority != 'Low'); | |||||
assert.equal(non_low_items.length, 0, 'No item without low priority'); | |||||
}, | |||||
() => done() | |||||
]); | |||||
}); |
@@ -0,0 +1,26 @@ | |||||
QUnit.module('views'); | |||||
QUnit.test("Test: Kanban view", function(assert) { | |||||
assert.expect(3); | |||||
let done = assert.async(); | |||||
let total_elements; | |||||
frappe.run_serially([ | |||||
() => frappe.set_route("List", "ToDo", "List"), | |||||
// calculate number of element in list | |||||
() => frappe.timeout(1), | |||||
() => total_elements = cur_list.data.length, | |||||
() => frappe.set_route("List", "ToDo", "Kanban", "Kanban test"), | |||||
() => frappe.timeout(1), | |||||
() => { | |||||
assert.equal('Kanban', cur_list.current_view, | |||||
"Current view is kanban."); | |||||
assert.equal("Kanban test", cur_list.list_renderer.page_title, | |||||
"Kanban view opened successfully."); | |||||
// check if all elements are visible in kanban view | |||||
assert.equal(total_elements, cur_list.data.length, | |||||
"All elements are visible in kanban view."); | |||||
}, | |||||
() => done() | |||||
]); | |||||
}); |
@@ -1,35 +0,0 @@ | |||||
QUnit.module('views'); | |||||
QUnit.test("Test option click [Module view]", function(assert) { | |||||
assert.expect(4); | |||||
let done = assert.async(); | |||||
frappe.run_serially([ | |||||
//click Document Share Report in Permissions section [Report] | |||||
() => frappe.set_route("modules", "Setup"), | |||||
() => frappe.timeout(0.5), | |||||
() => frappe.tests.click_and_wait('a.small:contains("Document Share Report")', 0), | |||||
() => assert.deepEqual(frappe.get_route(), ["Report", "DocShare", "Document Share Report"], "First click test."), | |||||
//click Print Setting in Printing section [Form] | |||||
() => frappe.set_route("modules", "Setup"), | |||||
() => frappe.timeout(0.5), | |||||
() => frappe.tests.click_and_wait('a.small:contains("Print Setting")', 0), | |||||
() => assert.deepEqual(frappe.get_route(), ["Form", "Print Settings"], "Second click test."), | |||||
//click Workflow Action in Workflow section [List] | |||||
() => frappe.set_route("modules", "Setup"), | |||||
() => frappe.timeout(0.5), | |||||
() => frappe.tests.click_and_wait('a.small:contains(" Workflow Action ")', 0), | |||||
() => assert.deepEqual(frappe.get_route(), ["List", "Workflow Action", "List"], "Third click test."), | |||||
//click Application Installer in Applications section | |||||
() => frappe.set_route("modules", "Setup"), | |||||
() => frappe.timeout(0.5), | |||||
() => frappe.tests.click_and_wait('a.small:contains("Application Installer")', 0), | |||||
() => assert.deepEqual(frappe.get_route(), ["applications"], "Fourth click test."), | |||||
() => done() | |||||
]); | |||||
}); |
@@ -0,0 +1,76 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | |||||
import unittest, frappe, requests, time | |||||
from frappe.test_runner import make_test_records | |||||
from frappe.utils.selenium_testdriver import TestDriver | |||||
try: | |||||
from urllib.parse import urlparse | |||||
except ImportError: | |||||
from urlparse import urlparse | |||||
class TestOAuth20(unittest.TestCase): | |||||
def setUp(self): | |||||
self.driver = TestDriver() | |||||
make_test_records("OAuth Client") | |||||
make_test_records("User") | |||||
self.client_id = frappe.get_all("OAuth Client", fields=["*"])[0].get("client_id") | |||||
# Set Frappe server URL reqired for id_token generation | |||||
frappe.db.set_value("Social Login Keys", None, "frappe_server_url", "http://localhost:8000") | |||||
frappe.db.commit() | |||||
def test_login_to_authorize_url(self): | |||||
# Go to Authorize url | |||||
self.driver.get( | |||||
"api/method/frappe.integrations.oauth2.authorize?client_id=" + | |||||
self.client_id + | |||||
"&scope=all%20openid&response_type=code&redirect_uri=http%3A%2F%2Flocalhost" | |||||
) | |||||
time.sleep(2) | |||||
# Login | |||||
username = self.driver.find("#login_email")[0] | |||||
username.send_keys("test@example.com") | |||||
password = self.driver.find("#login_password")[0] | |||||
password.send_keys("Eastern_43A1W") | |||||
sign_in = self.driver.find(".btn-login")[0] | |||||
sign_in.submit() | |||||
time.sleep(2) | |||||
# Allow access to resource | |||||
allow = self.driver.find("#allow")[0] | |||||
allow.click() | |||||
time.sleep(2) | |||||
# Get authorization code from redirected URL | |||||
auth_code = urlparse(self.driver.driver.current_url).query.split("=")[1] | |||||
payload = "grant_type=authorization_code&code=" | |||||
payload += auth_code | |||||
payload += "&redirect_uri=http%3A%2F%2Flocalhost&client_id=" | |||||
payload += self.client_id | |||||
headers = {'content-type':'application/x-www-form-urlencoded'} | |||||
# Request for bearer token | |||||
token_response = requests.post( frappe.get_site_config().host_name + | |||||
"/api/method/frappe.integrations.oauth2.get_token", data=payload, headers=headers) | |||||
# Parse bearer token json | |||||
bearer_token = token_response.json() | |||||
self.assertTrue(bearer_token.get("access_token")) | |||||
self.assertTrue(bearer_token.get("expires_in")) | |||||
self.assertTrue(bearer_token.get("id_token")) | |||||
self.assertTrue(bearer_token.get("refresh_token")) | |||||
self.assertTrue(bearer_token.get("scope")) | |||||
self.assertTrue(bearer_token.get("token_type") == "Bearer") |
@@ -3,5 +3,9 @@ frappe/tests/ui/test_list/test_list_filter.js | |||||
frappe/tests/ui/test_list/test_list_paging.js | frappe/tests/ui/test_list/test_list_paging.js | ||||
frappe/tests/ui/test_module_view.js | frappe/tests/ui/test_module_view.js | ||||
frappe/tests/ui/test_calendar_view.js | frappe/tests/ui/test_calendar_view.js | ||||
frappe/tests/ui/test_kanban/test_kanban_creation.js | |||||
frappe/tests/ui/test_kanban/test_kanban_view.js | |||||
frappe/tests/ui/test_kanban/test_kanban_filters.js | |||||
frappe/tests/ui/test_kanban/test_kanban_column.js | |||||
frappe/tests/ui/test_linked_with.js | frappe/tests/ui/test_linked_with.js | ||||
frappe/custom/doctype/customize_form/test_customize_form.js | frappe/custom/doctype/customize_form/test_customize_form.js |