Skip to content

Commit

Permalink
Merge pull request #9590 from saraycp/announcement_to_status_message
Browse files Browse the repository at this point in the history
Add announcement functionality to status message
  • Loading branch information
saraycp committed May 29, 2020
2 parents 9407691 + 30ac15a commit f86f173
Show file tree
Hide file tree
Showing 33 changed files with 419 additions and 112 deletions.
5 changes: 5 additions & 0 deletions ReleaseNotes-2.11
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,8 @@ Webui:
- /home/home_project
- /home/list_my


Breaking API changes:
=====================

* Status messages endpoints. Check the API documentation for the new format.
14 changes: 9 additions & 5 deletions docs/api/api/status_message.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<message severity="0">
a single sample message. contains some text ...
attributes are optional and ignored when sent (put) to the api.
the format of this example will be used as a convenient way to put a message.
</message>
<status_message>
<message>
a single sample message. contains some text ...
attributes are optional and ignored when sent (put) to the api.
the format of this example will be used as a convenient way to put a message.
</message>
<severity>information</severity>
<scope>all_users</scope>
</status_message>

14 changes: 14 additions & 0 deletions docs/api/api/status_message.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="status_message">
<xs:complexType>
<xs:sequence>
<xs:element name="message" type="xs:string"></xs:element>
<xs:element name="severity" type="xs:string"></xs:element>
<xs:element name="scope" type="xs:string" minOccurs="0" default="all_users"></xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:int"></xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
53 changes: 20 additions & 33 deletions src/api/app/controllers/status_messages_controller.rb
Original file line number Diff line number Diff line change
@@ -1,50 +1,37 @@
class StatusMessagesController < ApplicationController
class PermissionDeniedError < APIError
setup 403
end

class CreatingMessagesError < APIError; end
before_action :require_admin, only: [:create, :destroy]

def index
@messages = StatusMessage.alive.limit(params[:limit]).order('created_at DESC').includes(:user)
@count = @messages.size
end

def show
@messages = [StatusMessage.find(params[:id])]
@count = 1
render :index
@message = StatusMessage.find(params[:id])
end

def create
# check permissions
unless permissions.status_message_create
raise PermissionDeniedError, 'message(s) cannot be created, you have not sufficient permissions'
end

new_messages = Nokogiri::XML(request.raw_post, &:strict).root
@messages = []
if new_messages.css('message').present?
# message(s) are wrapped in outer xml tag 'status_messages'
new_messages.css('message').each do |msg|
@messages << StatusMessage.create!(message: msg.content, severity: msg['severity'], user: User.session!)
end
else
# TODO: make use of a validator
raise CreatingMessagesError, "no message #{new_messages.to_xml}" if new_messages.name != 'message'
# just one message, NOT wrapped in outer xml tag 'status_messages'
@messages << StatusMessage.create!(message: new_messages.content, severity: new_messages['severity'], user: User.session!)
end
render :index
status_message = StatusMessage.from_xml(validate_status_message)

authorize status_message

status_message.save!

render_ok
end

def destroy
# check permissions
unless permissions.status_message_create
raise PermissionDeniedError, 'message cannot be deleted, you have not sufficient permissions'
end

StatusMessage.find(params[:id]).delete
status_message = StatusMessage.find(params[:id])
authorize status_message
status_message.delete
render_ok
end

private

# TODO: make it more robust
def validate_status_message
Suse::Validator.validate(:status_message, request.raw_post)
request.raw_post
end
end
2 changes: 1 addition & 1 deletion src/api/app/controllers/webui/main_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Webui::MainController < Webui::WebuiController
skip_before_action :check_anonymous, only: [:index]

def index
@status_messages = StatusMessage.alive.includes(:user).limit(4).to_a
@status_messages = StatusMessage.alive.where(communication_scope: StatusMessage.communication_scopes_for_current_user).includes(:user).limit(4)
@workerstatus = Rails.cache.fetch('workerstatus_hash', expires_in: 10.minutes) do
Xmlhash.parse(WorkerStatus.hidden.to_xml)
end
Expand Down
18 changes: 17 additions & 1 deletion src/api/app/controllers/webui/status_messages_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
class Webui::StatusMessagesController < Webui::WebuiController
# permissions.status_message_create
before_action :require_admin, only: [:destroy, :create]
before_action :require_login, only: :acknowledge

def create
# TODO: make use of permissions.status_message_create
status_message = StatusMessage.new(message: params[:message], severity: params[:severity], user: User.session!)
status_message = StatusMessage.new(user: User.session!,
message: params[:status_message][:message],
severity: params[:status_message][:severity],
communication_scope: params[:status_message][:communication_scope])

