Merge branch 'release/1.14.3'

This commit is contained in:
Sojan 2021-03-27 18:45:38 +05:30
commit bbf74f218f
19 changed files with 201 additions and 82 deletions

View file

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

View file

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

View file

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

View file

@ -0,0 +1,61 @@
<template>
<button
class="button"
:class="[
variant,
size,
colorScheme,
classNames,
isDisabled ? 'disabled' : '',
]"
:disabled="isDisabled || isLoading"
@click="handleClick"
>
<spinner v-if="isLoading" size="small" />
<i v-if="icon" :class="icon"></i>
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
<script>
import Spinner from 'shared/components/Spinner.vue';
export default {
name: 'WootButton',
components: { Spinner },
props: {
variant: {
type: String,
default: '',
},
size: {
type: String,
default: '',
},
icon: {
type: String,
default: '',
},
colorScheme: {
type: String,
default: 'primary',
},
classNames: {
type: String,
default: '',
},
isDisabled: {
type: Boolean,
default: false,
},
isLoading: {
type: Boolean,
default: false,
},
},
methods: {
handleClick(evt) {
this.$emit('click', evt);
},
},
};
</script>

View file

@ -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() {

View file

@ -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 || {};

View file

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

View file

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

View file

@ -49,10 +49,13 @@ export default {
<style scoped lang="scss">
@import '~widget/assets/scss/mixins.scss';
$logo-size: 56px;
.header-expanded {
.logo {
width: 56px;
height: 56px;
width: $logo-size;
height: $logo-size;
border-radius: $logo-size;
}
}
</style>

View file

@ -71,7 +71,7 @@ export default {
if (workingHoursEnabled) {
return this.outOfOfficeMessage;
}
return this.$t('TEAM_AVAILABILITY.OFFLINE');
return '';
},
},
methods: {

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
shared: &shared
version: '1.14.2'
version: '1.14.3'
development:
<<: *shared

View file

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

View file

@ -1,6 +1,6 @@
{
"name": "@chatwoot/chatwoot",
"version": "1.14.2",
"version": "1.14.3",
"license": "MIT",
"scripts": {
"eslint": "eslint app/javascript --fix",

View file

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

View file

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

View file

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