Fixes #535: Send Trigger and Scheduler Notification Email/SMS to single user.
This commit is contained in:
parent
b644a571fb
commit
af10e5d1d2
7 changed files with 152 additions and 18 deletions
|
@ -348,20 +348,38 @@ class App.UiElement.ticket_perform_action
|
||||||
if !_.isArray(meta.recipient)
|
if !_.isArray(meta.recipient)
|
||||||
meta.recipient = [meta.recipient]
|
meta.recipient = [meta.recipient]
|
||||||
|
|
||||||
column_select_options = []
|
columnSelectOptions = []
|
||||||
for key, value of options
|
for key, value of options
|
||||||
selected = undefined
|
selected = undefined
|
||||||
for recipient in meta.recipient
|
for recipient in meta.recipient
|
||||||
if key is recipient
|
if key is recipient
|
||||||
selected = true
|
selected = true
|
||||||
column_select_options.push({ value: key, name: App.i18n.translateInline(value), selected: selected })
|
columnSelectOptions.push({ value: key, name: App.i18n.translateInline(value), selected: selected })
|
||||||
|
|
||||||
column_select = new App.ColumnSelect
|
columnSelectRecipientUserOptions = []
|
||||||
|
for user in App.User.all()
|
||||||
|
key = "userid_#{user.id}"
|
||||||
|
selected = undefined
|
||||||
|
for recipient in meta.recipient
|
||||||
|
if key is recipient
|
||||||
|
selected = true
|
||||||
|
columnSelectRecipientUserOptions.push({ value: key, name: "#{user.firstname} #{user.lastname}", selected: selected })
|
||||||
|
|
||||||
|
columnSelectRecipient = new App.ColumnSelect
|
||||||
attribute:
|
attribute:
|
||||||
name: "#{name}::recipient"
|
name: "#{name}::recipient"
|
||||||
options: column_select_options
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Variables',
|
||||||
|
group: columnSelectOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'User',
|
||||||
|
group: columnSelectRecipientUserOptions
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
selection = column_select.element()
|
selectionRecipient = columnSelectRecipient.element()
|
||||||
|
|
||||||
notificationElement = $( App.view('generic/ticket_perform_action/notification')(
|
notificationElement = $( App.view('generic/ticket_perform_action/notification')(
|
||||||
attribute: attribute
|
attribute: attribute
|
||||||
|
@ -369,7 +387,7 @@ class App.UiElement.ticket_perform_action
|
||||||
notificationType: notificationType
|
notificationType: notificationType
|
||||||
meta: meta || {}
|
meta: meta || {}
|
||||||
))
|
))
|
||||||
notificationElement.find('.js-recipient select').replaceWith(selection)
|
notificationElement.find('.js-recipient select').replaceWith(selectionRecipient)
|
||||||
|
|
||||||
visibilitySelection = App.UiElement.select.render(
|
visibilitySelection = App.UiElement.select.render(
|
||||||
name: "#{name}::internal"
|
name: "#{name}::internal"
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
<div class="columnSelect-pool js-pool">
|
<div class="columnSelect-pool js-pool">
|
||||||
<% for option in @attribute.options: %>
|
<% for option in @attribute.options: %>
|
||||||
<% if option.group != undefined: %>
|
<% if option.group != undefined: %>
|
||||||
<div class="columnSelect-group-label" title="<%= option.label %>"><%= option.label %></div>
|
<div class="columnSelect-group-label" title="<%= @T(option.label) %>"><%= @T(option.label) %></div>
|
||||||
<% for o in option.group: %>
|
<% for o in option.group: %>
|
||||||
<div class="columnSelect-option js-select js-option<%= ' is-hidden' if o.selected %>" data-value="<%= o.value %>" title="<%= o.title %>"><%= o.name %></div>
|
<div class="columnSelect-option js-select js-option<%= ' is-hidden' if o.selected %>" data-value="<%= o.value %>" title="<%= o.title %>"><%= o.name %></div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -12,14 +12,6 @@
|
||||||
<select></select>
|
<select></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="formGroup-label">
|
|
||||||
<label><%- @T('Recipient') %></label>
|
|
||||||
</div>
|
|
||||||
<div class="controls js-recipientUser u-positionOrigin">
|
|
||||||
<select></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% if @notificationType is 'email': %>
|
<% if @notificationType is 'email': %>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="formGroup-label">
|
<div class="formGroup-label">
|
||||||
|
|
|
@ -1340,6 +1340,13 @@ result
|
||||||
User.group_access(group_id, 'full').sort_by(&:login).each do |user|
|
User.group_access(group_id, 'full').sort_by(&:login).each do |user|
|
||||||
recipients_raw.push(user.email)
|
recipients_raw.push(user.email)
|
||||||
end
|
end
|
||||||
|
elsif recipient =~ /\Auserid_(\d+)\z/
|
||||||
|
user = User.lookup(id: $1)
|
||||||
|
if !user
|
||||||
|
logger.warn "Can't find configured Trigger Email recipient User with ID '#{$1}'"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
recipients_raw.push(user.email)
|
||||||
else
|
else
|
||||||
logger.error "Unknown email notification recipient '#{recipient}'"
|
logger.error "Unknown email notification recipient '#{recipient}'"
|
||||||
next
|
next
|
||||||
|
@ -1538,6 +1545,11 @@ result
|
||||||
owner_id
|
owner_id
|
||||||
when 'ticket_agents'
|
when 'ticket_agents'
|
||||||
User.group_access(group_id, 'full').sort_by(&:login)
|
User.group_access(group_id, 'full').sort_by(&:login)
|
||||||
|
when /\Auserid_(\d+)\z/
|
||||||
|
return $1 if User.exists?($1)
|
||||||
|
|
||||||
|
logger.warn "Can't find configured Trigger SMS recipient User with ID '#{$1}'"
|
||||||
|
nil
|
||||||
else
|
else
|
||||||
logger.error "Unknown sms notification recipient '#{recipient}'"
|
logger.error "Unknown sms notification recipient '#{recipient}'"
|
||||||
nil
|
nil
|
||||||
|
@ -1548,6 +1560,7 @@ result
|
||||||
Array(value['recipient'])
|
Array(value['recipient'])
|
||||||
.each_with_object([]) { |recipient_type, sum| sum.concat(Array(sms_recipients_by_type(recipient_type, article))) }
|
.each_with_object([]) { |recipient_type, sum| sum.concat(Array(sms_recipients_by_type(recipient_type, article))) }
|
||||||
.map { |user_or_id| User.lookup(id: user_or_id) }
|
.map { |user_or_id| User.lookup(id: user_or_id) }
|
||||||
|
.uniq(&:id)
|
||||||
.select { |user| user.mobile.present? }
|
.select { |user| user.mobile.present? }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -61,5 +61,36 @@ RSpec.describe Trigger do
|
||||||
expect(triggered_article.body).to match(time_in_zone.strftime('%d.%m.%y'))
|
expect(triggered_article.body).to match(time_in_zone.strftime('%d.%m.%y'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'recipients' do
|
||||||
|
|
||||||
|
let(:recipient1) { create(:agent_user, mobile: '+37061010000', groups: [ticket_group]) }
|
||||||
|
let(:recipient2) { create(:agent_user, mobile: '+37061010001', groups: [ticket_group]) }
|
||||||
|
let(:recipient3) { create(:agent_user, mobile: '+37061010002', groups: [ticket_group]) }
|
||||||
|
|
||||||
|
let(:ticket_group) { create(:group) }
|
||||||
|
|
||||||
|
let(:ticket) do
|
||||||
|
ticket = create(:ticket, group: ticket_group, created_by_id: create(:agent_user).id)
|
||||||
|
Observer::Transaction.commit
|
||||||
|
ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:channel, area: 'Sms::Notification')
|
||||||
|
create(:trigger,
|
||||||
|
disable_notification: false,
|
||||||
|
perform: {
|
||||||
|
'notification.sms': {
|
||||||
|
recipient: ['ticket_agents', "userid_#{recipient1.id}", "userid_#{recipient2.id}", "userid_#{recipient3.id}"],
|
||||||
|
body: 'Hello World!',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'contains no duplicates' do
|
||||||
|
expect(ticket.articles.last.preferences['sms_recipients'].sort).to eq([recipient1.mobile, recipient2.mobile, recipient3.mobile].sort)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,35 @@ RSpec.describe Trigger, type: :model do
|
||||||
|
|
||||||
it_behaves_like 'ApplicationModel', can_assets: { selectors: %i[condition perform] }
|
it_behaves_like 'ApplicationModel', can_assets: { selectors: %i[condition perform] }
|
||||||
|
|
||||||
|
describe 'validation' do
|
||||||
|
|
||||||
|
let(:condition) do
|
||||||
|
{ 'ticket.action' => { 'operator' => 'is', 'value' => 'create' } }
|
||||||
|
end
|
||||||
|
let(:perform) do
|
||||||
|
{ 'ticket.title' => { 'value'=>'triggered' } }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'notification.email' do
|
||||||
|
context 'missing recipient' do
|
||||||
|
|
||||||
|
let(:perform) do
|
||||||
|
{
|
||||||
|
'notification.email' => {
|
||||||
|
'subject' => 'Hello',
|
||||||
|
'body' => 'World!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error' do
|
||||||
|
expect { trigger.save! }.to raise_error(Exceptions::UnprocessableEntity, 'Invalid perform notification.email, recipient is missing!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
describe 'Send-email triggers' do
|
describe 'Send-email triggers' do
|
||||||
before do
|
before do
|
||||||
described_class.destroy_all # Default DB state includes three sample triggers
|
described_class.destroy_all # Default DB state includes three sample triggers
|
||||||
|
@ -87,6 +116,57 @@ RSpec.describe Trigger, type: :model do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'notification.email recipient' do
|
||||||
|
let!(:ticket) { create(:ticket) }
|
||||||
|
let!(:recipient1) { create(:user, email: 'test1@zammad-test.com') }
|
||||||
|
let!(:recipient2) { create(:user, email: 'test2@zammad-test.com') }
|
||||||
|
let!(:recipient3) { create(:user, email: 'test3@zammad-test.com') }
|
||||||
|
|
||||||
|
let(:perform) do
|
||||||
|
{
|
||||||
|
'notification.email' => {
|
||||||
|
'recipient' => recipient,
|
||||||
|
'subject' => 'Hello',
|
||||||
|
'body' => 'World!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before { Observer::Transaction.commit }
|
||||||
|
|
||||||
|
context 'mix of recipient group keyword and single recipient users' do
|
||||||
|
let(:recipient) { [ 'ticket_customer', "userid_#{recipient1.id}", "userid_#{recipient2.id}", "userid_#{recipient3.id}" ] }
|
||||||
|
|
||||||
|
it 'contains all recipients' do
|
||||||
|
expect(ticket.articles.last.to).to eq("#{ticket.customer.email}, #{recipient1.email}, #{recipient2.email}, #{recipient3.email}")
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'duplicate recipient' do
|
||||||
|
let(:recipient) { [ 'ticket_customer', "userid_#{ticket.customer.id}" ] }
|
||||||
|
|
||||||
|
it 'contains only one recipient' do
|
||||||
|
expect(ticket.articles.last.to).to eq(ticket.customer.email.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'list of single users only' do
|
||||||
|
let(:recipient) { [ "userid_#{recipient1.id}", "userid_#{recipient2.id}", "userid_#{recipient3.id}" ] }
|
||||||
|
|
||||||
|
it 'contains all recipients' do
|
||||||
|
expect(ticket.articles.last.to).to eq("#{recipient1.email}, #{recipient2.email}, #{recipient3.email}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'recipient group keyword only' do
|
||||||
|
let(:recipient) { 'ticket_customer' }
|
||||||
|
|
||||||
|
it 'contains matching recipient' do
|
||||||
|
expect(ticket.articles.last.to).to eq(ticket.customer.email.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for condition "ticket updated"' do
|
context 'for condition "ticket updated"' do
|
||||||
|
|
Loading…
Reference in a new issue