if status_message.save
flash[:success] = 'Status message was successfully created.'
Expand All @@ -26,4 +30,16 @@ def destroy

redirect_to(controller: 'main', action: 'index')
end

def acknowledge
status_message = StatusMessage.find(params[:id])
if status_message.acknowledge!
RabbitmqBus.send_to_bus('metrics', "user.acknowledged_status_message status_message_id=#{status_message.id}")
else
flash.now[:error] = "Could not accept status message: #{status_message.errors.full_messages.to_sentence}"
end
respond_to do |format|
format.js { render controller: 'status_message', action: 'acknowledge' }
end
end
end
5 changes: 5 additions & 0 deletions src/api/app/controllers/webui/webui_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Webui::WebuiController < ActionController::Base
before_action :set_influxdb_additional_tags
before_action :require_configuration
before_action :set_pending_announcement
before_action :current_announcement
after_action :clean_cache

# :notice and :alert are default, we add :success and :error
Expand Down Expand Up @@ -316,6 +317,10 @@ def set_pending_announcement
@pending_announcement = Announcement.last
end

def current_announcement
@current_announcement = StatusMessage.latest_for_current_user
end

def add_arrays(arr1, arr2)
# we assert that both have the same size
ret = []
Expand Down
10 changes: 6 additions & 4 deletions src/api/app/helpers/webui/main_helper.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
module Webui::MainHelper
def icon_for_status(message)
case message.severity
when 1
case message.severity.to_sym
when :green
{ class: 'fa-check-circle text-success', title: 'Success' }
when 2
when :yellow
{ class: 'fa-exclamation-triangle text-warning', title: 'Warning' }
when 3
when :red
{ class: 'fa-exclamation-circle text-danger', title: 'Alert' }
when :announcement
{ class: 'fa-bullhorn text-info', title: 'Announcement' }
else
{ class: 'fa-info-circle text-info', title: 'Info' }
end
Expand Down
41 changes: 40 additions & 1 deletion src/api/app/models/status_message.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,51 @@
class StatusMessage < ApplicationRecord
belongs_to :user
belongs_to :user # TODO: rename as creator
has_many :status_message_acknowledgements, dependent: :destroy
has_many :users, through: :status_message_acknowledgements

validates :user, :severity, :message, presence: true

scope :alive, -> { where(deleted_at: nil).order('created_at DESC') }
scope :announcements, -> { alive.where(severity: 'announcement') }

enum severity: { information: 0, green: 1, yellow: 2, red: 3, announcement: 4 }
enum communication_scope: { all_users: 0, logged_in_users: 1, admin_users: 2, in_beta_users: 3, in_rollout_users: 4 }

# xml: A Nokogiri object
def self.from_xml(xml)
StatusMessage.create! if xml.blank?
doc = Nokogiri::XML(xml, &:strict).root
message = doc.css('message').text
severity = doc.css('severity').text
scope = doc.css('scope').text
scope = 'all_users' if scope.blank?
StatusMessage.new(message: message, severity: severity, communication_scope: scope, user: User.session!)
end

def delete
self.deleted_at = Time.now
save
end

def acknowledge!
users << User.session!
end

def self.latest_for_current_user
announcement = StatusMessage.announcements.find_by(communication_scope: StatusMessage.communication_scopes_for_current_user)
return nil unless announcement
return nil if StatusMessageAcknowledgement.find_by(status_message: announcement, user: User.session)
announcement
end

def self.communication_scopes_for_current_user
scopes = [:all_users]
return scopes unless User.session
scopes << :admin_users if User.session.is_admin?
scopes << :in_rollout_users if User.session.in_rollout?
scopes << :in_beta_users if User.session.in_beta?
scopes << :logged_in_users
end
end

# == Schema Information
Expand Down
9 changes: 9 additions & 0 deletions src/api/app/models/status_message_acknowledgement.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class StatusMessageAcknowledgement < ApplicationRecord
belongs_to :status_message
belongs_to :user

validates :status_message, presence: true
validates :user, presence: true

validates :status_message_id, uniqueness: { scope: :user_id, message: 'You have already acknowledged the message' }
end
3 changes: 3 additions & 0 deletions src/api/app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class User < ApplicationRecord
has_and_belongs_to_many :announcements
has_many :commit_activities

has_many :status_message_acknowledgements, dependent: :destroy
has_many :acknowledged_status_messages, through: :status_message_acknowledgements, class_name: 'StatusMessage', source: 'status_message'

scope :confirmed, -> { where(state: 'confirmed') }
scope :all_without_nobody, -> { where.not(login: NOBODY_LOGIN) }
scope :not_deleted, -> { where.not(state: 'deleted') }
Expand Down
9 changes: 9 additions & 0 deletions src/api/app/policies/status_message_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class StatusMessagePolicy < ApplicationPolicy
def create?
user.is_admin?
end

