Commit 604b3f8a authored by nicolasdular's avatar nicolasdular

Add path based targeting to broadcast messages

This adds `target_path` to `BroadcastMessages` to allow to show
broadcast messages at certain URLs.
To match complex URLs, we allow to use `*` as wildcard characters for
the `target_url`. Since SQL is using `%` for wildcards, we need to
replace these characters in our query.
parent d250c3bc
...@@ -60,6 +60,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController ...@@ -60,6 +60,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
font font
message message
starts_at starts_at
target_path
)) ))
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
module BroadcastMessagesHelper module BroadcastMessagesHelper
def current_broadcast_messages
BroadcastMessage.current(request.path)
end
def broadcast_message(message) def broadcast_message(message)
return unless message.present? return unless message.present?
......
...@@ -20,7 +20,7 @@ class BroadcastMessage < ApplicationRecord ...@@ -20,7 +20,7 @@ class BroadcastMessage < ApplicationRecord
after_commit :flush_redis_cache after_commit :flush_redis_cache
def self.current def self.current(current_path = nil)
messages = cache.fetch(CACHE_KEY, as: BroadcastMessage, expires_in: cache_expires_in) do messages = cache.fetch(CACHE_KEY, as: BroadcastMessage, expires_in: cache_expires_in) do
current_and_future_messages current_and_future_messages
end end
...@@ -33,7 +33,7 @@ class BroadcastMessage < ApplicationRecord ...@@ -33,7 +33,7 @@ class BroadcastMessage < ApplicationRecord
# cache so we don't keep running this code all the time. # cache so we don't keep running this code all the time.
cache.expire(CACHE_KEY) if now_or_future.empty? cache.expire(CACHE_KEY) if now_or_future.empty?
now_or_future.select(&:now?) now_or_future.select(&:now?).select { |message| message.matches_current_path(current_path) }
end end
def self.current_and_future_messages def self.current_and_future_messages
...@@ -72,6 +72,12 @@ class BroadcastMessage < ApplicationRecord ...@@ -72,6 +72,12 @@ class BroadcastMessage < ApplicationRecord
now? || future? now? || future?
end end
def matches_current_path(current_path)
return true if current_path.blank? || target_path.blank?
current_path.match(Regexp.escape(target_path).gsub('\\*', '.*'))
end
def flush_redis_cache def flush_redis_cache
self.class.cache.expire(CACHE_KEY) self.class.cache.expire(CACHE_KEY)
end end
......
...@@ -38,6 +38,13 @@ ...@@ -38,6 +38,13 @@
= f.label :font, "Font Color" = f.label :font, "Font Color"
.col-sm-10 .col-sm-10
= f.color_field :font, class: "form-control text-font-color" = f.color_field :font, class: "form-control text-font-color"
.form-group.row
.col-sm-2.col-form-label
= f.label :target_path, _('Target Path')
.col-sm-10
= f.text_field :target_path, class: "form-control"
.form-text.text-muted
= _('Paths can contain wildcards, like */welcome')
.form-group.row .form-group.row
.col-sm-2.col-form-label .col-sm-2.col-form-label
= f.label :starts_at, _("Starts at (UTC)") = f.label :starts_at, _("Starts at (UTC)")
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
%th Preview %th Preview
%th Starts %th Starts
%th Ends %th Ends
%th Target Path
%th &nbsp; %th &nbsp;
%tbody %tbody
- @broadcast_messages.each do |message| - @broadcast_messages.each do |message|
...@@ -31,6 +32,8 @@ ...@@ -31,6 +32,8 @@
= message.starts_at = message.starts_at
%td %td
= message.ends_at = message.ends_at
%td
= message.target_path
%td %td
= link_to sprite_icon('pencil-square'), edit_admin_broadcast_message_path(message), title: 'Edit', class: 'btn' = link_to sprite_icon('pencil-square'), edit_admin_broadcast_message_path(message), title: 'Edit', class: 'btn'
= link_to sprite_icon('remove'), admin_broadcast_message_path(message), method: :delete, remote: true, title: 'Remove', class: 'js-remove-tr btn btn-danger' = link_to sprite_icon('remove'), admin_broadcast_message_path(message), method: :delete, remote: true, title: 'Remove', class: 'js-remove-tr btn btn-danger'
......
- BroadcastMessage.current&.each do |message| - current_broadcast_messages&.each do |message|
= broadcast_message(message) = broadcast_message(message)
---
title: Add path based targeting to broadcast messages
merge_request:
author:
type: added
# frozen_string_literal: true
class AddTargetPathToBroadcastMessage < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :broadcast_messages, :target_path, :string, limit: 255
end
end
...@@ -572,6 +572,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do ...@@ -572,6 +572,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
t.string "font" t.string "font"
t.text "message_html", null: false t.text "message_html", null: false
t.integer "cached_markdown_version" t.integer "cached_markdown_version"
t.string "target_path", limit: 255
t.index ["starts_at", "ends_at", "id"], name: "index_broadcast_messages_on_starts_at_and_ends_at_and_id" t.index ["starts_at", "ends_at", "id"], name: "index_broadcast_messages_on_starts_at_and_ends_at_and_id"
end end
......
...@@ -22,6 +22,7 @@ To add a broadcast message: ...@@ -22,6 +22,7 @@ To add a broadcast message:
1. Navigate to the **Admin Area > Messages** page. 1. Navigate to the **Admin Area > Messages** page.
1. Add the text for the message to the **Message** field. Markdown and emoji are supported. 1. Add the text for the message to the **Message** field. Markdown and emoji are supported.
1. If required, click the **Customize colors** link to edit the background color and font color of the message. 1. If required, click the **Customize colors** link to edit the background color and font color of the message.
1. If required, add a **Target Path** to only show the broadcast message on URLs matching that path. You can use the wildcard character `*` to match multiple URLs, for example `/users/*/issues`.
1. Select a date for the message to start and end. 1. Select a date for the message to start and end.
1. Click the **Add broadcast message** button. 1. Click the **Add broadcast message** button.
......
...@@ -12248,6 +12248,9 @@ msgstr "" ...@@ -12248,6 +12248,9 @@ msgstr ""
msgid "Path:" msgid "Path:"
msgstr "" msgstr ""
msgid "Paths can contain wildcards, like */welcome"
msgstr ""
msgid "Pause" msgid "Pause"
msgstr "" msgstr ""
...@@ -17100,6 +17103,9 @@ msgstr "" ...@@ -17100,6 +17103,9 @@ msgstr ""
msgid "Target Branch" msgid "Target Branch"
msgstr "" msgstr ""
msgid "Target Path"
msgstr ""
msgid "Target branch" msgid "Target branch"
msgstr "" msgstr ""
......
...@@ -16,12 +16,14 @@ describe 'Admin Broadcast Messages' do ...@@ -16,12 +16,14 @@ describe 'Admin Broadcast Messages' do
it 'Create a customized broadcast message' do it 'Create a customized broadcast message' do
fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**'
fill_in 'broadcast_message_color', with: '#f2dede' fill_in 'broadcast_message_color', with: '#f2dede'
fill_in 'broadcast_message_target_path', with: '*/user_onboarded'
fill_in 'broadcast_message_font', with: '#b94a48' fill_in 'broadcast_message_font', with: '#b94a48'
select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i'
click_button 'Add broadcast message' click_button 'Add broadcast message'
expect(current_path).to eq admin_broadcast_messages_path expect(current_path).to eq admin_broadcast_messages_path
expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST'
expect(page).to have_content '*/user_onboarded'
expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST'
expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"]) expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"])
end end
......
...@@ -88,6 +88,42 @@ describe BroadcastMessage do ...@@ -88,6 +88,42 @@ describe BroadcastMessage do
expect(Rails.cache).not_to receive(:delete).with(described_class::CACHE_KEY) expect(Rails.cache).not_to receive(:delete).with(described_class::CACHE_KEY)
expect(described_class.current.length).to eq(0) expect(described_class.current.length).to eq(0)
end end
it 'returns message if it matches the target path' do
message = create(:broadcast_message, target_path: "*/onboarding_completed")
expect(described_class.current('/users/onboarding_completed')).to include(message)
end
it 'returns message if part of the target path matches' do
create(:broadcast_message, target_path: "/users/*/issues")
expect(described_class.current('/users/name/issues').length).to eq(1)
end
it 'returns the message for empty target path' do
create(:broadcast_message, target_path: "")
expect(described_class.current('/users/name/issues').length).to eq(1)
end
it 'returns the message if target path is nil' do
create(:broadcast_message, target_path: nil)
expect(described_class.current('/users/name/issues').length).to eq(1)
end
it 'does not return message if target path does not match' do
create(:broadcast_message, target_path: "/onboarding_completed")
expect(described_class.current('/welcome').length).to eq(0)
end
it 'does not return message if target path does not match when using wildcard' do
create(:broadcast_message, target_path: "/users/*/issues")
expect(described_class.current('/group/groupname/issues').length).to eq(0)
end
end end
describe '#attributes' do describe '#attributes' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment