瀏覽代碼

[WIP] Email Inbox view for Communication

version-14
mbauskar 8 年之前
父節點
當前提交
eda7b659a3
共有 12 個檔案被更改,包括 252 行新增19 行删除
  1. +2
    -2
      frappe/boot.py
  2. +5
    -1
      frappe/core/doctype/communication/communication_list.js
  3. +33
    -0
      frappe/email/inbox.py
  4. +4
    -0
      frappe/public/build.json
  5. +3
    -5
      frappe/public/js/frappe/list/list_sidebar.html
  6. +33
    -3
      frappe/public/js/frappe/list/list_sidebar.js
  7. +12
    -4
      frappe/public/js/frappe/list/list_view.js
  8. +2
    -2
      frappe/public/js/frappe/ui/base_list.js
  9. +5
    -2
      frappe/public/js/frappe/views/communication.js
  10. +95
    -0
      frappe/public/js/frappe/views/inbox/inbox_view.js
  11. +24
    -0
      frappe/public/js/frappe/views/inbox/inbox_view_item_main_head.html
  12. +34
    -0
      frappe/public/js/frappe/views/inbox/inbox_view_item_row.html

+ 2
- 2
frappe/boot.py 查看文件

@@ -12,6 +12,7 @@ import frappe.desk.desk_page
from frappe.desk.form.load import get_meta_bundle from frappe.desk.form.load import get_meta_bundle
from frappe.utils.change_log import get_versions from frappe.utils.change_log import get_versions
from frappe.translate import get_lang_dict 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 from frappe.core.doctype.feedback_trigger.feedback_trigger import get_enabled_feedback_trigger


def get_bootinfo(): def get_bootinfo():
@@ -66,10 +67,9 @@ def get_bootinfo():
bootinfo.error_report_email = frappe.get_hooks("error_report_email") bootinfo.error_report_email = frappe.get_hooks("error_report_email")
bootinfo.calendars = sorted(frappe.get_hooks("calendars")) bootinfo.calendars = sorted(frappe.get_hooks("calendars"))
bootinfo.treeviews = frappe.get_hooks("treeviews") or [] 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.lang_dict = get_lang_dict()
bootinfo.feedback_triggers = get_enabled_feedback_trigger() bootinfo.feedback_triggers = get_enabled_feedback_trigger()
bootinfo.update(get_email_accounts(user=frappe.session.user))


return bootinfo return bootinfo




+ 5
- 1
frappe/core/doctype/communication/communication_list.js 查看文件

@@ -1,4 +1,8 @@
frappe.listview_settings['Communication'] = { 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"]] filters: [["status", "=", "Open"]]
}; };

+ 33
- 0
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
}

+ 4
- 0
frappe/public/build.json 查看文件

@@ -245,6 +245,7 @@
"public/js/frappe/views/calendar/calendar.js", "public/js/frappe/views/calendar/calendar.js",
"public/js/frappe/views/image/image_view.js", "public/js/frappe/views/image/image_view.js",
"public/js/frappe/views/kanban/kanban_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/header_select_all_like_filter.html",
"public/js/frappe/list/item_assigned_to_comment_count.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_row.html",
"public/js/frappe/views/image/image_view_item_main_head.html", "public/js/frappe/views/image/image_view_item_main_head.html",
"public/js/frappe/views/image/photoswipe_dom.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_board.html",
"public/js/frappe/views/kanban/kanban_column.html", "public/js/frappe/views/kanban/kanban_column.html",


+ 3
- 5
frappe/public/js/frappe/list/list_sidebar.html 查看文件

@@ -34,18 +34,16 @@
</ul> </ul>
</div> </div>
</li> </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"> <div class="btn-group">
<a class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ __("Email Inbox") }} <span class="caret"></span> {{ __("Email Inbox") }} <span class="caret"></span>
</a> </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> </ul>
</div> </div>
</li> </li>
{% } %}
<li class="assigned-to-me"> <li class="assigned-to-me">
<a>{%= __("Assigned To Me") %}</a> <a>{%= __("Assigned To Me") %}</a>
</li> </li>


+ 33
- 3
frappe/public/js/frappe/list/list_sidebar.js 查看文件

