From ff69ef731710b5a02a855d7cdbf095329dd7d687 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Fri, 26 Mar 2021 17:20:38 +0530 Subject: [PATCH 1/8] Enhancement: Sort labels (#1995) --- .../labels/AddLabelToConversation.vue | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/conversation/labels/AddLabelToConversation.vue b/app/javascript/dashboard/routes/dashboard/conversation/labels/AddLabelToConversation.vue index d83bf6870..5c897b372 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/labels/AddLabelToConversation.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/labels/AddLabelToConversation.vue @@ -87,14 +87,18 @@ export default { }, computed: { activeList() { - return this.accountLabels.filter(accountLabel => - this.savedLabels.includes(accountLabel.title) - ); + return this.accountLabels + .filter(accountLabel => this.savedLabels.includes(accountLabel.title)) + .sort((a, b) => { + return a.title.localeCompare(b.title); + }); }, inactiveList() { - return this.accountLabels.filter( - accountLabel => !this.savedLabels.includes(accountLabel.title) - ); + return this.accountLabels + .filter(accountLabel => !this.savedLabels.includes(accountLabel.title)) + .sort((a, b) => { + return a.title.localeCompare(b.title); + }); }, }, methods: { From a24698955191efc3f21407fc1bf537ddd67c2f63 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+sivin-git@users.noreply.github.com> Date: Fri, 26 Mar 2021 20:20:20 +0530 Subject: [PATCH 2/8] Enhancement: button component (#1932) --- .../dashboard/components/ui/WootButton.vue | 61 +++++++++++++++++++ app/javascript/packs/application.js | 2 + 2 files changed, 63 insertions(+) create mode 100644 app/javascript/dashboard/components/ui/WootButton.vue diff --git a/app/javascript/dashboard/components/ui/WootButton.vue b/app/javascript/dashboard/components/ui/WootButton.vue new file mode 100644 index 000000000..e97f41f41 --- /dev/null +++ b/app/javascript/dashboard/components/ui/WootButton.vue @@ -0,0 +1,61 @@ + + diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index b7abc18c2..8d5a926ee 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -12,6 +12,7 @@ import hljs from 'highlight.js'; import Multiselect from 'vue-multiselect'; import WootSwitch from 'components/ui/Switch'; import WootWizard from 'components/ui/Wizard'; +import WootButton from 'components/ui/WootButton'; import { sync } from 'vuex-router-sync'; import Vuelidate from 'vuelidate'; import VTooltip from 'v-tooltip'; @@ -48,6 +49,7 @@ Vue.use(hljs.vuePlugin); Vue.component('multiselect', Multiselect); Vue.component('woot-switch', WootSwitch); Vue.component('woot-wizard', WootWizard); +Vue.component('woot-button', WootButton); const i18nConfig = new VueI18n({ locale: 'en', From 9903e4789696554c54f352d16ab0963c4bb723a6 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Sat, 27 Mar 2021 10:52:14 +0530 Subject: [PATCH 3/8] chore: update rails to 6.0.3.6 (#1998) --- Gemfile.lock | 116 +++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 59 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f235fe683..e4a43ad6e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,58 +18,58 @@ GEM specs: action-cable-testing (0.6.1) actioncable (>= 5.0) - actioncable (6.0.3.5) - actionpack (= 6.0.3.5) + actioncable (6.0.3.6) + actionpack (= 6.0.3.6) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.3.5) - actionpack (= 6.0.3.5) - activejob (= 6.0.3.5) - activerecord (= 6.0.3.5) - activestorage (= 6.0.3.5) - activesupport (= 6.0.3.5) + actionmailbox (6.0.3.6) + actionpack (= 6.0.3.6) + activejob (= 6.0.3.6) + activerecord (= 6.0.3.6) + activestorage (= 6.0.3.6) + activesupport (= 6.0.3.6) mail (>= 2.7.1) - actionmailer (6.0.3.5) - actionpack (= 6.0.3.5) - actionview (= 6.0.3.5) - activejob (= 6.0.3.5) + actionmailer (6.0.3.6) + actionpack (= 6.0.3.6) + actionview (= 6.0.3.6) + activejob (= 6.0.3.6) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.0.3.5) - actionview (= 6.0.3.5) - activesupport (= 6.0.3.5) + actionpack (6.0.3.6) + actionview (= 6.0.3.6) + activesupport (= 6.0.3.6) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.3.5) - actionpack (= 6.0.3.5) - activerecord (= 6.0.3.5) - activestorage (= 6.0.3.5) - activesupport (= 6.0.3.5) + actiontext (6.0.3.6) + actionpack (= 6.0.3.6) + activerecord (= 6.0.3.6) + activestorage (= 6.0.3.6) + activesupport (= 6.0.3.6) nokogiri (>= 1.8.5) - actionview (6.0.3.5) - activesupport (= 6.0.3.5) + actionview (6.0.3.6) + activesupport (= 6.0.3.6) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.0.3.5) - activesupport (= 6.0.3.5) + activejob (6.0.3.6) + activesupport (= 6.0.3.6) globalid (>= 0.3.6) - activemodel (6.0.3.5) - activesupport (= 6.0.3.5) - activerecord (6.0.3.5) - activemodel (= 6.0.3.5) - activesupport (= 6.0.3.5) + activemodel (6.0.3.6) + activesupport (= 6.0.3.6) + activerecord (6.0.3.6) + activemodel (= 6.0.3.6) + activesupport (= 6.0.3.6) activerecord-import (1.0.7) activerecord (>= 3.2) - activestorage (6.0.3.5) - actionpack (= 6.0.3.5) - activejob (= 6.0.3.5) - activerecord (= 6.0.3.5) - marcel (~> 0.3.1) - activesupport (6.0.3.5) + activestorage (6.0.3.6) + actionpack (= 6.0.3.6) + activejob (= 6.0.3.6) + activerecord (= 6.0.3.6) + marcel (~> 1.0.0) + activesupport (6.0.3.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -261,7 +261,7 @@ GEM mime-types (~> 3.0) multi_xml (>= 0.5.2) httpclient (2.8.3) - i18n (1.8.8) + i18n (1.8.9) concurrent-ruby (~> 1.0) ice_nine (0.11.2) inflecto (0.0.2) @@ -303,19 +303,17 @@ GEM nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) - marcel (0.3.3) - mimemagic (~> 0.3.2) + marcel (1.0.0) maxminddb (0.1.22) memoist (0.16.2) method_source (1.0.0) mime-types (3.3.1) mime-types-data (~> 3.2015) mime-types-data (3.2020.0512) - mimemagic (0.3.5) mini_magick (4.10.1) - mini_mime (1.0.2) + mini_mime (1.0.3) mini_portile2 (2.5.0) - minitest (5.14.3) + minitest (5.14.4) momentjs-rails (2.20.1) railties (>= 3.1) msgpack (1.3.3) @@ -325,8 +323,8 @@ GEM net-http-persistent (4.0.0) connection_pool (~> 2.2) netrc (0.11.0) - nio4r (2.5.5) - nokogiri (1.11.1) + nio4r (2.5.7) + nokogiri (1.11.2) mini_portile2 (~> 2.5.0) racc (~> 1.4) oauth (0.5.4) @@ -357,29 +355,29 @@ GEM rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.0.3.5) - actioncable (= 6.0.3.5) - actionmailbox (= 6.0.3.5) - actionmailer (= 6.0.3.5) - actionpack (= 6.0.3.5) - actiontext (= 6.0.3.5) - actionview (= 6.0.3.5) - activejob (= 6.0.3.5) - activemodel (= 6.0.3.5) - activerecord (= 6.0.3.5) - activestorage (= 6.0.3.5) - activesupport (= 6.0.3.5) + rails (6.0.3.6) + actioncable (= 6.0.3.6) + actionmailbox (= 6.0.3.6) + actionmailer (= 6.0.3.6) + actionpack (= 6.0.3.6) + actiontext (= 6.0.3.6) + actionview (= 6.0.3.6) + activejob (= 6.0.3.6) + activemodel (= 6.0.3.6) + activerecord (= 6.0.3.6) + activestorage (= 6.0.3.6) + activesupport (= 6.0.3.6) bundler (>= 1.3.0) - railties (= 6.0.3.5) + railties (= 6.0.3.6) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - railties (6.0.3.5) - actionpack (= 6.0.3.5) - activesupport (= 6.0.3.5) + railties (6.0.3.6) + actionpack (= 6.0.3.6) + activesupport (= 6.0.3.6) method_source rake (>= 0.8.7) thor (>= 0.20.3, < 2.0) From c453455ad15d63cf9e85d5b5604dd0b94c11b139 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Sat, 27 Mar 2021 12:27:48 +0530 Subject: [PATCH 4/8] fix: Handle application errors in Sentry (#1982) - Handle notification errors for attachment messages - Fix empty identifiers being passed - Fix 404 when invalid source id - Handle webhook exceptions --- app/actions/contact_identify_action.rb | 4 +++- app/controllers/api/v1/widget/base_controller.rb | 3 ++- app/mailboxes/application_mailbox.rb | 4 ++-- app/models/notification.rb | 2 +- lib/exception_list.rb | 3 ++- spec/actions/contact_identify_action_spec.rb | 10 ++++++++++ .../v1/widget/conversations_controller_spec.rb | 15 +++++++++++++++ spec/models/notification_spec.rb | 8 ++++++++ 8 files changed, 43 insertions(+), 6 deletions(-) diff --git a/app/actions/contact_identify_action.rb b/app/actions/contact_identify_action.rb index ff8efd3b5..91060f215 100644 --- a/app/actions/contact_identify_action.rb +++ b/app/actions/contact_identify_action.rb @@ -34,7 +34,9 @@ class ContactIdentifyAction def update_contact custom_attributes = params[:custom_attributes] ? @contact.custom_attributes.merge(params[:custom_attributes]) : @contact.custom_attributes - @contact.update!(params.slice(:name, :email, :identifier).merge({ custom_attributes: custom_attributes })) + # blank identifier or email will throw unique index error + # TODO: replace reject { |_k, v| v.blank? } with compact_blank when rails is upgraded + @contact.update!(params.slice(:name, :email, :identifier).reject { |_k, v| v.blank? }.merge({ custom_attributes: custom_attributes })) ContactAvatarJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present? end diff --git a/app/controllers/api/v1/widget/base_controller.rb b/app/controllers/api/v1/widget/base_controller.rb index 52f19595e..4c7dd4220 100644 --- a/app/controllers/api/v1/widget/base_controller.rb +++ b/app/controllers/api/v1/widget/base_controller.rb @@ -32,7 +32,8 @@ class Api::V1::Widget::BaseController < ApplicationController @contact_inbox = @web_widget.inbox.contact_inboxes.find_by( source_id: auth_token_params[:source_id] ) - @contact = @contact_inbox.contact + @contact = @contact_inbox&.contact + raise ActiveRecord::RecordNotFound unless @contact end def create_conversation diff --git a/app/mailboxes/application_mailbox.rb b/app/mailboxes/application_mailbox.rb index a13110dfe..fb3b451b0 100644 --- a/app/mailboxes/application_mailbox.rb +++ b/app/mailboxes/application_mailbox.rb @@ -6,7 +6,7 @@ class ApplicationMailbox < ActionMailbox::Base def self.reply_mail? proc do |inbound_mail_obj| is_a_reply_email = false - inbound_mail_obj.mail.to.each do |email| + inbound_mail_obj.mail.to&.each do |email| username = email.split('@')[0] match_result = username.match(REPLY_EMAIL_USERNAME_PATTERN) if match_result @@ -21,7 +21,7 @@ class ApplicationMailbox < ActionMailbox::Base def self.support_mail? proc do |inbound_mail_obj| is_a_support_email = false - inbound_mail_obj.mail.to.each do |email| + inbound_mail_obj.mail.to&.each do |email| channel = Channel::Email.find_by('email = ? OR forward_to_email = ?', email, email) if channel.present? is_a_support_email = true diff --git a/app/models/notification.rb b/app/models/notification.rb index 5d559befe..a92faf67e 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -70,7 +70,7 @@ class Notification < ApplicationRecord I18n.t( 'notifications.notification_title.assigned_conversation_new_message', display_id: conversation.display_id, - content: primary_actor.content.truncate_words(10) + content: primary_actor.content&.truncate_words(10) ) when 'conversation_mention' I18n.t('notifications.notification_title.conversation_mention', display_id: conversation.display_id, name: secondary_actor.name) diff --git a/lib/exception_list.rb b/lib/exception_list.rb index edb5899ab..6ea285bd0 100644 --- a/lib/exception_list.rb +++ b/lib/exception_list.rb @@ -1,4 +1,5 @@ module ExceptionList URI_EXCEPTIONS = [Errno::ETIMEDOUT, Errno::ECONNREFUSED, URI::InvalidURIError, Net::OpenTimeout, SocketError].freeze - REST_CLIENT_EXCEPTIONS = [RestClient::NotFound, RestClient::GatewayTimeout, RestClient::BadRequest, RestClient::MethodNotAllowed].freeze + REST_CLIENT_EXCEPTIONS = [RestClient::NotFound, RestClient::GatewayTimeout, RestClient::BadRequest, + RestClient::MethodNotAllowed, RestClient::Forbidden].freeze end diff --git a/spec/actions/contact_identify_action_spec.rb b/spec/actions/contact_identify_action_spec.rb index dd09f7760..be139a22e 100644 --- a/spec/actions/contact_identify_action_spec.rb +++ b/spec/actions/contact_identify_action_spec.rb @@ -44,5 +44,15 @@ describe ::ContactIdentifyAction do expect { contact.reload }.to raise_error(ActiveRecord::RecordNotFound) end end + + context 'when contacts with blank identifiers exist and identify action is called with blank identifier' do + it 'updates the attributes of contact passed in to identify action' do + create(:contact, account: account, identifier: '') + params = { identifier: '', name: 'new name' } + result = described_class.new(contact: contact, params: params).perform + expect(result.id).to eq contact.id + expect(result.name).to eq 'new name' + end + end end end diff --git a/spec/controllers/api/v1/widget/conversations_controller_spec.rb b/spec/controllers/api/v1/widget/conversations_controller_spec.rb index 43d60db69..9ba2d066a 100644 --- a/spec/controllers/api/v1/widget/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/widget/conversations_controller_spec.rb @@ -25,6 +25,21 @@ RSpec.describe '/api/v1/widget/conversations/toggle_typing', type: :request do expect(json_response['status']).to eq(conversation.status) end end + + context 'with a conversation but invalid source id' do + it 'returns the correct conversation params' do + allow(Rails.configuration.dispatcher).to receive(:dispatch) + + payload = { source_id: 'invalid source id', inbox_id: web_widget.inbox.id } + token = ::Widget::TokenService.new(payload: payload).generate_token + get '/api/v1/widget/conversations', + headers: { 'X-Auth-Token' => token }, + params: { website_token: web_widget.website_token }, + as: :json + + expect(response).to have_http_status(:not_found) + end + end end describe 'POST /api/v1/widget/conversations' do diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index 0978bab4f..be10fc411 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -41,6 +41,14 @@ RSpec.describe Notification do #{message.content.truncate_words(10)}" end + it 'returns appropriate title suited for the notification type assigned_conversation_new_message when attachment message' do + # checking content nil should be suffice for attachments + message = create(:message, sender: create(:user), content: nil) + notification = create(:notification, notification_type: 'assigned_conversation_new_message', primary_actor: message) + + expect(notification.push_message_title).to eq "[New message] - ##{notification.conversation.display_id} " + end + it 'returns appropriate title suited for the notification type conversation_mention' do message = create(:message, sender: create(:user)) notification = create(:notification, notification_type: 'conversation_mention', primary_actor: message, secondary_actor: message.sender) From 8612f307b32d5082433ed7506ceb6bd1823fb683 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Sat, 27 Mar 2021 13:08:09 +0530 Subject: [PATCH 5/8] Chore: Update widget offline message (#2001) --- app/javascript/widget/components/ChatHeaderExpanded.vue | 7 +++++-- app/javascript/widget/components/TeamAvailability.vue | 2 +- app/javascript/widget/i18n/locale/en.json | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/javascript/widget/components/ChatHeaderExpanded.vue b/app/javascript/widget/components/ChatHeaderExpanded.vue index e528d336f..eaad2cbbc 100755 --- a/app/javascript/widget/components/ChatHeaderExpanded.vue +++ b/app/javascript/widget/components/ChatHeaderExpanded.vue @@ -49,10 +49,13 @@ export default { diff --git a/app/javascript/widget/components/TeamAvailability.vue b/app/javascript/widget/components/TeamAvailability.vue index 0460161c4..084538a9d 100644 --- a/app/javascript/widget/components/TeamAvailability.vue +++ b/app/javascript/widget/components/TeamAvailability.vue @@ -71,7 +71,7 @@ export default { if (workingHoursEnabled) { return this.outOfOfficeMessage; } - return this.$t('TEAM_AVAILABILITY.OFFLINE'); + return ''; }, }, methods: { diff --git a/app/javascript/widget/i18n/locale/en.json b/app/javascript/widget/i18n/locale/en.json index 47d68c31d..33a27d18b 100644 --- a/app/javascript/widget/i18n/locale/en.json +++ b/app/javascript/widget/i18n/locale/en.json @@ -10,7 +10,7 @@ }, "TEAM_AVAILABILITY": { "ONLINE": "We are online", - "OFFLINE": "We are offline" + "OFFLINE": "We are away at the moment" }, "REPLY_TIME": { "IN_A_FEW_MINUTES": "Typically replies in a few minutes", From 8285f5df15ae7414685c6029e7fa05c5fc4ddd33 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Sat, 27 Mar 2021 13:29:04 +0530 Subject: [PATCH 6/8] fix: Handle rendering HTML only email in UI (#1987) --- .../widgets/conversation/ConversationCard.vue | 4 ++- .../widgets/conversation/Message.vue | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue index 43cf1817d..bfea8c86c 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue @@ -143,7 +143,9 @@ export default { }, parsedLastMessage() { - return this.getPlainText(this.lastMessageInChat.content); + const { content_attributes: contentAttributes } = this.lastMessageInChat; + const { email: { subject } = {} } = contentAttributes || {}; + return this.getPlainText(subject || this.lastMessageInChat.content); }, chatInbox() { diff --git a/app/javascript/dashboard/components/widgets/conversation/Message.vue b/app/javascript/dashboard/components/widgets/conversation/Message.vue index 41d770cf6..63f7dd155 100644 --- a/app/javascript/dashboard/components/widgets/conversation/Message.vue +++ b/app/javascript/dashboard/components/widgets/conversation/Message.vue @@ -107,11 +107,23 @@ export default { this.contentAttributes, this.$t('CONVERSATION.NO_RESPONSE') ); - let messageContent = - this.formatMessage(this.data.content, this.isATweet) + - botMessageContent; - return messageContent; + const { + email: { html_content: { full: fullHTMLContent } = {} } = {}, + } = this.contentAttributes; + + if (fullHTMLContent && this.isIncoming) { + let parsedContent = new DOMParser().parseFromString( + fullHTMLContent || '', + 'text/html' + ); + if (!parsedContent.getElementsByTagName('parsererror').length) { + return parsedContent.body.innerHTML; + } + } + return ( + this.formatMessage(this.data.content, this.isATweet) + botMessageContent + ); }, contentAttributes() { return this.data.content_attributes || {}; @@ -261,4 +273,10 @@ export default { margin-left: var(--space-smaller); } } + +// This is a hack to hide quoted content from GMAIL +// Replace this with actual content parser +.gmail_quote { + display: none; +} From 6a55960d4aa1623b73c02426e63cf6db85bd3d9d Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Sat, 27 Mar 2021 13:54:50 +0530 Subject: [PATCH 7/8] fix: Revert quoted text styles (#2003) --- .../dashboard/components/widgets/conversation/Message.vue | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/conversation/Message.vue b/app/javascript/dashboard/components/widgets/conversation/Message.vue index 63f7dd155..7ad176568 100644 --- a/app/javascript/dashboard/components/widgets/conversation/Message.vue +++ b/app/javascript/dashboard/components/widgets/conversation/Message.vue @@ -273,10 +273,4 @@ export default { margin-left: var(--space-smaller); } } - -// This is a hack to hide quoted content from GMAIL -// Replace this with actual content parser -.gmail_quote { - display: none; -} From 33ca4e0e80e2310d28cd2c1ae60508a8e8618085 Mon Sep 17 00:00:00 2001 From: Sojan Date: Sat, 27 Mar 2021 18:44:57 +0530 Subject: [PATCH 8/8] Bump version to 1.14.3 --- config/app.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/app.yml b/config/app.yml index cb7316c63..62438f95b 100644 --- a/config/app.yml +++ b/config/app.yml @@ -1,5 +1,5 @@ shared: &shared - version: '1.14.2' + version: '1.14.3' development: <<: *shared diff --git a/package.json b/package.json index 0f0639392..f41c809e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chatwoot/chatwoot", - "version": "1.14.2", + "version": "1.14.3", "license": "MIT", "scripts": { "eslint": "eslint app/javascript --fix",