ajax_scaffold_plugin 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/CHANGELOG +3 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +13 -0
  4. data/app/views/ajax_scaffold/_column_headings.rhtml +20 -0
  5. data/app/views/ajax_scaffold/_column_totals.rhtml +10 -0
  6. data/app/views/ajax_scaffold/_form.rhtml +5 -0
  7. data/app/views/ajax_scaffold/_form_messages.rhtml +5 -0
  8. data/app/views/ajax_scaffold/_messages.rhtml +10 -0
  9. data/app/views/ajax_scaffold/_new_edit.rhtml +40 -0
  10. data/app/views/ajax_scaffold/_pagination_links.rhtml +26 -0
  11. data/app/views/ajax_scaffold/_row.rhtml +46 -0
  12. data/app/views/ajax_scaffold/cancel.rjs +12 -0
  13. data/app/views/ajax_scaffold/create.rjs +20 -0
  14. data/app/views/ajax_scaffold/destroy.rjs +11 -0
  15. data/app/views/ajax_scaffold/edit.rjs +16 -0
  16. data/app/views/ajax_scaffold/list.rhtml +1 -0
  17. data/app/views/ajax_scaffold/new.rjs +14 -0
  18. data/app/views/ajax_scaffold/table.rhtml +63 -0
  19. data/app/views/ajax_scaffold/update.rjs +19 -0
  20. data/init.rb +27 -0
  21. data/install.rb +1 -0
  22. data/lib/ajax_scaffold_plugin.rb +505 -0
  23. data/lib/base/ajax_scaffold.rb +180 -0
  24. data/public/blank.html +33 -0
  25. data/public/images/add.gif +0 -0
  26. data/public/images/arrow_down.gif +0 -0
  27. data/public/images/arrow_up.gif +0 -0
  28. data/public/images/indicator-small.gif +0 -0
  29. data/public/images/indicator.gif +0 -0
  30. data/public/javascripts/ajax_scaffold.js +126 -0
  31. data/public/javascripts/dhtml_history.js +961 -0
  32. data/public/javascripts/rico_corner.js +781 -0
  33. data/public/stylesheets/ajax_scaffold.css +482 -0
  34. data/tasks/ajax_scaffold_tasks.rake +4 -0
  35. data/test/ajax_scaffold_test.rb +8 -0
  36. metadata +90 -0
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ 3.2.0 ==========
2
+
3
+ * Initial release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Richard White
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,13 @@
1
+ ********************************************************************************
2
+ ** For all documentation see the project website: http://www.ajaxscaffold.com **
3
+ ********************************************************************************
4
+
5
+ Ajax Scaffold plugin by Scott Rutherford (scott@caronsoftware.com) & Richard White (rrwhite@gmail.com)
6
+
7
+ Uses DhtmlHistory by Brad Neuberg (bkn3@columbia.edu)
8
+ http://codinginparadise.org
9
+
10
+ Uses Querystringby by Adam Vandenberg
11
+ http://adamv.com/dev/javascript/querystring
12
+
13
+ Released under the MIT license (included)
@@ -0,0 +1,20 @@
1
+ <% for scaffold_column in @scaffold_columns %>
2
+ <% column_sort_direction = column_sort_direction(scaffold_column.name, params) %>
3
+ <% sort_params = params.merge(:controller => @scaffold_controller, :action => 'update_'+@prefix+'table',
4
+ :sort => scaffold_column.name, :sort_direction => column_sort_direction, :page => 1) %>
5
+ <% column_header_id = scaffold_column_header_id(sort_params.merge(:column_name => scaffold_column.name)) %>
6
+ <th id="<%= column_header_id %>" <%= "class=\"sorted #{current_sort_direction(params)}\"" if scaffold_column.name == current_sort(params) %>>
7
+ <% if scaffold_column.sortable? %>
8
+ <% href = url_for(sort_params) %>
9
+ <%= link_to_remote scaffold_column.label,
10
+ { :url => sort_params,
11
+ :before => "addAjaxTableUrlToHistory('#{href}')",
12
+ :loading => "Element.addClassName('#{column_header_id}','loading');",
13
+ :update => scaffold_content_id(sort_params) },
14
+ { :href => href } %>
15
+ <% else %>
16
+ <p><%= scaffold_column.label %></p>
17
+ <% end %>
18
+ </th>
19
+ <% end %>
20
+ <th></th>
@@ -0,0 +1,10 @@
1
+ <tr class="ajax-scaffold-totals">
2
+ <% for scaffold_column in @scaffold_columns %>
3
+ <td>
4
+ <% if show_totals_for_column?(scaffold_column.name) %><p><%= scaffold_column.total %></p><% else %>&nbsp;<% end %>
5
+ </td>
6
+ <% end %>
7
+ <% unless @no_edit && @no_delete %>
8
+ <td class="actions">&nbsp;</td>
9
+ <% end %>
10
+ </tr>
@@ -0,0 +1,5 @@
1
+ <fieldset>
2
+ <div class="row">
3
+ <%= all_input_tags(scaffold_class, scaffold_name, { :exclude => %w(created_on created_at updated_on updated_at) }) %>
4
+ </div>
5
+ </fieldset>
@@ -0,0 +1,5 @@
1
+ <%= render :partial => scaffold_partial_path('messages') %>
2
+
3
+ <% unless eval("@"+params[:scaffold_id]+".nil?") %>
4
+ <%= error_messages_for "#{params[:scaffold_id]}" %>
5
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <% for name in [:info, :warning, :error] %>
2
+ <% if @flash[name] %>
3
+ <p class="<%= "#{name}-message message" %>" >
4
+ <%= @flash[name] %>
5
+ <% if request.xhr? %>
6
+ <a href="#" onclick="Element.remove(this.parentNode); return false;">Close</a>
7
+ <% end %>
8
+ </p>
9
+ <% end %>
10
+ <% end %>
@@ -0,0 +1,40 @@
1
+ <% if not request.xhr? %>
2
+ <table class="ajax-scaffold" cellpadding="0" cellspacing="0">
3
+ <tbody>
4
+ <% end %>
5
+ <tr id="<%= element_row_id(@options) %>" <%= "style=\"display:none;\"" if request.xhr? %>>
6
+ <td id="<%= element_cell_id(@options) %>" class="<%= @options[:action] %>" colspan="<%= num_columns %>">
7
+
8
+ <%= form_remote_tag :url => @options.merge(:controller => @scaffold_controller),
9
+ :loading => "Element.show('#{loading_indicator_id(@options)}'); Form.disable('#{element_form_id(@options)}');",
10
+ :html => { :href => url_for(@options.merge(:controller => @scaffold_controller)),
11
+ :id => element_form_id(@options) } %>
12
+
13
+ <%= hidden_field_tag "scaffold_id", @options[:scaffold_id] %>
14
+
15
+ <h4><%= Inflector.titleize(@options[:action]) %> <%= @scaffold_singular_name.titleize if @suffix.empty? %></h4>
16
+
17
+ <% if request.xhr? %>
18
+ <div id="<%= element_messages_id(@options) %>" class="messages-container"></div>
19
+ <% else %>
20
+ <%= render :partial => scaffold_partial_path('form_messages') %>
21
+ <% end %>
22
+
23
+ <%= render :partial => scaffold_partial_path('form'), :locals => { :scaffold_class => @scaffold_class, :scaffold_name => @scaffold_singular_name } %>
24
+
25
+ <p class="form-footer">
26
+ <%= submit_tag Inflector.titleize(@options[:action]), :class => "submit" %>
27
+ <% cancel_params = @options.merge(:controller => @scaffold_controller, :action => 'cancel'+@suffix, :referring_action => @options[:action]) %>
28
+ <%= link_to_remote "Cancel",
29
+ { :url => cancel_params,
30
+ :loading => "Element.show('#{loading_indicator_id(@options)}');" },
31
+ { :href => url_for(cancel_params) } %>
32
+ <%= loading_indicator_tag @options %>
33
+ </p>
34
+ <%= end_form_tag %>
35
+ </td>
36
+ </tr>
37
+ <% if not request.xhr? %>
38
+ </tbody>
39
+ </table>
40
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <% pagination_params = params.merge(:controller => @scaffold_controller, :action => 'update_'+@prefix+'table') %>
2
+ <% indicator_params = pagination_params.merge(:action => 'pagination') %>
3
+ <% previous_url = url_for(pagination_params.merge(:page => paginator.current.previous)) %>
4
+ <% next_url = url_for(pagination_params.merge(:page => paginator.current.next)) %>
5
+ <% current_url = url_for(pagination_params.merge(:page => paginator.current)) %>
6
+
7
+ <script>
8
+ addAjaxTableUrlToHistory('<%= current_url %>')
9
+ </script>
10
+
11
+ <%= loading_indicator_tag indicator_params %>
12
+ <%= link_to_remote('Previous',
13
+ { :url => pagination_params.merge(:page => paginator.current.previous),
14
+ :before => "addAjaxTableUrlToHistory('#{previous_url}')",
15
+ :loading => "Element.show('#{loading_indicator_id(indicator_params)}');",
16
+ :update => scaffold_content_id(pagination_params) },
17
+ { :href => previous_url,
18
+ :class => "previous"}) if @paginator.current.previous %>
19
+ <%= pagination_ajax_links @paginator, pagination_params %>
20
+ <%= link_to_remote('Next',
21
+ { :url => pagination_params.merge(:page => paginator.current.next),
22
+ :before => "addAjaxTableUrlToHistory('#{next_url}')",
23
+ :loading => "Element.show('#{loading_indicator_id(indicator_params)}');",
24
+ :update => scaffold_content_id(pagination_params) },
25
+ { :href => next_url,
26
+ :class => "next"}) if @paginator.current.next %>
@@ -0,0 +1,46 @@
1
+ <% # The following is used when the browser doesn't have javascript enabled %>
2
+ <% eval("#{Inflector.underscore(@scaffold_class)} = row") %>
3
+ <% classAttr = cycle("", "class=\"even\"") %>
4
+ <% @options = params.merge(:controller => @scaffold_controller, :action => "view", :id => row.send("#{@scaffold_class.primary_key}")) %>
5
+
6
+ <tr <%= classAttr %> id="<%= element_row_id(@options) %>" <%= "style=\"display: none;\"" if hidden %>>
7
+ <% for scaffold_column in @scaffold_columns %>
8
+ <% column_value = eval(scaffold_column.eval) rescue nil %>
9
+ <% if show_totals_for_column?(scaffold_column.name) %>
10
+ <% scaffold_column.add_to_total(column_value) %>
11
+ <% end %>
12
+ <td class="<%= column_class(scaffold_column.name, column_value, current_sort(params), scaffold_column.class_name) %>" >
13
+ <%= format_column(column_value, scaffold_column.sanitize?) %>
14
+ </td>
15
+ <% end %>
16
+ <% unless @no_edit && @no_delete %>
17
+ <td class="actions">
18
+ <table cellpadding="0" cellspacing="0">
19
+ <tr>
20
+ <td class="indicator-container">
21
+ <%= loading_indicator_tag(@options) %>
22
+ </td>
23
+ <% unless @no_edit %>
24
+ <td>
25
+ <% edit_options = @options.merge(:action => 'edit'+@suffix) %>
26
+ <%= link_to_remote "Edit",
27
+ { :url => edit_options,
28
+ :loading => "Element.show('#{loading_indicator_id(@options)}');" },
29
+ { :href => url_for(edit_options) } %>
30
+ </td>
31
+ <% end %>
32
+ <% unless @no_delete %>
33
+ <td>
34
+ <% delete_options = @options.merge(:action => 'destroy'+@suffix) %>
35
+ <%= link_to_remote "Delete",
36
+ { :url => delete_options,
37
+ :confirm => 'Are you sure?',
38
+ :loading => "Element.show('#{loading_indicator_id(@options)}');" },
39
+ { :href => url_for( delete_options ) } %>
40
+ </td>
41
+ <% end %>
42
+ </tr>
43
+ </table>
44
+ </td>
45
+ <% end %>
46
+ </tr>
@@ -0,0 +1,12 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => params[:referring_action], :id => params[:id] }
2
+
3
+ if @successful
4
+ page.remove element_row_id(@options)
5
+ if @options[:action] == "update"+@suffix
6
+ page.show element_row_id(@options.merge(:action => "view"))
7
+ elsif @options[:action] == "create"+@suffix
8
+ page << "AjaxScaffold.displayMessageIfEmpty('#{scaffold_tbody_id(@options)}','#{empty_message_id(@options)}');"
9
+ end
10
+ else
11
+ page.hide loading_indicator_id(@options)
12
+ end
@@ -0,0 +1,20 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "view" }
2
+ @create_options = @options.merge(:action => "create"+@suffix, :id => params[:id])
3
+
4
+ if @successful
5
+ @options[:id] = eval("@"+params[:scaffold_id]+".id")
6
+ page.insert_html :bottom, scaffold_tbody_id(@options), :partial => scaffold_partial_path('row'),
7
+ :object => eval("@"+params[:scaffold_id]), :locals => { :hidden => true }
8
+ page << "new TableRow.MoveAfter('#{element_row_id(@create_options)}', '#{element_row_id(@options)}');"
9
+ page.remove element_row_id(@create_options)
10
+ page.show element_row_id(@options)
11
+ page << "AjaxScaffold.stripe('#{scaffold_tbody_id(@options)}');"
12
+ page << "AjaxScaffold.removeSortClasses('#{@options[:scaffold_id]}');"
13
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
14
+ else
15
+ page.replace_html element_messages_id(@create_options), :partial => scaffold_partial_path('form_messages')
16
+ page << "Form.enable('#{element_form_id(@create_options)}');"
17
+ page.hide loading_indicator_id(@create_options)
18
+ end
19
+
20
+
@@ -0,0 +1,11 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "view", :id => params[:id] }
2
+
3
+ if @successful
4
+ page.remove element_row_id(@options)
5
+ page << "AjaxScaffold.displayMessageIfEmpty('#{scaffold_tbody_id(@options)}','#{empty_message_id(@options)}');"
6
+ page << "AjaxScaffold.stripe('#{scaffold_tbody_id(@options)}');"
7
+ else
8
+ page.hide loading_indicator_id(@options)
9
+ end
10
+
11
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
@@ -0,0 +1,16 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "update"+@suffix, :id => params[:id] }
2
+ @view_options = @options.merge(:action => "view")
3
+
4
+ if @successful
5
+ page.hide element_row_id(@view_options)
6
+ page.insert_html :bottom, scaffold_tbody_id(@options), :partial => scaffold_partial_path('new_edit')
7
+ page << "new TableRow.MoveAfter('#{element_row_id(@view_options)}', '#{element_row_id(@options)}');"
8
+ page.show element_row_id(@options)
9
+ page << "Form.focusFirstElement('#{element_form_id(@options)}');"
10
+ page.visual_effect :highlight, element_cell_id(@options)
11
+ page.replace_html element_messages_id(@options), :partial => scaffold_partial_path('form_messages')
12
+ else
13
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
14
+ end
15
+
16
+ page.hide loading_indicator_id(@view_options)
@@ -0,0 +1 @@
1
+ <%= render_component :controller => @scaffold_controller, :action => @prefix+'table', :params => params %>
@@ -0,0 +1,14 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "create"+@suffix, :id => generate_temporary_id }
2
+ @new_options = @options.merge(:action => "new"+@suffix, :id => nil)
3
+
4
+ if @successful
5
+ page.insert_html :top, scaffold_tbody_id(@options), :partial => scaffold_partial_path('new_edit')
6
+ page.show element_row_id(@options)
7
+ page << "Form.focusFirstElement('#{element_form_id(@options)}');"
8
+ page.visual_effect :highlight, element_cell_id(@options)
9
+ page.replace_html element_messages_id(@options), :partial => scaffold_partial_path('form_messages')
10
+ else
11
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
12
+ end
13
+
14
+ page.hide loading_indicator_id(@new_options), empty_message_id(@options)
@@ -0,0 +1,63 @@
1
+ <% unless @width.empty? %>
2
+ <style>
3
+ .ajax-scaffold-<%= params[:scaffold_id] %> {
4
+ width: <%= @width %>;
5
+ }
6
+ </style>
7
+ <% end %>
8
+
9
+ <% if @show_wrapper %>
10
+ <div id="<%= params[:scaffold_id] %>" class="ajax-scaffold ajax-scaffold-<%= params[:scaffold_id] %>">
11
+ <div id="<%= scaffold_content_id(params) %>">
12
+ <% end %>
13
+ <div class="ajax-scaffold-header">
14
+ <% unless @no_create %>
15
+ <div class="actions">
16
+ <% new_params = params.merge(:controller => @scaffold_controller, :action => "new"+@suffix) %>
17
+ <%= loading_indicator_tag(new_params) %>
18
+ <%= link_to_remote "Create New",
19
+ { :url => new_params,
20
+ :loading => "Element.show('#{loading_indicator_id(new_params)}');" },
21
+ { :href => url_for(new_params), :class => "create" } %>
22
+ </div>
23
+ <% end %>
24
+ <h2><%= @scaffold_plural_name.titleize %></h2>
25
+ </div>
26
+ <table cellpadding="0" cellspacing="0">
27
+ <thead>
28
+ <tr>
29
+ <%= render :partial => scaffold_partial_path('column_headings') %>
30
+ </tr>
31
+ </thead>
32
+ <tbody>
33
+ <tr>
34
+ <td colspan="<%= num_columns %>" class="messages-container">
35
+ <div id="<%= scaffold_messages_id(params) %>">
36
+ <%= render :partial => scaffold_partial_path('messages') %>
37
+ </div>
38
+ <p id="<%= empty_message_id(params) %>" class="empty-message" <%= " style=\"display:none;\" " if !@collection.empty? %>>
39
+ No Entries
40
+ </p>
41
+ </td>
42
+ </tr>
43
+ </tbody>
44
+ <tbody id="<%= scaffold_tbody_id(params) %>">
45
+ <% if !@collection.empty? %>
46
+ <%= render :partial => scaffold_partial_path('row'), :collection => @collection, :locals => { :hidden => false } %>
47
+ <% if show_column_totals? %>
48
+ <%= render :partial => scaffold_partial_path('column_totals') %>
49
+ <% end %>
50
+ <% end %>
51
+ </tbody>
52
+ </table>
53
+ <div class="ajax-scaffold-footer">
54
+ <%= render :partial => scaffold_partial_path('pagination_links'), :locals => { :paginator => @paginator } %>
55
+ </div>
56
+ <% if @show_wrapper %>
57
+ </div>
58
+ </div>
59
+
60
+ <script type="text/javascript">
61
+ Rico.Corner.round('<%= params[:scaffold_id] %>', {color: '#005CB8', bgColor: '#fff', compact: true});
62
+ </script>
63
+ <% end %>
@@ -0,0 +1,19 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "view", :id => params[:id] }
2
+ @update_options = @options.merge(:action => "update"+@suffix)
3
+
4
+ if @successful
5
+ page.remove element_row_id(@options)
6
+ page.insert_html :bottom, scaffold_tbody_id(@options), :partial => scaffold_partial_path('row'),
7
+ :object => eval("@"+params[:scaffold_id]), :locals => { :hidden => true }
8
+ page << "new TableRow.MoveAfter('#{element_row_id(@update_options)}', '#{element_row_id(@options)}');"
9
+ page.remove element_row_id(@update_options)
10
+ page.show element_row_id(@options)
11
+ page << "AjaxScaffold.stripe('#{scaffold_tbody_id(@options)}');"
12
+ page << "AjaxScaffold.removeSortClasses('#{@options[:scaffold_id]}');"
13
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
14
+ else
15
+ page.replace_html element_messages_id(@update_options), :partial => scaffold_partial_path('form_messages')
16
+ page << "Form.enable('#{element_form_id(@update_options)}');"
17
+ page.hide loading_indicator_id(@update_options)
18
+ end
19
+
data/init.rb ADDED
@@ -0,0 +1,27 @@
1
+ # Include hook code here
2
+ require 'ajax_scaffold_plugin'
3
+
4
+ ActionController::Base.send(:include, AjaxScaffold)
5
+ ActionView::Base.send(:include, AjaxScaffold::Helper)
6
+
7
+ # copy all the files over to the main rails app, want to avoid .svn
8
+ source = File.join(directory,'/app/views/ajax_scaffold')
9
+ dest = File.join(RAILS_ROOT, '/app/views/ajax_scaffold')
10
+ FileUtils.mkdir(dest) unless File.exist?(dest)
11
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
12
+
13
+ source = File.join(directory,'/public')
14
+ dest = RAILS_ROOT + '/public'
15
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
16
+
17
+ source = File.join(directory,'/public/stylesheets')
18
+ dest = RAILS_ROOT + '/public/stylesheets'
19
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
20
+
21
+ source = File.join(directory,'/public/javascripts')
22
+ dest = RAILS_ROOT + '/public/javascripts'
23
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
24
+
25
+ source = File.join(directory,'/public/images')
26
+ dest = RAILS_ROOT + '/public/images'
27
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,505 @@
1
+ require 'base/ajax_scaffold'
2
+
3
+ module AjaxScaffold # :nodoc:
4
+
5
+ class ScaffoldColumn
6
+ attr_reader :name, :eval, :sort_sql, :label, :class_name, :sort, :total
7
+
8
+ # Only options[:name] is required. It will infer the eval and sort values
9
+ # based on the given class.
10
+ def initialize(klass, options)
11
+ @name = options[:name]
12
+ @eval = options[:eval].nil? ? "#{Inflector.underscore(klass.to_s)}.#{@name}" : options[:eval]
13
+ @label = options[:label].nil? ? Inflector.titleize(@name) : options[:label]
14
+ @sortable = options[:sortable].nil? ? true : options[:sortable]
15
+ @sort_sql = options[:sort_sql].nil? ? "#{klass.table_name}.#{@name}" : options[:sort_sql] unless !@sortable
16
+ @class_name = options[:class_name].nil? ? "" : options[:class_name]
17
+ @sanitize = options[:sanitize].nil? ? true : options[:sanitize]
18
+
19
+ #Plugin Specific Below
20
+ @sort = options[:sort]
21
+ @total = 0
22
+ end
23
+
24
+ def add_to_total(value)
25
+ @total += value.to_f
26
+ end
27
+ end
28
+
29
+ module Common
30
+ def current_page(params)
31
+ session[params[:scaffold_id]][:page]
32
+ end
33
+ end
34
+
35
+ # extend the class that include this with the methods in ClassMethods
36
+ def self.included(base)
37
+ super
38
+ base.extend(ClassMethods)
39
+ end
40
+
41
+ # Start Helper module
42
+ module Helper
43
+ include AjaxScaffold::Common
44
+
45
+ # default html for an input tag
46
+ def default_input_block
47
+ Proc.new { |record, column| "<div class=\"form-element\">\n "+
48
+ "<label for=\"#{record}_#{column.name}\">#{column.human_name}</label>\n "+
49
+ "#{input(record, column.name, { :class => 'text-input' } )}\n</div>\n" }
50
+ end
51
+
52
+ # create input tags for each field in a model
53
+ def all_input_tags(record, record_name, options)
54
+ input_block = options[:input_block] || default_input_block
55
+
56
+ if !options[:exclude].blank?
57
+ filtered_content_columns = record.content_columns.reject { |column| options[:exclude].include?(column.name) }
58
+ else
59
+ filtered_content_columns = record.content_columns
60
+ end
61
+
62
+ filtered_content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n")
63
+ end
64
+ end
65
+
66
+ module Controller
67
+
68
+ def sort_collection_by_method(collection, column, order)
69
+ # sort in place (sort!) using block
70
+ collection.sort!{|line1, line2| sort_lines_by_column(line1, line2, column, order)}
71
+ end
72
+
73
+ def sort_lines_by_column(line1, line2, column, sort_order)
74
+ # Compare each lines column values with <=>
75
+ value1 = eval_method_string(line1, column)
76
+ value2 = eval_method_string(line2, column)
77
+ if (value1.nil?)
78
+ res = -1
79
+ elsif (value2.nil?)
80
+ res = 1
81
+ else
82
+ res = (value1 != nil ? value1 : "") <=> (value2 != nil ? value2 : "")
83
+ end
84
+ # Reverse result if not asc
85
+ sort_order == 'asc' ? res : res * -1
86
+ end
87
+
88
+ def eval_method_string(object, method_str)
89
+ # need to eval each section of the method string as this could return nil
90
+ # at any point.
91
+ methods = method_str.split('.')
92
+ methods.each do |m|
93
+ object = eval("object.#{m}") rescue nil
94
+ return object if object.nil?
95
+ end
96
+ object
97
+ end
98
+
99
+ end
100
+
101
+ module Helper
102
+ include AjaxScaffold::Common
103
+
104
+ def show_totals_for_column?(column)
105
+ @scaffold_column_totals.include?(column)
106
+ end
107
+
108
+ def show_column_totals?
109
+ @scaffold_column_totals.size > 0
110
+ end
111
+
112
+ # return the base path to search for a partial
113
+ def scaffold_partial_path(partial)
114
+ @scaffold_plural_name+'/'+partial
115
+ end
116
+
117
+ # number of columns in a table
118
+ def num_columns
119
+ @scaffold_columns.length + 1
120
+ end
121
+
122
+ end
123
+
124
+ module ClassMethods
125
+
126
+ # Adds a swath of actions to the controller.
127
+ def ajax_scaffold(model_id, options = {})
128
+ options.assert_valid_keys(:class_name, :except, :rows_per_page, :suffix, :totals, :width, :rel_width)
129
+
130
+ # set up a few variables for use in the code generation
131
+ singular_name = model_id.to_s
132
+ class_name = options[:class_name] || singular_name.camelize
133
+ plural_name = singular_name.pluralize
134
+ rows_per_page = options[:rows_per_page] || 25
135
+ suffix = options[:suffix] ? "_#{singular_name}" : ""
136
+ prefix = options[:suffix] ? "#{plural_name}_" : ""
137
+ totals = options[:totals].nil? ? nil : options[:totals].join(',').to_s
138
+ width = options[:width].nil? ? (options[:rel_width].nil? ? nil : options[:rel_width]*100 ) : options[:width]
139
+
140
+ if (!width.nil?)
141
+ dimensions = options[:width].nil? ? "%" : "px"
142
+ width = width.to_s + dimensions
143
+ end
144
+
145
+ no_create, no_edit, no_delete = false, false, false
146
+ if (options[:except])
147
+ no_create = options[:except].include?('create')
148
+ no_edit = options[:except].include?('edit')
149
+ no_delete = options[:except].include?('delete')
150
+ end
151
+
152
+ module_eval <<-"end_eval", __FILE__, __LINE__
153
+ include AjaxScaffold::Common
154
+ include AjaxScaffold::Controller
155
+
156
+ verify :method => :post, :only => [ :destroy#{suffix}, :create#{suffix}, :update#{suffix} ],
157
+ :redirect_to => { :action => :#{prefix}table }
158
+
159
+ after_filter :clear_flashes
160
+
161
+ @@#{prefix}scaffold_columns = nil
162
+ @@#{prefix}scaffold_total_columns = nil
163
+ @@#{prefix}scaffold_columns_hash = nil
164
+
165
+ def #{prefix}scaffold_total_columns
166
+ csv_array = "#{totals}".length >= 1 ? CSV.parse_line("#{totals}") : []
167
+ @@#{prefix}scaffold_total_columns ||= csv_array
168
+ end
169
+
170
+ def #{prefix}scaffold_columns
171
+ @@#{prefix}scaffold_columns ||= build_#{prefix}scaffold_columns
172
+ end
173
+
174
+ def #{prefix}scaffold_columns_hash
175
+ @@#{prefix}scaffold_columns_hash ||= build_#{prefix}scaffold_columns_hash
176
+ end
177
+
178
+ def #{prefix}table
179
+ self.#{prefix}table_setup
180
+
181
+ render#{suffix}_template(:action => 'table')
182
+ end
183
+
184
+ def #{prefix}table_setup
185
+ update_params :default_scaffold_id => "#{singular_name}", :default_sort => nil, :default_sort_direction => default_#{prefix}sort_direction
186
+
187
+ @show_wrapper = true if @show_wrapper.nil?
188
+ @width = "#{width}"
189
+ sort_sql = #{prefix}scaffold_columns_hash[current_sort(params)].sort_sql rescue default_#{prefix}sort
190
+ sort = #{prefix}scaffold_columns_hash[current_sort(params)].sort rescue nil
191
+ sort_direction = current_sort_direction(params)
192
+
193
+ klass = Inflector.camelize("#{singular_name}").constantize
194
+ order = sort.nil? ? sort_sql : sort
195
+ order += " " + sort_direction
196
+ options = { :order => order,
197
+ :conditions => conditions_for_#{plural_name}_collection,
198
+ :direction => current_sort_direction(params),
199
+ :per_page => #{rows_per_page} }
200
+
201
+ count = count_#{plural_name}_collection(klass, options)
202
+ @paginator = Paginator.new(self, count, #{rows_per_page}, current_page(params))
203
+
204
+ if (sort.nil?)
205
+ @collection = page_and_sort_#{plural_name}_collection(klass, options, @paginator)
206
+ else
207
+ @collection = page_and_sort_#{plural_name}_collection_with_method(klass, options, @paginator)
208
+ end
209
+ end
210
+
211
+ # if multiple tables (suffix) then return to index (can be changed by overriding)
212
+ # if single table return to list
213
+ def #{prefix}return_to_main
214
+ action = "#{suffix}" == "" ? "#{prefix}list" : "index"
215
+ redirect_to :action => action
216
+ end
217
+
218
+ def update_#{prefix}table
219
+ @show_wrapper = false # don't show the outer wrapper elements if we are just updating an existing scaffold
220
+ if request.xhr?
221
+ #{prefix}table_setup
222
+ render#{suffix}_template(:action => 'table')
223
+ else
224
+ #{prefix}return_to_main
225
+ end
226
+ end
227
+
228
+ def #{prefix}list
229
+ render#{suffix}_template(:action => 'list.rhtml', :layout => true)
230
+ end
231
+ end_eval
232
+
233
+ if(suffix=="")
234
+ module_eval <<-"end_eval", __FILE__, __LINE__
235
+ def index
236
+ redirect_to(:action => :list)
237
+ end
238
+ end_eval
239
+ end
240
+
241
+ # now only create these methods if not excluded
242
+ unless (no_create)
243
+ module_eval <<-"end_eval", __FILE__, __LINE__
244
+ def new#{suffix}
245
+ @#{singular_name} = #{class_name}.new
246
+ @successful = true
247
+
248
+ return render#{suffix}_template(:action => 'new.rjs') if request.xhr?
249
+
250
+ # Javascript disabled fallback
251
+ if @successful
252
+ @options = { :action => "create" }
253
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
254
+ else
255
+ #{prefix}return_to_main
256
+ end
257
+ end
258
+
259
+ def create#{suffix}
260
+ begin
261
+ do_create#{suffix}
262
+ rescue
263
+ flash[:error], @successful = $!.to_s, false
264
+ end
265
+
266
+ return render#{suffix}_template(:action => 'create.rjs') if request.xhr?
267
+
268
+ if @successful
269
+ #{prefix}return_to_main
270
+ else
271
+ @options = { :scaffold_id => params[:scaffold_id], :action => "create" }
272
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
273
+ end
274
+ end
275
+
276
+ def do_create#{suffix}
277
+ @#{singular_name} = #{class_name}.new(params[:#{singular_name}])
278
+ @successful = @#{singular_name}.save
279
+ end
280
+ end_eval
281
+ end
282
+
283
+ unless (no_edit)
284
+ module_eval <<-"end_eval", __FILE__, __LINE__
285
+ def edit#{suffix}
286
+ begin
287
+ @#{singular_name} = #{class_name}.find(params[:id])
288
+ @successful = !@#{singular_name}.nil?
289
+ rescue
290
+ flash[:error], @successful = $!.to_s, false
291
+ end
292
+
293
+ return render#{suffix}_template(:action => 'edit.rjs') if request.xhr?
294
+
295
+ if @successful
296
+ @options = { :scaffold_id => params[:scaffold_id], :action => "update", :id => params[:id] }
297
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
298
+ else
299
+ #{prefix}return_to_main
300
+ end
301
+ end
302
+
303
+ def update#{suffix}
304
+ begin
305
+ do_update#{suffix}
306
+ rescue
307
+ flash[:error], @successful = $!.to_s, false
308
+ end
309
+
310
+ return render#{suffix}_template(:action => 'update.rjs') if request.xhr?
311
+
312
+ if @successful
313
+ #{prefix}return_to_main
314
+ else
315
+ @options = { :action => "update" }
316
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
317
+ end
318
+ end
319
+
320
+ def do_update#{suffix}
321
+ @#{singular_name} = #{class_name}.find(params[:id])
322
+ @successful = @#{singular_name}.update_attributes(params[:#{singular_name}])
323
+ end
324
+ end_eval
325
+ end
326
+
327
+ unless (no_delete)
328
+ module_eval <<-"end_eval", __FILE__, __LINE__
329
+ def destroy#{suffix}
330
+ begin
331
+ @successful = #{class_name}.find(params[:id]).destroy
332
+ rescue
333
+ flash[:error], @successful = $!.to_s, false
334
+ end
335
+
336
+ return render#{suffix}_template(:action => 'destroy.rjs') if request.xhr?
337
+
338
+ # Javascript disabled fallback
339
+ #{prefix}return_to_main
340
+ end
341
+ end_eval
342
+ end
343
+
344
+ module_eval <<-"end_eval", __FILE__, __LINE__
345
+ def cancel#{suffix}
346
+ @successful = true
347
+
348
+ return render#{suffix}_template(:action => 'cancel.rjs') if request.xhr?
349
+
350
+ # Javascript disabled fallback
351
+ #{prefix}return_to_main
352
+ end
353
+
354
+ protected
355
+ # Override this for custom selection conditions
356
+ def conditions_for_#{plural_name}_collection
357
+ nil
358
+ end
359
+
360
+ # Override to change the default sort from plural_name.id
361
+ def default_#{prefix}sort
362
+ "#{plural_name}.id"
363
+ end
364
+
365
+ # Override to change the default sort direction
366
+ def default_#{prefix}sort_direction
367
+ "asc"
368
+ end
369
+
370
+ # Returns the total number of items in the collection
371
+ def count_#{plural_name}_collection(model, options)
372
+ count_collection_for_pagination(model, options)
373
+ end
374
+
375
+ # Per model collection method, using sort_collection_by_method
376
+ def page_and_sort_#{plural_name}_collection_with_method(model, options, paginator)
377
+ # call: sort_collection_by_method(collection, column, order)
378
+ order_parts = options[:order].split(' ')
379
+ collection = model.find(:all, :conditions => options[:conditions],
380
+ :joins => options[:join] || options[:joins],
381
+ :include => options[:include],
382
+ :select => options[:select])
383
+
384
+ sort_collection_by_method(collection, order_parts[0], order_parts[1]).slice(paginator.current.offset,options[:per_page])
385
+ end
386
+
387
+ # Per model collection method delegates to Rails as default
388
+ def page_and_sort_#{plural_name}_collection(model, options, paginator)
389
+ find_collection_for_pagination(model, options, paginator)
390
+ end
391
+
392
+ private
393
+ def render#{suffix}_template( options = {} )
394
+ @scaffold_columns ||= #{prefix}scaffold_columns
395
+ @scaffold_column_totals = #{prefix}scaffold_total_columns
396
+ @scaffold_class = #{class_name}
397
+ @scaffold_singular_name, @scaffold_plural_name = "#{singular_name}", "#{plural_name}"
398
+ @scaffold_controller = "/" + self.class.to_s.sub(/Controller/, "").underscore
399
+ @no_create = #{no_create}
400
+ @no_edit = #{no_edit}
401
+ @no_delete = #{no_delete}
402
+ @suffix = "#{suffix}"
403
+ @prefix = "#{prefix}"
404
+
405
+ # check for template in following order:
406
+ # views/scaffold_class/template
407
+ # views/controller_class/template
408
+ # views/ajax_scaffold/template
409
+
410
+ action = options[:action]
411
+ layout = options[:layout] || false
412
+ if template_exists?("#{plural_name}/\#{action}")
413
+ render(:action => "../#{plural_name}/\#{action}", :layout => layout)
414
+
415
+ elsif template_exists?("\#{self.class.controller_path}/\#{action}")
416
+ render(:action => action, :layout => layout)
417
+
418
+ else
419
+ render(:template => "ajax_scaffold/\#{action}", :layout => layout)
420
+ end
421
+ end
422
+
423
+ def build_#{prefix}scaffold_columns
424
+ scaffold_columns = Array.new
425
+ #{class_name}.content_columns.each do |column|
426
+ scaffold_columns << ScaffoldColumn.new(#{class_name}, { :name => column.name })
427
+ end
428
+ scaffold_columns
429
+ end
430
+
431
+ def build_#{prefix}scaffold_columns_hash
432
+ scaffold_columns_hash = Hash.new
433
+ #{prefix}scaffold_columns.each do |scaffold_column|
434
+ scaffold_columns_hash[scaffold_column.name] = scaffold_column
435
+ end
436
+ scaffold_columns_hash
437
+ end
438
+
439
+ end_eval
440
+
441
+ # end ajax_scaffold method
442
+ end
443
+ # end ClassMethods
444
+ end
445
+
446
+ end
447
+
448
+ # override render here to ensure that the correct partial is picked up
449
+ module ::ActionView #:nodoc:
450
+ class Base
451
+
452
+ # Renders the template present at <tt>template_path</tt> (relative to the template_root).
453
+ # The hash in <tt>local_assigns</tt> is made available as local variables.
454
+ def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
455
+ if options.is_a?(String)
456
+ render_file(options, true, old_local_assigns)
457
+ elsif options == :update
458
+ update_page(&block)
459
+ elsif options.is_a?(Hash)
460
+ options[:locals] ||= {}
461
+ options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]
462
+
463
+ if options[:file]
464
+ render_file(options[:file], options[:use_full_path], options[:locals])
465
+ elsif options[:partial] && options[:collection]
466
+ partial = parse_partial_path(options[:partial])
467
+ render_partial_collection(partial, options[:collection], options[:spacer_template], options[:locals])
468
+ elsif options[:partial]
469
+ partial = parse_partial_path(options[:partial])
470
+ render_partial(partial, ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals])
471
+ elsif options[:inline]
472
+ render_template(options[:type] || :rhtml, options[:inline], nil, options[:locals] || {})
473
+ end
474
+ end
475
+ end
476
+
477
+ def parse_partial_path(partial_path)
478
+ path, partial_name = partial_pieces(partial_path)
479
+
480
+ if partial_path.include?('/')
481
+ unless (erb_template_exists?("#{path}/_#{partial_name}"))
482
+ path, partial_name = partial_pieces(partial_name)
483
+ partial_path = "#{path}/#{partial_name}"
484
+ end
485
+ end
486
+
487
+ unless (erb_template_exists?("#{path}/_#{partial_name}"))
488
+ partial_path = "ajax_scaffold/#{partial_name}"
489
+ end
490
+
491
+ partial_path
492
+ end
493
+ end
494
+
495
+ module Helpers
496
+ module AssetTagHelper
497
+ # produces all the includes in one shot
498
+ def ajax_scaffold_includes
499
+ js = javascript_include_tag(:defaults, 'dhtml_history', 'rico_corner', 'ajax_scaffold')
500
+ css = stylesheet_link_tag('ajax_scaffold')
501
+ js + "\n" + css
502
+ end
503
+ end
504
+ end
505
+ end