@@ -27,7 +27,7 @@ frappe.views.ListSidebar = Class.extend({
this.setup_assigned_to_me(); this.setup_assigned_to_me();
this.setup_views(); this.setup_views();
this.setup_kanban_boards(); this.setup_kanban_boards();
this.setup_email_inbox();
}, },
setup_views: function() { setup_views: function() {
var show_list_link = false; var show_list_link = false;
@@ -39,6 +39,10 @@ frappe.views.ListSidebar = Class.extend({
} }
//show link for kanban view //show link for kanban view
this.sidebar.find('.list-link[data-view="Kanban"]').removeClass('hide'); 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]) { if(frappe.treeview_settings[this.doctype]) {
this.sidebar.find(".tree-link").removeClass("hide"); this.sidebar.find(".tree-link").removeClass("hide");
@@ -46,11 +50,13 @@ frappe.views.ListSidebar = Class.extend({


this.current_view = 'List'; this.current_view = 'List';
var route = frappe.get_route(); 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]; this.current_view = route[2];


if(this.current_view === 'Kanban') { if(this.current_view === 'Kanban') {
this.kanban_board = route[3]; 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'); .attr('disabled', 'disabled').addClass('disabled');


//enable link for Kanban view //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') .attr('disabled', null).removeClass('disabled')


// show image link if image_view // 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() { setup_assigned_to_me: function() {
var me = this; var me = this;
this.page.sidebar.find(".assigned-to-me a").on("click", function() { this.page.sidebar.find(".assigned-to-me a").on("click", function() {


+ 12
- 4
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); this.list_renderer = new frappe.views.ImageView(opts);
} else if (this.current_view === 'Kanban') { } else if (this.current_view === 'Kanban') {
this.list_renderer = new frappe.views.KanbanView(opts); 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') { if (us.last_view === 'Kanban') {
route.push(us['Kanban'].last_kanban_board); route.push(us['Kanban'].last_kanban_board);
} }

if (us.last_view === 'Inbox')
route.push(us['Inbox'].last_email_account)
} }


frappe.set_route(route); frappe.set_route(route);
@@ -266,10 +271,13 @@ frappe.views.ListView = frappe.ui.BaseList.extend({
set_filters: function (filters) { set_filters: function (filters) {
var me = this; var me = this;
$.each(filters, function (i, f) { $.each(filters, function (i, f) {
hidden = false
if (f.length === 3) { if (f.length === 3) {
f = [me.doctype, f[0], f[1], f[2]] 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) { if (!this.list_renderer.settings.use_route) {
var route = frappe.get_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) { $.each(frappe.utils.get_args_dict_from_url(route[2]), function (key, val) {
me.set_filter(key, val, true); 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)) { if (!(this.can_delete || this.list_renderer.settings.selectable)) {
return; return;
} }
this.$page.on('change', '.list-row-checkbox, .list-select-all', function() {
this.$page.find('.list-row-checkbox').change(function () {
me.toggle_delete(); me.toggle_delete();
}); });
// after delete, hide delete button // after delete, hide delete button
@@ -771,7 +779,7 @@ frappe.views.ListView = frappe.ui.BaseList.extend({
.addClass('btn-danger'); .addClass('btn-danger');


checked_items_status.text( checked_items_status.text(
checked_items.length == 1
no_of_checked_items == 1
? __('1 item selected') ? __('1 item selected')
: __('{0} items selected', [checked_items.length]) : __('{0} items selected', [checked_items.length])
) )


+ 2
- 2
frappe/public/js/frappe/ui/base_list.js 查看文件

@@ -354,7 +354,7 @@ frappe.ui.BaseList = Class.extend({
} }


this.wrapper.find('.list-paging-area') this.wrapper.find('.list-paging-area')
.toggle(values.length > 0 || this.start > 0);
.toggle(values.length > 0|| this.start > 0);


// callbacks // callbacks
if (this.onrun) this.onrun(); if (this.onrun) this.onrun();
@@ -411,7 +411,7 @@ frappe.ui.BaseList = Class.extend({
} else { } else {
// no filter for this item, // no filter for this item,
// setup one // 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 + '%'); this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%');
} else { } else {
this.filter_list.add_filter(this.doctype, fieldname, '=', label); this.filter_list.add_filter(this.doctype, fieldname, '=', label);


+ 5
- 2
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 // 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 = [ fields = [
{label: __("From"), fieldtype: "Select", reqd: 1, fieldname: "sender", {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); ].concat(fields);
} }




+ 95
- 0
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 = $('<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];
}
});

+ 24
- 0
frappe/public/js/frappe/views/inbox/inbox_view_item_main_head.html 查看文件

@@ -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>

+ 34
- 0
frappe/public/js/frappe/views/inbox/inbox_view_item_row.html 查看文件

@@ -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>

Loading…
取消
儲存