def destroy?
user.is_admin?
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- if current_announcement
.row.justify-content-center#status-message-announcement
.col-12
.alert.fade.show.alert-notice
.row
.col-md-10
= render_as_markdown(current_announcement.message)
.col-md-2
- if User.session
= form_tag(acknowledge_status_message_path(current_announcement), remote: true, class: 'text-right') do
= submit_tag 'Got it', class: 'btn btn-sm btn-secondary'
1 change: 1 addition & 0 deletions src/api/app/views/layouts/webui/webui.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
.container-xxl.sticky-top.flash-and-announcement.text-word-break-all
- unless @hide_announcement_notification
#announcement= render partial: 'layouts/webui/announcement', locals: { pending_announcement: @pending_announcement }
= render partial: 'layouts/webui/status_message_announcement', locals: { current_announcement: @current_announcement }
#flash= render partial: 'layouts/webui/flash', object: flash
.modal.fade#modal{ tabindex: '-1', role: 'dialog', aria: { labelledby: 'modalLabel', hidden: true } }

Expand Down
7 changes: 7 additions & 0 deletions src/api/app/views/status_messages/_status_message.xml.builder
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
builder.status_message(id: status_message.id) do |m|
m.message status_message.message
m.user status_message.user.login
m.severity status_message.severity
m.scope status_message.communication_scope
m.created_at status_message.created_at
end
11 changes: 1 addition & 10 deletions src/api/app/views/status_messages/index.xml.builder
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
xml.status_messages(count: @count) do
@messages.each do |msg|
xml.message(
msg.message,
msg_id: msg.id,
user: msg.user.login,
severity: msg.severity,
created_at: msg.created_at,
deleted_at: msg.deleted_at
)
end
render(partial: 'status_message', collection: @messages, locals: { builder: xml })
end
1 change: 1 addition & 0 deletions src/api/app/views/status_messages/show.xml.builder
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
render(partial: 'status_message', locals: { builder: xml, status_message: @message })
15 changes: 0 additions & 15 deletions src/api/app/views/webui/main/_add_status_message_modal.html.haml

This file was deleted.

5 changes: 2 additions & 3 deletions src/api/app/views/webui/main/_status_messages.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
- if User.admin_session?
- unless flipper_responsive?
.card-footer
= link_to('#', 'data-toggle': 'modal', 'data-target': '#status-message-modal') do
= link_to(new_status_message_path, class: 'nav-link') do
%i.fas.fa-plus-circle.text-primary
Add status message
= render partial: 'add_status_message_modal'
Create Status Message
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
- content_for :actions do
- if User.admin_session?
%li.nav-item
= link_to('#', class: 'nav-link', 'data-toggle': 'modal', 'data-target': '#status-message-modal') do
= link_to(new_status_message_path, class: 'nav-link') do
%i.fas.fa-plus-square.fa-lg.mr-2
Create Status Message
%li.nav-item
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= render partial: 'webui/main/breadcrumb_items'
%li.breadcrumb-item.active{ 'aria-current' => 'page' }
Status Message
5 changes: 5 additions & 0 deletions src/api/app/views/webui/status_messages/acknowledge.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- if flash[:error]
flashContent = '#{escape_javascript(render(layout: false, partial: 'layouts/webui/flash', object: flash))}';
$('#flash').html(flashContent);
- else
$('#status-message-announcement').fadeOut()
27 changes: 27 additions & 0 deletions src/api/app/views/webui/status_messages/new.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
- @pagetitle = 'Create Status Message'
.row
.col
.card
.card-body
.row
.col-12
%h3= @pagetitle
.col-12.col-md-9.col-lg-6
= form_for(StatusMessage.new, url: status_messages_path) do |f|
.form-group
= f.label(:message) do
Message:
%abbr.text-danger{ title: 'required' } *
= f.text_area(:message, rows: 3, required: true, class: 'form-control')
.form-group
= f.label(:severity) do
Severity:
%abbr.text-danger{ title: 'required' } *
%small.form-text With "Announcement", the message will be displayed on the top and all over the web site.
- options = StatusMessage.severities.keys.map { |severity| [severity.humanize, severity] }
= f.select(:severity, options_for_select(options), {}, class: 'custom-select')
.form-group
= f.label(:communication_scope, 'Communication Scope:')
- options = StatusMessage.communication_scopes.keys.map { |scope| [scope.humanize, scope] }
= f.select(:communication_scope, options_for_select(options), {}, class: 'custom-select')
= f.submit 'Create', class: 'btn btn-primary'
Loading

0 comments on commit f86f173

Please sign in to comment.