Merge branch 'staging'
This commit is contained in:
commit
286c92aef0
36 changed files with 774 additions and 242 deletions
|
|
@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json
|
|||
from .exceptions import *
|
||||
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"
|
||||
|
||||
local = Local()
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import unittest
|
|||
|
||||
# test_records = frappe.get_test_records('DocType')
|
||||
|
||||
|
||||
class TestDocType(unittest.TestCase):
|
||||
def new_doctype(self, name):
|
||||
def new_doctype(self, name, unique=0):
|
||||
return frappe.get_doc({
|
||||
"doctype": "DocType",
|
||||
"module": "Core",
|
||||
"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}],
|
||||
"name": name
|
||||
})
|
||||
|
|
@ -28,4 +29,28 @@ class TestDocType(unittest.TestCase):
|
|||
frappe.delete_doc("DocType", name)
|
||||
|
||||
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_delete": 0,
|
||||
"allow_edit": 1,
|
||||
"allow_incomplete": 0,
|
||||
"allow_multiple": 0,
|
||||
"allow_print": 0,
|
||||
"amount": 0.0,
|
||||
"amount_based_on_field": 0,
|
||||
"breadcrumbs": "[{\"title\": _(\"My Account\"), \"route\": \"me\"}]",
|
||||
"creation": "2016-09-19 05:16:59.242754",
|
||||
"doc_type": "User",
|
||||
"docstatus": 0,
|
||||
"doctype": "Web Form",
|
||||
"idx": 0,
|
||||
"introduction_text": "",
|
||||
"is_standard": 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",
|
||||
"module": "Core",
|
||||
"name": "edit-profile",
|
||||
|
|
@ -29,6 +36,8 @@
|
|||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"label": "First Name",
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
|
|
@ -37,6 +46,8 @@
|
|||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"label": "Middle Name (Optional)",
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
|
|
@ -45,6 +56,8 @@
|
|||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"label": "Last Name",
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
|
|
@ -54,6 +67,8 @@
|
|||
"fieldtype": "Attach",
|
||||
"hidden": 0,
|
||||
"label": "User Image",
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
|
|
@ -61,6 +76,8 @@
|
|||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"label": "More Information",
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
|
|
@ -69,6 +86,18 @@
|
|||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"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,
|
||||
"reqd": 0
|
||||
},
|
||||
|
|
@ -78,6 +107,8 @@
|
|||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"label": "Language",
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"options": "Language",
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
|
|
|
|||
|
|
@ -2,25 +2,6 @@
|
|||
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 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
|
|
@ -59,14 +40,6 @@
|
|||
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 {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
|
@ -113,8 +86,7 @@
|
|||
.setup-wizard-slide .frappe-control[data-fieldtype="Attach Image"] {
|
||||
width: 140px;
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ frappe.setup.Wizard = Class.extend({
|
|||
</div>', {html:html}))
|
||||
},
|
||||
show_working: function() {
|
||||
$('header').find('.setup-wizard-brand').hide();
|
||||
this.hide_current_slide();
|
||||
frappe.set_route(this.page_name);
|
||||
this.current_slide = {"$wrapper": this.get_message(this.working_html()).appendTo(this.parent)};
|
||||
|
|
@ -506,7 +505,7 @@ var frappe_slides = [
|
|||
icon: "fa fa-user",
|
||||
fields: [
|
||||
{ "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",
|
||||
reqd:1},
|
||||
{ "fieldname": "email", "label": __("Email Address") + ' (' + __("Will be your login ID") + ')',
|
||||
|
|
@ -721,12 +720,4 @@ var utils = {
|
|||
frappe.setup.on("before_load", function() {
|
||||
// load slides
|
||||
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"
|
||||
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()
|
||||
def get_tags(doctype, txt, cat_tags):
|
||||
|
|
|
|||
|
|
@ -69,15 +69,15 @@ def add_subscribers(name, email_list):
|
|||
count = 0
|
||||
for email in email_list:
|
||||
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",
|
||||
{"email_group": name, "email": email}):
|
||||
{"email_group": name, "email": parsed_email}):
|
||||
frappe.get_doc({
|
||||
"doctype": "Email Group Member",
|
||||
"email_group": name,
|
||||
"email": email
|
||||
"email": parsed_email
|
||||
}).insert(ignore_permissions = frappe.flags.ignore_permissions)
|
||||
|
||||
count += 1
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ def backup_to_dropbox():
|
|||
access_token = generate_oauth2_access_token_from_oauth1_token(dropbox_settings)
|
||||
|
||||
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']
|
||||
set_dropbox_access_token(access_token['oauth2_token'])
|
||||
|
|
|
|||
16
frappe/integrations/doctype/oauth_client/test_records.json
Normal file
16
frappe/integrations/doctype/oauth_client/test_records.json
Normal file
|
|
@ -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
|
||||
if f.operator.lower() in ('in', 'not in'):
|
||||
values = f.value
|
||||
values = f.value or ''
|
||||
if not isinstance(values, (list, tuple)):
|
||||
values = values.split(",")
|
||||
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ class DbTable:
|
|||
# if index key exists
|
||||
if frappe.db.sql("""show index from `{0}`
|
||||
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))
|
||||
|
||||
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"')
|
||||
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.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
frappe/patches/v8_5/__init__.py
Normal file
0
frappe/patches/v8_5/__init__.py
Normal file
|
|
@ -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;
|
||||
margin-left: -7px;
|
||||
position: relative;
|
||||
max-width: calc(100% - 50px);
|
||||
padding-right: 0px;
|
||||
overflow: visible;
|
||||
}
|
||||
.timeline-item.user-content .avatar-medium {
|
||||
|
|
@ -294,6 +296,11 @@ h6.uppercase,
|
|||
height: 45px;
|
||||
width: 45px;
|
||||
}
|
||||
.timeline-item.user-content .action-btns {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 5px 15px 2px 5px;
|
||||
}
|
||||
.timeline-item.user-content .comment-header {
|
||||
background-color: #fafbfc;
|
||||
padding: 10px 15px 10px 13px;
|
||||
|
|
@ -301,12 +308,19 @@ h6.uppercase,
|
|||
color: #8D99A6;
|
||||
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 {
|
||||
color: #ff5858;
|
||||
cursor: pointer;
|
||||
}
|
||||
.timeline-item.user-content .reply {
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
}
|
||||
.timeline-item.user-content .reply > div > p:first-child {
|
||||
margin-top: 0px;
|
||||
|
|
@ -317,11 +331,13 @@ h6.uppercase,
|
|||
.timeline-item.user-content .reply hr {
|
||||
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 {
|
||||
padding: 4px 5px;
|
||||
padding: 0;
|
||||
}
|
||||
.timeline-item.user-content .edit-btn-container .edit {
|
||||
color: inherit;
|
||||
|
|
@ -515,6 +531,14 @@ h6.uppercase,
|
|||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
.flex-justify-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.flex-justify-end {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.hide-control {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,9 @@ body {
|
|||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.toggle-sidebar {
|
||||
margin-right: 0;
|
||||
}
|
||||
body[data-route^="Form"] .page-title .title-text {
|
||||
font-size: 16px;
|
||||
width: calc(100% - 30px);
|
||||
|
|
@ -331,4 +334,64 @@ body {
|
|||
body[data-route^="Form"] .page-head .sub-heading {
|
||||
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 {
|
||||
$(me.input_area).toggle(false);
|
||||
if (me.disp_area) {
|
||||
me.set_disp_area();
|
||||
me.set_disp_area(me.value);
|
||||
$(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)
|
||||
&& (this.value === 0 || value === 0)) {
|
||||
// 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.value = value;
|
||||
this.set_formatted_input(value);
|
||||
this.set_disp_area();
|
||||
this.set_disp_area(value);
|
||||
this.set_mandatory && this.set_mandatory(value);
|
||||
},
|
||||
set_formatted_input: function(value) {
|
||||
|
|
@ -752,29 +751,38 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({
|
|||
if(!$.fn.datepicker.language[lang]) {
|
||||
lang = 'en';
|
||||
}
|
||||
this.today_text = __("Today");
|
||||
this.datepicker_options = {
|
||||
language: lang,
|
||||
autoClose: true,
|
||||
todayButton: new Date(),
|
||||
todayButton: frappe.datetime.now_date(true),
|
||||
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'));
|
||||
onShow: () => {
|
||||
this.datepicker.$datepicker
|
||||
.find('.datepicker--button:visible')
|
||||
.text(me.today_text);
|
||||
|
||||
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);
|
||||
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() {
|
||||
this.$input.datepicker(this.datepicker_options);
|
||||
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({
|
||||
make_input: function() {
|
||||
var me = this;
|
||||
|
|
@ -823,13 +855,14 @@ frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({
|
|||
timepicker: true,
|
||||
onlyTimepicker: true,
|
||||
timeFormat: "hh:ii:ss",
|
||||
startDate: frappe.datetime.now_time(true),
|
||||
onSelect: function() {
|
||||
me.$input.trigger('change');
|
||||
},
|
||||
onShow: function() {
|
||||
$('.datepicker--button:visible').text(__('Now'));
|
||||
},
|
||||
todayButton: new Date()
|
||||
todayButton: frappe.datetime.now_time(true)
|
||||
});
|
||||
this.datepicker = this.$input.data('datepicker');
|
||||
this.refresh();
|
||||
|
|
@ -840,33 +873,21 @@ frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({
|
|||
&& ((this.last_value && this.last_value !== this.value)
|
||||
|| (!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() {
|
||||
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();
|
||||
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));
|
||||
}
|
||||
return value || "";
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -903,11 +924,12 @@ frappe.ui.form.ControlDateRange = frappe.ui.form.ControlData.extend({
|
|||
this.value = 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 {
|
||||
this.$input && this.$input.val("");
|
||||
}
|
||||
this.set_disp_area();
|
||||
this.set_disp_area(value || '');
|
||||
this.set_mandatory && this.set_mandatory(value);
|
||||
},
|
||||
parse: function(value) {
|
||||
|
|
@ -984,7 +1006,7 @@ frappe.ui.form.ControlCheck = frappe.ui.form.ControlData.extend({
|
|||
}
|
||||
this.last_value = 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() {
|
||||
var me = this;
|
||||
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;">\
|
||||
<div class="missing-image attach-missing-image"><i class="octicon octicon-device-camera"></i></div></div>')
|
||||
.appendTo(this.wrapper);
|
||||
|
|
|
|||
|
|
@ -248,14 +248,14 @@ frappe.ui.form.Timeline = Class.extend({
|
|||
c["edit"] = "";
|
||||
if(c.communication_type=="Comment" && (c.comment_type || "Comment") === "Comment") {
|
||||
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')) {
|
||||
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);
|
||||
if(!c.fullname) {
|
||||
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",
|
||||
"Like": "octicon octicon-heart",
|
||||
"Edit": "octicon octicon-pencil",
|
||||
"Relinked": "octicon octicon-check"
|
||||
"Relinked": "octicon octicon-check",
|
||||
"Reply": "octicon octicon-mail-reply"
|
||||
}[c.comment_type || c.communication_medium]
|
||||
|
||||
c.color = {
|
||||
|
|
@ -378,7 +379,8 @@ frappe.ui.form.Timeline = Class.extend({
|
|||
"Label": "#2c3e50",
|
||||
"Attachment": "#7f8c8d",
|
||||
"Attachment Removed": "#eee",
|
||||
"Relinked": "#16a085"
|
||||
"Relinked": "#16a085",
|
||||
"Reply": "#8d99a6"
|
||||
}[c.comment_type || c.communication_medium];
|
||||
|
||||
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 %}">
|
||||
{% 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) { %}
|
||||
<div class="avatar-frame" style="background-image: url({%= data.user_info.image %})"></div>
|
||||
{% } else { %}
|
||||
|
|
@ -10,88 +10,108 @@
|
|||
</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="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>
|
||||
{% if(data.communication_type==="Communication"
|
||||
|| data.communication_type==="Feedback"
|
||||
|| (data.communication_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 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>
|
||||
{% } %}
|
||||
|
||||
{% 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>
|
||||
|
||||
{% } else { %}
|
||||
</span>
|
||||
{% if(in_list(["Communication", "Feedback"], data.communication_type)) { %}
|
||||
{% if (frappe.model.can_read(\'Communication\')) { %}
|
||||
<span class="text-muted">–</span>
|
||||
{%= __("Details") %}
|
||||
<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 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 n-dash">–</span>
|
||||
{%= __("Details") %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
|
||||
{% 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 %}" title="{%= __("Reply") %}"><i class="octicon octicon-mail-reply"></i></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>
|
||||
{% } %}
|
||||
{% } %}
|
||||
<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 class="reply timeline-content-show" style="overflow-x: auto">
|
||||
<div class="reply timeline-content-show">
|
||||
<div class="timeline-item-content">
|
||||
{% if data.show_subject %}
|
||||
<p class="text-muted small">
|
||||
|
|
@ -143,7 +163,7 @@
|
|||
{% if(data.link_doctype && data.link_name) { %}
|
||||
</a>
|
||||
{% } %}
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
<span class="text-muted commented-on" style="font-weight: normal;">
|
||||
– {%= data.comment_on %}</span>
|
||||
</div>
|
||||
{% } else { %}
|
||||
|
|
@ -172,7 +192,7 @@
|
|||
</a>
|
||||
{% } %}
|
||||
{% } %}
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
<span class="text-muted commented-on" style="font-weight: normal;">
|
||||
– {%= data.comment_on %}</span>
|
||||
</div>
|
||||
{% } %}
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@ frappe.views.ListRenderer = Class.extend({
|
|||
},
|
||||
|
||||
get_indicator_html: function (doc) {
|
||||
var indicator = frappe.get_indicator(doc, this.doctype);
|
||||
var indicator = this.get_indicator_from_doc(doc);
|
||||
if (indicator) {
|
||||
return `<span class='indicator ${indicator[1]} filterable'
|
||||
data-filter='${indicator[2]}'>
|
||||
|
|
@ -419,15 +419,18 @@ frappe.views.ListRenderer = Class.extend({
|
|||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
get_indicator_dot: function (doc) {
|
||||
var indicator = frappe.get_indicator(doc, this.doctype);
|
||||
var indicator = this.get_indicator_from_doc(doc);
|
||||
if (!indicator) {
|
||||
return '';
|
||||
}
|
||||
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) {
|
||||
if (data.modified)
|
||||
this.prepare_when(data, data.modified);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@
|
|||
|
||||
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");
|
||||
|
||||
$.extend(frappe.datetime, {
|
||||
|
|
@ -91,8 +94,14 @@ $.extend(frappe.datetime, {
|
|||
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(only_time) {
|
||||
return moment(val, moment.defaultTimeFormat)
|
||||
.format(moment.defaultTimeFormat);
|
||||
}
|
||||
|
||||
var user_fmt = frappe.datetime.get_user_fmt().toUpperCase();
|
||||
if(typeof val !== "string" || val.indexOf(" ")===-1) {
|
||||
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) {
|
||||
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 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() {
|
||||
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) {
|
||||
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() {
|
||||
this.specific_values.map(d => {
|
||||
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": ""
|
||||
}),
|
||||
this.snap.text(this.width - 100, 0, d.name.toUpperCase()).attr({
|
||||
this.snap.text(this.width - 95, 0, d.name.toUpperCase()).attr({
|
||||
dy: ".32em",
|
||||
class: "specific-value",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -67,11 +67,11 @@ frappe.ui.notifications = {
|
|||
add_notification: function(name, value, doc_dt, target = false) {
|
||||
let label = this.config[name] ? this.config[name].label : name;
|
||||
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>
|
||||
</a></li>`)
|
||||
: $(`<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-bar" style="width: ${value}%"></div>
|
||||
</div></div>
|
||||
|
|
|
|||
|
|
@ -376,7 +376,8 @@ h6.uppercase, .h6.uppercase {
|
|||
border-radius: 3px;
|
||||
margin-left: -7px;
|
||||
position: relative;
|
||||
|
||||
max-width: calc(~"100% - 50px");
|
||||
padding-right: 0px;
|
||||
// to display the triangle beside the box
|
||||
overflow: visible;
|
||||
}
|
||||
|
|
@ -387,12 +388,24 @@ h6.uppercase, .h6.uppercase {
|
|||
width: 45px;
|
||||
}
|
||||
|
||||
.action-btns {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 5px 15px 2px 5px;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
background-color: @light-bg;
|
||||
padding: 10px 15px 10px 13px;
|
||||
margin: 0px;
|
||||
color: @text-muted;
|
||||
border-bottom: 1px solid @light-border-color;
|
||||
&.links-active {
|
||||
padding-right: 60px;
|
||||
}
|
||||
.commented-on-small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.octicon-heart {
|
||||
color: @heart-color;
|
||||
|
|
@ -402,6 +415,7 @@ h6.uppercase, .h6.uppercase {
|
|||
|
||||
.reply {
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
|
||||
& > div > p:first-child {
|
||||
margin-top: 0px;
|
||||
|
|
@ -417,11 +431,15 @@ h6.uppercase, .h6.uppercase {
|
|||
}
|
||||
|
||||
.close-btn-container {
|
||||
padding: 4px 10px 2px 5px;
|
||||
.close {
|
||||
color: inherit;
|
||||
opacity: 1;
|
||||
padding: 0 0 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-btn-container {
|
||||
padding: 4px 5px;
|
||||
padding: 0;
|
||||
|
||||
.edit {
|
||||
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 {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,6 +223,9 @@ body {
|
|||
}
|
||||
|
||||
@media(max-width: 767px) {
|
||||
.toggle-sidebar {
|
||||
margin-right: 0;
|
||||
}
|
||||
body[data-route^="Form"]{
|
||||
.page-title {
|
||||
.title-text {
|
||||
|
|
@ -406,4 +409,67 @@ body {
|
|||
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 %}
|
||||
</table>
|
||||
{% 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>
|
||||
<tr>
|
||||
{% for col in columns %}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,15 @@
|
|||
background-color: #7575ff;
|
||||
}
|
||||
|
||||
.for-login {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section {
|
||||
.for-forgot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.for-signup {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,9 @@ login.route = function() {
|
|||
|
||||
login.reset_sections = function(hide) {
|
||||
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() {
|
||||
$(this).removeClass().addClass('indicator').addClass('blue')
|
||||
|
|
|
|||
|
|
@ -31,6 +31,19 @@ class TestReportview(unittest.TestCase):
|
|||
self.assertTrue({"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):
|
||||
data = DatabaseQuery("DocField").execute(
|
||||
filters={"parent": "DocType"}, fields=["fieldname", "fieldtype"],
|
||||
|
|
|
|||
31
frappe/tests/ui/test_kanban/test_kanban_column.js
Normal file
31
frappe/tests/ui/test_kanban/test_kanban_column.js
Normal file
|
|
@ -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()
|
||||
]);
|
||||
});
|
||||
30
frappe/tests/ui/test_kanban/test_kanban_creation.js
Normal file
30
frappe/tests/ui/test_kanban/test_kanban_creation.js
Normal file
|
|
@ -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()
|
||||
]);
|
||||
});
|
||||
27
frappe/tests/ui/test_kanban/test_kanban_filters.js
Normal file
27
frappe/tests/ui/test_kanban/test_kanban_filters.js
Normal file
|
|
@ -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()
|
||||
]);
|
||||
});
|
||||
26
frappe/tests/ui/test_kanban/test_kanban_view.js
Normal file
26
frappe/tests/ui/test_kanban/test_kanban_view.js
Normal file
|
|
@ -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()
|
||||
]);
|
||||
});
|
||||
76
frappe/tests/ui/test_oauth20.py
Normal file
76
frappe/tests/ui/test_oauth20.py
Normal file
|
|
@ -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_module_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/custom/doctype/customize_form/test_customize_form.js
|
||||
Loading…
Add table
Reference in a new issue