mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 18:47:51 +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
|
||||
@obj.try(:available_name)
|
||||
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
|
||||
|
||||
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
|
||||
include MessageFilterHelpers
|
||||
include Liquidable
|
||||
NUMBER_OF_PERMITTED_ATTACHMENTS = 15
|
||||
|
||||
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
|
||||
|
||||
require 'rails_helper'
|
||||
require Rails.root.join 'spec/models/concerns/liquidable_shared.rb'
|
||||
|
||||
RSpec.describe Message, type: :model do
|
||||
context 'with validations' do
|
||||
@@ -9,6 +10,10 @@ RSpec.describe Message, type: :model do
|
||||
it { is_expected.to validate_presence_of(:account_id) }
|
||||
end
|
||||
|
||||
describe 'concerns' do
|
||||
it_behaves_like 'liqudable'
|
||||
end
|
||||
|
||||
describe '#reopen_conversation' do
|
||||
let(:conversation) { create(:conversation) }
|
||||
let(:message) { build(:message, message_type: :incoming, conversation: conversation) }
|
||||
|
||||
Reference in New Issue
Block a user