Commit 83d14b44 authored by Fabio Huser's avatar Fabio Huser

Add user dismiss option to admin broadcast messages

This commit introduces a new configuration option for broadcast
messages to make them user dismissable. This option can be
enabled/disabled per broadcast message by any instance
administrator.
parent 90bd0076
...@@ -6,7 +6,7 @@ const handleOnDismiss = ({ currentTarget }) => { ...@@ -6,7 +6,7 @@ const handleOnDismiss = ({ currentTarget }) => {
dataset: { id }, dataset: { id },
} = currentTarget; } = currentTarget;
Cookies.set(`hide_broadcast_notification_message_${id}`, true); Cookies.set(`hide_broadcast_message_${id}`, true);
const notification = document.querySelector(`.js-broadcast-notification-${id}`); const notification = document.querySelector(`.js-broadcast-notification-${id}`);
notification.parentNode.removeChild(notification); notification.parentNode.removeChild(notification);
......
...@@ -25,9 +25,11 @@ export default () => { ...@@ -25,9 +25,11 @@ export default () => {
$broadcastMessageType.on('change', () => { $broadcastMessageType.on('change', () => {
const $broadcastMessageColorFormGroup = $('.js-broadcast-message-background-color-form-group'); const $broadcastMessageColorFormGroup = $('.js-broadcast-message-background-color-form-group');
const $broadcastMessageDismissableFormGroup = $('.js-broadcast-message-dismissable-form-group');
const $broadcastNotificationMessagePreview = $('.js-broadcast-notification-message-preview'); const $broadcastNotificationMessagePreview = $('.js-broadcast-notification-message-preview');
$broadcastMessageColorFormGroup.toggleClass('hidden'); $broadcastMessageColorFormGroup.toggleClass('hidden');
$broadcastMessageDismissableFormGroup.toggleClass('hidden');
$broadcastBannerMessagePreview.toggleClass('hidden'); $broadcastBannerMessagePreview.toggleClass('hidden');
$broadcastNotificationMessagePreview.toggleClass('hidden'); $broadcastNotificationMessagePreview.toggleClass('hidden');
}); });
......
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
@extend .broadcast-message; @extend .broadcast-message;
@extend .alert-warning; @extend .alert-warning;
text-align: center; text-align: center;
.broadcast-message-dismiss {
color: inherit;
}
} }
.broadcast-notification-message { .broadcast-notification-message {
...@@ -36,6 +40,10 @@ ...@@ -36,6 +40,10 @@
&.preview { &.preview {
position: static; position: static;
} }
.broadcast-message-dismiss {
color: $gray-800;
}
} }
.toggle-colors { .toggle-colors {
......
...@@ -62,6 +62,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController ...@@ -62,6 +62,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
starts_at starts_at
target_path target_path
broadcast_type broadcast_type
dismissable
)) ))
end end
end end
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
module BroadcastMessagesHelper module BroadcastMessagesHelper
def current_broadcast_banner_messages def current_broadcast_banner_messages
BroadcastMessage.current_banner_messages(request.path) BroadcastMessage.current_banner_messages(request.path).select do |message|
cookies["hide_broadcast_message_#{message.id}"].blank?
end
end end
def current_broadcast_notification_message def current_broadcast_notification_message
not_hidden_messages = BroadcastMessage.current_notification_messages(request.path).select do |message| not_hidden_messages = BroadcastMessage.current_notification_messages(request.path).select do |message|
cookies["hide_broadcast_notification_message_#{message.id}"].blank? cookies["hide_broadcast_message_#{message.id}"].blank?
end end
not_hidden_messages.last not_hidden_messages.last
end end
......
...@@ -46,6 +46,13 @@ ...@@ -46,6 +46,13 @@
= render_suggested_colors = render_suggested_colors
.form-group.row.js-broadcast-message-dismissable-form-group{ class: ('hidden' unless @broadcast_message.banner? ) }
.col-sm-2.col-form-label.pt-0
= f.label :starts_at, _("Dismissable")
.col-sm-10
= f.check_box :dismissable
= f.label :dismissable do
= _('Allow users to dismiss the broadcast message')
.form-group.row.js-toggle-colors-container.toggle-colors.hide .form-group.row.js-toggle-colors-container.toggle-colors.hide
.col-sm-2.col-form-label .col-sm-2.col-form-label
= f.label :font, "Font Color" = f.label :font, "Font Color"
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
%div %div
= sprite_icon('bullhorn', size: 16, css_class: 'vertical-align-text-top') = sprite_icon('bullhorn', size: 16, css_class: 'vertical-align-text-top')
= render_broadcast_message(message) = render_broadcast_message(message)
- if message.notification? && opts[:preview].blank? - if (message.notification? || message.dismissable?) && opts[:preview].blank?
%button.js-dismiss-current-broadcast-notification.btn.btn-link.text-dark.pl-2.pr-2{ 'aria-label' => _('Close'), :type => 'button', data: { id: message.id } } %button.broadcast-message-dismiss.js-dismiss-current-broadcast-notification.btn.btn-link.pl-2.pr-2{ 'aria-label' => _('Close'), :type => 'button', data: { id: message.id } }
%i.fa.fa-times %i.fa.fa-times
# frozen_string_literal: true
class AddDismissableToBroadcastMessages < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :broadcast_messages, :dismissable, :boolean
end
end
...@@ -573,6 +573,7 @@ ActiveRecord::Schema.define(version: 2020_03_03_074328) do ...@@ -573,6 +573,7 @@ ActiveRecord::Schema.define(version: 2020_03_03_074328) do
t.integer "cached_markdown_version" t.integer "cached_markdown_version"
t.string "target_path", limit: 255 t.string "target_path", limit: 255
t.integer "broadcast_type", limit: 2, default: 1, null: false t.integer "broadcast_type", limit: 2, default: 1, null: false
t.boolean "dismissable"
t.index ["ends_at", "broadcast_type", "id"], name: "index_broadcast_message_on_ends_at_and_broadcast_type_and_id" t.index ["ends_at", "broadcast_type", "id"], name: "index_broadcast_message_on_ends_at_and_broadcast_type_and_id"
end end
......
...@@ -36,7 +36,8 @@ Example response: ...@@ -36,7 +36,8 @@ Example response:
"id":1, "id":1,
"active": false, "active": false,
"target_path": "*/welcome", "target_path": "*/welcome",
"broadcast_type": "banner" "broadcast_type": "banner",
"dismissable": false
} }
] ]
``` ```
...@@ -73,7 +74,8 @@ Example response: ...@@ -73,7 +74,8 @@ Example response:
"id":1, "id":1,
"active":false, "active":false,
"target_path": "*/welcome", "target_path": "*/welcome",
"broadcast_type": "banner" "broadcast_type": "banner",
"dismissable": false
} }
``` ```
...@@ -87,15 +89,16 @@ POST /broadcast_messages ...@@ -87,15 +89,16 @@ POST /broadcast_messages
Parameters: Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|:------------|:---------|:---------|:------------------------------------------------------| |:----------------|:---------|:---------|:------------------------------------------------------|
| `message` | string | yes | Message to display. | | `message` | string | yes | Message to display. |
| `starts_at` | datetime | no | Starting time (defaults to current time). | | `starts_at` | datetime | no | Starting time (defaults to current time). |
| `ends_at` | datetime | no | Ending time (defaults to one hour from current time). | | `ends_at` | datetime | no | Ending time (defaults to one hour from current time). |
| `color` | string | no | Background color hex code. | | `color` | string | no | Background color hex code. |
| `font` | string | no | Foreground color hex code. | | `font` | string | no | Foreground color hex code. |
| `target_path`| string | no | Target path of the broadcast message. | | `target_path` | string | no | Target path of the broadcast message. |
| `broadcast_type`| string | no | Appearance type (defaults to banner) | | `broadcast_type`| string | no | Appearance type (defaults to banner) |
| `dismissable` | boolean | no | Can the user dismiss the message? |
Example request: Example request:
...@@ -116,6 +119,7 @@ Example response: ...@@ -116,6 +119,7 @@ Example response:
"active": true, "active": true,
"target_path": "*/welcome", "target_path": "*/welcome",
"broadcast_type": "notification", "broadcast_type": "notification",
"dismissable": false
} }
``` ```
...@@ -129,16 +133,17 @@ PUT /broadcast_messages/:id ...@@ -129,16 +133,17 @@ PUT /broadcast_messages/:id
Parameters: Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|:------------|:---------|:---------|:-----------------------------------| |:----------------|:---------|:---------|:--------------------------------------|
| `id` | integer | yes | ID of broadcast message to update. | | `id` | integer | yes | ID of broadcast message to update. |
| `message` | string | no | Message to display. | | `message` | string | no | Message to display. |
| `starts_at` | datetime | no | Starting time. | | `starts_at` | datetime | no | Starting time. |
| `ends_at` | datetime | no | Ending time. | | `ends_at` | datetime | no | Ending time. |
| `color` | string | no | Background color hex code. | | `color` | string | no | Background color hex code. |
| `font` | string | no | Foreground color hex code. | | `font` | string | no | Foreground color hex code. |
| `target_path`| string | no | Target path of the broadcast message. | | `target_path` | string | no | Target path of the broadcast message. |
| `broadcast_type`| string | no | Appearance type (defaults to banner) | | `broadcast_type`| string | no | Appearance type (defaults to banner) |
| `dismissable` | boolean | no | Can the user dismiss the message? |
Example request: Example request:
...@@ -159,6 +164,7 @@ Example response: ...@@ -159,6 +164,7 @@ Example response:
"active": true, "active": true,
"target_path": "*/welcome", "target_path": "*/welcome",
"broadcast_type": "notification", "broadcast_type": "notification",
"dismissable": false
} }
``` ```
......
...@@ -28,7 +28,7 @@ To add a broadcast message: ...@@ -28,7 +28,7 @@ To add a broadcast message:
NOTE: **Note:** NOTE: **Note:**
Once a broadcast message has expired, it is no longer displayed in the UI but is still listed in the Once a broadcast message has expired, it is no longer displayed in the UI but is still listed in the
list of broadcast messages. list of broadcast messages. User can also dismiss a broadcast message if the option **Dismissable** is set.
## Editing a broadcast message ## Editing a broadcast message
......
...@@ -36,6 +36,7 @@ module API ...@@ -36,6 +36,7 @@ module API
optional :font, type: String, desc: 'Foreground color' optional :font, type: String, desc: 'Foreground color'
optional :target_path, type: String, desc: 'Target path' optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast type. Defaults to banner', default: -> { 'banner' } optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast type. Defaults to banner', default: -> { 'banner' }
optional :dismissable, type: Boolean, desc: 'Is dismissable'
end end
post do post do
authenticated_as_admin! authenticated_as_admin!
...@@ -75,6 +76,7 @@ module API ...@@ -75,6 +76,7 @@ module API
optional :font, type: String, desc: 'Foreground color' optional :font, type: String, desc: 'Foreground color'
optional :target_path, type: String, desc: 'Target path' optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast Type' optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast Type'
optional :dismissable, type: Boolean, desc: 'Is dismissable'
end end
put ':id' do put ':id' do
authenticated_as_admin! authenticated_as_admin!
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module API module API
module Entities module Entities
class BroadcastMessage < Grape::Entity class BroadcastMessage < Grape::Entity
expose :id, :message, :starts_at, :ends_at, :color, :font, :target_path, :broadcast_type expose :id, :message, :starts_at, :ends_at, :color, :font, :target_path, :broadcast_type, :dismissable
expose :active?, as: :active expose :active?, as: :active
end end
end end
......
...@@ -1666,6 +1666,9 @@ msgstr "" ...@@ -1666,6 +1666,9 @@ msgstr ""
msgid "Allow this secondary node to replicate content on Object Storage" msgid "Allow this secondary node to replicate content on Object Storage"
msgstr "" msgstr ""
msgid "Allow users to dismiss the broadcast message"
msgstr ""
msgid "Allow users to register any application to use GitLab as an OAuth provider" msgid "Allow users to register any application to use GitLab as an OAuth provider"
msgstr "" msgstr ""
...@@ -6910,6 +6913,9 @@ msgstr "" ...@@ -6910,6 +6913,9 @@ msgstr ""
msgid "Dismiss trial promotion" msgid "Dismiss trial promotion"
msgstr "" msgstr ""
msgid "Dismissable"
msgstr ""
msgid "Dismissed" msgid "Dismissed"
msgstr "" msgstr ""
......
...@@ -18,6 +18,7 @@ describe 'Admin Broadcast Messages' do ...@@ -18,6 +18,7 @@ describe 'Admin Broadcast Messages' do
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_target_path', with: '*/user_onboarded'
fill_in 'broadcast_message_font', with: '#b94a48' fill_in 'broadcast_message_font', with: '#b94a48'
check 'broadcast_message_dismissable'
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'
...@@ -26,6 +27,7 @@ describe 'Admin Broadcast Messages' do ...@@ -26,6 +27,7 @@ describe 'Admin Broadcast Messages' do
expect(page).to have_content '*/user_onboarded' 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"])
expect(page).to have_selector 'a', text: 'Dismiss'
end end
it 'creates a customized broadcast notification message' do it 'creates a customized broadcast notification message' do
......
...@@ -3,28 +3,58 @@ ...@@ -3,28 +3,58 @@
require 'spec_helper' require 'spec_helper'
describe 'Broadcast Messages' do describe 'Broadcast Messages' do
let!(:broadcast_message) { create(:broadcast_message, broadcast_type: 'notification', message: 'SampleMessage') } shared_examples 'a Broadcast Messages' do
it 'shows broadcast message' do
visit root_path
expect(page).to have_content 'SampleMessage'
end
end
shared_examples 'a dismissable Broadcast Messages' do
it 'hides broadcast message after dismiss', :js do
visit root_path
find('.js-dismiss-current-broadcast-notification').click
expect(page).not_to have_content 'SampleMessage'
end
it 'broadcast message is still hidden after refresh', :js do
visit root_path
find('.js-dismiss-current-broadcast-notification').click
visit root_path
expect(page).not_to have_content 'SampleMessage'
end
end
describe 'banner type' do
let!(:broadcast_message) { create(:broadcast_message, message: 'SampleMessage') }
it_behaves_like 'a Broadcast Messages'
it 'shows broadcast message' do it 'shows broadcast message' do
visit root_path visit root_path
expect(page).to have_content 'SampleMessage' expect(page).not_to have_selector('.js-dismiss-current-broadcast-notification')
end
end end
it 'hides broadcast message after dismiss', :js do describe 'dismissable banner type' do
visit root_path let!(:broadcast_message) { create(:broadcast_message, dismissable: true, message: 'SampleMessage') }
find('.js-dismiss-current-broadcast-notification').click it_behaves_like 'a Broadcast Messages'
expect(page).not_to have_content 'SampleMessage' it_behaves_like 'a dismissable Broadcast Messages'
end end
it 'broadcast message is still hidden after refresh', :js do describe 'notification type' do
visit root_path let!(:broadcast_message) { create(:broadcast_message, broadcast_type: 'notification', message: 'SampleMessage') }
find('.js-dismiss-current-broadcast-notification').click it_behaves_like 'a Broadcast Messages'
visit root_path
expect(page).not_to have_content 'SampleMessage' it_behaves_like 'a dismissable Broadcast Messages'
end end
end end
...@@ -14,7 +14,7 @@ describe BroadcastMessagesHelper do ...@@ -14,7 +14,7 @@ describe BroadcastMessagesHelper do
context 'when last broadcast message is hidden' do context 'when last broadcast message is hidden' do
before do before do
helper.request.cookies["hide_broadcast_notification_message_#{broadcast_message_2.id}"] = 'true' helper.request.cookies["hide_broadcast_message_#{broadcast_message_2.id}"] = 'true'
end end
it { is_expected.to eq broadcast_message_1 } it { is_expected.to eq broadcast_message_1 }
......
...@@ -17,7 +17,7 @@ describe API::BroadcastMessages do ...@@ -17,7 +17,7 @@ describe API::BroadcastMessages do
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_kind_of(Array) expect(json_response).to be_kind_of(Array)
expect(json_response.first.keys) expect(json_response.first.keys)
.to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type)) .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
end end
end end
...@@ -28,7 +28,7 @@ describe API::BroadcastMessages do ...@@ -28,7 +28,7 @@ describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq message.id expect(json_response['id']).to eq message.id
expect(json_response.keys) expect(json_response.keys)
.to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type)) .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
end end
end end
...@@ -111,6 +111,15 @@ describe API::BroadcastMessages do ...@@ -111,6 +111,15 @@ describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
end end
it 'accepts an active dismissable value ' do
attrs = { message: 'new message', dismissable: true }
post api('/broadcast_messages', admin), params: attrs
expect(response).to have_gitlab_http_status(201)
expect(json_response['dismissable']).to eq true
end
end end
end end
...@@ -187,6 +196,15 @@ describe API::BroadcastMessages do ...@@ -187,6 +196,15 @@ describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
end end
it 'accepts a new dismissable value ' do
attrs = { message: 'new message', dismissable: true }
put api("/broadcast_messages/#{message.id}", admin), params: attrs
expect(response).to have_gitlab_http_status(200)
expect(json_response['dismissable']).to eq true
end
end end
end end
......
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