@@ -10,6 +10,7 @@ const path_join = path.resolve; | |||||
const app = require('express')(); | const app = require('express')(); | ||||
const http = require('http').Server(app); | const http = require('http').Server(app); | ||||
const io = require('socket.io')(http); | const io = require('socket.io')(http); | ||||
const touch = require("touch"); | |||||
// basic setup | // basic setup | ||||
const sites_path = path_join(__dirname, '..', '..', '..', 'sites'); | const sites_path = path_join(__dirname, '..', '..', '..', 'sites'); | ||||
@@ -42,6 +43,7 @@ function build(minify) { | |||||
for (const output_path in build_map) { | for (const output_path in build_map) { | ||||
pack(output_path, build_map[output_path], minify); | pack(output_path, build_map[output_path], minify); | ||||
} | } | ||||
touch(path_join(sites_path, '.build'), {force:true}); | |||||
} | } | ||||
let socket_connection = false; | let socket_connection = false; | ||||
@@ -228,7 +230,7 @@ function watch_less(ondirty) { | |||||
const less_paths = app_paths.map(path => path_join(path, 'public', 'less')); | const less_paths = app_paths.map(path => path_join(path, 'public', 'less')); | ||||
const to_watch = filter_valid_paths(less_paths); | const to_watch = filter_valid_paths(less_paths); | ||||
chokidar.watch(to_watch).on('change', (filename, stats) => { | |||||
chokidar.watch(to_watch).on('change', (filename) => { | |||||
console.log(filename, 'dirty'); | console.log(filename, 'dirty'); | ||||
var last_index = filename.lastIndexOf('/'); | var last_index = filename.lastIndexOf('/'); | ||||
const less_path = filename.slice(0, last_index); | const less_path = filename.slice(0, last_index); | ||||
@@ -236,17 +238,18 @@ function watch_less(ondirty) { | |||||
filename = filename.split('/').pop(); | filename = filename.split('/').pop(); | ||||
compile_less_file(filename, less_path, public_path) | compile_less_file(filename, less_path, public_path) | ||||
.then(css_file_path => { | |||||
// build the target css file for which this css file is input | |||||
for (const target in build_map) { | |||||
const sources = build_map[target]; | |||||
if (sources.includes(css_file_path)) { | |||||
pack(target, sources); | |||||
ondirty && ondirty(target); | |||||
break; | |||||
.then(css_file_path => { | |||||
// build the target css file for which this css file is input | |||||
for (const target in build_map) { | |||||
const sources = build_map[target]; | |||||
if (sources.includes(css_file_path)) { | |||||
pack(target, sources); | |||||
ondirty && ondirty(target); | |||||
break; | |||||
} | |||||
} | } | ||||
} | |||||
}) | |||||
}); | |||||
touch(path_join(sites_path, '.build'), {force:true}); | |||||
}); | }); | ||||
} | } | ||||
@@ -265,6 +268,7 @@ function watch_js(ondirty) { | |||||
// break; | // break; | ||||
} | } | ||||
} | } | ||||
touch(path_join(sites_path, '.build'), {force:true}); | |||||
}); | }); | ||||
} | } | ||||
@@ -10,7 +10,7 @@ frappe.ui.form.on("Note", { | |||||
// toggle edit | // toggle edit | ||||
frm.add_custom_button("Edit", function() { | frm.add_custom_button("Edit", function() { | ||||
frm.events.set_editable(frm, !frm.is_note_editable); | frm.events.set_editable(frm, !frm.is_note_editable); | ||||
}) | |||||
}); | |||||
frm.events.set_editable(frm, false); | frm.events.set_editable(frm, false); | ||||
} | } | ||||
}, | }, | ||||
@@ -24,12 +24,12 @@ frappe.ui.form.on("Note", { | |||||
frm.set_df_property("content", "read_only", editable ? 0: 1); | frm.set_df_property("content", "read_only", editable ? 0: 1); | ||||
// hide all other fields | // hide all other fields | ||||
$.each(frm.fields_dict, function(fieldname, field) { | |||||
$.each(frm.fields_dict, function(fieldname) { | |||||
if(fieldname !== "content") { | if(fieldname !== "content") { | ||||
frm.set_df_property(fieldname, "hidden", editable ? 0: 1); | frm.set_df_property(fieldname, "hidden", editable ? 0: 1); | ||||
} | } | ||||
}) | |||||
}); | |||||
// no label, description for content either | // no label, description for content either | ||||
frm.get_field("content").toggle_label(editable); | frm.get_field("content").toggle_label(editable); | ||||
@@ -49,7 +49,7 @@ hr { | |||||
border-top: none; | border-top: none; | ||||
} | } | ||||
.email-footer-container { | .email-footer-container { | ||||
margin-top: 10px; | |||||
margin-top: 30px; | |||||
} | } | ||||
.email-footer-container > div:not(:last-child) { | .email-footer-container > div:not(:last-child) { | ||||
margin-bottom: 5px; | margin-bottom: 5px; | ||||
@@ -183,6 +183,25 @@ | |||||
.listview-main-section .octicon-heart { | .listview-main-section .octicon-heart { | ||||
cursor: pointer; | cursor: pointer; | ||||
} | } | ||||
.listview-main-section .page-form { | |||||
padding-left: 17px; | |||||
} | |||||
@media (max-width: 991px) { | |||||
.listview-main-section .page-form { | |||||
padding-left: 25px; | |||||
} | |||||
} | |||||
.listview-main-section .page-form .octicon-search { | |||||
float: left; | |||||
padding-top: 7px; | |||||
margin-left: -4px; | |||||
margin-right: -4px; | |||||
} | |||||
@media (max-width: 991px) { | |||||
.listview-main-section .page-form .octicon-search { | |||||
margin-left: -12px; | |||||
} | |||||
} | |||||
.like-action.octicon-heart { | .like-action.octicon-heart { | ||||
color: #ff5858; | color: #ff5858; | ||||
} | } | ||||
@@ -486,7 +486,7 @@ frappe.Application = Class.extend({ | |||||
return; | return; | ||||
} | } | ||||
window.onbeforeunload = function () { | window.onbeforeunload = function () { | ||||
if (frappe.flags.in_test) return false; | |||||
if (frappe.flags.in_test) return null; | |||||
var unsaved_docs = []; | var unsaved_docs = []; | ||||
for (doctype in locals) { | for (doctype in locals) { | ||||
for (name in locals[doctype]) { | for (name in locals[doctype]) { | ||||
@@ -233,6 +233,7 @@ frappe.ui.form.Timeline = Class.extend({ | |||||
new frappe.views.CommunicationComposer({ | new frappe.views.CommunicationComposer({ | ||||
doc: me.frm.doc, | doc: me.frm.doc, | ||||
txt: "", | txt: "", | ||||
subject: __('Reply'), | |||||
frm: me.frm, | frm: me.frm, | ||||
last_email: last_email | last_email: last_email | ||||
}); | }); | ||||
@@ -185,6 +185,9 @@ frappe.ui.BaseList = Class.extend({ | |||||
} | } | ||||
if (this.meta) { | if (this.meta) { | ||||
var filter_count = 1; | |||||
$(`<span class="octicon octicon-search text-muted small"></span>`) | |||||
.appendTo(this.page.page_form); | |||||
this.page.add_field({ | this.page.add_field({ | ||||
fieldtype: 'Data', | fieldtype: 'Data', | ||||
label: 'ID', | label: 'ID', | ||||
@@ -193,14 +196,14 @@ frappe.ui.BaseList = Class.extend({ | |||||
onchange: () => { me.refresh(true); } | onchange: () => { me.refresh(true); } | ||||
}); | }); | ||||
this.meta.fields.forEach(function(df) { | |||||
this.meta.fields.forEach(function(df, i) { | |||||
if(df.in_standard_filter && !frappe.model.no_value_type.includes(df.fieldtype)) { | if(df.in_standard_filter && !frappe.model.no_value_type.includes(df.fieldtype)) { | ||||
let options = df.options; | let options = df.options; | ||||
let condition = '='; | let condition = '='; | ||||
let fieldtype = df.fieldtype; | let fieldtype = df.fieldtype; | ||||
if (['Text', 'Small Text', 'Text Editor', 'Data'].includes(fieldtype)) { | if (['Text', 'Small Text', 'Text Editor', 'Data'].includes(fieldtype)) { | ||||
fieldtype = 'Data', | |||||
condition = 'like' | |||||
fieldtype = 'Data'; | |||||
condition = 'like'; | |||||
} | } | ||||
if(df.fieldtype == "Select" && df.options) { | if(df.fieldtype == "Select" && df.options) { | ||||
options = df.options.split("\n"); | options = df.options.split("\n"); | ||||
@@ -209,7 +212,7 @@ frappe.ui.BaseList = Class.extend({ | |||||
options = options.join("\n"); | options = options.join("\n"); | ||||
} | } | ||||
} | } | ||||
me.page.add_field({ | |||||
let f = me.page.add_field({ | |||||
fieldtype: fieldtype, | fieldtype: fieldtype, | ||||
label: __(df.label), | label: __(df.label), | ||||
options: options, | options: options, | ||||
@@ -217,6 +220,13 @@ frappe.ui.BaseList = Class.extend({ | |||||
condition: condition, | condition: condition, | ||||
onchange: () => {me.refresh(true);} | onchange: () => {me.refresh(true);} | ||||
}); | }); | ||||
filter_count ++; | |||||
if (filter_count > 3) { | |||||
$(f.wrapper).addClass('hidden-sm').addClass('hidden-xs'); | |||||
} | |||||
if (filter_count > 5) { | |||||
return false; | |||||
} | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||
@@ -400,8 +400,13 @@ frappe.ui.Page = Class.extend({ | |||||
.addClass('col-md-2') | .addClass('col-md-2') | ||||
.attr("title", __(df.label)).tooltip(); | .attr("title", __(df.label)).tooltip(); | ||||
// html fields in toolbar are only for display | |||||
if (df.fieldtype=='HTML') { | |||||
return; | |||||
} | |||||
// hidden fields dont have $input | // hidden fields dont have $input | ||||
if(!f.$input) f.make_input(); | |||||
if (!f.$input) f.make_input(); | |||||
f.$input.addClass("input-sm").attr("placeholder", __(df.label)); | f.$input.addClass("input-sm").attr("placeholder", __(df.label)); | ||||
@@ -12,7 +12,7 @@ frappe.views.CommunicationComposer = Class.extend({ | |||||
make: function() { | make: function() { | ||||
var me = this; | var me = this; | ||||
this.dialog = new frappe.ui.Dialog({ | this.dialog = new frappe.ui.Dialog({ | ||||
title: (this.subject || ""), | |||||
title: (this.subject || __("New Email")), | |||||
no_submit_on_enter: true, | no_submit_on_enter: true, | ||||
fields: this.get_fields(), | fields: this.get_fields(), | ||||
primary_action_label: __("Send"), | primary_action_label: __("Send"), | ||||
@@ -444,6 +444,7 @@ frappe.views.CommunicationComposer = Class.extend({ | |||||
send_email: function(btn, form_values, selected_attachments, print_html, print_format) { | send_email: function(btn, form_values, selected_attachments, print_html, print_format) { | ||||
var me = this; | var me = this; | ||||
me.dialog.hide(); | |||||
if((form_values.send_email || form_values.communication_medium === "Email") && !form_values.recipients) { | if((form_values.send_email || form_values.communication_medium === "Email") && !form_values.recipients) { | ||||
frappe.msgprint(__("Enter Email Recipient(s)")); | frappe.msgprint(__("Enter Email Recipient(s)")); | ||||
@@ -496,8 +497,6 @@ frappe.views.CommunicationComposer = Class.extend({ | |||||
[ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) ); | [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) ); | ||||
} | } | ||||
me.dialog.hide(); | |||||
if ((frappe.last_edited_communication[me.doc] || {})[me.key]) { | if ((frappe.last_edited_communication[me.doc] || {})[me.key]) { | ||||
delete frappe.last_edited_communication[me.doc][me.key]; | delete frappe.last_edited_communication[me.doc][me.key]; | ||||
} | } | ||||
@@ -506,7 +505,7 @@ frappe.views.CommunicationComposer = Class.extend({ | |||||
cur_frm.timeline.input.val(""); | cur_frm.timeline.input.val(""); | ||||
cur_frm.reload_doc(); | cur_frm.reload_doc(); | ||||
} | } | ||||
// try the success callback if it exists | // try the success callback if it exists | ||||
if (me.success) { | if (me.success) { | ||||
try { | try { | ||||
@@ -515,10 +514,10 @@ frappe.views.CommunicationComposer = Class.extend({ | |||||
console.log(e); | console.log(e); | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
frappe.msgprint(__("There were errors while sending email. Please try again.")); | frappe.msgprint(__("There were errors while sending email. Please try again.")); | ||||
// try the error callback if it exists | // try the error callback if it exists | ||||
if (me.error) { | if (me.error) { | ||||
try { | try { | ||||
@@ -64,7 +64,7 @@ hr { | |||||
} | } | ||||
.email-footer-container { | .email-footer-container { | ||||
margin-top: 10px; | |||||
margin-top: 30px; | |||||
& > div:not(:last-child) { | & > div:not(:last-child) { | ||||
margin-bottom: 5px; | margin-bottom: 5px; | ||||
@@ -226,8 +226,27 @@ | |||||
padding: 5px 15px; | padding: 5px 15px; | ||||
} | } | ||||
.listview-main-section .octicon-heart { | |||||
cursor: pointer; | |||||
.listview-main-section { | |||||
.octicon-heart { | |||||
cursor: pointer; | |||||
} | |||||
.page-form { | |||||
padding-left: 17px; | |||||
@media (max-width: @screen-sm) { | |||||
padding-left: 25px; | |||||
} | |||||
.octicon-search { | |||||
float: left; | |||||
padding-top: 7px; | |||||
margin-left: -4px; | |||||
margin-right: -4px; | |||||
@media (max-width: @screen-sm) { | |||||
margin-left: -12px; | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
.like-action.octicon-heart { | .like-action.octicon-heart { | ||||
@@ -20,7 +20,7 @@ | |||||
<link rel="icon" | <link rel="icon" | ||||
href="{{ favicon or "/assets/frappe/images/favicon.png" }}" type="image/x-icon"> | href="{{ favicon or "/assets/frappe/images/favicon.png" }}" type="image/x-icon"> | ||||
{% for include in include_css -%} | {% for include in include_css -%} | ||||
<link type="text/css" rel="stylesheet" href="{{ include }}"> | |||||
<link type="text/css" rel="stylesheet" href="{{ include }}?ver={{ build_version }}"> | |||||
{%- endfor -%} | {%- endfor -%} | ||||
</head> | </head> | ||||
<body> | <body> | ||||
@@ -55,7 +55,7 @@ | |||||
</script> | </script> | ||||
{% for include in include_js %} | {% for include in include_js %} | ||||
<script type="text/javascript" src="{{ include }}"></script> | |||||
<script type="text/javascript" src="{{ include }}?ver={{ build_version }}"></script> | |||||
{% endfor %} | {% endfor %} | ||||
{% include "templates/includes/app_analytics/google_analytics.html" %} | {% include "templates/includes/app_analytics/google_analytics.html" %} | ||||
{% include "templates/includes/app_analytics/mixpanel_analytics.html" %} | {% include "templates/includes/app_analytics/mixpanel_analytics.html" %} | ||||
@@ -35,7 +35,8 @@ def get_context(context): | |||||
# remove script tags from boot | # remove script tags from boot | ||||
boot_json = re.sub("\<script\>[^<]*\</script\>", "", boot_json) | boot_json = re.sub("\<script\>[^<]*\</script\>", "", boot_json) | ||||
return { | |||||
context.update({ | |||||
"no_cache": 1, | |||||
"build_version": get_build_version(), | "build_version": get_build_version(), | ||||
"include_js": hooks["app_include_js"], | "include_js": hooks["app_include_js"], | ||||
"include_css": hooks["app_include_css"], | "include_css": hooks["app_include_css"], | ||||
@@ -46,7 +47,7 @@ def get_context(context): | |||||
(boot.user.background_image or boot.default_background_image) or None), | (boot.user.background_image or boot.default_background_image) or None), | ||||
"google_analytics_id": frappe.conf.get("google_analytics_id"), | "google_analytics_id": frappe.conf.get("google_analytics_id"), | ||||
"mixpanel_id": frappe.conf.get("mixpanel_id") | "mixpanel_id": frappe.conf.get("mixpanel_id") | ||||
} | |||||
}) | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def get_desk_assets(build_version): | def get_desk_assets(build_version): | ||||
@@ -64,7 +65,7 @@ def get_desk_assets(build_version): | |||||
try: | try: | ||||
with open(os.path.join(frappe.local.sites_path, path) ,"r") as f: | with open(os.path.join(frappe.local.sites_path, path) ,"r") as f: | ||||
assets[0]["data"] = assets[0]["data"] + "\n" + text_type(f.read(), "utf-8") | assets[0]["data"] = assets[0]["data"] + "\n" + text_type(f.read(), "utf-8") | ||||
except IOError as e: | |||||
except IOError: | |||||
pass | pass | ||||
for path in data["include_css"]: | for path in data["include_css"]: | ||||
@@ -78,5 +79,4 @@ def get_desk_assets(build_version): | |||||
} | } | ||||
def get_build_version(): | def get_build_version(): | ||||
return str(os.path.getmtime(os.path.join(frappe.local.sites_path, "assets", "js", | |||||
"desk.min.js"))) | |||||
return str(os.path.getmtime(os.path.join(frappe.local.sites_path, '.build'))) |
@@ -9,16 +9,16 @@ | |||||
{% block page_content %} | {% block page_content %} | ||||
<!-- {{ for_test }} --> | <!-- {{ for_test }} --> | ||||
<section class='for-login'> | <section class='for-login'> | ||||
<div class="login-content page-card" style="margin-top: 20px;"> | |||||
<div class="login-content page-card" style="margin-top: 60px;"> | |||||
<form class="form-signin form-login" role="form"> | <form class="form-signin form-login" role="form"> | ||||
<div class="page-card-head"> | <div class="page-card-head"> | ||||
<span class="indicator blue" data-text="{{ _("Sign In") }}"></span> | <span class="indicator blue" data-text="{{ _("Sign In") }}"></span> | ||||
</div> | </div> | ||||
<input type="text" id="login_email" | <input type="text" id="login_email" | ||||
class="form-control" placeholder="{{ | |||||
_('Email address or Mobile number') | |||||
if frappe.utils.cint(frappe.db.get_value('System Settings', 'System Settings', 'allow_login_using_mobile_number')) | |||||
class="form-control" placeholder="{{ | |||||
_('Email address or Mobile number') | |||||
if frappe.utils.cint(frappe.db.get_value('System Settings', 'System Settings', 'allow_login_using_mobile_number')) | |||||
else _('Email address') }}" | else _('Email address') }}" | ||||
required autofocus> | required autofocus> | ||||
@@ -27,6 +27,7 @@ | |||||
"nightwatch": "^0.9.16", | "nightwatch": "^0.9.16", | ||||
"redis": "^2.7.1", | "redis": "^2.7.1", | ||||
"socket.io": "^2.0.1", | "socket.io": "^2.0.1", | ||||
"superagent": "^3.5.2" | |||||
"superagent": "^3.5.2", | |||||
"touch": "^3.1.0" | |||||
} | } | ||||
} | } |