[Ovirt-devel] [PATCH] refactored popups and grids to pull more into the layouts,

Hugh O. Brock hbrock at redhat.com
Thu May 29 21:05:42 UTC 2008


On Thu, May 29, 2008 at 03:58:56PM -0400, Scott Seago wrote:
> Scott Seago wrote:
>>
>> ------------------------------------------------------------------------
>>
>> _______________________________________________
>> Ovirt-devel mailing list
>> Ovirt-devel at redhat.com
>> https://www.redhat.com/mailman/listinfo/ovirt-devel
>

> >From 2351f8a85747668407463440c8c05c641788564b Mon Sep 17 00:00:00 2001
> From: Scott Seago <sseago at redhat.com>
> Date: Thu, 29 May 2008 15:54:19 -0400
> Subject: [PATCH] refactored popups and grids to pull more into the layouts, and static js functions into ovirt.js.
> 
> Also, the children and all_children methods on Pool now accept find parameters.
> 
> Signed-off-by: Scott Seago <sseago at redhat.com>
> ---
>  wui/src/app/controllers/application.rb             |   24 +-
>  wui/src/app/controllers/hardware_controller.rb     |   46 ++-
>  wui/src/app/controllers/host_controller.rb         |    3 -
>  wui/src/app/controllers/storage_controller.rb      |    2 +-
>  wui/src/app/models/pool.rb                         |   11 +-
>  wui/src/app/views/dashboard/index.html.erb         |   50 --
>  wui/src/app/views/hardware/move.rhtml              |   13 +-
>  wui/src/app/views/hardware/new.html.erb            |   38 +-
>  wui/src/app/views/hardware/show_hosts.rhtml        |    1 +
>  wui/src/app/views/hardware/show_storage.rhtml      |    1 +
>  wui/src/app/views/host/_grid.rhtml                 |   21 +-
>  wui/src/app/views/host/addhost.html.erb            |   71 +--
>  wui/src/app/views/layouts/popup.rhtml              |    7 +-
>  wui/src/app/views/layouts/redux.rhtml              |   64 +--
>  wui/src/app/views/permission/new.rhtml             |   26 +-
>  wui/src/app/views/resources/new.rhtml              |   27 +-
>  wui/src/app/views/resources/show.rhtml             |   78 ---
>  wui/src/app/views/storage/_grid.rhtml              |   11 +-
>  wui/src/app/views/storage/addstorage.html.erb      |   48 +-
>  wui/src/app/views/storage/new.rhtml                |   42 +-
>  wui/src/app/views/vm/new.rhtml                     |   28 +-
>  wui/src/public/javascripts/ovirt.js                |  138 +++++
>  .../betternestedset/lib/#better_nested_set.rb#     |  583 --------------------
>  .../betternestedset/lib/better_nested_set.rb       |   33 +-
>  24 files changed, 350 insertions(+), 1016 deletions(-)
>  create mode 100644 wui/src/public/javascripts/ovirt.js
>  delete mode 100644 wui/src/vendor/plugins/betternestedset/lib/#better_nested_set.rb#
> 
> diff --git a/wui/src/app/controllers/application.rb b/wui/src/app/controllers/application.rb
> index 5f20be3..3ca5125 100644
> --- a/wui/src/app/controllers/application.rb
> +++ b/wui/src/app/controllers/application.rb
> @@ -84,18 +84,30 @@ class ApplicationController < ActionController::Base
>        end
>      end
>    end
> -  def json_list(full_items, attributes)
> +
> +  # don't define find_opts for array inputs
> +  def json_list(full_items, attributes, arg_list=[], find_opts={})
>      page = params[:page].to_i
> -    item_list = full_items.paginate(:page => page, 
> -                                    :order => "#{params[:sortname]} #{params[:sortorder]}", 
> -                                    :per_page => params[:rp])
> +    paginate_opts = {:page => page, 
> +                     :order => "#{params[:sortname]} #{params[:sortorder]}", 
> +                     :per_page => params[:rp]}
> +    arg_list << find_opts.merge(paginate_opts)
> +    item_list = full_items.paginate(*arg_list)
>      json_hash = {}
>      json_hash[:page] = page
> -    json_hash[:total] = full_items.size
> +    json_hash[:total] = item_list.total_entries
>      json_hash[:rows] = item_list.collect do |item|
>        item_hash = {}
>        item_hash[:id] = item.id
> -      item_hash[:cell] = attributes.collect {|attr| item.send(attr)}
> +      item_hash[:cell] = attributes.collect do |attr| 
> +        if attr.is_a? Array
> +          value = item
> +          attr.each { |attr_item| value = value.send(attr_item)}
> +          value
> +        else
> +          item.send(attr)
> +        end
> +      end
>        item_hash
>      end
>      render :json => json_hash.to_json
> diff --git a/wui/src/app/controllers/hardware_controller.rb b/wui/src/app/controllers/hardware_controller.rb
> index ad857f1..0f0c904 100644
> --- a/wui/src/app/controllers/hardware_controller.rb
> +++ b/wui/src/app/controllers/hardware_controller.rb
> @@ -229,17 +229,27 @@ class HardwareController < ApplicationController
>      if params[:id]
>        pre_json
>        hosts = @pool.hosts
> +      find_opts = {}
> +      include_pool = false
>      else
> -      # FIXME: no permissions checks here yet, no filtering of current pool yet
> -      hosts = Host.find(:all)
> +      # FIXME: no permissions or usage checks here yet
> +      # filtering on which pool to exclude
> +      id = params[:exclude_id]
> +      hosts = Host
> +      find_opts = {:include => :hardware_pool, 
> +        :conditions => ["pools.id != ?", id]}
> +      include_pool = true
>      end
> -    json_list(hosts, 
> -              [:id, :hostname, :uuid, :hypervisor_type, :num_cpus, :cpu_speed, :arch, :memory_in_mb, :is_disabled_str])
> +    attr_list = [:id, :hostname, :uuid, :hypervisor_type, :num_cpus, :cpu_speed, :arch, :memory_in_mb, :is_disabled_str]
> +    attr_list.insert(2, [:hardware_pool, :name]) if include_pool
> +    json_list(hosts, attr_list, [:all], find_opts)
>    end
>  
>    def vm_pools_json
> -    json_list(@pool.sub_vm_resource_pools, 
> -              [:id, :name])
> +    json_list(Pool, 
> +              [:id, :name], 
> +              [@pool, :children],
> +              {:finder => 'call_finder', :conditions => ["type = 'VmResourcePool'"]})
>    end
>  
>    def users_json
> @@ -251,12 +261,20 @@ class HardwareController < ApplicationController
>      if params[:id]
>        pre_json
>        storage_pools = @pool.storage_pools
> +      find_opts = {}
> +      include_pool = false
>      else
> -      # FIXME: no permissions checks here yet, no filtering of current pool yet
> -      storage_pools = StoragePool.find(:all)
> +      # FIXME: no permissions or usage checks here yet
> +      # filtering on which pool to exclude
> +      id = params[:exclude_id]
> +      storage_pools = StoragePool
> +      find_opts = {:include => :hardware_pool, 
> +        :conditions => ["pools.id != ?", id]}
> +      include_pool = true
>      end
> -    json_list(storage_pools, 
> -              [:id, :display_name, :ip_addr, :get_type_label])
> +    attr_list = [:id, :display_name, :ip_addr, :get_type_label]
> +    attr_list.insert(2, [:hardware_pool, :name]) if include_pool
> +    json_list(storage_pools, attr_list, [:all], find_opts)
>    end
>  
>    def storage_volumes_json
> @@ -283,8 +301,10 @@ class HardwareController < ApplicationController
>      resource_ids = resource_ids_str.split(",").collect {|x| x.to_i} if resource_ids_str
>      begin
>        @pool.create_with_resources(@parent, resource_type, resource_ids)
> -      render :json => { :object => "pool", :success => true, 
> +      reply = { :object => "pool", :success => true, 
>                          :alert => "Hardware Pool was successfully created." }
> +      reply[:resource_type] = resource_type if resource_type
> +      render :json => reply
>      rescue
>        render :json => { :object => "pool", :success => false, 
>                          :errors => @pool.errors.localize_error_messages.to_a  }
> @@ -308,7 +328,7 @@ class HardwareController < ApplicationController
>    # in addition to the current pool (which is checked). We also need to fail
>    # for hosts that aren't currently empty
>    def add_hosts
> -    host_ids_str = params[:host_ids]
> +    host_ids_str = params[:resource_ids]
>      host_ids = host_ids_str.split(",").collect {|x| x.to_i}
>  
>      begin
> @@ -347,7 +367,7 @@ class HardwareController < ApplicationController
>    # in addition to the current pool (which is checked). We also need to fail
>    # for storage that aren't currently empty
>    def add_storage
> -    storage_pool_ids_str = params[:storage_pool_ids]
> +    storage_pool_ids_str = params[:resource_ids]
>      storage_pool_ids = storage_pool_ids_str.split(",").collect {|x| x.to_i}
>      
>      begin
> diff --git a/wui/src/app/controllers/host_controller.rb b/wui/src/app/controllers/host_controller.rb
> index f89997f..623cc0c 100644
> --- a/wui/src/app/controllers/host_controller.rb
> +++ b/wui/src/app/controllers/host_controller.rb
> @@ -58,9 +58,6 @@ class HostController < ApplicationController
>  
>    def addhost
>      @hardware_pool = Pool.find(params[:hardware_pool_id])
> -    @unassigned = Pool.root.hosts.size
> -    # FIXME: @assigned should match  the updated assigned hosts query when that's done
> -    @assigned = Host.find(:all).size
>      render :layout => 'popup'    
>    end
>  
> diff --git a/wui/src/app/controllers/storage_controller.rb b/wui/src/app/controllers/storage_controller.rb
> index 1c5b392..c8831e6 100644
> --- a/wui/src/app/controllers/storage_controller.rb
> +++ b/wui/src/app/controllers/storage_controller.rb
> @@ -88,7 +88,7 @@ class StorageController < ApplicationController
>  
>    def new2
>      @storage_pools = @storage_pool.hardware_pool.storage_volumes
> -    render :layout => 'popup'    
> +    render :layout => false
>    end
>  
>    def insert_refresh_task
> diff --git a/wui/src/app/models/pool.rb b/wui/src/app/models/pool.rb
> index 490c5e6..5390beb 100644
> --- a/wui/src/app/models/pool.rb
> +++ b/wui/src/app/models/pool.rb
> @@ -69,10 +69,10 @@ class Pool < ActiveRecord::Base
>    end
>  
>    def sub_hardware_pools
> -    Pool.select_hardware_pools(children)
> +    children({:conditions => "type='HardwarePool'"})
>    end
>    def sub_vm_resource_pools
> -    Pool.select_vm_pools(children)
> +    children({:conditions => "type='VmResourcePool'"})
>    end
>    def self_and_like_siblings
>      self_and_siblings.select {|pool| pool[:type] == self.class.name}
> @@ -156,7 +156,12 @@ class Pool < ActiveRecord::Base
>        hash
>      end
>    end
> -    
> +
> +  def self.call_finder(*args)
> +    obj = args.shift
> +    method = args.shift
> +    obj.send(method, *args)
> +  end    
>  
>    protected
>    def traverse_parents
> diff --git a/wui/src/app/views/dashboard/index.html.erb b/wui/src/app/views/dashboard/index.html.erb
> index 8715261..92cb4da 100644
> --- a/wui/src/app/views/dashboard/index.html.erb
> +++ b/wui/src/app/views/dashboard/index.html.erb
> @@ -1,57 +1,7 @@
> -    <div id="data">
> -      <div class="inside">
> -
> -         <div id="dataTableWrapper">
> -
> -          <%= render :partial => "/layouts/quick_stats", :locals => { :hpool => @default_pool, :vms => @vms } %>
> -
> -          <div class="dataTable">
> -
> -              <%= render :partial => "/layouts/hardware_pool_type_list", :locals => { :hardware_pools => @hardware_pools } %>
> -
> -            </div> <!-- end #dataTable -->
> -
> -            <div class="data-section">
> -              <div class="data-section-header"><strong>Available Hosts (<%= @available_hosts.size %>)</strong></div>
> -              <div class="data-section-stats">Statistics Data</div>
> -              <div class="data-section-table">
> -                <div class="inside">
> -                  <%= render :partial => "/host/list", :locals => { :hosts => @available_hosts } %>
> -                </div>
> -              </div>
> -            </div>
> -
> -            <div class="data-section">
> -              <div class="data-section-header"><strong>Available Storage (<%= @available_storage_volumes.size %>)</strong></div>
> -              <div class="data-section-header"><strong>Storage Pools (<%= @storage_pools.size %>)</strong></div>
> -              <div class="data-section-stats">Statistics Data</div>
> -              <div class="data-section-table">
> -                <div class="inside">
> -                  <%= render :partial => "/storage/list_volumes", :locals => { :storage_volumes => @available_storage_volumes } %>
> -                </div>
> -              </div>
> -            </div>
> -
> -          </div> <!-- end #dataTableWrapper -->
> -
> -      </div> <!-- end #data.inside -->
> -      </div> <!-- end #data -->
> -
> -  </td>
>    <td id="right">
> -          <div class="heading"> </div>
>            <div class="tools">
>  
>            <h3>Actions</h3>
> -          <%if @can_modify -%>
> -          <div class="actions">
> -<!--
> -              <%= link_to 'Add Storage', { :controller => 'storage', :action => 'new', :hardware_pool_id => @default_pool}, { :class => "create" } %>
> -              <hr/>
> --->
> -              <%= link_to 'Create New Pool', { :controller => "hardware", :action => 'new', :parent_id => @default_pool}, { :class => "create" } %>
> -          </div>
> -          <% end -%>
>  
>            <div class="actions">
>              <div><%= link_to_if @can_set_perms, 'User Permissions', { :controller => 'permission', :action => 'new', :pool_id => @default_pool }, { :class => "edit" } %></div>
> diff --git a/wui/src/app/views/hardware/move.rhtml b/wui/src/app/views/hardware/move.rhtml
> index 190fc03..f5824c6 100644
> --- a/wui/src/app/views/hardware/move.rhtml
> +++ b/wui/src/app/views/hardware/move.rhtml
> @@ -1,3 +1,10 @@
> +<%- content_for :title do -%>
> +  Move  <%= @resource_type.capitalize %>
> +<%- end -%>
> +<%- content_for :description do -%>
> +  Select an existing hardware pool or create a new pool for selected <%= @resource_type %>. 
> +<%- end -%>
> +
>  <script type="text/javascript">
>        $(document).ready(function(){         
>          $("#move_tree").treeview({
> @@ -34,12 +41,7 @@ $('#move_to_new_pool').click(function(){
>  });
>  </script>
>  
> -<div id="window">
>  
> -<div class="dialog_title_small">
> -	<div class="header">Move  <%= @resource_type.capitalize %></div>
> -    <div>Select an existing hardware pool or create a new pool for selected <%= @resource_type %>. </div>
> -</div>
>  
>  <div class="dialog_tree">
>    <ul id="move_tree" class="filetree treeview-famfamfam treeview"></ul>
> @@ -63,4 +65,3 @@ $('#move_to_new_pool').click(function(){
>            <div class="button_right_blue"></div>
>          </div> 
>  </div>
> -</div>
> diff --git a/wui/src/app/views/hardware/new.html.erb b/wui/src/app/views/hardware/new.html.erb
> index a533871..f594009 100644
> --- a/wui/src/app/views/hardware/new.html.erb
> +++ b/wui/src/app/views/hardware/new.html.erb
> @@ -1,40 +1,30 @@
> -<div class="dialog_title_small">
> -  <div class="header">Add New Hardware Pool</div>
> -  <div>Add a new Hardware Pool to the <%= @parent.name %> pool.
> -    <%= "Checked #{@resource_type} will be added to the new pool." if @resource_type %>
> -  </div>
> -</div>
> +<%- content_for :title do -%>
> +  <%= _("Add New Hardware Pool") %>
> +<%- end -%>
> +<%- content_for :description do -%>
> +  Add a new Hardware Pool to the <%= @parent.name %> pool.
> +  <%= "Checked #{@resource_type} will be added to the new pool." if @resource_type %>
> +<%- end -%>
>  
>  <form method="POST" action="<%= url_for :action => 'create' %>" id="pool_form" >
> -<div class="dialog_form">
> -  <%= render :partial => 'form' %>
> -</div>
> -<%= popup_footer("$('#pool_form').submit()", "Create Hardware Pool") %>
> +  <div class="dialog_form">
> +    <%= render :partial => 'form' %>
> +  </div>
> +  <%= popup_footer("$('#pool_form').submit()", "Create Hardware Pool") %>
>  </form>
> -  <script type="text/javascript">
> +
> +<script type="text/javascript">
>  $(function() {
>      var hwpooloptions = {
>          target:        '<%= url_for :action => 'create' %>',   // target element to update
>  	dataType:      'json',
> -        success:       afterNewHwPool  // post-submit callback
> +        success:       afterHwPool  // post-submit callback
>      };
>  
>      // bind form using 'ajaxForm' 
>      $('#pool_form').ajaxForm(hwpooloptions); 
>  });
> -function afterNewHwPool(response, status){
> -    ajax_validation(response, status)
> -    if (response.success) {
> -      jQuery(document).trigger('close.facebox');
> -      // FIXME do we need to reload the tree here
> -      <%= "$('##{@resource_type}_grid').flexReload()" if @resource_type %>
> -      //$("#vmpools_grid").flexReload()
> -    }
> -}
>  </script>
>  
> -<%- content_for :title do -%>
> -<%= _("New Hardware Pool") %>
> -<%- end -%>
>  
>  
> diff --git a/wui/src/app/views/hardware/show_hosts.rhtml b/wui/src/app/views/hardware/show_hosts.rhtml
> index ae0e8af..bcbc2b8 100644
> --- a/wui/src/app/views/hardware/show_hosts.rhtml
> +++ b/wui/src/app/views/hardware/show_hosts.rhtml
> @@ -51,6 +51,7 @@
>      <div class="data_section">
>         <%= render :partial => "/host/grid", :locals => { :table_id => "hosts_grid",
>                                                           :hwpool_id => @pool.id,
> +                                                         :exclude_id => nil,
>                                                           :on_select => "hosts_select" } %>
>      </div>
>  <div class="selection_detail" id="hosts_selection">
> diff --git a/wui/src/app/views/hardware/show_storage.rhtml b/wui/src/app/views/hardware/show_storage.rhtml
> index 3d547db..f0b14bc 100644
> --- a/wui/src/app/views/hardware/show_storage.rhtml
> +++ b/wui/src/app/views/hardware/show_storage.rhtml
> @@ -68,6 +68,7 @@
>  <div class="data_section">
>         <%= render :partial => "/storage/grid", :locals => { :table_id => "storage_grid",
>                                                              :hwpool_id => @pool.id,
> +                                                         :exclude_id => nil,
>                                                           :on_select => "storage_select" } %>
>  </div>
>  
> diff --git a/wui/src/app/views/host/_grid.rhtml b/wui/src/app/views/host/_grid.rhtml
> index 338bc78..25bdfd7 100644
> --- a/wui/src/app/views/host/_grid.rhtml
> +++ b/wui/src/app/views/host/_grid.rhtml
> @@ -7,18 +7,19 @@
>  	$("#<%= table_id %>").flexigrid
>  	(
>  	{
> -	url: '<%=  url_for :controller => "hardware", :action => "hosts_json", :id => hwpool_id %>',
> +	url: '<%=  url_for :controller => "hardware", :action => "hosts_json", :id => hwpool_id, :exclude_id => exclude_id %>',
>  	dataType: 'json',
>  	colModel : [
> -		{display: '', name : 'id', width : 20, sortable : false, align: 'left', process: <%= table_id %>checkbox},
> -		{display: 'Hostname', name : 'hostname', width : 60, sortable : true, align: 'left'},
> -	        {display: 'UUID', name : 'uuid', width : 180, sortable : true, align: 'left'},
> -		{display: 'Hypervisor Type', name : 'hypervisor_type', width : 80, sortable : true, align: 'left'},
> -		{display: 'CPUs', name : 'num_cpus', width : 40, sortable : true, align: 'left'},
> -		{display: 'Speed (MHz)', name : 'cpu_speed', width : 60, sortable : true, align: 'right'},
> -		{display: 'Arch', name : 'arch', width : 50, sortable : true, align: 'right'},
> -		{display: 'RAM (MB)', name : 'memory', width : 60, sortable : true, align: 'right'},
> -		{display: 'Disabled', name : 'is_disabled', width : 50, sortable : true, align: 'right'}
> +		{display: '', width : 20, align: 'left', process: <%= table_id %>checkbox},
> +		{display: 'Hostname', name : 'hostname', width : 60, align: 'left'},
> +		<%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_id %>
> +	        {display: 'UUID', name : 'uuid', width : 180, align: 'left'},
> +		{display: 'Hypervisor Type', name : 'hypervisor_type', width : 80, align: 'left'},
> +		{display: 'CPUs', name : 'num_cpus', width : 40, align: 'left'},
> +		{display: 'Speed (MHz)', name : 'cpu_speed', width : 60, align: 'right'},
> +		{display: 'Arch', name : 'arch', width : 50, align: 'right'},
> +		{display: 'RAM (MB)', name : 'memory', width : 60, align: 'right'},
> +		{display: 'Disabled', name : 'is_disabled', width : 50, align: 'right'}
>  		],
>  	sortname: "hostname",
>  	sortorder: "asc",
> diff --git a/wui/src/app/views/host/addhost.html.erb b/wui/src/app/views/host/addhost.html.erb
> index 69e0e66..bc3c797 100644
> --- a/wui/src/app/views/host/addhost.html.erb
> +++ b/wui/src/app/views/host/addhost.html.erb
> @@ -1,55 +1,18 @@
> -<div class="dialog_title">
> -	<div class="header">Add Host</div>
> -    <div>Select hosts from the list below to add to the <%= @hardware_pool.name %> hardware pool.  <a href="#">Learn how to manage hosts</a></div>
> +<%- content_for :title do -%>
> +  <%= _("Add Host") %>
> +<%- end -%>
> +<%- content_for :description do -%>
> +  Select hosts from the list below to add to the <%= @hardware_pool.name %> hardware pool.  <a href="#">Learn how to manage hosts</a>
> +<%- end -%>
> +
> +<div class="panel_header"></div>
> +<div class="dialog_body">
> +  <%= render :partial => "/host/grid", :locals => { :table_id => "addhosts_grid",
> +             :hwpool_id => nil, :exclude_id => @hardware_pool.id, 
> +             :on_select => "false"  } %>
>  </div>
> -<div id="toolbar_nav">
> -<ul>
> -    <li class="current" id="unassigned_tab"><a href="#" onClick = "show_unassigned()"><img style="vertical-align:middle;" src="" />  Unassigned Hosts (<%= @unassigned %>)</a></li>
> -    <li id="assigned_tab"><a href="#" onClick = "show_assigned()"><img style="vertical-align:middle;" src="" />  Assigned Hosts (<%= @assigned %>)</a></li>
> -</ul>
> -</div>
> -<script type="text/javascript">
> -  function show_unassigned()
> -  {
> -      $("#assigned_tab").removeClass('current');
> -      $("#unassigned_tab").addClass('current');
> -      $("#addhosts_assigned_grid_div").hide()
> -      $("#addhosts_unassigned_grid_div").show()
> -  }
> -  function show_assigned()
> -  {
> -      $("#assigned_tab").addClass('current');
> -      $("#unassigned_tab").removeClass('current');
> -      $("#addhosts_unassigned_grid_div").hide()
> -      $("#addhosts_assigned_grid_div").show()
> -  }
> -  function add_hosts()
> -  {
> -    var assigned_selected= get_selected_checkboxes(document.addhosts_assigned_grid_form)
> -    var unassigned_selected= get_selected_checkboxes(document.addhosts_unassigned_grid_form)
> -    var hosts = Array.concat(assigned_selected,unassigned_selected)
> -    if (validate_selected(hosts, "host")) {
> -      $.post('<%= url_for :controller => "hardware", :action => "add_hosts", :id => @hardware_pool %>',
> -             { host_ids: hosts.toString() },
> -              function(data,status){ 
> -                jQuery(document).trigger('close.facebox');
> -                $("#hosts_grid").flexReload()
> -		if (data.alert) {
> -		  alert(data.alert);
> -                }
> -               }, 'json');
> -    }
> -  }
> -</script>
> -  <div class="panel_header"></div>
> -  <div class="dialog_body">
> -       <%= render :partial => "/host/grid", :locals => { :table_id => "addhosts_unassigned_grid",
> -           :hwpool_id => Pool.root.id, :on_select => "false"  } %>
> -       <%= render :partial => "/host/grid", :locals => { :table_id => "addhosts_assigned_grid",
> -           :hwpool_id => nil, :on_select => "false" } %>
> -  </div>
> -</div>
> -<script type="text/javascript">
> -  $("#addhosts_assigned_grid_div").hide()
> -</script>
> -<%= popup_footer("add_hosts()", "Add Hosts") %>
> +
> +<%= popup_footer("add_hosts('#{url_for :controller => "hardware", 
> +                                      :action => "add_hosts", 
> +                                      :id => @hardware_pool}')", 
> +                 "Add Hosts") %>
> diff --git a/wui/src/app/views/layouts/popup.rhtml b/wui/src/app/views/layouts/popup.rhtml
> index 736517c..55dda58 100644
> --- a/wui/src/app/views/layouts/popup.rhtml
> +++ b/wui/src/app/views/layouts/popup.rhtml
> @@ -1,2 +1,7 @@
> -<%# currently nothing for popups here. %>
> +<div id="window">
> +<div class="dialog_title_small">
> +  <div class="header"><%= yield :title -%></div>
> +  <div><%= yield :description -%></div>
> +</div>
>  <%= yield  %> 
> +</div>
> diff --git a/wui/src/app/views/layouts/redux.rhtml b/wui/src/app/views/layouts/redux.rhtml
> index 72a49f8..63a9a56 100644
> --- a/wui/src/app/views/layouts/redux.rhtml
> +++ b/wui/src/app/views/layouts/redux.rhtml
> @@ -27,6 +27,9 @@
>    <%= javascript_include_tag "ui.slider.js" -%>
>    <%= javascript_include_tag "jquery.cookie.js" -%>
>    <%= javascript_include_tag "jquery.form.js" -%>
> +
> +  <!-- ovirt-specific functions defined here
> +  <%= javascript_include_tag "ovirt.js" -%>
>      <script type="text/javascript">
>        $(document).ready(function(){         
>          $("#tree").treeview({
> @@ -39,67 +42,10 @@
>              action_type: "hyperlink"
>  	    })
>  	}); 
> -        
> -  function get_selected_checkboxes(obj_form)
> -  {
> -    var selected_array = new Array()
> -    var selected_index = 0
> -    var checkboxes
> -    if (obj_form.grid_checkbox) {
> -      if (obj_form.grid_checkbox.length == undefined) {
> -        checkboxes = [obj_form.grid_checkbox]
> -      } else {
> -        checkboxes = obj_form.grid_checkbox
> -      }
> -      for(var i=0; i < checkboxes.length; i++){
> -      if(checkboxes[i].checked)
> -        {
> -          selected_array[selected_index]= checkboxes[i].value
> -          selected_index++
> -        }
> -      }
> -    }
> -    return selected_array
> -  }
> -  function validate_selected(selected_array, name)
> -  {
> -    if (selected_array.length == 0) {
> -      alert("Please select at least one " + name + "  to continue")
> -      return false
> -    } else {
> -      return true
> -    }
> -  }
> -  function ajax_validation(response, status)
> -  {
> -    if (response.object) {
> -      $(".fieldWithErrors").removeClass("fieldWithErrors");
> -      $("div.errorExplanation").remove();
> -      if (!response.success) {
> -        for(i=0; i<response.errors.length; i++) { 
> -          var element = $("div.form_field:has(#"+response.object + "_" + response.errors[i][0]+")");
> -          if (element) {
> -            element.addClass("fieldWithErrors");
> -            for(j=0; j<response.errors[i][1].length; j++) { 
> -              element.append('<div class="errorExplanation">'+response.errors[i][1][j]+'</div>');
> -            }
> -          }
> -        }
> -      }
> -      if (response.alert) {
> -        alert(response.alert)
> -      }
> -    }
> -  }
> -        
> -      </script>
> -      <script type="text/javascript">
> -       jQuery(document).ready(function($) {
> +      $(document).ready(function(){         
>          $('a[rel*=facebox]').facebox()
>        })
> -      </script>
> -      <script type="text/javascript">
> -       jQuery(document).ready(function($) {
> +      $(document).ready(function(){         
>          $('a[rel*=close]').trigger('close.facebox')
>        })
>        </script>
> diff --git a/wui/src/app/views/permission/new.rhtml b/wui/src/app/views/permission/new.rhtml
> index 45dc239..70fb66b 100644
> --- a/wui/src/app/views/permission/new.rhtml
> +++ b/wui/src/app/views/permission/new.rhtml
> @@ -1,7 +1,9 @@
> -<div class="dialog_title_small">
> -  <div class="header">Add New User</div>
> -  <div>Add a new user to  <%= @permission.pool.name %> pool.</div>
> -</div>
> +<%- content_for :title do -%>
> +  Add New User
> +<%- end -%>
> +<%- content_for :description do -%>
> +  Add a new user to  <%= @permission.pool.name %> pool.
> +<%- end -%>
>  
>  <form method="POST" action="<%= url_for :action => 'create' %>" id="permission_form" >
>  <div class="dialog_form">
> @@ -9,28 +11,16 @@
>  </div>
>  <%= popup_footer("$('#permission_form').submit()", "Create User Permission") %>
>  </form>
> +
>    <script type="text/javascript">
>  $(function() {
>      var permissionoptions = {
>          target:        '<%= url_for :action => 'create' %>',   // target element to update
>  	dataType:      'json',
> -        success:       afterNewPermission  // post-submit callback
> +        success:       afterPermission  // post-submit callback
>      };
>  
>      // bind form using 'ajaxForm' 
>      $('#permission_form').ajaxForm(permissionoptions); 
>  });
> -function afterNewPermission(response, status){
> -    ajax_validation(response, status)
> -    if (response.success) {
> -      jQuery(document).trigger('close.facebox');
> -      $("#users_grid").flexReload()
> -    }
> -}
>  </script>
> -
> -<%- content_for :title do -%>
> -<%= "Edit Permissions for #{@permission.pool.name}" %>
> -<%- end -%>
> -
> -
> diff --git a/wui/src/app/views/resources/new.rhtml b/wui/src/app/views/resources/new.rhtml
> index 5897a5e..6d856f7 100644
> --- a/wui/src/app/views/resources/new.rhtml
> +++ b/wui/src/app/views/resources/new.rhtml
> @@ -1,7 +1,9 @@
> -<div class="dialog_title_small">
> -  <div class="header">Add New Virtual Machine Pool</div>
> -  <div>Add a new Virtual Machine Pool to the <%= @parent.name %> pool.</div>
> -</div>
> +<%- content_for :title do -%>
> +  Add New Virtual Machine Pool
> +<%- end -%>
> +<%- content_for :description do -%>
> +  Add a new Virtual Machine Pool to the <%= @parent.name %> pool.
> +<%- end -%>
>  
>  <form method="POST" action="<%= url_for :action => 'create' %>" id="vm_pool_form" >
>  <div class="dialog_form">
> @@ -9,27 +11,16 @@
>  </div>
>  <%= popup_footer("$('#vm_pool_form').submit()", "Create Virtual Machine Pool") %>
>  </form>
> -  <script type="text/javascript">
> +
> +<script type="text/javascript">
>  $(function() {
>      var vmpooloptions = {
>          target:        '<%= url_for :action => 'create' %>',   // target element to update
>  	dataType:      'json',
> -        success:       afterNewVmPool  // post-submit callback
> +        success:       afterVmPool  // post-submit callback
>      };
>  
>      // bind form using 'ajaxForm' 
>      $('#vm_pool_form').ajaxForm(vmpooloptions); 
>  });
> -function afterNewVmPool(response, status){
> -    ajax_validation(response, status)
> -    if (response.success) {
> -      jQuery(document).trigger('close.facebox');
> -      $("#vmpools_grid").flexReload()
> -    }
> -}
>  </script>
> -
> -<%- content_for :title do -%>
> -<%= _("New VM Resource Pool") %>
> -<%- end -%>
> -
> diff --git a/wui/src/app/views/resources/show.rhtml b/wui/src/app/views/resources/show.rhtml
> index c42ebeb..ecf8fc6 100644
> --- a/wui/src/app/views/resources/show.rhtml
> +++ b/wui/src/app/views/resources/show.rhtml
> @@ -1,87 +1,9 @@
>  
> -<div class="panel_header"></div>
> -    <div id="data">
> -      <div class="inside">
> -
> -        <%= error_messages_for 'vm_actions' %>
> -
> -          <div id="dataTableWrapper">
> -
> -          <div class="dataTable">
> -              <div class="inside">
> -
> -                <div class="data-table-column">
> -
> -              <% form_tag( {:controller => 'resources', :action => 'vm_actions'}, {:name => "vm_actions", :method => "post"}) do -%>
> -              <%= tag :input, { "type" => "hidden", "name" => "vm_actions[vm_resource_pool_id]", "value" => "@vm_resource_pool.id"}  %>
> -              <%= tag :input, { "type" => "submit", "name" => "vm_actions[#{VmTask::ACTION_START_VM}]", "value" => "Start"}  %>
> -              <%= tag :input, { "type" => "submit", "name" => "vm_actions[#{VmTask::ACTION_SHUTDOWN_VM}]", "value" => "Stop"}  %>
> -              <select onchange="return confirm_and_submit(this)" name="vm_actions[other_actions]">
> -                <option class="other">Other Actions</option>
> -                <option></option>
> -              <% for action in @action_values %>
> -                <option value="<%= action[1]%>"><%= action[0]%></option>
> -              <% end %>
> -                <option>-------------------</option>
> -                <option class="remove" value="destroy" onSelect="confirm('Are you sure?'">Destroy</option>
> -              </select>
> -
> -              <br/>
> -              <%= render :partial => '/layouts/alertbox' %>
> -              <br/>
> -
> -              <%= render :partial => "/vm/list", :locals => { :vms => @vm_resource_pool.vms } %>
> -              <% end -%>
> -
> -                </div>
> -
> -              </div> <!-- end #data-table.inside -->
> -            </div> <!-- end #dataTable -->
> -
> -            <div class="data-section">
> -              <div class="data-section-header"><strong>VM Resource Pool Quota</strong></div>
> -              <div class="data-section-stats">Statistics Data</div>
> -              <div class="data-section-table">
> -                <div class="inside">
> -
> -                <table>
> -                  <tr>
> -                  <th></th>
> -                  <td> in use / awaiting use / total allowed</td>
> -                  </tr>
> -                <% resources = @vm_resource_pool.full_resources %>
> -                <% for item in resources[:labels] %>
> -                <% total_limit = resources[:total][item[1]]
> -                   total_limit = "unlimited" if total_limit.nil? %>
> -                  <tr>
> -                  <th><%= item[0]%>:</th>
> -                  <td><%= resources[:allocated][:current][item[1]] %> / <%= resources[:allocated][:pending][item[1]] %> / <%= total_limit %>
> -                      <%= item[2]%></td>
> -                  </tr>
> -                <% end %>
> -                </table>
> -
> -                </div>
> -              </div>
> -            </div>
> -
> -
> -          </div> <!-- end #dataTableWrapper -->
> -
> -      </div> <!-- end #data.inside -->
> -      </div> <!-- end #data -->
> -
> -  </td>
>    <td id="right">
>      <div class="heading"> </div>
>      <div class="tools">
>      <h3>Actions</h3>
>  
> -  <%if @can_modify -%>
> -  <div class="actions create">
> -    <%= link_to_if @vm_resource_pool, 'Create New VM', {:controller => "vm", :action => 'new', :vm_resource_pool_id => @vm_resource_pool}, { :class => "create" } %>
> -  </div>
> -  <% end -%>
>    <div class="actions">
>      <%if @is_hwpool_admin -%>
>      <%= link_to 'Edit VM Resource Pool', { :action => 'edit', :id => @vm_resource_pool }, { :class => "edit" } %>
> diff --git a/wui/src/app/views/storage/_grid.rhtml b/wui/src/app/views/storage/_grid.rhtml
> index 56fb621..feb855a 100644
> --- a/wui/src/app/views/storage/_grid.rhtml
> +++ b/wui/src/app/views/storage/_grid.rhtml
> @@ -7,13 +7,14 @@
>  	$("#<%= table_id %>").flexigrid
>  	(
>  	{
> -	url: '<%=  url_for :controller => "hardware", :action => "storage_pools_json", :id => hwpool_id %>',
> +	url: '<%=  url_for :controller => "hardware", :action => "storage_pools_json", :id => hwpool_id, :exclude_id => exclude_id %>',
>  	dataType: 'json',
>  	colModel : [
> -		{display: '', name : 'id', width : 20, sortable : false, align: 'left', process: <%= table_id %>checkbox},
> -		{display: 'Alias', name : 'display_name', width : 180, sortable : false, align: 'left'},
> -	        {display: 'IP', name : 'ip_addr', width : 80, sortable : true, align: 'left'},
> -		{display: 'Type', name : 'type', width : 80, sortable : true, align: 'left'}
> +		{display: '', width : 20, align: 'left', process: <%= table_id %>checkbox},
> +		{display: 'Alias', width : 180, align: 'left'},
> +		<%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_id %>
> +	        {display: 'IP', name : 'ip_addr', width : 80, align: 'left'},
> +		{display: 'Type', name : 'storage_pools.type', width : 80, align: 'left'}
>  		],
>  	sortname: "ip_addr",
>  	sortorder: "asc",
> diff --git a/wui/src/app/views/storage/addstorage.html.erb b/wui/src/app/views/storage/addstorage.html.erb
> index a747502..e465d12 100644
> --- a/wui/src/app/views/storage/addstorage.html.erb
> +++ b/wui/src/app/views/storage/addstorage.html.erb
> @@ -1,14 +1,17 @@
> -<div class="dialog_title">
> -	<div class="header">Add Storage</div>
> -    <div>Select storage pools from the list below to add to the <%= @hardware_pool.name %> hardware pool.</div>
> -</div>
> +<%- content_for :title do -%>
> +  Add Storage
> +<%- end -%>
> +<%- content_for :description do -%>
> +  Select storage pools from the list below to add to the <%= @hardware_pool.name %> hardware pool.
> +<%- end -%>
> +
> +<!-- we'll need this when we combine add/create for storage
>  <div id="toolbar_nav">
>  <ul>
>      <li class="current" id="unassigned_tab"><a href="#" onClick = "show_unassigned()"><img style="vertical-align:middle;" src="" />  Unassigned Storage (<%= @unassigned %>)</a></li>
>      <li id="assigned_tab"><a href="#" onClick = "show_assigned()"><img style="vertical-align:middle;" src="" />  Assigned Storage (<%= @assigned %>)</a></li>
>  </ul>
>  </div>
> -<script type="text/javascript">
>    function show_unassigned()
>    {
>        $("#assigned_tab").removeClass('current');
> @@ -23,34 +26,17 @@
>        $("#addstorage_unassigned_grid_div").hide()
>        $("#addstorage_assigned_grid_div").show()
>    }
> -  function add_storage()
> -  {
> -    var assigned_selected= get_selected_checkboxes(document.addstorage_assigned_grid_form)
> -    var unassigned_selected= get_selected_checkboxes(document.addstorage_unassigned_grid_form)
> -    var storage = Array.concat(assigned_selected,unassigned_selected)
> -    if (validate_selected(storage, "storage pool")) {
> -      $.post('<%= url_for :controller => "hardware", :action => "add_storage", :id => @hardware_pool %>',
> -             { storage_pool_ids: storage.toString() },
> -              function(data,status){ 
> -                jQuery(document).trigger('close.facebox');
> -                $("#storage_grid").flexReload()
> -		if (data.alert) {
> -		  alert(data.alert);
> -                }
> -               }, 'json');
> -    }
> -  }
> -</script>
> +  $("#addstorage_assigned_grid_div").hide()
> +-->
>    <div class="panel_header"></div>
>    <div class="dialog_body">
> -       <%= render :partial => "/storage/grid", :locals => { :table_id => "addstorage_unassigned_grid",
> -           :hwpool_id => Pool.root.id, :on_select => "false" } %>
> -       <%= render :partial => "/storage/grid", :locals => { :table_id => "addstorage_assigned_grid",
> -           :hwpool_id => nil, :on_select => "false" } %>
> +       <%= render :partial => "/storage/grid", :locals => { :table_id => "addstorage_grid",
> +           :hwpool_id => nil, :exclude_id => @hardware_pool.id, 
> +           :on_select => "false" } %>
>    </div>
>  </div>
> -<script type="text/javascript">
> -  $("#addstorage_assigned_grid_div").hide()
> -</script>
> -<%= popup_footer("add_storage()", "Add Selected Storage Pools") %>
> +<%= popup_footer("add_storage('#{url_for :controller => 'hardware', 
> +                                        :action => 'add_storage', 
> +                                        :id => @hardware_pool}')", 
> +                 "Add Selected Storage Pools") %>
>  
> diff --git a/wui/src/app/views/storage/new.rhtml b/wui/src/app/views/storage/new.rhtml
> index 6f88ce3..13dedb9 100644
> --- a/wui/src/app/views/storage/new.rhtml
> +++ b/wui/src/app/views/storage/new.rhtml
> @@ -1,51 +1,47 @@
> -<div class="dialog_title_small">
> -  <div class="header">Add New Storage Pool</div>
> -  <div>Add a new Storage Pool to this oVirt server.</div>
> -</div>
> +<%- content_for :title do -%>
> +  Add New Storage Pool
> +<%- end -%>
> +<%- content_for :description do -%>
> +  Add a new Storage Pool to this oVirt server.
> +<%- end -%>
>  
>  <div class="dialog_form">
>      <%= error_messages_for 'storage_pool' %>
> -  <% form_tag  do %>
>  
> +  <% form_tag  do %>
>      <!--[form:storage_pool]-->
>      <%= hidden_field_tag 'hardware_pool_id', @hardware_pool.id %>
> -    <%= select_tag_with_label "Storage Type:", 'storage_type', @storage_types, :onChange => "load_details()"  %>
> +    <%= select_tag_with_label "Storage Type:", 'storage_type', @storage_types, :onChange => "load_storage_subform()"  %>
>      <!--[eoform:storage_pool]-->
>    <% end %>
>  
>    <div class="clear_row"></div>
>    <div class="clear_row"></div>
>  </div>
> -<form method="POST" action="<%= url_for :action => 'create' %>" id="storage_pool_form" >
> -<div class="dialog_form">
> -  <div id="new_storage_pool"/>
>  
> -</div>
> -<%= popup_footer("$('#storage_pool_form').submit()", "New Storage Pool") %>
> +<form method="POST" action="<%= url_for :action => 'create' %>" id="storage_pool_form" >
> +  <div class="dialog_form">
> +    <div id="new_storage_pool"/>
> +  </div>
> +  <%= popup_footer("$('#storage_pool_form').submit()", "New Storage Pool") %>
>  </form>
> -  <script type="text/javascript">
> -  function load_details()
> +
> +<script type="text/javascript">
> +  function load_storage_subform()
>    {
>      $('#new_storage_pool').load('<%= url_for :controller => "storage", :action => "new2" %>',
>                                  { hardware_pool_id: $('[name=hardware_pool_id]').val(), storage_type: $('[name=storage_type]').val()})
>    }
> -  load_details()
> +  load_storage_subform()
>  $(function() {
>      var storageoptions = {
>          target:        '<%= url_for :action => 'create' %>',   // target element to update
>          //beforeSubmit:  showStorageRequest,  // pre-submit callback
>  	dataType:      'json',
> -        success:       afterNewStoragePool  // post-submit callback
> +        success:       afterStoragePool  // post-submit callback
>      };
>  
>      // bind form using 'ajaxForm' 
>      $('#storage_pool_form').ajaxForm(storageoptions); 
>  });
> -function afterNewStoragePool(response, status){
> -    ajax_validation(response, status)
> -    if (response.success) {
> -      jQuery(document).trigger('close.facebox');
> -      $("#storage_grid").flexReload()
> -    }
> -}
> -  </script>
> +</script>
> diff --git a/wui/src/app/views/vm/new.rhtml b/wui/src/app/views/vm/new.rhtml
> index 60dbcd6..767f806 100644
> --- a/wui/src/app/views/vm/new.rhtml
> +++ b/wui/src/app/views/vm/new.rhtml
> @@ -1,12 +1,9 @@
> -<div id="window">
> -
> +<%- content_for :title do -%>
> +  Add Virtual Machine
> +<%- end -%>
> +<%- content_for :description do -%>
> +<%- end -%>
>  
> -  <!-- DIALOG  TITLE -->
> -  <div class="dialog_title_small">
> -    <div class="header">Add Virtual Machine</div>
> -  </div>
> -  
> -  
>    <!-- DIALOG  BODY -->
>    <form method="POST" action="<%= url_for :action => 'create' %>" id="vm_form" >
>    <div class="dialog_form">
> @@ -15,9 +12,6 @@
>    <!-- DIALOG  FOOTER -->
>    <%= popup_footer("$('#vm_form').submit()", "Add Virtual Machine") %>
>    </form>
> -</div>
> -
> -
>  
>  <script type="text/javascript">
>  $(function() {
> @@ -25,21 +19,11 @@ $(function() {
>          target:        '<%= url_for :action => 'create' %>',   // target element to update
>          //beforeSubmit:  showStorageRequest,  // pre-submit callback
>  	dataType:      'json',
> -        success:       afterNewVm  // post-submit callback
> +        success:       afterVm  // post-submit callback
>      };
>  
>      // bind form using 'ajaxForm' 
>      $('#vm_form').ajaxForm(vmoptions); 
>  });
> -function afterNewVm(response, status){
> -    ajax_validation(response, status)
> -    if (response.success) {
> -      jQuery(document).trigger('close.facebox');
> -      $("#vms_grid").flexReload()
> -    }
> -}
>  </script>
>  
> -<%- content_for :title do -%>
> -<%= "New Virtual Machine" %>
> -<%- end -%>
> diff --git a/wui/src/public/javascripts/ovirt.js b/wui/src/public/javascripts/ovirt.js
> new file mode 100644
> index 0000000..675f20d
> --- /dev/null
> +++ b/wui/src/public/javascripts/ovirt.js
> @@ -0,0 +1,138 @@
> +// ovirt-specific javascript functions are defined here
> +
> +
> +// helper functions for dialogs and action links
> +
> +
> +// returns an array of selected values for flexigrid checkboxes
> +function get_selected_checkboxes(obj_form)
> +{
> +  var selected_array = new Array()
> +  var selected_index = 0
> +  var checkboxes
> +  if (obj_form.grid_checkbox) {
> +    if (obj_form.grid_checkbox.length == undefined) {
> +      checkboxes = [obj_form.grid_checkbox]
> +    } else {
> +      checkboxes = obj_form.grid_checkbox
> +    }
> +    for(var i=0; i < checkboxes.length; i++){
> +    if(checkboxes[i].checked)
> +      {
> +        selected_array[selected_index]= checkboxes[i].value
> +        selected_index++
> +      }
> +    }
> +  }
> +  return selected_array
> +}
> +
> +
> +// make sure that at least one item is selected to continue
> +function validate_selected(selected_array, name)
> +{
> +  if (selected_array.length == 0) {
> +    alert("Please select at least one " + name + "  to continue")
> +    return false
> +  } else {
> +    return true
> +  }
> +}
> +
> +function add_hosts(url)
> +{
> +    hosts= get_selected_checkboxes(document.addhosts_grid_form)
> +    if (validate_selected(hosts, "host")) {
> +      $.post(url,
> +             { resource_ids: hosts.toString() },
> +              function(data,status){ 
> +                jQuery(document).trigger('close.facebox');
> +                $("#hosts_grid").flexReload()
> +		if (data.alert) {
> +		  alert(data.alert);
> +                }
> +               }, 'json');
> +    }
> +}
> +function add_storage(url)
> +{
> +    storage= get_selected_checkboxes(document.addstorage_grid_form)
> +    if (validate_selected(storage, "storage pool")) {
> +      $.post(url,
> +             { resource_ids: storage.toString() },
> +              function(data,status){ 
> +                jQuery(document).trigger('close.facebox');
> +                $("#storage_grid").flexReload()
> +		if (data.alert) {
> +		  alert(data.alert);
> +                }
> +               }, 'json');
> +    }
> +}
> +// deal with ajax form response, filling in validation messages where required.
> +function ajax_validation(response, status)
> +{
> +  if (response.object) {
> +    $(".fieldWithErrors").removeClass("fieldWithErrors");
> +    $("div.errorExplanation").remove();
> +    if (!response.success) {
> +      for(i=0; i<response.errors.length; i++) { 
> +        var element = $("div.form_field:has(#"+response.object + "_" + response.errors[i][0]+")");
> +        if (element) {
> +          element.addClass("fieldWithErrors");
> +          for(j=0; j<response.errors[i][1].length; j++) { 
> +            element.append('<div class="errorExplanation">'+response.errors[i][1][j]+'</div>');
> +          }
> +        }
> +      }
> +    }
> +    if (response.alert) {
> +      alert(response.alert)
> +    }
> +  }
> +}
> +
> +// callback actions for dialog submissions
> +function afterHwPool(response, status){
> +    ajax_validation(response, status)
> +    if (response.success) {
> +      jQuery(document).trigger('close.facebox');
> +      // FIXME do we need to reload the tree here
> +
> +      // this is for reloading the host/storage grid when 
> +      // adding hosts/storage to a new HW pool
> +      if (response.resource_type) {
> +        $('#' + response.resource_type + '_grid').flexReload()
> +      }
> +      // do we have HW pools grid?
> +      //$("#vmpools_grid").flexReload()
> +    }
> +}
> +function afterVmPool(response, status){
> +    ajax_validation(response, status)
> +    if (response.success) {
> +      jQuery(document).trigger('close.facebox');
> +      $("#vmpools_grid").flexReload()
> +    }
> +}
> +function afterStoragePool(response, status){
> +    ajax_validation(response, status)
> +    if (response.success) {
> +      jQuery(document).trigger('close.facebox');
> +      $("#storage_grid").flexReload()
> +    }
> +}
> +function afterPermission(response, status){
> +    ajax_validation(response, status)
> +    if (response.success) {
> +      jQuery(document).trigger('close.facebox');
> +      $("#users_grid").flexReload()
> +    }
> +}
> +function afterVm(response, status){
> +    ajax_validation(response, status)
> +    if (response.success) {
> +      jQuery(document).trigger('close.facebox');
> +      $("#vms_grid").flexReload()
> +    }
> +}
> diff --git a/wui/src/vendor/plugins/betternestedset/lib/#better_nested_set.rb# b/wui/src/vendor/plugins/betternestedset/lib/#better_nested_set.rb#
> deleted file mode 100644
> index d430419..0000000
> --- a/wui/src/vendor/plugins/betternestedset/lib/#better_nested_set.rb#
> +++ /dev/null
> @@ -1,583 +0,0 @@
> -module SymetrieCom
> -  module Acts #:nodoc:
> -    module NestedSet #:nodoc:
> -      def self.included(base)
> -        base.extend(ClassMethods)              
> -      end
> -      # This module provides an enhanced acts_as_nested_set mixin for ActiveRecord.
> -      # Please see the README for background information, examples, and tips on usage.
> -      module ClassMethods
> -        # Configuration options are:
> -        # * +parent_column+ - Column name for the parent/child foreign key (default: +parent_id+).
> -        # * +left_column+ - Column name for the left index (default: +lft+). 
> -        # * +right_column+ - Column name for the right index (default: +rgt+). NOTE: 
> -        #   Don't use +left+ and +right+, since these are reserved database words.
> -        # * +scope+ - Restricts what is to be considered a tree. Given a symbol, it'll attach "_id" 
> -        #   (if it isn't there already) and use that as the foreign key restriction. It's also possible 
> -        #   to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
> -        #   Example: <tt>acts_as_nested_set :scope => 'tree_id = #{tree_id} AND completed = 0'</tt>
> -        # * +text_column+ - Column name for the title field (optional). Used as default in the 
> -        #   {your-class}_options_for_select helper method. If empty, will use the first string field 
> -        #   of your model class.
> -        def acts_as_nested_set(options = {})          
> -          
> -          options[:scope] = "#{options[:scope]}_id".intern if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
> -          
> -          write_inheritable_attribute(:acts_as_nested_set_options,
> -             { :parent_column  => (options[:parent_column] || 'parent_id'),
> -               :left_column    => (options[:left_column]   || 'lft'),
> -               :right_column   => (options[:right_column]  || 'rgt'),
> -               :scope          => (options[:scope] || '1 = 1'),
> -               :text_column    => (options[:text_column] || columns.collect{|c| (c.type == :string) ? c.name : nil }.compact.first),
> -               :class          => self # for single-table inheritance
> -              } )
> -          
> -          class_inheritable_reader :acts_as_nested_set_options
> -          
> -          if acts_as_nested_set_options[:scope].is_a?(Symbol)
> -            scope_condition_method = %(
> -              def scope_condition
> -                if #{acts_as_nested_set_options[:scope].to_s}.nil?
> -                  "#{acts_as_nested_set_options[:scope].to_s} IS NULL"
> -                else
> -                  "#{acts_as_nested_set_options[:scope].to_s} = \#{#{acts_as_nested_set_options[:scope].to_s}}"
> -                end
> -              end
> -            )
> -          else
> -            scope_condition_method = "def scope_condition() \"#{acts_as_nested_set_options[:scope]}\" end"
> -          end
> -          
> -          # no bulk assignment
> -          attr_protected  acts_as_nested_set_options[:left_column].intern,
> -                          acts_as_nested_set_options[:right_column].intern,
> -                          acts_as_nested_set_options[:parent_column].intern
> -          # no assignment to structure fields
> -          module_eval <<-"end_eval", __FILE__, __LINE__
> -            def #{acts_as_nested_set_options[:left_column]}=(x)
> -              raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{acts_as_nested_set_options[:left_column]}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
> -            end
> -            def #{acts_as_nested_set_options[:right_column]}=(x)
> -              raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{acts_as_nested_set_options[:right_column]}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
> -            end
> -            def #{acts_as_nested_set_options[:parent_column]}=(x)
> -              raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{acts_as_nested_set_options[:parent_column]}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
> -            end
> -            #{scope_condition_method}
> -          end_eval
> -          
> -          
> -          include SymetrieCom::Acts::NestedSet::InstanceMethods
> -          extend SymetrieCom::Acts::NestedSet::ClassMethods
> -          
> -          # adds the helper for the class
> -#          ActionView::Base.send(:define_method, "#{Inflector.underscore(self.class)}_options_for_select") { special=nil
> -#              "#{acts_as_nested_set_options[:text_column]} || "#{self.class} id #{id}"
> -#            }
> -          
> -        end
> -        
> -        
> -        # Returns the single root for the class (or just the first root, if there are several).
> -        # Deprecation note: the original acts_as_nested_set allowed roots to have parent_id = 0,
> -        # so we currently do the same. This silliness will not be tolerated in future versions, however.
> -        def root
> -          acts_as_nested_set_options[:class].find(:first, :conditions => "(#{acts_as_nested_set_options[:parent_column]} IS NULL OR #{acts_as_nested_set_options[:parent_column]} = 0)")
> -        end
> -        
> -        # Returns the roots and/or virtual roots of all trees. See the explanation of virtual roots in the README.
> -        def roots
> -          acts_as_nested_set_options[:class].find(:all, :conditions => "(#{acts_as_nested_set_options[:parent_column]} IS NULL OR #{acts_as_nested_set_options[:parent_column]} = 0)", :order => "#{acts_as_nested_set_options[:left_column]}")
> -        end
> -        
> -        # Checks the left/right indexes of all records, 
> -        # returning the number of records checked. Throws ActiveRecord::ActiveRecordError if it finds a problem.
> -        def check_all
> -          total = 0
> -          transaction do
> -            # if there are virtual roots, only call check_full_tree on the first, because it will check other virtual roots in that tree.
> -            total = roots.inject(0) {|sum, r| sum + (r[r.left_col_name] == 1 ? r.check_full_tree : 0 )}
> -            raise ActiveRecord::ActiveRecordError, "Scope problems or nodes without a valid root" unless acts_as_nested_set_options[:class].count == total
> -          end
> -          return total
> -        end
> -        
> -        # Re-calculate the left/right values of all nodes. Can be used to convert ordinary trees into nested sets.
> -        def renumber_all
> -          scopes = []
> -          # only call it once for each scope_condition (if the scope conditions are messed up, this will obviously cause problems)
> -          roots.each do |r|
> -            r.renumber_full_tree unless scopes.include?(r.scope_condition)
> -            scopes << r.scope_condition
> -          end
> -        end
> -        
> -        # Returns an SQL fragment that matches _items_ *and* all of their descendants, for use in a WHERE clause.
> -        # You can pass it a single object, a single ID, or an array of objects and/or IDs.
> -        #   # if a.lft = 2, a.rgt = 7, b.lft = 12 and b.rgt = 13
> -        #   Set.sql_for([a,b]) # returns "((lft BETWEEN 2 AND 7) OR (lft BETWEEN 12 AND 13))"
> -        # Returns "1 != 1" if passed no items. If you need to exclude items, just use "NOT (#{sql_for(items)})".
> -        # Note that if you have multiple trees, it is up to you to apply your scope condition.
> -        def sql_for(items)
> -          items = [items] unless items.is_a?(Array)
> -          # get objects for IDs
> -          items.collect! {|s| s.is_a?(acts_as_nested_set_options[:class]) ? s : acts_as_nested_set_options[:class].find(s)}.uniq
> -          items.reject! {|e| e.new_record?} # exclude unsaved items, since they don't have left/right values yet
> -          
> -          return "1 != 1" if items.empty? # PostgreSQL didn't like '0', and SQLite3 didn't like 'FALSE'
> -          items.map! {|e| "(#{acts_as_nested_set_options[:left_column]} BETWEEN #{e[acts_as_nested_set_options[:left_column]]} AND #{e[acts_as_nested_set_options[:right_column]]})" }
> -          "(#{items.join(' OR ')})"
> -        end
> -        
> -      end
> -
> -      # This module provides instance methods for an enhanced acts_as_nested_set mixin. Please see the README for background information, examples, and tips on usage.
> -      module InstanceMethods
> -        # convenience methods to make the code more readable
> -        def left_col_name()#:nodoc:
> -          acts_as_nested_set_options[:left_column]
> -        end
> -        def right_col_name()#:nodoc:
> -          acts_as_nested_set_options[:right_column]
> -        end
> -        def parent_col_name()#:nodoc:
> -          acts_as_nested_set_options[:parent_column]
> -        end
> -        alias parent_column parent_col_name#:nodoc: Deprecated
> -        def base_set_class()#:nodoc:
> -          acts_as_nested_set_options[:class] # for single-table inheritance
> -        end
> -        
> -        # On creation, automatically add the new node to the right of all existing nodes in this tree.
> -        def before_create # already protected by a transaction
> -          maxright = base_set_class.maximum(right_col_name, :conditions => scope_condition) || 0
> -          self[left_col_name] = maxright+1
> -          self[right_col_name] = maxright+2
> -        end
> -        
> -        # On destruction, delete all children and shift the lft/rgt values back to the left so the counts still work.
> -        def before_destroy # already protected by a transaction
> -          return if self[right_col_name].nil? || self[left_col_name].nil?
> -          self.reload # in case a concurrent move has altered the indexes
> -          dif = self[right_col_name] - self[left_col_name] + 1
> -          base_set_class.delete_all( "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]})" )
> -          base_set_class.update_all("#{left_col_name} = CASE \
> -                                      WHEN #{left_col_name} > #{self[right_col_name]} THEN (#{left_col_name} - #{dif}) \
> -                                      ELSE #{left_col_name} END, \
> -                                 #{right_col_name} = CASE \
> -                                      WHEN #{right_col_name} > #{self[right_col_name]} THEN (#{right_col_name} - #{dif} ) \
> -                                      ELSE #{right_col_name} END",
> -                                 scope_condition)
> -        end
> -        
> -        # By default, records are compared and sorted using the left column.
> -        def <=>(x)
> -          self[left_col_name] <=> x[left_col_name]
> -        end
> -        
> -        # Deprecated. Returns true if this is a root node.
> -        def root?
> -          parent_id = self[parent_col_name]
> -          (parent_id == 0 || parent_id.nil?) && self[right_col_name] && self[left_col_name] && (self[right_col_name] > self[left_col_name])
> -        end
> -        
> -        # Deprecated. Returns true if this is a child node
> -        def child?                          
> -          parent_id = self[parent_col_name]
> -          !(parent_id == 0 || parent_id.nil?) && (self[left_col_name] > 1) && (self[right_col_name] > self[left_col_name])
> -        end
> -        
> -        # Deprecated. Returns true if we have no idea what this is
> -        def unknown?
> -          !root? && !child?
> -        end
> -        
> -        # Returns this record's root ancestor.
> -        def root
> -          # the BETWEEN clause is needed to ensure we get the right virtual root, if using those
> -          base_set_class.find(:first, :conditions => "#{scope_condition} \
> -            AND (#{parent_col_name} IS NULL OR #{parent_col_name} = 0) AND (#{self[left_col_name]} BETWEEN #{left_col_name} AND #{right_col_name})")
> -        end
> -        
> -        # Returns the root or virtual roots of this record's tree (a tree cannot have more than one real root). See the explanation of virtual roots in the README.
> -        def roots
> -          base_set_class.find(:all, :conditions => "#{scope_condition} AND (#{parent_col_name} IS NULL OR #{parent_col_name} = 0)", :order => "#{left_col_name}")
> -        end
> -        
> -        # Returns this record's parent.
> -        def parent
> -          base_set_class.find(self[parent_col_name]) if self[parent_col_name]
> -        end
> -        
> -        # Returns an array of all parents, starting with the root.
> -        def ancestors
> -          self_and_ancestors - [self]
> -        end
> -        
> -        # Returns an array of all parents plus self, starting with the root.
> -        def self_and_ancestors
> -          base_set_class.find(:all, :conditions => "#{scope_condition} AND (#{self[left_col_name]} BETWEEN #{left_col_name} AND #{right_col_name})", :order => left_col_name )
> -        end
> -        
> -        # Returns all the children of this node's parent, except self.
> -        def siblings
> -          self_and_siblings - [self]
> -        end
> -        
> -        # Returns all the children of this node's parent, including self.
> -        def self_and_siblings
> -          if self[parent_col_name].nil? || self[parent_col_name].zero?
> -            [self]
> -          else
> -            base_set_class.find(:all, :conditions => "#{scope_condition} AND #{parent_col_name} = #{self[parent_col_name]}", :order => left_col_name)
> -          end
> -        end
> -        
> -        # Returns the level of this object in the tree, root level being 0.
> -        def level
> -          return 0 if self[parent_col_name].nil?
> -          base_set_class.count(:conditions => "#{scope_condition} AND (#{self[left_col_name]} BETWEEN #{left_col_name} AND #{right_col_name})") - 1
> -        end
> -        
> -        # Returns the number of nested children of this object.
> -        def all_children_count
> -          return (self[right_col_name] - self[left_col_name] - 1)/2
> -        end
> -        
> -        # Returns itself and all nested children.
> -        # Pass :exclude => item, or id, or [items or id] to exclude one or more items *and* all of their descendants.
> -        def full_set(special=nil)
> -          if special && special[:exclude]
> -            exclude_str = " AND NOT (#{base_set_class.sql_for(special[:exclude])}) "
> -          elsif new_record? || self[right_col_name] - self[left_col_name] == 1
> -            return [self]
> -          endas
> -          base_set_class.find(:all, :conditions => "#{scope_condition} #{exclude_str} AND (#{left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]})", :order => left_col_name)
> -        end
> -        
> -        # Returns all children and nested children.
> -        # Pass :exclude => item, or id, or [items or id] to exclude one or more items *and* all of their descendants.
> -        def all_children(special=nil)
> -          full_set(special) - [self]
> -        end
> -        
> -        # Returns this record's immediate children.
> -        def children
> -          base_set_class.find(:all, :conditions => "#{scope_condition} AND #{parent_col_name} = #{self.id}", :order => left_col_name)
> -        end
> -        
> -        # Deprecated
> -        alias direct_children children
> -        
> -        # Returns this record's terminal children (nodes without children).
> -        def leaves
> -          base_set_class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]}) AND #{left_col_name} + 1 = #{right_col_name}", :order => left_col_name)
> -        end
> -        
> -        # Returns the count of this record's terminal children (nodes without children).
> -        def leaves_count
> -          base_set_class.count(:conditions => "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]}) AND #{left_col_name} + 1 = #{right_col_name}")
> -        end
> -        
> -        # Checks the left/right indexes of one node and all descendants. 
> -        # Throws ActiveRecord::ActiveRecordError if it finds a problem.
> -        def check_subtree
> -          transaction do
> -            self.reload
> -            check # this method is implemented via #check, so that we don't generate lots of unnecessary nested transactions
> -          end
> -        end
> -        
> -        # Checks the left/right indexes of the entire tree that this node belongs to, 
> -        # returning the number of records checked. Throws ActiveRecord::ActiveRecordError if it finds a problem.
> -        # This method is needed because check_subtree alone cannot find gaps between virtual roots, orphaned nodes or endless loops.
> -        def check_full_tree
> -          total_nodes = 0
> -          transaction do
> -            # virtual roots make this method more complex than it otherwise would be
> -            n = 1
> -            roots.each do |r| 
> -              raise ActiveRecord::ActiveRecordError, "Gaps between roots in the tree containing record ##{r.id}" if r[left_col_name] != n
> -              r.check_subtree
> -              n = r[right_col_name] + 1
> -            end
> -            total_nodes = roots.inject(0) {|sum, r| sum + r.all_children_count + 1 }
> -            unless base_set_class.count(:conditions => "#{scope_condition}") == total_nodes
> -              raise ActiveRecord::ActiveRecordError, "Orphaned nodes or endless loops in the tree containing record ##{self.id}"
> -            end
> -          end
> -          return total_nodes
> -        end
> -        
> -        # Re-calculate the left/right values of all nodes in this record's tree. Can be used to convert an ordinary tree into a nested set.
> -        def renumber_full_tree
> -          indexes = []
> -          n = 1
> -          transaction do
> -            for r in roots # because we may have virtual roots
> -              n = r.calc_numbers(n, indexes)
> -            end
> -            for i in indexes
> -              base_set_class.update_all("#{left_col_name} = #{i[:lft]}, #{right_col_name} = #{i[:rgt]}", "#{self.class.primary_key} = #{i[:id]}")
> -            end
> -          end
> -          ## reload?
> -        end
> -        
> -        # Deprecated. Adds a child to this object in the tree.  If this object hasn't been initialized,
> -        # it gets set up as a root node.
> -        #
> -        # This method exists only for compatibility and will be removed in future versions.
> -        def add_child(child)
> -          transaction do
> -            self.reload; child.reload # for compatibility with old version
> -            # the old version allows records with nil values for lft and rgt
> -            unless self[left_col_name] && self[right_col_name]
> -              if child[left_col_name] || child[right_col_name]
> -                raise ActiveRecord::ActiveRecordError, "If parent lft or rgt are nil, you can't add a child with non-nil lft or rgt"
> -              end
> -              base_set_class.update_all("#{left_col_name} = CASE \
> -                                          WHEN id = #{self.id} \
> -                                            THEN 1 \
> -                                          WHEN id = #{child.id} \
> -                                            THEN 3 \
> -                                          ELSE #{left_col_name} END, \
> -                                     #{right_col_name} = CASE \
> -                                          WHEN id = #{self.id} \
> -                                            THEN 2 \
> -                                          WHEN id = #{child.id} \
> -                                            THEN 4 \
> -                                         ELSE #{right_col_name} END",
> -                                      scope_condition)
> -              self.reload; child.reload
> -            end
> -            unless child[left_col_name] && child[right_col_name]
> -              maxright = base_set_class.maximum(right_col_name, :conditions => scope_condition) || 0
> -              base_set_class.update_all("#{left_col_name} = CASE \
> -                                          WHEN id = #{child.id} \
> -                                            THEN #{maxright + 1} \
> -                                          ELSE #{left_col_name} END, \
> -                                      #{right_col_name} = CASE \
> -                                          WHEN id = #{child.id} \
> -                                            THEN #{maxright + 2} \
> -                                          ELSE #{right_col_name} END",
> -                                      scope_condition)
> -              child.reload
> -            end
> -            
> -            child.move_to_child_of(self)
> -            # self.reload ## even though move_to calls target.reload, at least one object in the tests was not reloading (near the end of test_common_usage)
> -          end
> -        # self.reload
> -        # child.reload
> -        #
> -        # if child.root?
> -        #   raise ActiveRecord::ActiveRecordError, "Adding sub-tree isn\'t currently supported"
> -        # else
> -        #   if ( (self[left_col_name] == nil) || (self[right_col_name] == nil) )
> -        #     # Looks like we're now the root node!  Woo
> -        #     self[left_col_name] = 1
> -        #     self[right_col_name] = 4
> -        #     
> -        #     # What do to do about validation?
> -        #     return nil unless self.save
> -        #     
> -        #     child[parent_col_name] = self.id
> -        #     child[left_col_name] = 2
> -        #     child[right_col_name]= 3
> -        #     return child.save
> -        #   else
> -        #     # OK, we need to add and shift everything else to the right
> -        #     child[parent_col_name] = self.id
> -        #     right_bound = self[right_col_name]
> -        #     child[left_col_name] = right_bound
> -        #     child[right_col_name] = right_bound + 1
> -        #     self[right_col_name] += 2
> -        #     self.class.transaction {
> -        #       self.class.update_all( "#{left_col_name} = (#{left_col_name} + 2)",  "#{scope_condition} AND #{left_col_name} >= #{right_bound}" )
> -        #       self.class.update_all( "#{right_col_name} = (#{right_col_name} + 2)",  "#{scope_condition} AND #{right_col_name} >= #{right_bound}" )
> -        #       self.save
> -        #       child.save
> -        #     }
> -        #   end
> -        # end
> -        end
> -        
> -        # Move this node to the left of _target_ (you can pass an object or just an id).
> -        # Unsaved changes in either object will be lost. Raises ActiveRecord::ActiveRecordError if it encounters a problem.
> -        def move_to_left_of(target)
> -          self.move_to target, :left
> -        end
> -        
> -        # Move this node to the right of _target_ (you can pass an object or just an id).
> -        # Unsaved changes in either object will be lost. Raises ActiveRecord::ActiveRecordError if it encounters a problem.
> -        def move_to_right_of(target)
> -          self.move_to target, :right
> -        end
> -        
> -        # Make this node a child of _target_ (you can pass an object or just an id).
> -        # Unsaved changes in either object will be lost. Raises ActiveRecord::ActiveRecordError if it encounters a problem.
> -        def move_to_child_of(target)
> -          self.move_to target, :child
> -        end
> -        
> -        protected
> -        def move_to(target, position) #:nodoc:
> -          raise ActiveRecord::ActiveRecordError, "You cannot move a new node" if new_record?
> -          raise ActiveRecord::ActiveRecordError, "You cannot move a node if left or right is nil" unless self[left_col_name] && self[right_col_name]
> -          
> -          transaction do
> -            self.reload # the lft/rgt values could be stale (target is reloaded below)
> -            if target.is_a?(base_set_class)
> -              target.reload # could be stale
> -            else
> -              target = base_set_class.find(target) # load object if we were given an ID
> -            end
> -            
> -            if (target[left_col_name] >= self[left_col_name]) && (target[right_col_name] <= self[right_col_name])
> -              raise ActiveRecord::ActiveRecordError, "Impossible move, target node cannot be inside moved tree."
> -            end
> -            
> -            # prevent moves between different trees
> -            if target.scope_condition != scope_condition
> -              raise ActiveRecord::ActiveRecordError, "Scope conditions do not match. Is the target in the same tree?"
> -            end
> -            
> -            # the move: we just need to define two adjoining segments of the left/right index and swap their positions
> -            bound = case position
> -              when :child then target[right_col_name]
> -              when :left  then target[left_col_name]
> -              when :right then target[right_col_name] + 1
> -              else raise ActiveRecord::ActiveRecordError, "Position should be :child, :left or :right ('#{position}' received)."
> -            end
> -            
> -            if bound > self[right_col_name]
> -              bound = bound - 1
> -              other_bound = self[right_col_name] + 1
> -            else
> -              other_bound = self[left_col_name] - 1
> -            end
> -            
> -            return if bound == self[right_col_name] || bound == self[left_col_name] # there would be no change, and other_bound is now wrong anyway
> -            
> -            # we have defined the boundaries of two non-overlapping intervals, 
> -            # so sorting puts both the intervals and their boundaries in order
> -            a, b, c, d = [self[left_col_name], self[right_col_name], bound, other_bound].sort
> -            
> -            # change nil to NULL for new parent
> -            if position == :child
> -              new_parent = target.id
> -            else
> -              new_parent = target[parent_col_name].nil? ? 'NULL' : target[parent_col_name]
> -            end
> -            
> -            base_set_class.update_all("\
> -              #{left_col_name} = CASE \
> -                WHEN #{left_col_name} BETWEEN #{a} AND #{b} THEN #{left_col_name} + #{d - b} \
> -                WHEN #{left_col_name} BETWEEN #{c} AND #{d} THEN #{left_col_name} + #{a - c} \
> -                ELSE #{left_col_name} END, \
> -              #{right_col_name} = CASE \
> -                WHEN #{right_col_name} BETWEEN #{a} AND #{b} THEN #{right_col_name} + #{d - b} \
> -                WHEN #{right_col_name} BETWEEN #{c} AND #{d} THEN #{right_col_name} + #{a - c} \
> -                ELSE #{right_col_name} END, \
> -              #{parent_col_name} = CASE \
> -                WHEN #{self.class.primary_key} = #{self.id} THEN #{new_parent} \
> -                ELSE #{parent_col_name} END",
> -              scope_condition)
> -            self.reload
> -            target.reload
> -          end
> -        end
> -        
> -        def check #:nodoc:
> -          # performance improvements (3X or more for tables with lots of columns) by using :select to load just id, lft and rgt
> -          ## i don't use the scope condition here, because it shouldn't be needed
> -          my_children = base_set_class.find(:all, :conditions => "#{parent_col_name} = #{self.id}",
> -            :order => left_col_name, :select => "#{self.class.primary_key}, #{left_col_name}, #{right_col_name}")
> -          
> -          if my_children.empty?
> -            unless self[left_col_name] && self[right_col_name]
> -              raise ActiveRecord::ActiveRecordError, "#{self.class.name}##{self.id}.#{right_col_name} or #{left_col_name} is blank"
> -            end
> -            unless self[right_col_name] - self[left_col_name] == 1
> -              raise ActiveRecord::ActiveRecordError, "#{self.class.name}##{self.id}.#{right_col_name} should be 1 greater than #{left_col_name}"
> -            end
> -          else
> -            n = self[left_col_name]
> -            for c in (my_children) # the children come back ordered by lft
> -              unless c[left_col_name] && c[right_col_name]
> -                raise ActiveRecord::ActiveRecordError, "#{self.class.name}##{c.id}.#{right_col_name} or #{left_col_name} is blank"
> -              end
> -              unless c[left_col_name] == n + 1
> -                raise ActiveRecord::ActiveRecordError, "#{self.class.name}##{c.id}.#{left_col_name} should be 1 greater than #{n}"
> -              end
> -              c.check
> -              n = c[right_col_name]
> -            end
> -            unless self[right_col_name] == n + 1
> -              raise ActiveRecord::ActiveRecordError, "#{self.class.name}##{self.id}.#{right_col_name} should be 1 greater than #{n}"
> -            end
> -          end
> -        end
> -        
> -        # used by the renumbering methods
> -        def calc_numbers(n, indexes) #:nodoc:
> -          my_lft = n
> -          # performance improvements (3X or more for tables with lots of columns) by using :select to load just id, lft and rgt
> -          ## i don't use the scope condition here, because it shouldn't be needed
> -          my_children = base_set_class.find(:all, :conditions => "#{parent_col_name} = #{self.id}",
> -            :order => left_col_name, :select => "#{self.class.primary_key}, #{left_col_name}, #{right_col_name}")
> -          if my_children.empty?
> -            my_rgt = (n += 1)
> -          else
> -            for c in (my_children)
> -              n = c.calc_numbers(n + 1, indexes)
> -            end
> -            my_rgt = (n += 1)
> -          end
> -          indexes << {:id => self.id, :lft => my_lft, :rgt => my_rgt} unless self[left_col_name] == my_lft && self[right_col_name] == my_rgt
> -          return n
> -        end
> -        
> -        
> -        
> -        # The following code is my crude method of making things concurrency-safe.
> -        # Basically, we need to ensure that whenever a record is saved, the lft/rgt
> -        # values are _not_ written to the database, because if any changes to the tree
> -        # structure occurrred since the object was loaded, the lft/rgt values could 
> -        # be out of date and corrupt the indexes. 
> -        # I hope that someone with a little more ruby-foo can look at this and come
> -        # up with a more elegant solution.
> -        private
> -          # override ActiveRecord to prevent lft/rgt values from being saved (can corrupt indexes under concurrent usage)
> -          def update #:nodoc:
> -            connection.update(
> -              "UPDATE #{self.class.table_name} " +
> -              "SET #{quoted_comma_pair_list(connection, special_attributes_with_quotes(false))} " +
> -              "WHERE #{self.class.primary_key} = #{quote_value(id)}",
> -              "#{self.class.name} Update"
> -            )
> -          end
> -
> -          # exclude the lft/rgt columns from update statements
> -          def special_attributes_with_quotes(include_primary_key = true) #:nodoc:
> -            attributes.inject({}) do |quoted, (name, value)|
> -              if column = column_for_attribute(name)
> -                quoted[name] = quote_value(value, column) unless (!include_primary_key && column.primary) || [acts_as_nested_set_options[:left_column], acts_as_nested_set_options[:right_column]].include?(column.name)
> -              end
> -              quoted
> -            end
> -          end
> -
> -          # i couldn't figure out how to call attributes_with_quotes without cutting and pasting this private method in.  :(
> -          # Quote strings appropriately for SQL statements.
> -          def quote_value(value, column = nil) #:nodoc:
> -            self.class.connection.quote(value, column)
> -          end
> -
> -      end
> -    end
> -  end
> -end
> -
> -
> diff --git a/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb b/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb
> index 43b46ca..ed5d3e3 100644
> --- a/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb
> +++ b/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb
> @@ -246,24 +246,33 @@ module SymetrieCom
>          
>          # Returns itself and all nested children.
>          # Pass :exclude => item, or id, or [items or id] to exclude one or more items *and* all of their descendants.
> -        def full_set(special=nil)
> -          if special && special[:exclude]
> -            exclude_str = " AND NOT (#{base_set_class.sql_for(special[:exclude])}) "
> +        # in addition to the standard find opts
> +        def full_set(find_opts={})
> +          exclude = find_opts.delete(:exclude)
> +          if exclude
> +            exclude_str = " AND NOT (#{base_set_class.sql_for(exclude)}) "
>            elsif new_record? || self[right_col_name] - self[left_col_name] == 1
>              return [self]
>            end
> -          base_set_class.find(:all, :conditions => "#{scope_condition} #{exclude_str} AND (#{left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]})", :order => left_col_name)
> +          opts = merge_incoming_opts({:conditions => "#{scope_condition} #{exclude_str} AND (#{left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]})", 
> +                                       :order => left_col_name},
> +                                     find_opts)
> +          base_set_class.find(:all, opts)
>          end
>          
>          # Returns all children and nested children.
>          # Pass :exclude => item, or id, or [items or id] to exclude one or more items *and* all of their descendants.
> -        def all_children(special=nil)
> -          full_set(special) - [self]
> +        # in addition to the standard find opts
> +        def all_children(find_opts=nil)
> +          full_set(find_opts) - [self]
>          end
>          
>          # Returns this record's immediate children.
> -        def children
> -          base_set_class.find(:all, :conditions => "#{scope_condition} AND #{parent_col_name} = #{self.id}", :order => left_col_name)
> +        def children(find_opts={})
> +          opts = merge_incoming_opts({:conditions => "#{scope_condition} AND #{parent_col_name} = #{self.id}", 
> +                                      :order => left_col_name},
> +                                     find_opts)
> +          base_set_class.find(:all, opts)
>          end
>          
>          # Deprecated
> @@ -575,6 +584,14 @@ module SymetrieCom
>              self.class.connection.quote(value, column)
>            end
>  
> +          # accept incoming opts to allow filtering of results.
> +          # So far only tested in limited use cases encountered in oVirt devel.
> +          def merge_incoming_opts(set_opts, incoming_opts)
> +            new_conditions = incoming_opts.delete(:conditions)
> +            set_opts[:conditions] = "(#{set_opts[:conditions]}) AND (#{new_conditions})" if new_conditions
> +            set_opts.merge(incoming_opts)
> +          end
> +
>        end
>      end
>    end
> -- 
> 1.5.4.1
> 

This looks like it does what it's supposed to. ACK, and
committed. Note that the add-host-to-pool dialog exposed a bug in the
pager wherein if there are more than 10 hosts you can't click "OK" --
but nothing in this patch caused that bug.

--Hugh




More information about the ovirt-devel mailing list