feat: Enable template variables in channel greeting messages (#6971)
This commit is contained in:
@@ -1,2 +1,5 @@
|
|||||||
class AccountDrop < BaseDrop
|
class AccountDrop < BaseDrop
|
||||||
|
def name
|
||||||
|
@obj.try(:name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,4 +10,26 @@ module EmailHelper
|
|||||||
def normalize_email_with_plus_addressing(email)
|
def normalize_email_with_plus_addressing(email)
|
||||||
"#{email.split('@').first.split('+').first}@#{email.split('@').last}".downcase
|
"#{email.split('@').first.split('+').first}@#{email.split('@').last}".downcase
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_email_variables(conversation, email)
|
||||||
|
case email
|
||||||
|
when modified_liquid_content(email)
|
||||||
|
template = Liquid::Template.parse(modified_liquid_content(email))
|
||||||
|
template.render(message_drops(conversation))
|
||||||
|
when URI::MailTo::EMAIL_REGEXP
|
||||||
|
email
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def modified_liquid_content(email)
|
||||||
|
# This regex is used to match the code blocks in the content
|
||||||
|
# We don't want to process liquid in code blocks
|
||||||
|
email.gsub(/`(.*?)`/m, '{% raw %}`\\1`{% endraw %}')
|
||||||
|
end
|
||||||
|
|
||||||
|
def message_drops(conversation)
|
||||||
|
{
|
||||||
|
'contact' => ContactDrop.new(conversation.contact)
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,7 +17,16 @@
|
|||||||
<div v-if="isBusinessHoursEnabled" class="business-hours-wrap">
|
<div v-if="isBusinessHoursEnabled" class="business-hours-wrap">
|
||||||
<label class="unavailable-input-wrap">
|
<label class="unavailable-input-wrap">
|
||||||
{{ $t('INBOX_MGMT.BUSINESS_HOURS.UNAVAILABLE_MESSAGE_LABEL') }}
|
{{ $t('INBOX_MGMT.BUSINESS_HOURS.UNAVAILABLE_MESSAGE_LABEL') }}
|
||||||
<textarea v-model="unavailableMessage" type="text" />
|
<label v-if="isRichEditorEnabled" class="richtext">
|
||||||
|
<woot-message-editor
|
||||||
|
v-model="unavailableMessage"
|
||||||
|
:enable-variables="true"
|
||||||
|
:is-format-mode="true"
|
||||||
|
class="input"
|
||||||
|
:min-height="4"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<textarea v-else v-model="unavailableMessage" type="text" />
|
||||||
</label>
|
</label>
|
||||||
<div class="timezone-input-wrap">
|
<div class="timezone-input-wrap">
|
||||||
<label>
|
<label>
|
||||||
@@ -61,7 +70,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||||
import SettingsSection from 'dashboard/components/SettingsSection';
|
import SettingsSection from 'dashboard/components/SettingsSection';
|
||||||
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
||||||
import BusinessDay from './BusinessDay';
|
import BusinessDay from './BusinessDay';
|
||||||
import {
|
import {
|
||||||
timeSlotParse,
|
timeSlotParse,
|
||||||
@@ -79,8 +90,9 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
SettingsSection,
|
SettingsSection,
|
||||||
BusinessDay,
|
BusinessDay,
|
||||||
|
WootMessageEditor,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin],
|
mixins: [alertMixin, inboxMixin],
|
||||||
props: {
|
props: {
|
||||||
inbox: {
|
inbox: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -115,6 +127,15 @@ export default {
|
|||||||
timeZones() {
|
timeZones() {
|
||||||
return [...timeZoneOptions()];
|
return [...timeZoneOptions()];
|
||||||
},
|
},
|
||||||
|
isRichEditorEnabled() {
|
||||||
|
if (
|
||||||
|
this.isATwilioChannel ||
|
||||||
|
this.isATwitterInbox ||
|
||||||
|
this.isAFacebookInbox
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
inbox() {
|
inbox() {
|
||||||
@@ -190,4 +211,11 @@ export default {
|
|||||||
.business-hours-wrap {
|
.business-hours-wrap {
|
||||||
margin-bottom: var(--space-medium);
|
margin-bottom: var(--space-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.richtext {
|
||||||
|
padding: 0 var(--space-normal);
|
||||||
|
border-radius: var(--border-radius-normal);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
margin: 0 0 var(--space-normal);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<woot-message-editor
|
<woot-message-editor
|
||||||
v-model="greetingsMessage"
|
v-model="greetingsMessage"
|
||||||
:is-format-mode="true"
|
:is-format-mode="true"
|
||||||
|
:enable-variables="true"
|
||||||
class="input"
|
class="input"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:min-height="4"
|
:min-height="4"
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ module Liquidable
|
|||||||
'contact' => ContactDrop.new(conversation.contact),
|
'contact' => ContactDrop.new(conversation.contact),
|
||||||
'agent' => UserDrop.new(sender),
|
'agent' => UserDrop.new(sender),
|
||||||
'conversation' => ConversationDrop.new(conversation),
|
'conversation' => ConversationDrop.new(conversation),
|
||||||
'inbox' => InboxDrop.new(inbox)
|
'inbox' => InboxDrop.new(inbox),
|
||||||
|
'account' => AccountDrop.new(conversation.account)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def liquid_processable_message?
|
def liquid_processable_message?
|
||||||
content.present? && message_type == 'outgoing'
|
content.present? && (message_type == 'outgoing' || message_type == 'template')
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_liquid_in_content
|
def process_liquid_in_content
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
class ActionService
|
class ActionService
|
||||||
|
include EmailHelper
|
||||||
|
|
||||||
def initialize(conversation)
|
def initialize(conversation)
|
||||||
@conversation = conversation.reload
|
@conversation = conversation.reload
|
||||||
end
|
end
|
||||||
@@ -59,6 +61,7 @@ class ActionService
|
|||||||
emails = emails[0].gsub(/\s+/, '').split(',')
|
emails = emails[0].gsub(/\s+/, '').split(',')
|
||||||
|
|
||||||
emails.each do |email|
|
emails.each do |email|
|
||||||
|
email = parse_email_variables(@conversation, email)
|
||||||
ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later
|
ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ RSpec.describe AutomationRules::ActionService do
|
|||||||
describe '#perform with send_email_transcript action' do
|
describe '#perform with send_email_transcript action' do
|
||||||
before do
|
before do
|
||||||
rule.actions << { action_name: 'send_email_transcript', action_params: ['contact@example.com, agent@example.com,agent1@example.com'] }
|
rule.actions << { action_name: 'send_email_transcript', action_params: ['contact@example.com, agent@example.com,agent1@example.com'] }
|
||||||
|
rule.save
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'will send email to transcript to action params emails' do
|
it 'will send email to transcript to action params emails' do
|
||||||
@@ -102,6 +103,17 @@ RSpec.describe AutomationRules::ActionService do
|
|||||||
|
|
||||||
described_class.new(rule, account, conversation).perform
|
described_class.new(rule, account, conversation).perform
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'will send email to transcript to contacts' do
|
||||||
|
rule.actions = [{ action_name: 'send_email_transcript', action_params: ['{{contact.email}}'] }]
|
||||||
|
rule.save
|
||||||
|
|
||||||
|
mailer = double
|
||||||
|
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
|
||||||
|
allow(mailer).to receive(:conversation_transcript).with(conversation, conversation.contact.email)
|
||||||
|
|
||||||
|
described_class.new(rule.reload, account, conversation).perform
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,5 +8,26 @@ describe ::MessageTemplates::Template::Greeting do
|
|||||||
described_class.new(conversation: conversation).perform
|
described_class.new(conversation: conversation).perform
|
||||||
expect(conversation.messages.count).to eq(1)
|
expect(conversation.messages.count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates the greeting messages with template variable' do
|
||||||
|
conversation.inbox.update!(greeting_message: 'Hey, {{contact.name}} welcome to our board.')
|
||||||
|
described_class.new(conversation: conversation).perform
|
||||||
|
expect(conversation.messages.count).to eq(1)
|
||||||
|
expect(conversation.messages.last.content).to eq("Hey, #{conversation.contact.name} welcome to our board.")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates the greeting messages with more than one variable strings' do
|
||||||
|
conversation.inbox.update!(greeting_message: 'Hey, {{contact.name}} welcome to our board. - from {{account.name}}')
|
||||||
|
described_class.new(conversation: conversation).perform
|
||||||
|
expect(conversation.messages.count).to eq(1)
|
||||||
|
expect(conversation.messages.last.content).to eq("Hey, #{conversation.contact.name} welcome to our board. - from #{conversation.account.name}")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates the greeting messages' do
|
||||||
|
conversation.inbox.update!(greeting_message: 'Hello welcome to our board.')
|
||||||
|
described_class.new(conversation: conversation).perform
|
||||||
|
expect(conversation.messages.count).to eq(1)
|
||||||
|
expect(conversation.messages.last.content).to eq('Hello welcome to our board.')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,5 +9,22 @@ describe ::MessageTemplates::Template::OutOfOffice do
|
|||||||
expect(conversation.messages.template.count).to eq(1)
|
expect(conversation.messages.template.count).to eq(1)
|
||||||
expect(conversation.messages.template.first.content).to eq(conversation.inbox.out_of_office_message)
|
expect(conversation.messages.template.first.content).to eq(conversation.inbox.out_of_office_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates the out of office messages with template variable' do
|
||||||
|
conversation.inbox.update!(out_of_office_message: 'Hey, {{contact.name}} we are unavailable at the moment.')
|
||||||
|
described_class.new(conversation: conversation).perform
|
||||||
|
expect(conversation.messages.count).to eq(1)
|
||||||
|
expect(conversation.messages.last.content).to eq("Hey, #{conversation.contact.name} we are unavailable at the moment.")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates the out of office messages with more than one variable strings' do
|
||||||
|
conversation.inbox.update!(out_of_office_message:
|
||||||
|
'Hey, {{contact.name}} we are unavailable at the moment. - from {{account.name}}')
|
||||||
|
described_class.new(conversation: conversation).perform
|
||||||
|
expect(conversation.messages.count).to eq(1)
|
||||||
|
expect(conversation.messages.last.content).to eq(
|
||||||
|
"Hey, #{conversation.contact.name} we are unavailable at the moment. - from #{conversation.account.name}"
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user