diff --git a/frappe/boot.py b/frappe/boot.py
index cbf8d7ac81..b05ad1b452 100644
--- a/frappe/boot.py
+++ b/frappe/boot.py
@@ -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
diff --git a/frappe/core/doctype/communication/communication_list.js b/frappe/core/doctype/communication/communication_list.js
index 458991f52f..53ae86a504 100644
--- a/frappe/core/doctype/communication/communication_list.js
+++ b/frappe/core/doctype/communication/communication_list.js
@@ -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"]]
};
diff --git a/frappe/email/inbox.py b/frappe/email/inbox.py
new file mode 100644
index 0000000000..e40b4fbdb9
--- /dev/null
+++ b/frappe/email/inbox.py
@@ -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
+ }
\ No newline at end of file
diff --git a/frappe/public/build.json b/frappe/public/build.json
index 3b350e2813..e3ce55d28d 100755
--- a/frappe/public/build.json
+++ b/frappe/public/build.json
@@ -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",
diff --git a/frappe/public/js/frappe/list/list_sidebar.html b/frappe/public/js/frappe/list/list_sidebar.html
index eb28a8ae1b..f5d9142622 100644
--- a/frappe/public/js/frappe/list/list_sidebar.html
+++ b/frappe/public/js/frappe/list/list_sidebar.html
@@ -34,18 +34,16 @@
- {% if(doctype == "Communication") { %}
-
+
- {% } %}
{%= __("Assigned To Me") %}
diff --git a/frappe/public/js/frappe/list/list_sidebar.js b/frappe/public/js/frappe/list/list_sidebar.js
index d1431adc97..1a4a8380d9 100644
--- a/frappe/public/js/frappe/list/list_sidebar.js
+++ b/frappe/public/js/frappe/list/list_sidebar.js
@@ -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) {
+ $(' ').appendTo($dropdown);
+ divider = true;
+ }
+ $(''+account.email_id+' ').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() {
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index a13e62dfb8..48f8f3e4e3 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -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])
)
diff --git a/frappe/public/js/frappe/ui/base_list.js b/frappe/public/js/frappe/ui/base_list.js
index 0583564eda..35c3f1d266 100644
--- a/frappe/public/js/frappe/ui/base_list.js
+++ b/frappe/public/js/frappe/ui/base_list.js
@@ -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);
diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js
index 4f20bd2c98..95a3fe1543 100755
--- a/frappe/public/js/frappe/views/communication.js
+++ b/frappe/public/js/frappe/views/communication.js
@@ -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);
}
diff --git a/frappe/public/js/frappe/views/inbox/inbox_view.js b/frappe/public/js/frappe/views/inbox/inbox_view.js
new file mode 100644
index 0000000000..18a155da25
--- /dev/null
+++ b/frappe/public/js/frappe/views/inbox/inbox_view.js
@@ -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 = $('')
+ .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
${route[3] || ''} not found`));
+ return;
+ }
+ return route[3];
+ }
+});
diff --git a/frappe/public/js/frappe/views/inbox/inbox_view_item_main_head.html b/frappe/public/js/frappe/views/inbox/inbox_view_item_main_head.html
new file mode 100644
index 0000000000..40ebaa94cf
--- /dev/null
+++ b/frappe/public/js/frappe/views/inbox/inbox_view_item_main_head.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ {% if (_checkbox) { %}
+
+ {% } %}
+ {%= __("Subject") %}
+
+
+
+
+ {%= __(is_sent_emails ? "To": "From") %}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frappe/public/js/frappe/views/inbox/inbox_view_item_row.html b/frappe/public/js/frappe/views/inbox/inbox_view_item_row.html
new file mode 100644
index 0000000000..f7c2431a3e
--- /dev/null
+++ b/frappe/public/js/frappe/views/inbox/inbox_view_item_row.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+ {%= is_sent_emails? data.recipients: data.sender %}
+
+
+
+
+
+
+
+
+
+
+
+ {%= comment_when(data.modified, true) %}
+
+
+
+
+
\ No newline at end of file