Hi, Datatables plugin (used for images and instances listing) has some issues when JS is off and pagination is server-side, this patch replaces it with simpler/cleaner HTML table which works better without JS.
From: Jan Provaznik jprovazn@redhat.com
Replaced Datatables plugin with simpler HTML table because some issues when JS is off. Major issue with Datatables is that it doesn't degrades gracefully when JS is off and server-side pagination.
Images and instances are now displayed by HTML table (there is new helper 'paginated_table'), this table keeps all common functions like sorting, paginating, searching and code is cleaner. Works with JS on or off. --- src/app/controllers/image_controller.rb | 55 +++------------- src/app/controllers/instance_controller.rb | 92 +++++++++++++++------------ src/app/controllers/pool_controller.rb | 63 +++++-------------- src/app/helpers/application_helper.rb | 70 +++++++++++++++++++++ src/app/models/image.rb | 18 ------ src/app/models/instance.rb | 13 ---- src/app/views/image/_images.haml | 13 ++++ src/app/views/image/show.haml | 17 +++++ src/app/views/image/show.html.erb | 83 ------------------------- src/app/views/instance/_instances.haml | 17 +++++ src/app/views/instance/index.html.erb | 4 +- src/app/views/instance/new.html.erb | 72 ++++++++-------------- src/app/views/instance/select_image.haml | 4 + src/app/views/instance/show.html.erb | 29 +--------- src/app/views/layouts/aggregator.haml | 1 + src/app/views/pool/list.haml | 3 + src/app/views/pool/list.html.erb | 38 ----------- src/public/javascripts/application.js | 35 +++++++++++ src/public/stylesheets/dcloud.css | 79 ++++++++++++++++++++++++ 19 files changed, 349 insertions(+), 357 deletions(-) create mode 100644 src/app/views/image/_images.haml create mode 100644 src/app/views/image/show.haml delete mode 100644 src/app/views/image/show.html.erb create mode 100644 src/app/views/instance/_instances.haml create mode 100644 src/app/views/instance/select_image.haml create mode 100644 src/app/views/pool/list.haml delete mode 100644 src/app/views/pool/list.html.erb
diff --git a/src/app/controllers/image_controller.rb b/src/app/controllers/image_controller.rb index d7858b1..b75f692 100644 --- a/src/app/controllers/image_controller.rb +++ b/src/app/controllers/image_controller.rb @@ -26,57 +26,24 @@ class ImageController < ApplicationController end
def show - # FIXME: check on privilege IMAGE_VIEW which currently doesn't exist - #require_privilege(Privilege::POOL_VIEW, @pool) - end - - def images_paginate - # FIXME: check on privilege IMAGE_VIEW which currently doesn't exist - #require_privilege(Privilege::POOL_VIEW, @pool) - - # datatables sends pagination in format: - # iDisplayStart - start index - # iDisplayLength - num of recs - # => we need to count page num - page = params[:iDisplayStart].to_i / Image::per_page - - if params[:mode].to_s == 'simple' - simple_mode = true - cols = Image::COLUMNS_SIMPLE - default_order_col = 1 - else - cols = Image::COLUMNS - simple_mode = false - default_order_col = 2 + if params[:create_instance] + redirect_to :controller => 'instance', :action => 'new', 'instance[image_id]' => (params[:ids] || []).first end
- order_col_rec = cols[params[:iSortCol_0].to_i] - order_col = cols[default_order_col] unless order_col_rec && order_col_rec[:opts][:searchable] - order = order_col[:id] + " " + (params[:sSortDir_0] == 'desc' ? 'desc' : 'asc') + require_privilege(Privilege::IMAGE_VIEW)
- @images = Image.search_filter(params[:sSearch], Image::SEARCHABLE_COLUMNS).paginate( - :page => page + 1, + @order_dir = params[:order_dir] == 'desc' ? 'desc' : 'asc' + @order = params[:order] || 'name' + @images = Image.search_filter(params[:search], Image::SEARCHABLE_COLUMNS).paginate( + :page => params[:page] || 1, + :order => @order + ' ' + @order_dir, :include => :instances, - :order => order, :conditions => {:provider_id => nil} )
- expand_button_html = "<img src='/images/dir_closed.png'>" - - data = @images.map do |i| - if simple_mode - [i.id, i.name, i.architecture, i.instances.size] - else - [i.id, expand_button_html, i.name, i.architecture, i.instances.size, "TODO: some description here?"] - end + if request.xhr? and params[:partial] + render :partial => 'images' + return end - - render :json => { - :sEcho => params[:sEcho], - :iTotalRecords => @images.total_entries, - :iTotalDisplayRecords => @images.total_entries, - :aaData => data - } end - end diff --git a/src/app/controllers/instance_controller.rb b/src/app/controllers/instance_controller.rb index 039ed3a..293c877 100644 --- a/src/app/controllers/instance_controller.rb +++ b/src/app/controllers/instance_controller.rb @@ -23,58 +23,68 @@ require 'util/taskomatic'
class InstanceController < ApplicationController before_filter :require_user + layout :layout
- def index + def layout + return "aggregator" unless request.xhr? end
- def paginated - # datatables sends pagination in format: - # iDisplayStart - start index - # iDisplayLength - num of recs - # => we need to count page num - page = params[:iDisplayStart].to_i / Instance::per_page - - order_col_rec = Instance::COLUMNS[params[:iSortCol_0].to_i] - order_col = Instance::COLUMNS[2] unless order_col_rec && order_col_rec[:opts][:searchable] - order = order_col[:id] + " " + (params[:sSortDir_0] == 'desc' ? 'desc' : 'asc') - - # FIXME only return those instances in pools which user has instance_view - @instances = Instance.search_filter(params[:sSearch], Instance::SEARCHABLE_COLUMNS).paginate( - :page => page + 1, - :order => order + def index + require_privilege(Privilege::INSTANCE_VIEW) + + @order_dir = params[:order_dir] == 'desc' ? 'desc' : 'asc' + @order = params[:order] || 'name' + @instances = Instance.search_filter(params[:search], Instance::SEARCHABLE_COLUMNS).paginate( + :page => params[:page] || 1, + :order => @order + ' ' + @order_dir )
- recs = @instances.map do |i| - [ - i.id, - i.get_action_list.map {|action| "<a href="#{url_for :controller => "instance", :action => "instance_action", :id => i.id, :instance_action => action}">#{action}</a>"}.join(" | "), - i.name, - i.state, - i.hardware_profile.name, - i.image.name, - i.cloud_account.nil? ? "" : i.cloud_account.provider.name, - i.cloud_account.nil? ? "" : i.cloud_account.name - ] + if request.xhr? and params[:partial] + render :partial => 'instances' + return end - - render :json => { - :sEcho => params[:sEcho], - :iTotalRecords => @instances.total_entries, - :iTotalDisplayRecords => @instances.total_entries, - :aaData => recs - } end
- # Right now this is essentially a duplicate of PoolController#show, - # but really it should be a single instance should we decide to have a page - # for that. Redirect on create was all that brought you here anyway, so - # should be unused for the moment. - def show - @instances = Instance.find(:all, :conditions => {:pool_id => params[:id]}) - @pool = Pool.find(params[:id]) - require_privilege(Privilege::INSTANCE_VIEW,@pool) + def select_image + if params[:select] + redirect_to :action => 'new', 'instance[image_id]' => (params[:ids] || []).first + end + + require_privilege(Privilege::IMAGE_VIEW) + @order_dir = params[:order_dir] == 'desc' ? 'desc' : 'asc' + @order = params[:order] || 'name' + @images = Image.search_filter(params[:search], Image::SEARCHABLE_COLUMNS).paginate( + :page => params[:page] || 1, + :order => @order + ' ' + @order_dir, + :conditions => {:provider_id => nil} + ) + + if request.xhr? and params[:partial] + render :partial => 'image/images' + return + end end
+ ## Right now this is essentially a duplicate of PoolController#show, + # # but really it should be a single instance should we decide to have a page + # # for that. Redirect on create was all that brought you here anyway, so + # # should be unused for the moment. + #def show + # require_privilege(Privilege::INSTANCE_VIEW,@pool) + # @pool = Pool.find(params[:id]) + # @order_dir = params[:order_dir] == 'desc' ? 'desc' : 'asc' + # @order = params[:order] || 'name' + # @instances = Instance.search_filter(params[:search], Instance::SEARCHABLE_COLUMNS).paginate( + # :page => params[:page] || 1, + # :order => @order + ' ' + @order_dir, + # :conditions => {:pool_id => @pool.id} + # ) + # if request.xhr? and params[:partial] + # render :partial => 'instances' + # return + # end + #end + def new @instance = Instance.new(params[:instance]) require_privilege(Privilege::INSTANCE_MODIFY, @instance.pool) if @instance.pool diff --git a/src/app/controllers/pool_controller.rb b/src/app/controllers/pool_controller.rb index e687c0b..fdb903d 100644 --- a/src/app/controllers/pool_controller.rb +++ b/src/app/controllers/pool_controller.rb @@ -36,10 +36,23 @@ class PoolController < ApplicationController #FIXME: clean this up, many error cases here @pool = Pool.find(params[:id]) require_privilege(Privilege::INSTANCE_VIEW,@pool) - # pass nil into Taskomatic as we're not working off a task here - Taskomatic.new(nil,logger).pool_refresh(@pool) - @pool.reload - @instances = @pool.instances + ## pass nil into Taskomatic as we're not working off a task here + #Taskomatic.new(nil,logger).pool_refresh(@pool) + #@pool.reload + + @order_dir = params[:order_dir] == 'desc' ? 'desc' : 'asc' + @order = params[:order] || 'name' + + @instances = Instance.search_filter(params[:search], Instance::SEARCHABLE_COLUMNS).paginate( + :page => params[:page] || 1, + :order => @order + ' ' + @order_dir, + :conditions => {:pool_id => @pool.id} + ) + + if request.xhr? and params[:partial] + render :partial => 'instance/instances' + return + end end
def hardware_profiles @@ -89,48 +102,6 @@ class PoolController < ApplicationController def delete end
- def instances_paginate - @pool = Pool.find(params[:id]) - require_privilege(Privilege::POOL_VIEW, @pool) - - # datatables sends pagination in format: - # iDisplayStart - start index - # iDisplayLength - num of recs - # => we need to count page num - page = params[:iDisplayStart].to_i / Instance::per_page - - order_col_rec = Instance::COLUMNS[params[:iSortCol_0].to_i] - order_col = Instance::COLUMNS[2] unless order_col_rec && order_col_rec[:opts][:searchable] - order = order_col[:id] + " " + (params[:sSortDir_0] == 'desc' ? 'desc' : 'asc') - - @instances = Instance.search_filter(params[:sSearch], Instance::SEARCHABLE_COLUMNS).paginate( - :page => page + 1, - :order => order, - :conditions => {:pool_id => @pool.id} - ) - - recs = @instances.map do |i| - [ - i.id, - i.get_action_list.map {|action| "<a href="#{url_for :controller => "instance", :action => "instance_action", :id => i.id, :instance_action => action}">#{action}</a>"}.join(" | "), - i.name, - i.state, - i.hardware_profile.name, - i.image.name, - i.cloud_account.nil? ? "" : i.cloud_account.provider.name, - i.cloud_account.nil? ? "" : i.cloud_account.name - ] - end - - render :json => { - :sEcho => params[:sEcho], - :iTotalRecords => @instances.total_entries, - :iTotalDisplayRecords => @instances.total_entries, - :aaData => recs - } - end - - def accounts_for_pool @pool = Pool.find(params[:pool_id]) require_privilege(Privilege::ACCOUNT_VIEW,@pool) diff --git a/src/app/helpers/application_helper.rb b/src/app/helpers/application_helper.rb index 4565fb0..9109e0f 100644 --- a/src/app/helpers/application_helper.rb +++ b/src/app/helpers/application_helper.rb @@ -185,4 +185,74 @@ module ApplicationHelper end day_str + hours_to_seconds end + + def column_header(column, order, order_dir, check_all) + next_dir = order_dir.to_s == 'asc' ? 'desc' : 'asc' + if order and column[:id] == order + dir = order_dir.to_s == 'desc' ? 'desc' : 'asc' + cls = "ordercol #{dir}" + else + cls = nil + end + + if column[:sortable] + label = link_to( + column[:header], + {:action => controller.action_name, :partial => true, :order => column[:id], :order_dir => next_dir, :search => params[:search]}, + :class => cls) + elsif check_all.to_s == column[:id] + label = check_box_tag 'check_all' + else + label = column[:header] + end + + content_tag 'th', label + end + + def paginated_table(html_id, columns, data, opts = {}) + search_url = url_for(:partial => true, :order => opts[:order], :order_dir => opts[:order_dir]) + extend_table_js = "<script type="text/javascript">extend_table('#{html_id}');#{opts[:load_callback]};</script>" + extend_sfield_js = "<script type="text/javascript">extend_table_search_field('#{html_id}', '#{search_url}')</script>" + + rows = data.map do |rec| + if block_given? + capture_haml{yield rec} + else + content_tag 'tr', :class => cycle('even', 'odd') do + columns.map { |c| content_tag 'td', rec[c[:id]] } + end + end + end + + header_cols = columns.map {|c| column_header(c, opts[:order], opts[:order_dir], opts[:check_all])} + + table = content_tag 'table' do + content_tag('thead', content_tag('tr', header_cols)) + content_tag('tbody', rows) + end + + search = content_tag 'div', :class => 'search_field' do + 'Search ' + text_field_tag('search', params[:search], :size => 10) + end + + header = content_tag 'div', :class => 'header' do + search + content_tag('div', opts[:title], :class => 'title') + end + + footer = content_tag 'div', :class => 'footer' do + will_paginate(data, :params => {:partial => true}).to_s + + page_entries_info(data).to_s + end + + ajax_content = table + footer + extend_table_js + + if params[:partial] and request.xhr? + ajax_content + else + content_tag 'div', :id => html_id, :class => "dtable #{opts[:class]}" do + header + + content_tag('div', ajax_content, :class => 'wrapper') + + extend_sfield_js + end + end + end end diff --git a/src/app/models/image.rb b/src/app/models/image.rb index 9df4185..f3304cf 100644 --- a/src/app/models/image.rb +++ b/src/app/models/image.rb @@ -48,24 +48,6 @@ class Image < ActiveRecord::Base
validates_presence_of :architecture, :if => :provider
- # used to get sorting column in controller and in view to generate datatable definition and - # html table structure - COLUMNS = [ - {:id => 'id', :header => '<input type="checkbox" id="image_id_all" onclick="checkAll(event)">', :opts => {:checkbox_id => 'image_id', :searchable => false, :sortable => false, :width => '1px', :class => 'center'}}, - {:id => 'expand_button', :header => '', :opts => {:searchable => false, :sortable => false, :width => '1px'}}, - {:id => 'name', :header => 'Name', :opts => {:width => "30%"}}, - {:id => 'architecture', :header => 'Architecture', :opts => {:width => "10%"}}, - {:id => 'instances', :header => 'Instances', :opts => {:sortable => false, :width => "10%"}}, - {:id => 'details', :header => '', :opts => {:sortable => false, :visible => false, :searchable => false}}, - ] - - COLUMNS_SIMPLE = [ - {:id => 'id', :header => '', :opts => {:visible => false, :searchable => false, :sortable => false, :width => '1px', :class => 'center'}}, - {:id => 'name', :header => 'Name', :opts => {:width => "50%"}}, - {:id => 'architecture', :header => 'Architecture', :opts => {:width => "30%"}}, - {:id => 'instances', :header => 'Instances', :opts => {:sortable => false, :width => "20%"}}, - ] - SEARCHABLE_COLUMNS = %w(name architecture)
def provider_image? diff --git a/src/app/models/instance.rb b/src/app/models/instance.rb index 6be4931..0212e24 100644 --- a/src/app/models/instance.rb +++ b/src/app/models/instance.rb @@ -59,19 +59,6 @@ class Instance < ActiveRecord::Base STATES = [STATE_NEW, STATE_PENDING, STATE_RUNNING, STATE_SHUTTING_DOWN, STATE_STOPPED, STATE_CREATE_FAILED]
- # used to get sorting column in controller and in view to generate datatable definition and - # html table structure - COLUMNS = [ - {:id => 'id', :header => '<input type="checkbox" id="image_id_all" onclick="checkAll(event)">', :opts => {:checkbox_id => 'image_id', :searchable => false, :sortable => false, :width => '1px', :class => 'center'}}, - {:id => 'actions', :header => 'Actions', :opts => {:width => "10%", :sortable => false}}, - {:id => 'name', :header => 'Name', :opts => {:width => "25%"}}, - {:id => 'state', :header => 'State', :opts => {:width => "10%"}}, - {:id => 'hwprofile', :header => 'HW profile', :opts => {:width => "15%"}}, - {:id => 'template', :header => 'Template', :opts => {:sortable => false, :width => "15%"}}, - {:id => 'provider', :header => 'Provider', :opts => {:width => "10%"}}, - {:id => 'account', :header => 'Account', :opts => {:width => "10%"}}, - ] - SEARCHABLE_COLUMNS = %w(name state)
validates_inclusion_of :state, diff --git a/src/app/views/image/_images.haml b/src/app/views/image/_images.haml new file mode 100644 index 0000000..0ef78d9 --- /dev/null +++ b/src/app/views/image/_images.haml @@ -0,0 +1,13 @@ +- columns = [ | + {:id => 'id', :header => ''}, | + {:id => 'name', :header => 'Name', :sortable => true}, | + {:id => 'architecture', :header => 'Architecture', :sortable => true}, | + {:id => 'instances', :header => 'Instances'}, | +] | + += paginated_table('images_table', columns, @images, {:load_callback => 'table_loaded()', :order => @order, :order_dir => @order_dir, :title => 'List of templates', :check_all => 'id'}) do |rec| + %tr{:class => "#{cycle('even', 'odd')}"} + %td= check_box_tag 'ids[]', rec[:id] + %td{:class => 'image_name'}= rec[:name] + %td= rec[:architecture] + %td= rec[:instances] diff --git a/src/app/views/image/show.haml b/src/app/views/image/show.haml new file mode 100644 index 0000000..5720443 --- /dev/null +++ b/src/app/views/image/show.haml @@ -0,0 +1,17 @@ +- content_for :scripts do + :javascript + function check_checkboxes() { + var checked_count = $('input[name="ids[]"]:checked', $("#images_table")).length; + $('input[name="create_instance"]').attr('disabled', checked_count != 1); + } + function table_loaded() { + //$("input[name='ids[]']", $("#images_table")).change(function() {console.warn('x');check_checkboxes()}); + //FIXME: this doesn't work in 100%, but don't know better way + $("#images_table").click(function() {check_checkboxes()}); + check_checkboxes(); + } + +- form_tag({:action => 'show'}, {:class => 'dtable_form'}) do + .action_buttons + = submit_tag "Create instance", :name => "create_instance" + = render :partial => 'images' diff --git a/src/app/views/image/show.html.erb b/src/app/views/image/show.html.erb deleted file mode 100644 index 5a7cb53..0000000 --- a/src/app/views/image/show.html.erb +++ /dev/null @@ -1,83 +0,0 @@ -<script type="text/javascript"> - - function fnOpenClose() { - $('td img', dataTable_images_table.fnGetNodes() ).each( function () { - $(this).click( function () { - var nTr = this.parentNode.parentNode; - if ( this.src.match('dir_open') ) - { - /* This row is already open - close it */ - this.src = "/images/dir_closed.png"; - /* fnClose doesn't do anything for server-side processing - do it ourselves :-) */ - var nRemove = $(nTr).next()[0]; - nRemove.parentNode.removeChild( nRemove ); - } - else - { - /* Open this row */ - this.src = "/images/dir_open.png"; - dataTable_images_table.fnOpen( nTr, dataTable_images_table.fnGetData(nTr)[5], 'details' ); - } - }); - }); - } - - function getCheckedRows() { - return $('input[name="image_id[]"]:checked', dataTable_images_table).length; - } - - function showButtons() { - getCheckedRows(); - $('input[name="create_image"]').attr('disabled', getCheckedRows() != 1); - } - - function checkRow(ev) { - var box = $('input[name="image_id[]"]', ev.target.parentNode); - if ($(ev.target).attr('name') != "image_id[]") - box.attr('checked', !box.attr('checked')); - showButtons(); - } - - function checkAll(ev) { - $('input[name="image_id[]"]', dataTable_images_table).attr('checked', $(ev.target).attr('checked')) - } - - function drawCallback() { - $('#image_id_all').attr('checked', false); - fnOpenClose(); - } - - function createInstance() { - var selected_row = $('input[name="image_id[]"]:checked', dataTable_images_table); - location.href = '<%= url_for(:controller => "instance", :action => "new") %>?instance[image_id]=' + selected_row.attr('value'); - } -</script> - -<%= datatable( - Image::COLUMNS.map {|c| c[:opts]}, - { - :table_dom_id => 'images_table', - :per_page => Image::per_page, - :sort_by => "[2, 'asc']", - :serverside => true, - :ajax_source => url_for(:controller => 'image', :action => 'images_paginate'), - :append => ".fnSetFilteringDelay()", - :persist_state => false, - :click_callback => "function(ev) {checkRow(ev);}", - :draw_callback => "drawCallback", - } -) %> - <div style="padding:20px"> - <input type="button" value="Create instance" name="create_image" disabled=true onclick="createInstance()"> - </div> - <table class="datatable display" id="images_table"> - <thead> - <tr> - <% Image::COLUMNS.each do |c| %> - <%= "<th>#{c[:header]}</th>" %> - <% end %> - </tr> - </thead> - <tbody> - </tbody> -</table> diff --git a/src/app/views/instance/_instances.haml b/src/app/views/instance/_instances.haml new file mode 100644 index 0000000..cc5a2c2 --- /dev/null +++ b/src/app/views/instance/_instances.haml @@ -0,0 +1,17 @@ +- columns = [ | + {:id => 'id', :header => ''}, | + {:id => 'name', :header => 'Name', :sortable => true}, | + {:header => 'Details'}, | + {:id => 'template', :header => 'Template'}, | + {:id => 'state', :sortable => true, :header => 'State'}, | + {:id => 'time_last_running', :header => 'Time last running'}, | +] | + += paginated_table('instances_table', columns, @instances, {:order => @order, :order_dir => @order_dir, :title => 'List of instances', :check_all => 'id'}) do |rec| + %tr{:class => "#{cycle('even', 'odd')}"} + %td= check_box_tag 'ids[]', rec.id + %td= rec.name + %td Details + %td= rec.image.name + %td= rec.state + %td= rec.time_last_running diff --git a/src/app/views/instance/index.html.erb b/src/app/views/instance/index.html.erb index c19c3fa..e57f78c 100644 --- a/src/app/views/instance/index.html.erb +++ b/src/app/views/instance/index.html.erb @@ -1 +1,3 @@ -<%= render :partial => 'list' %> +<% form_tag({:action => 'instance_action'}, {:class => 'dtable_form'}) do %> + <%= render :partial => 'instance/instances' %> +<% end %> diff --git a/src/app/views/instance/new.html.erb b/src/app/views/instance/new.html.erb index 86810bc..fa46ac4 100644 --- a/src/app/views/instance/new.html.erb +++ b/src/app/views/instance/new.html.erb @@ -1,23 +1,32 @@ <script type="text/javascript"> - function showImages(ev) { - $("#images_table_wrapper").css('display', 'block'); - images_dialog = $("#images_table_wrapper").dialog({ - title: "Select template for new instance", - dialogClass: 'flora', - width: 600, - height: 500, - modal: true, - overlay: {opacity: 0.2, background: "black"} + $(document).ready(function() { + $(".select_image").click(function() { + var wrapper = $("#select_template_dialog"); + if (wrapper.length == 0) wrapper = $('<div id="select_template_dialog"></div>'); + wrapper.dialog({ + title: "Select template for new instance", + width: 600, + height: 500, + modal: true, + overlay: {opacity: 0.2, background: "black"} + }); + wrapper.load('<%= url_for :action => 'select_image' %>', null, extend_select_button); + return false; }); - } + });
- function selectImage(ev) { - var image_id = dataTable_images_table.fnGetData(ev.target.parentNode)[0]; - var image_name = dataTable_images_table.fnGetData(ev.target.parentNode)[1]; - $("#images_table_wrapper").css('display', 'none'); - $("#images_table_wrapper").dialog('close'); - $('input[name="image_selector"]').attr('value', image_name); - $('input[name="instance[image_id]"]').attr('value', image_id); + function extend_select_button() { + $(":submit[name=select]").click(function() { + var checkbox = $("input[name='ids[]']:checked").first(); + var id = checkbox.val(); + if (id !== undefined) { + var name = $(".image_name", checkbox.parent().parent()).text(); + $(".select_image").text(name); + $("input[name='instance[image_id]']").val(id); + } + $("#select_template_dialog").dialog('close'); + return false; + }); }
function poolSelected(field) { @@ -35,7 +44,7 @@ <li><label>Name<span>Name for this new Instance</span></label><%= text_field :instance, :name, :class => "txtfield" %></li>
<li><label>Template<span>Choose a template to use</span></label> - <input type="button" style="cursor:pointer" onclick="showImages(event)" name="image_selector" value="<%= @instance.image ? @instance.image.name : "click to select template"%>"/> + <%= link_to(@instance.image ? @instance.image.name : "Select template...", {:action => 'select_image'}, {:class => 'actionlink select_image'}) %> </li> <input type=hidden name="instance[image_id]" value="<%= @instance.image ? @instance.image.id : '' %>">
@@ -71,30 +80,3 @@ <%= submit_tag "Save", :class => "submit" %> <% end %> </div> - -<%= datatable( - Image::COLUMNS_SIMPLE.map {|c| c[:opts]}, - { - :table_dom_id => 'images_table', - :per_page => Image::per_page, - :sort_by => "[2, 'asc']", - :serverside => true, - :ajax_source => url_for(:controller => 'image', :action => 'images_paginate') + "?mode=simple", - :append => ".fnSetFilteringDelay()", - :click_callback => "function(event) {selectImage(event)}", - } -) %> - -<div style="display:none" id="images_table_wrapper"> - <table class="datatable display" id="images_table"> - <thead> - <tr> - <% Image::COLUMNS_SIMPLE.each do |c| %> - <%= "<th>#{c[:header]}</th>" %> - <% end %> - </tr> - </thead> - <tbody> - </tbody> - </table> -</div> diff --git a/src/app/views/instance/select_image.haml b/src/app/views/instance/select_image.haml new file mode 100644 index 0000000..c58d459 --- /dev/null +++ b/src/app/views/instance/select_image.haml @@ -0,0 +1,4 @@ +- form_tag({:action => 'select_image'}, {:class => 'dtable_form'}) do + = render :partial => 'image/images' + .action_buttons + = submit_tag "Select", :name => "select" diff --git a/src/app/views/instance/show.html.erb b/src/app/views/instance/show.html.erb index a2bfe2f..689ead1 100644 --- a/src/app/views/instance/show.html.erb +++ b/src/app/views/instance/show.html.erb @@ -1,29 +1,2 @@ -<% if @instances.size == 0 %> -<h1>There are no pools to display</h1> -<% else %> - <table> - <thead> - <tr> - <th scope="col">Actions</th> - <th scope="col">Name</th> - </tr> - </thead> - <tbody> - <%@instances.each {|instance| %> - <tr> - <td> - <%instance.get_action_list.each {|action|%> - <%= link_to action, :controller => "instance", - :action => "instance_action", - :id => instance, - :instance_action => action %> - <br/> - <% } %> - </td> - <td><div><%= instance.name %></div></td> - </tr> - <% } %> - </tbody> - </table> -<% end %> +<%= render :partial => 'instance/instances' %> <%= link_to "Add a new instance", :controller => "instance", :action => "new", :id => @pool %> diff --git a/src/app/views/layouts/aggregator.haml b/src/app/views/layouts/aggregator.haml index 0a187bb..c79dce7 100644 --- a/src/app/views/layouts/aggregator.haml +++ b/src/app/views/layouts/aggregator.haml @@ -14,6 +14,7 @@ = stylesheet_link_tag 'jquery.ui-1.8.1/jquery-ui-1.8.1.custom.css' = stylesheet_link_tag 'jquery-datatables/demo_table_jui.css'
+ = javascript_include_tag "application.js" = javascript_include_tag "jquery-1.4.2.min.js" = javascript_include_tag "facebox.js" = javascript_include_tag "jquery.ui-1.8.1/jquery-ui-1.8.1.custom.min.js" diff --git a/src/app/views/pool/list.haml b/src/app/views/pool/list.haml new file mode 100644 index 0000000..2014ed9 --- /dev/null +++ b/src/app/views/pool/list.haml @@ -0,0 +1,3 @@ += render :partial => "instance/instances" + += link_to "Add a new instance", {:controller => "instance", :action => "new", "instance[pool_id]" => @pool}, :class=>"actionlink" diff --git a/src/app/views/pool/list.html.erb b/src/app/views/pool/list.html.erb deleted file mode 100644 index 38e46e7..0000000 --- a/src/app/views/pool/list.html.erb +++ /dev/null @@ -1,38 +0,0 @@ -<script type="text/javascript"> - function clickRow(ev) { - var box = $('input[name="image_id[]"]', ev.target.parentNode); - if ($(ev.target).attr('name') != "image_id[]") - box.attr('checked', !box.attr('checked')); - } - - function checkAll(ev) { - $('input[name="image_id[]"]', dataTable_instances_table).attr('checked', $(ev.target).attr('checked')) - } -</script> - -<%= datatable( - Instance::COLUMNS.map {|c| c[:opts]}, - { - :table_dom_id => 'instances_table', - :per_page => Instance::per_page, - :sort_by => "[3, 'asc']", - :serverside => true, - :ajax_source => url_for(:controller => 'pool', :action => 'instances_paginate') + "/#{@pool.id}", - :append => ".fnSetFilteringDelay()", - :persist_state => false, - :click_callback => "function(ev) {clickRow(ev);}", - } -) %> - -<table class="datatable display" id="instances_table"> - <thead> - <tr> - <% Instance::COLUMNS.each do |c| %> - <%= "<th>#{c[:header]}</th>" %> - <% end %> - </tr> - </thead> - <tbody> - </tbody> -</table> -<%= link_to "Add a new instance", {:controller => "instance", :action => "new", "instance[pool_id]" => @pool}, :class=>"actionlink"%> diff --git a/src/public/javascripts/application.js b/src/public/javascripts/application.js index fe45776..be1bc91 100644 --- a/src/public/javascripts/application.js +++ b/src/public/javascripts/application.js @@ -1,2 +1,37 @@ // Place your application-specific JavaScript functions and classes here // This file is automatically included by javascript_include_tag :defaults + +function extend_table(id, search_url) { + var table = $("#" + id); + var wrapper = $(".wrapper", table); + // make column head links ajax + $("table thead tr th a", table).click(function() {wrapper.load($(this).attr('href'));return false;}); + + // pagination links should by ajax too + $(".pagination a", table).click(function() {wrapper.load($(this).attr('href'));return false;}); + + // check all checkboxes when checking "check all" + $("input[name='check_all']", table).click(function() {$("input[name='ids[]']", table).attr('checked', $(this).attr('checked'))}); + + // check checkbox if row is clicked + $("table tbody tr", table).click(function(ev) { + if ($(ev.target).attr('name') == 'ids[]') return; + var box = $("input[name='ids[]']", this); + box.attr('checked', !box.attr('checked')); + }); +} + +// table ajax search with 1 sec delay +function extend_table_search_field(id, search_url) { + var table = $("#" + id); + var wrapper = $(".wrapper", table); + $(".search_field input", table).keypress(function() { + if (table.search_lock) return; + table.search_lock = true; + setTimeout(function() { + table.search_lock = false; + var search = $(".search_field input", table).val(); + wrapper.load(search_url + '&search=' + search); + }, 1000) + }); +} diff --git a/src/public/stylesheets/dcloud.css b/src/public/stylesheets/dcloud.css index 0183a55..ec0ea63 100644 --- a/src/public/stylesheets/dcloud.css +++ b/src/public/stylesheets/dcloud.css @@ -301,3 +301,82 @@ a.button_link { #dashboard-tabs ul, #provider-tabs ul, #pool-tabs ul{ float: none; } + +/* paginated data table */ +.dtable { + border: 1px solid #bbb; +} + +.dtable table { + width: 100%; +} + +.dtable_form { + width: 96%; + padding: 20px 2% 20px 2%; +} + +.dtable tr:hover { + background-color: #eee; +} + +.dtable table th { + text-align: left; +} + +.dtable .ordercol { + background-position:left center; + background-repeat:no-repeat; + padding-left:20px; +} + +.dtable .asc { + background-image: url(../images/Sort_down_11.png); +} + +.dtable .desc { + background-image: url(../images/Sort_up_11.png); +} + +.dtable .header { + height: 25px; + background-color: #eee; + border-bottom: 1px solid #bbb; +} + +.dtable .footer { + height: 24px; + background-color: #eee; + border-top: 1px solid #bbb; + line-height: 1.8; + padding-left: 10px; +} + +.dtable .search_field { + font-size: 15px; + float: right; +} + +.dtable .search_field input { + font-size: 15px; +} + +.dtable .pagination { + float: right; + padding-right: 10px; +} + +.dtable .pagination a { + color: blue; +} + +.dtable .title { + font-size: 15px; + font-weight: bold; + padding-left: 10px; +} + +.action_buttons { + padding-top: 10px; + padding-bottom: 10px; +}
Sending patch 2/2 as attachment because of long lines issue.
On 07/01/2010 03:58 PM, jprovazn@redhat.com wrote:
Hi, Datatables plugin (used for images and instances listing) has some issues when JS is off and pagination is server-side, this patch replaces it with simpler/cleaner HTML table which works better without JS. _______________________________________________ deltacloud-devel mailing list deltacloud-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/deltacloud-devel
Hi, ignore this thread please. I sent patch again (this version was not applicable on actual next branch).
Jan
deltacloud-devel@lists.fedorahosted.org