[Ovirt-devel] [PATCH] refactored popups and grids to pull more into the layouts, and static js functions into ovirt.js.
Scott Seago
sseago at redhat.com
Thu May 29 19:54:19 UTC 2008
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
--------------040805000303080804070604--
More information about the ovirt-devel
mailing list