mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-31 19:17:48 +00:00
feat: Add support for template variables in messages content (#6215)
Fixes: #6078 Co-authored-by: Sojan <sojan@pepalo.com>
This commit is contained in:
17
app/drops/contact_drop.rb
Normal file
17
app/drops/contact_drop.rb
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
class ContactDrop < BaseDrop
|
||||||
|
def email
|
||||||
|
@obj.try(:email)
|
||||||
|
end
|
||||||
|
|
||||||
|
def phone_number
|
||||||
|
@obj.try(:phone_number)
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_name
|
||||||
|
@obj.try(:name).try(:split).try(:first)
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_name
|
||||||
|
@obj.try(:name).try(:split).try(:last) if @obj.try(:name).try(:split).try(:size) > 1
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -2,4 +2,12 @@ class UserDrop < BaseDrop
|
|||||||
def available_name
|
def available_name
|
||||||
@obj.try(:available_name)
|
@obj.try(:available_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def first_name
|
||||||
|
@obj.try(:name).try(:split).try(:first)
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_name
|
||||||
|
@obj.try(:name).try(:split).try(:last) if @obj.try(:name).try(:split).try(:size) > 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
36
app/models/concerns/liquidable.rb
Normal file
36
app/models/concerns/liquidable.rb
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
module Liquidable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
acts_as_taggable_on :labels
|
||||||
|
before_create :process_liquid_in_content
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def message_drops
|
||||||
|
{
|
||||||
|
'contact' => ContactDrop.new(conversation.contact),
|
||||||
|
'agent' => UserDrop.new(sender),
|
||||||
|
'conversation' => ConversationDrop.new(conversation),
|
||||||
|
'inbox' => InboxDrop.new(inbox)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def liquid_processable_message?
|
||||||
|
content.present? && message_type == 'outgoing'
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_liquid_in_content
|
||||||
|
return unless liquid_processable_message?
|
||||||
|
|
||||||
|
template = Liquid::Template.parse(modified_liquid_content)
|
||||||
|
self.content = template.render(message_drops)
|
||||||
|
end
|
||||||
|
|
||||||
|
def modified_liquid_content
|
||||||
|
# This regex is used to match the code blocks in the content
|
||||||
|
# We don't want to process liquid in code blocks
|
||||||
|
content.gsub(/`(.*?)`/m, '{% raw %}`\\1`{% endraw %}')
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
class Message < ApplicationRecord
|
class Message < ApplicationRecord
|
||||||
include MessageFilterHelpers
|
include MessageFilterHelpers
|
||||||
|
include Liquidable
|
||||||
NUMBER_OF_PERMITTED_ATTACHMENTS = 15
|
NUMBER_OF_PERMITTED_ATTACHMENTS = 15
|
||||||
|
|
||||||
before_validation :ensure_content_type
|
before_validation :ensure_content_type
|
||||||
|
|||||||
26
spec/drops/contact_drop_spec.rb
Normal file
26
spec/drops/contact_drop_spec.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ::ContactDrop do
|
||||||
|
subject(:contact_drop) { described_class.new(contact) }
|
||||||
|
|
||||||
|
let!(:contact) { create(:contact) }
|
||||||
|
|
||||||
|
context 'when first name' do
|
||||||
|
it 'returns first name' do
|
||||||
|
contact.update!(name: 'John Doe')
|
||||||
|
expect(subject.first_name).to eq 'John'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when last name' do
|
||||||
|
it 'returns the last name' do
|
||||||
|
contact.update!(name: 'John Doe')
|
||||||
|
expect(subject.last_name).to eq 'Doe'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns empty when last name not present' do
|
||||||
|
contact.update!(name: 'John')
|
||||||
|
expect(subject.last_name).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
26
spec/drops/user_drop_spec.rb
Normal file
26
spec/drops/user_drop_spec.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ::UserDrop do
|
||||||
|
subject(:user_drop) { described_class.new(user) }
|
||||||
|
|
||||||
|
let!(:user) { create(:user) }
|
||||||
|
|
||||||
|
context 'when first name' do
|
||||||
|
it 'returns first name' do
|
||||||
|
user.update!(name: 'John Doe')
|
||||||
|
expect(subject.first_name).to eq 'John'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when last name' do
|
||||||
|
it 'returns the last name' do
|
||||||
|
user.update!(name: 'John Doe')
|
||||||
|
expect(subject.last_name).to eq 'Doe'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns empty when last name not present' do
|
||||||
|
user.update!(name: 'John')
|
||||||
|
expect(subject.last_name).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
58
spec/models/concerns/liquidable_shared.rb
Normal file
58
spec/models/concerns/liquidable_shared.rb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
shared_examples_for 'liqudable' do
|
||||||
|
context 'when liquid is present in content' do
|
||||||
|
let(:contact) { create(:contact, name: 'john', phone_number: '+912883') }
|
||||||
|
let(:conversation) { create(:conversation, id: 1, contact: contact) }
|
||||||
|
|
||||||
|
context 'when message is incoming' do
|
||||||
|
let(:message) { build(:message, conversation: conversation, message_type: 'incoming') }
|
||||||
|
|
||||||
|
it 'will not process liquid in content' do
|
||||||
|
message.content = 'hey {{contact.name}} how are you?'
|
||||||
|
message.save!
|
||||||
|
expect(message.content).to eq 'hey {{contact.name}} how are you?'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when message is outgoing' do
|
||||||
|
let(:message) { build(:message, conversation: conversation, message_type: 'outgoing') }
|
||||||
|
|
||||||
|
it 'set replaces liquid variables in message' do
|
||||||
|
message.content = 'hey {{contact.name}} how are you?'
|
||||||
|
message.save!
|
||||||
|
expect(message.content).to eq 'hey john how are you?'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'process liquid operators like default value' do
|
||||||
|
message.content = 'Can we send you an email at {{ contact.email | default: "default" }} ?'
|
||||||
|
message.save!
|
||||||
|
expect(message.content).to eq 'Can we send you an email at default ?'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return empty string when value is not available' do
|
||||||
|
message.content = 'Can we send you an email at {{contact.email}}?'
|
||||||
|
message.save!
|
||||||
|
expect(message.content).to eq 'Can we send you an email at ?'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will not process liquid tags in multiple code blocks' do
|
||||||
|
message.content = 'hey {{contact.name}} how are you? ```code: {{contact.name}}``` ``` code: {{contact.name}} ``` test `{{contact.name}}`'
|
||||||
|
message.save!
|
||||||
|
expect(message.content).to eq 'hey john how are you? ```code: {{contact.name}}``` ``` code: {{contact.name}} ``` test `{{contact.name}}`'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will not process liquid tags in single ticks' do
|
||||||
|
message.content = 'hey {{contact.name}} how are you? ` code: {{contact.name}} ` ` code: {{contact.name}} ` test'
|
||||||
|
message.save!
|
||||||
|
expect(message.content).to eq 'hey john how are you? ` code: {{contact.name}} ` ` code: {{contact.name}} ` test'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will not throw error for broken quotes' do
|
||||||
|
message.content = 'hey {{contact.name}} how are you? ` code: {{contact.name}} ` ` code: {{contact.name}} test'
|
||||||
|
message.save!
|
||||||
|
expect(message.content).to eq 'hey john how are you? ` code: {{contact.name}} ` ` code: john test'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
require Rails.root.join 'spec/models/concerns/liquidable_shared.rb'
|
||||||
|
|
||||||
RSpec.describe Message, type: :model do
|
RSpec.describe Message, type: :model do
|
||||||
context 'with validations' do
|
context 'with validations' do
|
||||||
@@ -9,6 +10,10 @@ RSpec.describe Message, type: :model do
|
|||||||
it { is_expected.to validate_presence_of(:account_id) }
|
it { is_expected.to validate_presence_of(:account_id) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'concerns' do
|
||||||
|
it_behaves_like 'liqudable'
|
||||||
|
end
|
||||||
|
|
||||||
describe '#reopen_conversation' do
|
describe '#reopen_conversation' do
|
||||||
let(:conversation) { create(:conversation) }
|
let(:conversation) { create(:conversation) }
|
||||||
let(:message) { build(:message, message_type: :incoming, conversation: conversation) }
|
let(:message) { build(:message, message_type: :incoming, conversation: conversation) }
|
||||||
|
|||||||
Reference in New Issue
Block a user