Revision 42117380
Added by Tomer Brisker about 9 years ago
app/assets/javascripts/dashboard.js | ||
---|---|---|
|
||
$(document).on('ContentLoad', function(){start_gridster()});
|
||
|
||
$(document).on("click",".gridster>ul>li>.close" ,function(){ hide_widget(this);});
|
||
$(document).on("click",".widget_control .minimize" ,function(){ hide_widget(this);});
|
||
$(document).on("click",".widget_control .remove" ,function(){ remove_widget(this);});
|
||
|
||
function start_gridster(){
|
||
if (!$(".gridster>ul>li>.close").exists()) {
|
||
$(".gridster>ul>li").prepend("<a class='close'>×</a>");
|
||
}
|
||
|
||
read_position();
|
||
var gridster = $(".gridster>ul").gridster({
|
||
widget_margins: [10, 10],
|
||
widget_base_dimensions: [82, 340],
|
||
... | ... | |
|
||
}
|
||
|
||
function save_position(){
|
||
var positions = JSON.stringify(serialize_grid());
|
||
localStorage.setItem("grid-data", positions);
|
||
window.location.reload();
|
||
function remove_widget(item){
|
||
var widget = $(item).parents('li.gs_w');
|
||
var gridster = $(".gridster>ul").gridster().data('gridster');
|
||
if (confirm(__("Are you sure you want to delete this widget from your dashboard?"))){
|
||
$.ajax({
|
||
type: 'delete',
|
||
url: $(item).data('url'),
|
||
success: function(){
|
||
$.jnotify(__("Widget removed from dashboard."), 'success', false);
|
||
gridster.remove_widget(widget);
|
||
},
|
||
error: function(){
|
||
$.jnotify(__("Error removing widget from dashboard."), 'error', true);
|
||
},
|
||
});
|
||
}
|
||
}
|
||
|
||
function save_position(path){
|
||
var positions = serialize_grid();
|
||
$.ajax({type: 'POST',
|
||
url: path,
|
||
data: {'widgets': positions},
|
||
success: function(){
|
||
$.jnotify(__("Widget positions successfully saved."), 'success', false);
|
||
},
|
||
error: function(){
|
||
$.jnotify(__("Failed to save widget positions."), 'error', true);
|
||
},
|
||
dataType: 'json'});
|
||
}
|
||
|
||
function serialize_grid(){
|
||
var result = [];
|
||
var result = {};
|
||
$(".gridster>ul>li").each(function(i, widget) {
|
||
result.push({
|
||
name: $(widget).attr('data-name'),
|
||
id: $(widget).attr('data-id'),
|
||
hide: $(widget).attr('data-hide'),
|
||
col: $(widget).attr('data-col'),
|
||
row: $(widget).attr('data-row'),
|
||
size_x: $(widget).attr('data-sizex'),
|
||
size_y: $(widget).attr('data-sizey')
|
||
});
|
||
$widget = $(widget);
|
||
result[$widget.data('id')] = {
|
||
hide: $widget.data('hide'),
|
||
col: $widget.data('col'),
|
||
row: $widget.data('row'),
|
||
sizex: $widget.data('sizex'),
|
||
sizey: $widget.data('sizey')
|
||
};
|
||
});
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
function read_position(){
|
||
if (localStorage.getItem("grid-data") !== null) {
|
||
var positions = JSON.parse(localStorage.getItem("grid-data"));
|
||
|
||
for (var i = 0; i < positions.length; i++) {
|
||
var position = positions[i];
|
||
var widget = $(".gridster>ul>li[data-id="+position.id+"]");
|
||
widget.attr("data-hide", position.hide);
|
||
if(position.hide == 'true') {
|
||
widget.attr("data-row", '');
|
||
widget.attr("data-col", '');
|
||
} else {
|
||
widget.attr("data-row", position.row);
|
||
widget.attr("data-col", position.col);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function reset_position(){
|
||
localStorage.removeItem("grid-data");
|
||
window.location.reload();
|
||
}
|
||
|
||
function fill_restore_list(){
|
||
$("ul>li.widget-restore").remove();
|
||
var restore_list = [];
|
||
... | ... | |
|
||
gridster.register_widget(widget);
|
||
fill_restore_list();
|
||
}
|
||
}
|
app/assets/stylesheets/dashboard.scss | ||
---|---|---|
*/
|
||
|
||
#dashboard{
|
||
#status-table li{
|
||
margin: 10px 0;
|
||
list-style: none;
|
||
h4{
|
||
float:right;
|
||
line-height: 10px;
|
||
.status-table{
|
||
li{
|
||
margin: 10px 0;
|
||
list-style: none;
|
||
h4{
|
||
float:right;
|
||
line-height: 10px;
|
||
}
|
||
}
|
||
.total{
|
||
border-top: 1px solid #ddd;
|
||
text-align: right;
|
||
padding-top: 10px;
|
||
}
|
||
}
|
||
.header{
|
||
border-bottom: 1px solid #ddd;
|
||
padding: 10px 20px;
|
||
}
|
||
.total{
|
||
border-top: 1px solid #ddd;
|
||
text-align: right;
|
||
padding-top: 10px;
|
||
padding: 0px 10px 10px;
|
||
margin-top: 0;
|
||
}
|
||
.stats-well {
|
||
min-height: 363px;
|
||
min-height: 360px;
|
||
}
|
||
.statistics-bar{
|
||
height: 270px;
|
||
width: 90%;
|
||
height: 260px;
|
||
width: 90%;
|
||
margin: 0 10px;
|
||
}
|
||
.statistics-chart{
|
||
height: 240px;
|
||
width: 95%;
|
||
}
|
||
.widget_control a{
|
||
float: right;
|
||
font-size: 21px;
|
||
font-weight: bold;
|
||
line-height: 1;
|
||
color: #000;
|
||
text-shadow: 0 1px 0 #fff;
|
||
opacity: 0.2;
|
||
cursor: pointer;
|
||
margin: 0 3px;
|
||
&:hover{
|
||
text-decoration: none;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
... | ... | |
margin-right: 10px;
|
||
margin-top: 80px;
|
||
float: right;
|
||
}
|
||
}
|
app/controllers/dashboard_controller.rb | ||
---|---|---|
class DashboardController < ApplicationController
|
||
include Foreman::Controller::AutoCompleteSearch
|
||
before_filter :prefetch_data, :only => :index
|
||
before_filter :find_resource, :only => [:destroy]
|
||
|
||
def index
|
||
respond_to do |format|
|
||
... | ... | |
end
|
||
end
|
||
|
||
def create
|
||
Dashboard::Manager.add_widget_to_user(User.current, params[:widget])
|
||
redirect_to root_path
|
||
end
|
||
|
||
def destroy
|
||
if @widget.present? && @widget.user == User.current
|
||
User.current.widgets.destroy(@widget)
|
||
status = :ok
|
||
else
|
||
status = :forbidden
|
||
logger.warn "#{User.current} attempted to remove widget id #{params[:id]} and failed."
|
||
end
|
||
respond_to do |format|
|
||
format.json {render :json => params[:id], :status => status}
|
||
end
|
||
end
|
||
|
||
def reset_default
|
||
Dashboard::Manager.reset_user_to_default(User.current)
|
||
redirect_to root_path
|
||
end
|
||
|
||
def save_positions
|
||
errors = []
|
||
params[:widgets].each do |id, values|
|
||
widget = User.current.widgets.where("id = #{id}").first
|
||
errors << widget.errors unless widget.update_attributes(values)
|
||
end
|
||
respond_to do |format|
|
||
if errors.empty?
|
||
format.json { render :json => {}, :status => :ok }
|
||
else
|
||
format.json { render :json => errors, :status => :bad_request }
|
||
end
|
||
end
|
||
rescue => exception
|
||
process_ajax_error exception, 'save positions'
|
||
end
|
||
|
||
private
|
||
|
||
def prefetch_data
|
||
... | ... | |
@hosts = dashboard.hosts
|
||
@report = dashboard.report
|
||
end
|
||
|
||
def resource_name
|
||
"widget"
|
||
end
|
||
end
|
app/helpers/dashboard_helper.rb | ||
---|---|---|
def dashboard_actions
|
||
[_("Generated at %s") % Time.zone.now.to_s(:short),
|
||
select_action_button(_("Manage dashboard"), {},
|
||
link_to_function(_("Save dashboard"), 'save_position()'),
|
||
link_to_function(_("Reset to default"), 'reset_position()'),
|
||
link_to_function(_("Save dashboard"), "save_position('#{save_positions_widgets_path}')"),
|
||
link_to(_("Reset to default"), reset_default_widgets_path, :method => :put),
|
||
content_tag(:li,'',:class=>'divider'),
|
||
content_tag(:li,_("Restore widgets"), :class=>'nav-header', :id=>'restore_list' )
|
||
)]
|
||
end
|
||
|
||
def widget_list
|
||
Dashboard::Manager.widgets
|
||
def render_widget(widget)
|
||
render(:partial => widget.template, :locals => widget.data)
|
||
end
|
||
|
||
def widget_data(widget)
|
||
{:data=>{:id => widget.id, :name => widget.name, :row => widget.row, :col => widget.col, :sizex=>widget.sizex, :sizey => widget.sizey, :hide=>widget.hide}}
|
||
end
|
||
|
||
def count_reports(hosts)
|
app/helpers/layout_helper.rb | ||
---|---|---|
f.number_field attr, options
|
||
end
|
||
end
|
||
|
||
def last_days(days)
|
||
content_tag(:h6, n_("last %s day", "last %s days", days) % days, :class => 'ca')
|
||
end
|
||
end
|
app/models/user.rb | ||
---|---|---|
has_many :filters, :through => :cached_roles
|
||
has_many :permissions, :through => :filters
|
||
has_many :cached_usergroup_members
|
||
has_many :widgets, :dependent => :destroy
|
||
|
||
has_many :user_mail_notifications, :dependent => :destroy
|
||
has_many :mail_notifications, :through => :user_mail_notifications
|
app/models/widget.rb | ||
---|---|---|
class Widget < ActiveRecord::Base
|
||
belongs_to :user
|
||
|
||
validates :user_id, :name, :template, :presence => true
|
||
validates :sizex, :sizey, :col, :row, :numericality => {:only_integer => true}
|
||
|
||
serialize :data
|
||
|
||
before_validation :default_values
|
||
|
||
def default_values
|
||
self.sizex ||= 4
|
||
self.sizey ||= 1
|
||
self.col ||= 1
|
||
self.row ||= 1
|
||
self.hide ||= false
|
||
self.data ||= {}
|
||
end
|
||
end
|
app/services/dashboard/loader.rb | ||
---|---|---|
# We require these files explicitly as the menu classes can't be reloaded
|
||
# to keep the singletons working.
|
||
require 'menu/node'
|
||
require 'menu/item'
|
||
require 'menu/divider'
|
||
require 'menu/toggle'
|
||
require 'menu/manager'
|
||
|
||
module Dashboard
|
||
class Loader
|
||
module Loader
|
||
# Default widgets that are displayed on the dashboard
|
||
DEFAULT_WIDGETS = [ {:template=>'status_widget', :sizex=>8,:sizey=>1,:name=> N_('Status table')},
|
||
{:template=>'status_chart_widget', :sizex=>4,:sizey=>1,:name=> N_('Status chart')},
|
||
{:template=>'reports_widget', :sizex=>6,:sizey=>1,:name=> N_('Report summary')},
|
||
{:template=>'distribution_widget', :sizex=>6,:sizey=>1,:name=> N_('Distribution chart')}]
|
||
# Widget templates that are allowed on dashboard. Default widgets automatically allow their templates.
|
||
ALLOWED_TEMPLATES = []
|
||
|
||
def self.load
|
||
Manager.map do |dashboard|
|
||
dashboard.widget 'status_widget', :row=>1,:col=>1,:sizex=>8,:sizey=>1,:name=> N_('Status table')
|
||
dashboard.widget 'status_chart_widget', :row=>1,:col=>9,:sizex=>4,:sizey=>1,:name=> N_('Status chart')
|
||
dashboard.widget 'reports_widget', :row=>2,:col=>1,:sizex=>6,:sizey=>1,:name=> N_('Report summary')
|
||
dashboard.widget 'distribution_widget', :row=>2,:col=>7,:sizex=>6,:sizey=>1,:name=> N_('Distribution chart')
|
||
end
|
||
DEFAULT_WIDGETS.each{ |widget| Dashboard::Manager.register_default_widget(widget) }
|
||
Dashboard::Manager.register_allowed_templates(ALLOWED_TEMPLATES)
|
||
end
|
||
end
|
||
end
|
app/services/dashboard/manager.rb | ||
---|---|---|
module Dashboard
|
||
module Manager
|
||
class << self
|
||
def map
|
||
@widgets ||= []
|
||
mapper = Mapper.new(@widgets)
|
||
if block_given?
|
||
yield mapper
|
||
else
|
||
mapper
|
||
end
|
||
end
|
||
@default_widgets = []
|
||
@allowed_templates = Set.new()
|
||
|
||
def widgets
|
||
# force menu reload in development when auto loading modified files
|
||
@widgets ||= Dashboard::Loader.load
|
||
end
|
||
def self.default_widgets
|
||
@default_widgets
|
||
end
|
||
|
||
class Mapper
|
||
attr_reader :widgets
|
||
|
||
def initialize(widgets)
|
||
@widgets = widgets
|
||
end
|
||
|
||
# Adds an widget at the end of the list. Available options:
|
||
# * before, after: specify where the widget should be inserted (eg. :after => :activity)
|
||
def push(obj, options = {})
|
||
# menu widget position
|
||
if options[:first]
|
||
@widgets.unshift(obj)
|
||
elsif (before = options[:before]) && exists?(before)
|
||
@widgets.insert( position_of(before), obj)
|
||
elsif (after = options[:after]) && exists?(after)
|
||
@widgets.insert( position_of(after) + 1, obj)
|
||
else
|
||
@widgets.push(obj)
|
||
end
|
||
end
|
||
|
||
def widget(id, options = {})
|
||
push(Widget.new(id, options), options)
|
||
end
|
||
|
||
# Removes a menu widget
|
||
def delete(name)
|
||
if found = self.find(name)
|
||
@widgets.remove!(found)
|
||
end
|
||
end
|
||
def self.register_default_widget(widget)
|
||
@default_widgets << widget
|
||
@allowed_templates << widget[:template]
|
||
end
|
||
|
||
# Checks if a menu widget exists
|
||
def exists?(name)
|
||
@widgets.any? {|widget| widget.name == name}
|
||
end
|
||
def self.register_allowed_templates(templates)
|
||
@allowed_templates.merge(templates)
|
||
end
|
||
|
||
def find(name)
|
||
@widgets.find {|widget| widget.name == name}
|
||
end
|
||
def self.add_widget_to_user(user, widget)
|
||
raise ::Foreman::Exception.new(N_("Unallowed template for dashboard widget: %s"), widget[:template]) unless @allowed_templates.include?(widget[:template])
|
||
user.widgets.create!(widget)
|
||
end
|
||
|
||
def position_of(name)
|
||
@widgets.each do |widget|
|
||
if widget.name == name
|
||
return widget.position
|
||
end
|
||
end
|
||
end
|
||
def self.reset_user_to_default(user)
|
||
user.widgets.clear
|
||
@default_widgets.each {|widget|
|
||
add_widget_to_user(user, widget)
|
||
}
|
||
end
|
||
end
|
||
end
|
app/services/dashboard/widget.rb | ||
---|---|---|
module Dashboard
|
||
class Widget
|
||
attr_reader :id, :name, :col, :row, :sizex, :sizey, :hide
|
||
|
||
def initialize(id, options)
|
||
@id = id
|
||
@name = options[:name] || id
|
||
@col = options[:col] || 1
|
||
@row = options[:row] || 1
|
||
@sizex = options[:sizex] || 4
|
||
@sizey = options[:sizey] || 1
|
||
@hide = options[:hide] || false
|
||
end
|
||
end
|
||
end
|
app/services/foreman/access_permissions.rb | ||
---|---|---|
end
|
||
|
||
permission_set.security_block :dashboard do |map|
|
||
map.permission :access_dashboard, {:dashboard => [:index],
|
||
map.permission :access_dashboard, {:dashboard => [:index, :save_positions, :reset_default, :create, :destroy],
|
||
:"api/v1/dashboard" => [:index],
|
||
:"api/v2/dashboard" => [:index]
|
||
}
|
app/services/foreman/plugin.rb | ||
---|---|---|
ComputeResource.register_provider provider
|
||
end
|
||
|
||
def widget(id, options)
|
||
Dashboard::Manager.map.widget(id, options)
|
||
def widget(template, options)
|
||
Dashboard::Manager.register_default_widget({:template=>template}.merge!(options))
|
||
end
|
||
|
||
# To add FiltersHelper#search_path override,
|
app/views/dashboard/_distribution_widget.html.erb | ||
---|---|---|
<h4 style="text-align: center;"><%= n_("Run distribution in the last %s minute", "Run distribution in the last %s minutes", Setting[:puppet_interval]) % Setting[:puppet_interval] %></h4>
|
||
<h4 class="header">
|
||
<%= n_("Run distribution in the last %s minute", "Run distribution in the last %s minutes", Setting[:puppet_interval]) % Setting[:puppet_interval] %>
|
||
</h4>
|
||
<%= render_run_distribution(@hosts, :class => 'statistics-bar') %>
|
app/views/dashboard/_reports_widget.html.erb | ||
---|---|---|
<h4 style="text-align: center;"><%= _("Latest Events") %></h4>
|
||
<h4 class="header">
|
||
<%= _("Latest Events") %>
|
||
</h4>
|
||
<% events = latest_events %>
|
||
<% if events.empty? %>
|
||
<p class="ca"><%= _("No interesting reports received in the last week") %></p>
|
app/views/dashboard/_status_chart_widget.html.erb | ||
---|---|---|
<div id='status-chart'>
|
||
<h4 class="header ca"><%= _('Host Configuration Chart') %></h4>
|
||
<%= render_overview(@report, :class => 'statistics-pie small') %>
|
||
</div>
|
||
<h4 class="header">
|
||
<%= _('Host Configuration Chart') %>
|
||
</h4>
|
||
<%= render_overview(@report, :class => 'statistics-pie small') %>
|
app/views/dashboard/_status_widget.html.erb | ||
---|---|---|
<div id='status-table'>
|
||
<h4 class="header"><%= _('Host Configuration Status') %></h4>
|
||
<ul>
|
||
<%= searchable_links _('Hosts that had performed modifications without error'),
|
||
"last_report > \"#{Setting[:puppet_interval] + 5} minutes ago\" and (status.applied > 0 or status.restarted > 0) and (status.failed = 0)",
|
||
:active_hosts_ok_enabled
|
||
%>
|
||
<h4 class="header">
|
||
<%= _('Host Configuration Status') %>
|
||
</h4>
|
||
<ul>
|
||
<%= searchable_links _('Hosts that had performed modifications without error'),
|
||
"last_report > \"#{Setting[:puppet_interval] + 5} minutes ago\" and (status.applied > 0 or status.restarted > 0) and (status.failed = 0)",
|
||
:active_hosts_ok_enabled
|
||
%>
|
||
|
||
<%= searchable_links _('Hosts in error state'),
|
||
"last_report > \"#{Setting[:puppet_interval] + 5} minutes ago\" and (status.failed > 0 or status.failed_restarts > 0) and status.enabled = true",
|
||
:bad_hosts_enabled
|
||
%>
|
||
<%= searchable_links _('Hosts in error state'),
|
||
"last_report > \"#{Setting[:puppet_interval] + 5} minutes ago\" and (status.failed > 0 or status.failed_restarts > 0) and status.enabled = true",
|
||
:bad_hosts_enabled
|
||
%>
|
||
|
||
<%=searchable_links _("Good host reports in the last %s") % time_ago_in_words((Setting[:puppet_interval]+5).minutes.ago),
|
||
"last_report > \"#{Setting[:puppet_interval]+5} minutes ago\" and status.enabled = true and status.applied = 0 and status.failed = 0 and status.pending = 0",
|
||
:ok_hosts_enabled
|
||
%>
|
||
<%=searchable_links _("Good host reports in the last %s") % time_ago_in_words((Setting[:puppet_interval]+5).minutes.ago),
|
||
"last_report > \"#{Setting[:puppet_interval]+5} minutes ago\" and status.enabled = true and status.applied = 0 and status.failed = 0 and status.pending = 0",
|
||
:ok_hosts_enabled
|
||
%>
|
||
|
||
<%= searchable_links _('Hosts that had pending changes'),
|
||
'status.pending > 0 and status.enabled = true',
|
||
:pending_hosts_enabled
|
||
%>
|
||
<%= searchable_links _('Hosts that had pending changes'),
|
||
'status.pending > 0 and status.enabled = true',
|
||
:pending_hosts_enabled
|
||
%>
|
||
|
||
<%= searchable_links _('Out of sync hosts'),
|
||
"last_report < \"#{Setting[:puppet_interval] + 5} minutes ago\" and status.enabled = true",
|
||
:out_of_sync_hosts_enabled
|
||
%>
|
||
<%= searchable_links _('Out of sync hosts'),
|
||
"last_report < \"#{Setting[:puppet_interval] + 5} minutes ago\" and status.enabled = true",
|
||
:out_of_sync_hosts_enabled
|
||
%>
|
||
|
||
<%= searchable_links _('Hosts with no reports'),
|
||
"not has last_report and status.enabled = true",
|
||
:reports_missing
|
||
%>
|
||
<%= searchable_links _('Hosts with no reports'),
|
||
"not has last_report and status.enabled = true",
|
||
:reports_missing
|
||
%>
|
||
|
||
<%= searchable_links _('Hosts with alerts disabled'),
|
||
"status.enabled = false",
|
||
:disabled_hosts
|
||
%>
|
||
<%= searchable_links _('Hosts with alerts disabled'),
|
||
"status.enabled = false",
|
||
:disabled_hosts
|
||
%>
|
||
|
||
<h4 class="total"><%= _("Total Hosts: %s") % @report[:total_hosts] %></h4>
|
||
</ul>
|
||
</div>
|
||
<h4 class="total"><%= _("Total Hosts: %s") % @report[:total_hosts] %></h4>
|
||
</ul>
|
app/views/dashboard/index.html.erb | ||
---|---|---|
<%= javascript 'dashboard' %>
|
||
<% title _('Overview') %>
|
||
<%= title_actions dashboard_actions %>
|
||
|
||
<div class="row">
|
||
<div id='dashboard' class="gridster col-md-12">
|
||
<ul>
|
||
<%= widget_list.map do |widget|
|
||
content_tag(:li, render(widget.id), :data=>{:id => widget.id, :name => widget.name || widget.id, :row => widget.row, :col => widget.col, :sizex=>widget.sizex, :sizey => widget.sizey, :hide=>widget.hide})
|
||
end.join(' ').html_safe
|
||
%>
|
||
</ul>
|
||
</div>
|
||
<% if User.current.widgets.empty? %>
|
||
<div class="col-md-12 ca">
|
||
<%= link_to(_("Get default dashboard widgets"), reset_default_widgets_path, :method => :put, :class=>'btn btn-default') %>
|
||
</div>
|
||
<% else %>
|
||
<div id='dashboard' class="gridster col-md-12">
|
||
<ul>
|
||
<% User.current.widgets.each do |widget| %>
|
||
<%= content_tag(:li, widget_data(widget)) do %>
|
||
<div class='widget_control'>
|
||
<a class='remove' data-url='<%= widget_path(widget) %>'>×</a>
|
||
<a class='minimize'>−</a>
|
||
</div>
|
||
<div class="widget <%= widget.name.parameterize %>">
|
||
<%= render_widget(widget) %>
|
||
</div>
|
||
<% end %>
|
||
<% end %>
|
||
</ul>
|
||
</div>
|
||
<% end %>
|
||
</div>
|
config/routes.rb | ||
---|---|---|
|
||
end
|
||
|
||
resources :widgets, :controller => 'dashboard', :only => [:create, :destroy] do
|
||
collection do
|
||
post 'save_positions', :to => 'dashboard#save_positions'
|
||
put 'reset_default', :to => 'dashboard#reset_default'
|
||
end
|
||
end
|
||
|
||
root :to => 'dashboard#index'
|
||
match 'dashboard', :to => 'dashboard#index', :as => "dashboard"
|
||
match 'dashboard/auto_complete_search', :to => 'hosts#auto_complete_search', :as => "auto_complete_search_dashboards"
|
db/migrate/20140928140206_create_widgets.rb | ||
---|---|---|
class CreateWidgets < ActiveRecord::Migration
|
||
def change
|
||
create_table :widgets do |t|
|
||
t.references :user, :index => true
|
||
|
||
t.string :template, :null => false
|
||
t.string :name, :null => false
|
||
t.text :data
|
||
t.integer :sizex, :default => 4
|
||
t.integer :sizey, :default => 1
|
||
t.integer :col, :default => 1
|
||
t.integer :row, :default => 1
|
||
t.boolean :hide, :default => false
|
||
|
||
t.timestamps
|
||
end
|
||
end
|
||
end
|
db/migrate/20150225124617_add_default_widgets.rb | ||
---|---|---|
class AddDefaultWidgets < ActiveRecord::Migration
|
||
def up
|
||
User.all.each do |user|
|
||
Dashboard::Manager.reset_user_to_default(user)
|
||
end
|
||
end
|
||
|
||
def down
|
||
end
|
||
end
|
test/integration/dashboard_test.rb | ||
---|---|---|
class DashboardTest < ActionDispatch::IntegrationTest
|
||
def setup
|
||
FactoryGirl.create(:host)
|
||
Dashboard::Manager.reset_user_to_default(users(:admin))
|
||
end
|
||
|
||
def assert_dashboard_link(text)
|
test/unit/widget_test.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class WidgetTest < ActiveSupport::TestCase
|
||
setup do
|
||
@user = FactoryGirl.create(:user)
|
||
end
|
||
|
||
test 'new user should have no widgets' do
|
||
assert_blank(@user.widgets)
|
||
end
|
||
|
||
test 'reset to default should add default widgets to user' do
|
||
assert_difference('@user.widgets.count', Dashboard::Manager.default_widgets.count) do
|
||
Dashboard::Manager.reset_user_to_default(@user)
|
||
end
|
||
end
|
||
|
||
test 'adding widget to user should fill in default values for missing fields' do
|
||
widget_hash = { :template => Dashboard::Manager.default_widgets[0][:template],
|
||
:name => Dashboard::Manager.default_widgets[0][:name] }
|
||
assert Dashboard::Manager.add_widget_to_user(@user, widget_hash)
|
||
assert_equal @user.widgets.count, 1
|
||
widget = @user.widgets.first
|
||
assert_equal widget.sizex, 4
|
||
assert_equal widget.sizey, 1
|
||
assert_equal widget.col, 1
|
||
assert_equal widget.row, 1
|
||
refute widget.hide
|
||
assert_blank widget.data
|
||
assert_equal widget.user_id, @user.id
|
||
end
|
||
|
||
test 'adding widget with unallowed template raises exception' do
|
||
widget_hash = { :template => 'malicious_template',
|
||
:name => 'malicious template name'}
|
||
assert_raises ::Foreman::Exception do
|
||
Dashboard::Manager.add_widget_to_user(@user, widget_hash)
|
||
end
|
||
end
|
||
end
|
Also available in: Unified diff
Fixes #8106 - Dashboard rewrite to allow better customization