From 1dbd6c83eae6ab6bf643953cb70eeb37e65bf049 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 31 Mar 2016 11:52:28 +0530 Subject: [PATCH] [rename] messages -> chat --- frappe/config/desk.py | 2 +- frappe/desk/page/chat/__init__.py | 0 .../{messages/messages.css => chat/chat.css} | 0 .../{messages/messages.js => chat/chat.js} | 45 +++++++++--------- frappe/desk/page/chat/chat.json | 23 +++++++++ .../{messages/messages.py => chat/chat.py} | 4 +- .../chat_main.html} | 0 .../messages_row.html => chat/chat_row.html} | 8 +--- .../chat_sidebar.html} | 0 frappe/desk/page/messages/README.md | 1 - frappe/desk/page/messages/__init__.py | 3 -- frappe/desk/page/messages/messages.json | 20 -------- frappe/model/utils/link_count.py | 6 ++- frappe/public/images/ui/bot.png | Bin 0 -> 14886 bytes frappe/public/js/frappe/desk.js | 8 ++++ frappe/public/js/frappe/misc/user.js | 8 ++++ frappe/public/js/legacy/form.js | 8 +++- frappe/tests/test_document.py | 4 -- 18 files changed, 77 insertions(+), 63 deletions(-) create mode 100644 frappe/desk/page/chat/__init__.py rename frappe/desk/page/{messages/messages.css => chat/chat.css} (100%) rename frappe/desk/page/{messages/messages.js => chat/chat.js} (81%) create mode 100644 frappe/desk/page/chat/chat.json rename frappe/desk/page/{messages/messages.py => chat/chat.py} (97%) rename frappe/desk/page/{messages/messages_main.html => chat/chat_main.html} (100%) rename frappe/desk/page/{messages/messages_row.html => chat/chat_row.html} (75%) rename frappe/desk/page/{messages/messages_sidebar.html => chat/chat_sidebar.html} (100%) delete mode 100644 frappe/desk/page/messages/README.md delete mode 100644 frappe/desk/page/messages/__init__.py delete mode 100644 frappe/desk/page/messages/messages.json create mode 100644 frappe/public/images/ui/bot.png diff --git a/frappe/config/desk.py b/frappe/config/desk.py index 9677ecdd9c..22b1eb0e83 100644 --- a/frappe/config/desk.py +++ b/frappe/config/desk.py @@ -22,7 +22,7 @@ def get_data(): }, { "type": "page", - "label": _("Messages"), + "label": _("Chat"), "name": "messages", "description": _("Chat messages and other notifications."), "data_doctype": "Communication" diff --git a/frappe/desk/page/chat/__init__.py b/frappe/desk/page/chat/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/desk/page/messages/messages.css b/frappe/desk/page/chat/chat.css similarity index 100% rename from frappe/desk/page/messages/messages.css rename to frappe/desk/page/chat/chat.css diff --git a/frappe/desk/page/messages/messages.js b/frappe/desk/page/chat/chat.js similarity index 81% rename from frappe/desk/page/messages/messages.js rename to frappe/desk/page/chat/chat.js index 6e6299d144..6e6fbc6b39 100644 --- a/frappe/desk/page/messages/messages.js +++ b/frappe/desk/page/chat/chat.js @@ -1,32 +1,27 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt -// TODO -// new message popup - -frappe.provide('frappe.desk.pages.messages'); - -frappe.pages.messages.on_page_load = function(parent) { +frappe.pages.chat.on_page_load = function(parent) { var page = frappe.ui.make_app_page({ parent: parent, }); - page.set_title('' + page.set_title('' + ''); - $(".navbar-center").html(__("Messages")); + $(".navbar-center").html(__("Chat")); - frappe.desk.pages.messages = new frappe.desk.pages.Messages(parent); + frappe.pages.chat.chat = new frappe.Chat(parent); } -frappe.pages.messages.on_page_show = function() { +frappe.pages.chat.on_page_show = function() { // clear title prefix frappe.utils.set_title_prefix(""); frappe.breadcrumbs.add("Desk"); } -frappe.desk.pages.Messages = Class.extend({ +frappe.Chat = Class.extend({ init: function(wrapper, page) { this.wrapper = wrapper; this.page = wrapper.page; @@ -46,7 +41,7 @@ frappe.desk.pages.Messages = Class.extend({ var me = this; frappe.realtime.on('new_message', function(comment) { if(comment.modified_by !== user || comment.communication_type === 'Bot') { - if(frappe.get_route()[0] === 'messages') { + if(frappe.get_route()[0] === 'chat') { var current_contact = $(cur_page.page).find('[data-contact]').data('contact'); var on_broadcast_page = current_contact === user; if ((current_contact == comment.owner) @@ -56,7 +51,7 @@ frappe.desk.pages.Messages = Class.extend({ setTimeout(function() { me.prepend_comment(comment); }, 1000); } } else { - frappe.utils.notify(__("Message from {0}", [comment.sender_full_name]), comment.content); + frappe.utils.notify(__("Message from {0}", [frappe.user_info(comment.owner).fullname]), comment.content); } } }); @@ -64,23 +59,23 @@ frappe.desk.pages.Messages = Class.extend({ prepend_comment: function(comment) { var $row = $('
'); - frappe.desk.pages.messages.list.data.unshift(comment); - frappe.desk.pages.messages.list.render_row($row, comment); - frappe.desk.pages.messages.list.$w.prepend($row); + frappe.pages.chat.chat.list.data.unshift(comment); + frappe.pages.chat.chat.list.render_row($row, comment); + frappe.pages.chat.chat.list.$w.prepend($row); }, make_sidebar: function() { var me = this; return frappe.call({ module:'frappe.desk', - page:'messages', + page:'chat', method:'get_active_users', callback: function(r,rt) { // sort r.message.sort(function(a, b) { return cint(b.has_session) - cint(a.has_session); }); // render - me.page.sidebar.html(frappe.render_template("messages_sidebar", {data: r.message})); + me.page.sidebar.html(frappe.render_template("chat_sidebar", {data: r.message})); // bind click me.page.sidebar.find("a").on("click", function() { @@ -105,7 +100,7 @@ frappe.desk.pages.Messages = Class.extend({ make_messages: function(contact) { var me = this; - this.page.main.html($(frappe.render_template("messages_main", { "contact": contact }))); + this.page.main.html($(frappe.render_template("chat_main", { "contact": contact }))); var text_area = this.page.main.find(".messages-textarea").on("focusout", function() { // on touchscreen devices, scroll to top @@ -127,7 +122,7 @@ frappe.desk.pages.Messages = Class.extend({ if(txt) { return frappe.call({ module: 'frappe.desk', - page:'messages', + page:'chat', method:'post', args: { txt: txt, @@ -164,7 +159,7 @@ frappe.desk.pages.Messages = Class.extend({ this.list = new frappe.ui.Listing({ parent: this.page.main.find(".message-list"), page: this.page, - method: 'frappe.desk.page.messages.messages.get_list', + method: 'frappe.desk.page.chat.chat.get_list', args: { contact: contact }, @@ -172,7 +167,7 @@ frappe.desk.pages.Messages = Class.extend({ freeze: false, render_row: function(wrapper, data) { me.prepare(data); - var row = $(frappe.render_template("messages_row", { + var row = $(frappe.render_template("chat_row", { data: data })).appendTo(wrapper); row.find(".avatar, .indicator").tooltip(); @@ -184,7 +179,7 @@ frappe.desk.pages.Messages = Class.extend({ delete: function(ele) { $(ele).parent().css('opacity', 0.6); return frappe.call({ - method: 'frappe.desk.page.messages.messages.delete', + method: 'frappe.desk.page.chat.chat.delete', args: {name : $(ele).attr('data-name')}, callback: function() { $(ele).parents(".list-row:first").toggle(false); @@ -220,6 +215,10 @@ frappe.desk.pages.Messages = Class.extend({ data.is_mine = true; } + if(data.owner==data.reference_name && data.communication_type === "Bot") { + data.owner = 'bot'; + } + data.content = frappe.markdown(data.content); } diff --git a/frappe/desk/page/chat/chat.json b/frappe/desk/page/chat/chat.json new file mode 100644 index 0000000000..891ea9d935 --- /dev/null +++ b/frappe/desk/page/chat/chat.json @@ -0,0 +1,23 @@ +{ + "content": null, + "creation": "2012-06-14 18:44:56", + "docstatus": 0, + "doctype": "Page", + "icon": "", + "idx": 1, + "modified": "2016-03-31 02:02:13.503910", + "modified_by": "Administrator", + "module": "Desk", + "name": "chat", + "owner": "Administrator", + "page_name": "messages", + "roles": [ + { + "role": "All" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "title": "Chat" +} \ No newline at end of file diff --git a/frappe/desk/page/messages/messages.py b/frappe/desk/page/chat/chat.py similarity index 97% rename from frappe/desk/page/messages/messages.py rename to frappe/desk/page/chat/chat.py index 4cd770cfc7..53c2043b34 100644 --- a/frappe/desk/page/messages/messages.py +++ b/frappe/desk/page/chat/chat.py @@ -41,6 +41,7 @@ def get_list(arg=None): return frappe.db.sql("""select * from `tabCommunication` where communication_type in ('Chat', 'Notification') + and comment_type != 'Bot' and reference_doctype ='User' and (owner=%(contact)s or reference_name=%(user)s @@ -78,7 +79,8 @@ def get_active_users(): # in case of administrator data.append({"name": frappe.session.user, "has_session": 100}) - data.append({"name": "Bot", "has_session": 100}) + if 'System Manager' in frappe.get_roles(): + data.append({"name": "Bot", "has_session": 100}) return data diff --git a/frappe/desk/page/messages/messages_main.html b/frappe/desk/page/chat/chat_main.html similarity index 100% rename from frappe/desk/page/messages/messages_main.html rename to frappe/desk/page/chat/chat_main.html diff --git a/frappe/desk/page/messages/messages_row.html b/frappe/desk/page/chat/chat_row.html similarity index 75% rename from frappe/desk/page/messages/messages_row.html rename to frappe/desk/page/chat/chat_row.html index fae334fe65..64c90f7ac2 100644 --- a/frappe/desk/page/messages/messages_row.html +++ b/frappe/desk/page/chat/chat_row.html @@ -9,16 +9,12 @@ style="width: 20px; height: 16px; display: inline-block;"> {% } %} -
+
{%= data.content %}
@@ -26,7 +22,7 @@
{%= comment_when(data.modified) %}
diff --git a/frappe/desk/page/messages/messages_sidebar.html b/frappe/desk/page/chat/chat_sidebar.html similarity index 100% rename from frappe/desk/page/messages/messages_sidebar.html rename to frappe/desk/page/chat/chat_sidebar.html diff --git a/frappe/desk/page/messages/README.md b/frappe/desk/page/messages/README.md deleted file mode 100644 index e37bf64e3c..0000000000 --- a/frappe/desk/page/messages/README.md +++ /dev/null @@ -1 +0,0 @@ -Chat-like interface with list of messages, updates by various users, transactions. \ No newline at end of file diff --git a/frappe/desk/page/messages/__init__.py b/frappe/desk/page/messages/__init__.py deleted file mode 100644 index 0e57cb68c3..0000000000 --- a/frappe/desk/page/messages/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - diff --git a/frappe/desk/page/messages/messages.json b/frappe/desk/page/messages/messages.json deleted file mode 100644 index 97d2c7da90..0000000000 --- a/frappe/desk/page/messages/messages.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "creation": "2012-06-14 18:44:56.000000", - "docstatus": 0, - "doctype": "Page", - "icon": "icon-envelope", - "idx": 1, - "modified": "2013-07-11 14:43:32.000001", - "modified_by": "Administrator", - "module": "Desk", - "name": "messages", - "owner": "Administrator", - "page_name": "messages", - "roles": [ - { - "role": "All" - } - ], - "standard": "Yes", - "title": "Messages" -} diff --git a/frappe/model/utils/link_count.py b/frappe/model/utils/link_count.py index 0d543ee859..bc521824e4 100644 --- a/frappe/model/utils/link_count.py +++ b/frappe/model/utils/link_count.py @@ -24,11 +24,13 @@ def notify_link_count(doctype, name): def update_link_count(): '''increment link count in the `idx` column for the given document''' link_count = frappe.cache().get_value('_link_count') + + # reset the count + frappe.cache().delete_value('_link_count') + if link_count: for key, count in link_count.iteritems(): if key[0] not in ignore_doctypes: frappe.db.sql('update `tab{0}` set idx = idx + {1} where name=%s'.format(key[0], count), key[1]) - # reset the count - frappe.cache().delete_value('_link_count') diff --git a/frappe/public/images/ui/bot.png b/frappe/public/images/ui/bot.png new file mode 100644 index 0000000000000000000000000000000000000000..02aeea037196928000ead041dee8ba434d360a73 GIT binary patch literal 14886 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