@@ -12,6 +12,7 @@ import frappe.desk.desk_page | |||
from frappe.desk.form.load import get_meta_bundle | |||
from frappe.utils.change_log import get_versions | |||
from frappe.translate import get_lang_dict | |||
from frappe.email.inbox import get_email_accounts | |||
from frappe.core.doctype.feedback_trigger.feedback_trigger import get_enabled_feedback_trigger | |||
def get_bootinfo(): | |||
@@ -66,10 +67,9 @@ def get_bootinfo(): | |||
bootinfo.error_report_email = frappe.get_hooks("error_report_email") | |||
bootinfo.calendars = sorted(frappe.get_hooks("calendars")) | |||
bootinfo.treeviews = frappe.get_hooks("treeviews") or [] | |||
bootinfo.email_accounts = frappe.get_all('User Email', fields=['email_account', 'email_id'], | |||
filters=dict(parent=frappe.session.user)) | |||
bootinfo.lang_dict = get_lang_dict() | |||
bootinfo.feedback_triggers = get_enabled_feedback_trigger() | |||
bootinfo.update(get_email_accounts(user=frappe.session.user)) | |||
return bootinfo | |||
@@ -1,4 +1,8 @@ | |||
frappe.listview_settings['Communication'] = { | |||
add_fields: ["sent_or_received", "recipients", "subject", "communication_medium", "communication_type"], | |||
add_fields: [ | |||
"sent_or_received","recipients", "subject", | |||
"communication_medium", "communication_type", | |||
"sender" | |||
], | |||
filters: [["status", "=", "Open"]] | |||
}; |
@@ -0,0 +1,33 @@ | |||
import frappe | |||
def get_email_accounts(user=None): | |||
if not user: | |||
user = frappe.session.user | |||
email_accounts = [] | |||
accounts = frappe.get_all("User Email", filters={ "parent": user }, | |||
fields=["email_account", "email_id"], | |||
distinct=True, order_by="idx") | |||
if not accounts: | |||
return None | |||
email_accounts.append({ | |||
"email_account": "Sent", | |||
"email_id": "Sent Mail" | |||
}) | |||
all_accounts = ",".join([ account.get("email_account") for account in accounts ]) | |||
if len(accounts) > 1: | |||
email_accounts.append({ | |||
"email_account": all_accounts, | |||
"email_id": "All Accounts" | |||
}) | |||
email_accounts.extend(accounts) | |||
return { | |||
"email_accounts": email_accounts, | |||
"all_accounts": all_accounts | |||
} |
@@ -245,6 +245,7 @@ | |||
"public/js/frappe/views/calendar/calendar.js", | |||
"public/js/frappe/views/image/image_view.js", | |||
"public/js/frappe/views/kanban/kanban_view.js", | |||
"public/js/frappe/views/inbox/inbox_view.js", | |||
"public/js/frappe/list/header_select_all_like_filter.html", | |||
"public/js/frappe/list/item_assigned_to_comment_count.html", | |||
@@ -253,6 +254,9 @@ | |||
"public/js/frappe/views/image/image_view_item_row.html", | |||
"public/js/frappe/views/image/image_view_item_main_head.html", | |||
"public/js/frappe/views/image/photoswipe_dom.html", | |||
"public/js/frappe/views/inbox/inbox_view_item_row.html", | |||
"public/js/frappe/views/inbox/inbox_view_item_main_head.html", | |||
"public/js/frappe/views/kanban/kanban_board.html", | |||
"public/js/frappe/views/kanban/kanban_column.html", | |||
@@ -34,18 +34,16 @@ | |||
</ul> | |||
</div> | |||
</li> | |||
{% if(doctype == "Communication") { %} | |||
<li class="hide list-link" data-view="Email Inbox"> | |||
<li class="hide list-link" data-view="Inbox"> | |||
<div class="btn-group"> | |||
<a class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | |||
{{ __("Email Inbox") }} <span class="caret"></span> | |||
</a> | |||
<ul class="dropdown-menu inbox-dropdown" style="max-height: 300px; overflow-y: auto;"> | |||
<li class="new-kanban-board"><a>{{ __("New Kanban Board") }}</a></li> | |||
<ul class="dropdown-menu email-account-dropdown" style="max-height: 300px; overflow-y: auto;"> | |||
<li class="new-email-account"><a>{{ __("New Email Account") }}</a></li> | |||
</ul> | |||
</div> | |||
</li> | |||
{% } %} | |||
<li class="assigned-to-me"> | |||
<a>{%= __("Assigned To Me") %}</a> | |||
</li> | |||
@@ -27,7 +27,7 @@ frappe.views.ListSidebar = Class.extend({ | |||
this.setup_assigned_to_me(); | |||
this.setup_views(); | |||
this.setup_kanban_boards(); | |||
this.setup_email_inbox(); | |||
}, | |||
setup_views: function() { | |||
var show_list_link = false; | |||
@@ -39,6 +39,10 @@ frappe.views.ListSidebar = Class.extend({ | |||
} | |||
//show link for kanban view | |||
this.sidebar.find('.list-link[data-view="Kanban"]').removeClass('hide'); | |||
if(this.doctype === "Communication"){ | |||
this.sidebar.find('.list-link[data-view="Inbox"]').removeClass('hide'); | |||
show_list_link = true; | |||
} | |||
if(frappe.treeview_settings[this.doctype]) { | |||
this.sidebar.find(".tree-link").removeClass("hide"); | |||
@@ -46,11 +50,13 @@ frappe.views.ListSidebar = Class.extend({ | |||
this.current_view = 'List'; | |||
var route = frappe.get_route(); | |||
if(route.length > 2 && in_list(['Gantt', 'Image', 'Kanban', 'Calendar'], route[2])) { | |||
if(route.length > 2 && in_list(['Gantt', 'Image', 'Kanban', 'Calendar', 'Inbox'], route[2])) { | |||
this.current_view = route[2]; | |||
if(this.current_view === 'Kanban') { | |||
this.kanban_board = route[3]; | |||
} else if (this.current_view === 'Inbox') { | |||
this.email_account = route[3] || frappe.boot.all_accounts; | |||
} | |||
} | |||
@@ -59,7 +65,7 @@ frappe.views.ListSidebar = Class.extend({ | |||
.attr('disabled', 'disabled').addClass('disabled'); | |||
//enable link for Kanban view | |||
this.sidebar.find('.list-link[data-view="Kanban"] a') | |||
this.sidebar.find('.list-link[data-view="Kanban"] a, .list-link[data-view="Inbox"] a') | |||
.attr('disabled', null).removeClass('disabled') | |||
// show image link if image_view | |||
@@ -234,6 +240,30 @@ frappe.views.ListSidebar = Class.extend({ | |||
} | |||
}); | |||
}, | |||
setup_email_inbox: function() { | |||
// get active email account for the user and add in dropdown | |||
if(this.doctype != "Communication") | |||
return; | |||
var $dropdown = this.page.sidebar.find('.email-account-dropdown'); | |||
var divider = false; | |||
accounts = frappe.boot.email_accounts; | |||
accounts.forEach(function(account) { | |||
var route = ["List", "Communication", "Inbox", account.email_account].join('/'); | |||
if(!divider) { | |||
$('<li role="separator" class="divider"></li>').appendTo($dropdown); | |||
divider = true; | |||
} | |||
$('<li><a href="#'+ route + '">'+account.email_id+'</a></li>').appendTo($dropdown); | |||
if(account.email_id === "Sent Mail") | |||
divider = false | |||
}); | |||
$dropdown.find('.new-email-account').click(function() { | |||
frappe.new_doc("Email Account") | |||
}); | |||
}, | |||
setup_assigned_to_me: function() { | |||
var me = this; | |||
this.page.sidebar.find(".assigned-to-me a").on("click", function() { | |||
@@ -185,6 +185,8 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
this.list_renderer = new frappe.views.ImageView(opts); | |||
} else if (this.current_view === 'Kanban') { | |||
this.list_renderer = new frappe.views.KanbanView(opts); | |||
} else if (this.current_view === 'Inbox') { | |||
this.list_renderer = new frappe.views.InboxView(opts) | |||
} | |||
}, | |||
@@ -210,6 +212,9 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
if (us.last_view === 'Kanban') { | |||
route.push(us['Kanban'].last_kanban_board); | |||
} | |||
if (us.last_view === 'Inbox') | |||
route.push(us['Inbox'].last_email_account) | |||
} | |||
frappe.set_route(route); | |||
@@ -266,10 +271,13 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
set_filters: function (filters) { | |||
var me = this; | |||
$.each(filters, function (i, f) { | |||
hidden = false | |||
if (f.length === 3) { | |||
f = [me.doctype, f[0], f[1], f[2]] | |||
} else if (f.length === 5) { | |||
hidden = f.pop(4) || false | |||
} | |||
me.filter_list.add_filter(f[0], f[1], f[2], f[3]); | |||
me.filter_list.add_filter(f[0], f[1], f[2], f[3], hidden); | |||
}); | |||
}, | |||
@@ -447,7 +455,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
if (!this.list_renderer.settings.use_route) { | |||
var route = frappe.get_route(); | |||
if (route[2] && !in_list(['Image', 'Gantt', 'Kanban', 'Calendar'], route[2])) { | |||
if (route[2] && !in_list(['Image', 'Gantt', 'Kanban', 'Calendar', 'Inbox'], route[2])) { | |||
$.each(frappe.utils.get_args_dict_from_url(route[2]), function (key, val) { | |||
me.set_filter(key, val, true); | |||
}); | |||
@@ -750,7 +758,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
if (!(this.can_delete || this.list_renderer.settings.selectable)) { | |||
return; | |||
} | |||
this.$page.on('change', '.list-row-checkbox, .list-select-all', function() { | |||
this.$page.find('.list-row-checkbox').change(function () { | |||
me.toggle_delete(); | |||
}); | |||
// after delete, hide delete button | |||
@@ -771,7 +779,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||
.addClass('btn-danger'); | |||
checked_items_status.text( | |||
checked_items.length == 1 | |||
no_of_checked_items == 1 | |||
? __('1 item selected') | |||
: __('{0} items selected', [checked_items.length]) | |||
) | |||
@@ -354,7 +354,7 @@ frappe.ui.BaseList = Class.extend({ | |||
} | |||
this.wrapper.find('.list-paging-area') | |||
.toggle(values.length > 0 || this.start > 0); | |||
.toggle(values.length > 0|| this.start > 0); | |||
// callbacks | |||
if (this.onrun) this.onrun(); | |||
@@ -411,7 +411,7 @@ frappe.ui.BaseList = Class.extend({ | |||
} else { | |||
// no filter for this item, | |||
// setup one | |||
if (['_user_tags', '_comments', '_assign', '_liked_by'].includes(fieldname)) { | |||
if (['_user_tags', '_comments', '_assign', '_liked_by'].ƒincludes(fieldname)) { | |||
this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%'); | |||
} else { | |||
this.filter_list.add_filter(this.doctype, fieldname, '=', label); | |||
@@ -85,10 +85,13 @@ frappe.views.CommunicationComposer = Class.extend({ | |||
]; | |||
// add from if user has access to multiple email accounts | |||
if(frappe.boot.email_accounts && frappe.boot.email_accounts.length > 1) { | |||
email_accounts = frappe.boot.email_accounts.filter(function(account, idx){ | |||
return !inList(["All Accounts", "Sent"], account.email_account) | |||
}) | |||
if(frappe.boot.email_accounts && email_accounts.length > 1) { | |||
fields = [ | |||
{label: __("From"), fieldtype: "Select", reqd: 1, fieldname: "sender", | |||
options: frappe.boot.email_accounts.map(function(e) { return e.email_id; }) } | |||
options: accounts.map(function(e) { return e.email_id; }) } | |||
].concat(fields); | |||
} | |||
@@ -0,0 +1,95 @@ | |||
/** | |||
* frappe.views.EmailInboxView | |||
*/ | |||
frappe.provide("frappe.views"); | |||
frappe.views.InboxView = frappe.views.ListRenderer.extend({ | |||
name: 'Inbox', | |||
render_view: function(values) { | |||
var me = this; | |||
var email_account = this.get_email_account(); | |||
this.emails = values; | |||
// save email account in user_settings | |||
frappe.model.user_settings.save("Communication", 'Inbox', { | |||
last_email_account: email_account | |||
}); | |||
this.render_inbox_view(); | |||
}, | |||
render_inbox_view: function() { | |||
var html = this.emails.map(this.render_email_row.bind(this)).join(""); | |||
this.container = $('<div>') | |||
.addClass('inbox-container') | |||
.appendTo(this.wrapper); | |||
this.container.append(html); | |||
}, | |||
render_email_row: function(email) { | |||
return frappe.render_template("inbox_view_item_row", { | |||
data: email, | |||
is_sent_emails: this.is_sent_emails, | |||
}); | |||
}, | |||
set_defaults: function() { | |||
this._super(); | |||
this.show_no_result = false; | |||
this.page_title = __("Email Inbox"); | |||
}, | |||
init_settings: function() { | |||
this._super(); | |||
this.filters = this.get_inbox_filters(); | |||
}, | |||
should_refresh: function() { | |||
var to_refresh = this._super(); | |||
if(!to_refresh) { | |||
this.last_email_account = this.current_email_account || ''; | |||
this.current_email_account = this.get_email_account(); | |||
this.is_sent_emails = this.current_email_account === "Sent"? true: false | |||
to_refresh = this.current_email_account !== this.last_email_account; | |||
} | |||
if(to_refresh){ | |||
this.list_view.page.main.find(".list-headers").empty(); | |||
this.list_view.init_headers(); | |||
} | |||
return to_refresh; | |||
}, | |||
get_inbox_filters: function() { | |||
var email_account = this.get_email_account(); | |||
var default_filters = [ | |||
["Communication", "communication_type", "=", "Communication", true], | |||
["Communication", "communication_medium", "=", "Email", true], | |||
] | |||
var filters = [] | |||
if (email_account === "Sent") | |||
filters = default_filters.concat([ | |||
["Communication", "sent_or_received", "=", "Sent", true] | |||
]) | |||
else | |||
filters = default_filters.concat([ | |||
["Communication", "sent_or_received", "=", "Received", true], | |||
["Communication", "email_account", "=", email_account, true] | |||
]) | |||
return filters | |||
}, | |||
get_header_html: function() { | |||
var header = frappe.render_template('inbox_view_item_main_head', { | |||
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable) | |||
&& !this.no_delete), | |||
is_sent_emails: this.is_sent_emails | |||
}); | |||
return header; | |||
}, | |||
get_email_account: function() { | |||
var route = frappe.get_route(); | |||
if(!route[3] || !frappe.boot.email_accounts.find(b => b.email_account === route[3])) { | |||
frappe.throw(__(`Email Account <b>${route[3] || ''}</b> not found`)); | |||
return; | |||
} | |||
return route[3]; | |||
} | |||
}); |
@@ -0,0 +1,24 @@ | |||
<div class="list-row list-row-head" data-list-renderer="Inbox"> | |||
<div class="row doclist-row"> | |||
<div class="col-sm-10 list-row-left"> | |||
<!-- title + columns --> | |||
<div class="row"> | |||
<div class="col-sm-8 col-xs-12 list-col ellipsis h6 text-muted"> | |||
<div class="list-value"> | |||
{% if (_checkbox) { %} | |||
<input class="list-select-all" type="checkbox" | |||
title="{%= __("Select All") %}"> | |||
{% } %} | |||
<span class="list-col-title">{%= __("Subject") %}</span> | |||
</div> | |||
</div> | |||
<div class="col-sm-4 hidden-xs list-col ellipsis h6 text-muted"> | |||
<div class="list-value"> | |||
<span class="list-col-title">{%= __(is_sent_emails ? "To": "From") %}</span> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="col-sm-2 hidden-xs list-row-right"></div> | |||
</div> | |||
</div> |
@@ -0,0 +1,34 @@ | |||
<div class="list-row"> | |||
<div class="row doclist-row {% if (data._checkbox) { %} has-checkbox {% } %}"> | |||
<div class="col-sm-10 col-xs-10 list-row-left"> | |||
<div class="row"> | |||
<div class="col-sm-8 list-col ellipsis h6 text-muted"> | |||
<span class="list-value"> | |||
{% if (data._checkbox) { %} | |||
<input class="list-row-checkbox" type="checkbox" data-name="{{data.name}}"> | |||
{% } %} | |||
<a class="grey list-id" href="#Form/{%= data._doctype_encoded %}/{%= data._name_encoded %}"> | |||
{%= data.subject %} | |||
</a> | |||
</span> | |||
</div> | |||
<div class="col-sm-4 hidden-xs list-col ellipsis h6 text-muted"> | |||
<span class="filterable text-muted" data-filter="sender,=,{%= data.sender %}"> | |||
{%= is_sent_emails? data.recipients: data.sender %} | |||
</span> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="col-sm-2 col-xs-2 text-right list-row-right" style="padding-left:0px"> | |||
<div class="visible-xs"> | |||
<span class="text-muted"><i class="fa fa-paperclip fa-large"></i></span> | |||
</div> | |||
<div class="hidden-xs"> | |||
<span class="list-row-modified text-muted"><i class="fa fa-paperclip fa-large"></i></span> | |||
<span class="list-row-modified text-muted"> | |||
{%= comment_when(data.modified, true) %} | |||
</span> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |