Skip to content

Commit

Permalink
Fixes #16043 - add select all hosts option
Browse files Browse the repository at this point in the history
  • Loading branch information
amirfefer committed Apr 3, 2017
1 parent fb5e3f2 commit 2bd1882
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 17 deletions.
63 changes: 57 additions & 6 deletions app/assets/javascripts/host_checkbox.js
Expand Up @@ -4,11 +4,17 @@ $.foremanSelectedHosts = readFromCookie();

// triggered by a host checkbox change
function hostChecked(box) {
var multiple_alert = $("#multiple-alert");
var cid = parseInt(box.id.replace("host_ids_", ""));
if (box.checked)
addHostId(cid);
else
else{
rmHostId(cid);
if (multiple_alert.length){
multiple_alert.hide('slow');
multiple_alert.data('multiple', false)
}
}
$.cookie($.cookieName, JSON.stringify($.foremanSelectedHosts), { secure: location.protocol === 'https:' });
toggle_actions();
update_counter();
Expand Down Expand Up @@ -89,14 +95,41 @@ function cleanHostsSelection() {
return false;
}

function multiple_selection() {
var total = $("#pagination").data("count");
var alert_text = Jed.sprintf(__("All <b> %d </b> hosts are selected. "), total);
var undo_text = __("Undo selection");
var multiple_alert = $("#multiple-alert");
multiple_alert.find(".text").html(alert_text + '<a href="#" onclick="undo_multiple_selection();">' + undo_text + '</a>');
multiple_alert.data('multiple', true)
}

function undo_multiple_selection() {
var pagination = pagination_metadata();
var alert_text = Jed.sprintf(__("All %s hosts on this page are selected. "), pagination.per_page);
var select_text = Jed.sprintf(__("Select all<b> %s </b> hosts"), pagination.total);
var multiple_alert = $("#multiple-alert");
multiple_alert.find(".text").html( alert_text + '<a href="#" onclick="multiple_selection();">' + select_text + '</a>');
multiple_alert.data('multiple', false)
}

function toggleCheck() {
var pagination = pagination_metadata();
var multiple_alert = $("#multiple-alert");
var checked = $("#check_all").is(':checked');
$('.host_select_boxes').each(function(index, box) {
box.checked = checked;
hostChecked(box);
});
if(!checked)
if(checked && (pagination.per_page - pagination.total < 0) ) {
multiple_alert.show('slow');
multiple_alert.data('multiple', true);
}
else if (!checked) {
multiple_alert.hide('slow');
multiple_alert.data('multiple', false);
cleanHostsSelection();
}
return false;
}

Expand All @@ -118,20 +151,33 @@ $(function() {
function submit_modal_form() {
if (!$('#keep_selected').is(':checked'))
removeForemanHostsCookie();
if ($("#multiple-alert").data('multiple')){
var query = $("<input>")
.attr("type", "hidden")
.attr("name", "search").val($("#search").val());
$("#confirmation-modal form").append(query);
}
$("#confirmation-modal form").submit();
$('#confirmation-modal').modal('hide');
}

function build_modal(element, url) {
var is_multiple = $("#multiple-alert");
if (is_multiple.data('multiple'))
var data = {search: $("#search").val()};
else
var data = {host_ids: $.foremanSelectedHosts};
var title = $(element).attr('data-dialog-title');
$('#confirmation-modal .modal-header h4').text(title);
$('#confirmation-modal .modal-body').empty()
.append("<div class='modal-spinner spinner spinner-lg'></div>");
$('#confirmation-modal').modal();
$("#confirmation-modal .modal-body").load(url + " #content",{host_ids: $.foremanSelectedHosts},
$("#confirmation-modal .modal-body").load(url + " #content", data,
function(response, status, xhr) {
$("#loading").hide();
$('#submit_multiple').val('');
if (is_multiple.data('multiple'))
$("#multiple-modal-alert").show();
var b = $("#confirmation-modal .btn-primary");
if ($(response).find('#content form select').length > 0)
b.addClass("disabled").attr("disabled", true);
Expand All @@ -146,12 +192,17 @@ function build_redirect(url) {
window.location.replace(url);
}

function pagination_metadata() {
var pagination = $("#pagination");
var total = pagination.data("count");
var per_page = pagination.data("per-page");
return { total: total, per_page: per_page }
}

function update_counter() {
var item = $("#check_all");
if ($.foremanSelectedHosts) {
if ($.foremanSelectedHosts)
$(".select_count").text($.foremanSelectedHosts.length);
item.attr("checked", $.foremanSelectedHosts.length > 0 );
}
var title = "";
if (item.attr("checked"))
title = $.foremanSelectedHosts.length + " - " + item.attr("uncheck-title");
Expand Down
12 changes: 9 additions & 3 deletions app/controllers/hosts_controller.rb
Expand Up @@ -41,6 +41,7 @@ class HostsController < ApplicationController
before_action :validate_power_action, :only => :update_multiple_power_state

helper :hosts, :reports, :interfaces
helper_method :multiple_with_filter?

def index(title = nil)
begin
Expand Down Expand Up @@ -751,6 +752,10 @@ def find_resource
@host
end

def multiple_with_filter?
params.key?(:search)
end

def load_vars_for_ajax
return unless @host

Expand All @@ -764,10 +769,11 @@ def load_vars_for_ajax

def find_multiple
# Lets search by name or id and make sure one of them exists first
if params[:host_names].present? || params[:host_ids].present?
@hosts = resource_base.where("hosts.id IN (?) or hosts.name IN (?)", params[:host_ids], params[:host_names])
if params.key?(:host_names) || params.key?(:host_ids) || multiple_with_filter?
@hosts = resource_base.search_for(params[:search]) if multiple_with_filter?
@hosts ||= resource_base.where("hosts.id IN (?) or hosts.name IN (?)", params[:host_ids], params[:host_names])
if @hosts.empty?
error _('No hosts were found with that id or name')
error _('No hosts were found with that id, name or query filter')
redirect_to(hosts_path)
return false
end
Expand Down
7 changes: 7 additions & 0 deletions app/helpers/hosts_and_hostgroups_helper.rb
Expand Up @@ -42,4 +42,11 @@ def realm_field(f, can_override = false, override = false)
{ :help_inline => :indicator }
).html_safe
end

def multiple_filter(hosts)
return unless multiple_with_filter?
no_filter = _("Reminder: <strong> All #{hosts.size} hosts are selected </strong>").html_safe
with_filter = _("Reminder: <strong> All #{hosts.size} hosts are selected </strong> for query filter #{h(params[:search])}").html_safe
params[:search].blank? ? no_filter : with_filter
end
end
7 changes: 6 additions & 1 deletion app/helpers/layout_helper.rb
Expand Up @@ -81,7 +81,8 @@ def page_entries_info(collection, options = {})
end

def will_paginate_with_info(collection = nil, options = {})
content_tag(:div, :id => "pagination", :class => "row") do
content_tag(:div, :id => "pagination", :class => "row",
:data => ({'count' => collection.total_entries, 'per-page' => per_page(collection)})) do
page_entries_info(collection, options) +
will_paginate(collection, options)
end
Expand Down Expand Up @@ -188,6 +189,10 @@ def render_if_partial_exists(path, f)
render path, :f => f if lookup_context.exists?(path, [], true)
end

def per_page(collection)
[Setting[:entries_per_page], collection.total_entries].min
end

private

def table_css_classes(classes = '')
Expand Down
16 changes: 10 additions & 6 deletions app/views/hosts/_selected_hosts.html.erb
@@ -1,6 +1,9 @@
<div class="row">
<table class="<%= table_css_classes %>">
<thead>
<%= alert(:class => 'alert-info hide', :id => 'multiple-modal-alert', :close => true, :header => '',
:text => multiple_filter(hosts)) %>
<% unless multiple_with_filter? %>
<table class="<%= table_css_classes %>">
<thead>
<tr>
<th>
<%= _("Name") %>
Expand All @@ -14,8 +17,8 @@
<%= raw("<th>" + _("Location") + "</th>") if SETTINGS[:locations_enabled]%>
<%= raw("<th>" + _("Organization") + "</th>") if SETTINGS[:organizations_enabled]%>
</tr>
</thead>
<tbody>
</thead>
<tbody>
<% associations = [:hostgroup, :environment] %>
<% associations << :organization if SETTINGS[:organizations_enabled] %>
<% associations << :location if SETTINGS[:locations_enabled] %>
Expand All @@ -33,8 +36,9 @@
<% end %>
</tr>
<% end %>
</tbody>
</table>
</tbody>
</table>
<%= check_box_tag "keep_selected", "", false, :title => _("Keep selected hosts for a future action") %>
<%= _('Keep selected hosts for a future action') %><br/><br/>
<% end %>
</div>
3 changes: 3 additions & 0 deletions app/views/hosts/index.html.erb
@@ -1,2 +1,5 @@
<% title_actions multiple_actions_select, csv_link, button_group(new_link(_("Create Host"))) %>
<%= alert(:class => 'alert-info hide', :id => 'multiple-alert', :close => false, :header => '',
:text => _("All #{per_page(@hosts)} hosts on this page are selected. ").html_safe +
link_to_function((_("Select all <b> #{@hosts.total_entries.to_s} </b> hosts")).html_safe, "multiple_selection()")) %>
<%= render 'list', :hosts => @hosts, :header => @title || _("Hosts") %>
46 changes: 45 additions & 1 deletion test/controllers/hosts_controller_test.rb
Expand Up @@ -374,7 +374,7 @@ def setup_user_and_host(operation, filter = nil, &block)
post :update_multiple_hostgroup, {:host_ids => [-1], :host_names => ["no.such.host"]}, set_session_user

assert_redirected_to hosts_url
assert_equal "No hosts were found with that id or name", flash[:error]
assert_equal "No hosts were found with that id, name or query filter", flash[:error]
end

test 'multiple hostgroup change by host ids' do
Expand Down Expand Up @@ -500,6 +500,50 @@ def setup_multiple_compute_resource
set_session_user.merge(:user => users(:admin).id)
end

test "find multiple hosts by filter query" do
setup_user_and_host "edit"
post :update_multiple_owner, { :search => "",
:owner => { :id => users(:one).id_and_type}},
set_session_user.merge(:user => users(:admin).id)
as_admin do
assert_equal users(:one).id_and_type, @host1.reload.is_owned_by
assert_equal users(:one).id_and_type, @host2.reload.is_owned_by
end
end

test "use filter query which generate a collection" do
setup_user_and_host "edit"
post :update_multiple_owner, { :search => "owner = #{users(:admin).login}",
:owner => { :id => users(:one).id_and_type}},
set_session_user.merge(:user => users(:admin).id)
as_admin do
assert_equal users(:one).id_and_type, @host1.reload.is_owned_by
assert_equal users(:one).id_and_type, @host2.reload.is_owned_by
end
end

test "use a filter query which generates empty collection" do
setup_user_and_host "edit"
post :update_multiple_owner, { :search => "owner = #{users(:one).login}",
:owner => { :id => users(:one).id_and_type}},
set_session_user.merge(:user => users(:admin).id)
as_admin do
assert_equal users(:admin).id_and_type, @host1.reload.is_owned_by
assert_equal users(:admin).id_and_type, @host2.reload.is_owned_by
end
end

test "use empty filter query when it exists in params" do
setup_user_and_host "edit"
post :update_multiple_owner, {:host_ids => [@host1.id], :search => "",
:owner => { :id => users(:one).id_and_type}},
set_session_user.merge(:user => users(:admin).id)
as_admin do
assert_equal users(:one).id_and_type, @host1.reload.is_owned_by
assert_equal users(:one).id_and_type, @host2.reload.is_owned_by
end
end

describe "setting puppet proxy on multiple hosts" do
before do
setup_user_and_host "edit"
Expand Down

0 comments on commit 2bd1882

Please sign in to comment.