admin_assistant 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README +3 -28
  2. data/Rakefile +18 -10
  3. data/init.rb +1 -0
  4. data/install.rb +5 -1
  5. data/lib/admin_assistant/active_record_column.rb +211 -0
  6. data/lib/admin_assistant/association_target.rb +35 -0
  7. data/lib/admin_assistant/belongs_to_column.rb +186 -0
  8. data/lib/admin_assistant/builder.rb +222 -35
  9. data/lib/admin_assistant/column.rb +266 -297
  10. data/lib/admin_assistant/default_search_column.rb +41 -0
  11. data/lib/admin_assistant/file_column_column.rb +73 -0
  12. data/lib/admin_assistant/form_view.rb +7 -68
  13. data/lib/admin_assistant/helper.rb +4 -2
  14. data/lib/admin_assistant/index.rb +101 -81
  15. data/lib/admin_assistant/paperclip_column.rb +45 -0
  16. data/lib/admin_assistant/polymorphic_belongs_to_column.rb +102 -0
  17. data/lib/admin_assistant/request/autocomplete.rb +47 -0
  18. data/lib/admin_assistant/request/base.rb +176 -0
  19. data/lib/admin_assistant/request/create.rb +27 -0
  20. data/lib/admin_assistant/request/destroy.rb +15 -0
  21. data/lib/admin_assistant/request/edit.rb +11 -0
  22. data/lib/admin_assistant/request/index.rb +26 -0
  23. data/lib/admin_assistant/request/new.rb +19 -0
  24. data/lib/admin_assistant/request/show.rb +24 -0
  25. data/lib/admin_assistant/request/update.rb +44 -0
  26. data/lib/admin_assistant/search.rb +82 -0
  27. data/lib/admin_assistant/show_view.rb +20 -0
  28. data/lib/admin_assistant/virtual_column.rb +61 -0
  29. data/lib/admin_assistant.rb +190 -85
  30. data/lib/javascripts/admin_assistant.js +253 -0
  31. data/lib/stylesheets/activescaffold.css +219 -0
  32. data/lib/stylesheets/default.css +119 -0
  33. data/lib/views/_polymorphic_field_search.html.erb +89 -0
  34. data/lib/views/_restricted_autocompleter.html.erb +53 -0
  35. data/lib/views/autocomplete.html.erb +11 -0
  36. data/lib/views/form.html.erb +6 -3
  37. data/lib/views/index.html.erb +53 -46
  38. data/lib/views/show.html.erb +19 -0
  39. data/vendor/ar_query/MIT-LICENSE +20 -0
  40. data/vendor/ar_query/README +0 -0
  41. data/vendor/ar_query/init.rb +1 -0
  42. data/vendor/ar_query/install.rb +1 -0
  43. data/vendor/ar_query/lib/ar_query.rb +137 -0
  44. data/vendor/ar_query/spec/ar_query_spec.rb +253 -0
  45. data/vendor/ar_query/tasks/ar_query_tasks.rake +0 -0
  46. data/vendor/ar_query/uninstall.rb +1 -0
  47. metadata +39 -16
  48. data/lib/admin_assistant/request.rb +0 -183
  49. data/lib/stylesheets/admin_assistant.css +0 -75
@@ -0,0 +1,73 @@
1
+ class AdminAssistant
2
+ class FileColumnColumn < Column
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name.to_s
7
+ end
8
+
9
+ def contains?(column_name)
10
+ column_name.to_s == @name
11
+ end
12
+
13
+ class View < AdminAssistant::Column::View
14
+ def file_exists?(record)
15
+ if @file_exists_method
16
+ @file_exists_method.call record
17
+ else
18
+ !source_for_image_tag(record).nil?
19
+ end
20
+ end
21
+
22
+ def image_html(record)
23
+ @action_view.image_tag(
24
+ source_for_image_tag(record), :size => @image_size
25
+ )
26
+ end
27
+
28
+ def source_for_image_tag(record)
29
+ if @file_url_method
30
+ @file_url_method.call record
31
+ else
32
+ @action_view.instance_variable_set :@record, record
33
+ @action_view.url_for_file_column 'record', @column.name
34
+ end
35
+ end
36
+ end
37
+
38
+ class FormView < View
39
+ include AdminAssistant::Column::FormViewMethods
40
+
41
+ def default_html(form)
42
+ if file_exists?(form.object)
43
+ check_box_tag = @action_view.check_box_tag(
44
+ "#{form.object.class.name.underscore}[#{name}(destroy)]"
45
+ )
46
+ <<-HTML
47
+ <p>Current image:<br />#{image_html(form.object)}</p>
48
+ <p>Remove: #{check_box_tag}</p>
49
+ <p>Update: #{form.file_field(name)}</p>
50
+ HTML
51
+ else
52
+ "<p>Add: #{form.file_field(name)}</p>"
53
+ end
54
+ end
55
+ end
56
+
57
+ class IndexView < View
58
+ include AdminAssistant::Column::IndexViewMethods
59
+
60
+ def unconfigured_html(record)
61
+ image_html(record) if file_exists?(record)
62
+ end
63
+ end
64
+
65
+ class ShowView < View
66
+ include AdminAssistant::Column::ShowViewMethods
67
+
68
+ def html(record)
69
+ image_html(record) if file_exists?(record)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -13,52 +13,17 @@ class AdminAssistant
13
13
  end
14
14
  end
15
15
 
16
- def after_column_html(column)
17
- if after = render_from_custom_template("_after_#{column.name}_input")
18
- after
19
- else
20
- helper_method = "after_#{column.name}_input"
21
- if @action_view.respond_to?(helper_method)
22
- @action_view.send(helper_method, @record)
23
- end
24
- end
25
- end
26
-
27
- def column_html(column, rails_form)
28
- hff = render_from_custom_template "_#{column.name}_input"
29
- hff ||= column_html_from_helper_method(column)
30
- hff ||= if settings.read_only.include?(column.name)
31
- column.form_value(@record)
32
- elsif column.respond_to?(:add_to_form)
33
- column.add_to_form(rails_form)
34
- else
35
- virtual_column_html column
36
- end
37
- if ah = after_column_html(column)
38
- hff << ah
39
- end
40
- hff
41
- end
42
-
43
- def column_html_from_helper_method(column)
44
- html_method = "#{column.name}_html_for_form"
45
- if @action_view.respond_to?(html_method)
46
- @action_view.send(html_method, @record)
47
- end
48
- end
49
-
50
16
  def column_names
51
- settings.column_names ||
52
- model_class.columns.reject { |ar_column|
53
- %w(id created_at updated_at).include?(ar_column.name)
54
- }.map { |ar_column|
55
- @admin_assistant.column_name_or_assoc_name(ar_column.name)
56
- }
17
+ if %w(new create).include?(@action_view.action_name)
18
+ settings.columns_for_new
19
+ elsif %w(edit update).include?(@action_view.action_name)
20
+ settings.columns_for_edit
21
+ end
57
22
  end
58
23
 
59
24
  def columns
60
- @admin_assistant.columns(column_names).map { |c|
61
- c.view(@action_view, :input => settings.inputs[c.name.to_sym])
25
+ @admin_assistant.accumulate_columns(column_names).map { |c|
26
+ c.form_view @action_view, @admin_assistant
62
27
  }
63
28
  end
64
29
 
@@ -83,18 +48,6 @@ class AdminAssistant
83
48
  @admin_assistant.model_class
84
49
  end
85
50
 
86
- def render_from_custom_template(slug)
87
- template = File.join(
88
- RAILS_ROOT, 'app/views', controller.controller_path, "#{slug}.html.erb"
89
- )
90
- if File.exist?(template)
91
- @action_view.render(
92
- :file => template,
93
- :locals => {model_class.name.underscore.to_sym => @record}
94
- )
95
- end
96
- end
97
-
98
51
  def settings
99
52
  @admin_assistant.form_settings
100
53
  end
@@ -106,19 +59,5 @@ class AdminAssistant
106
59
  def title
107
60
  (@record.id ? "Edit" : "New") + " #{@admin_assistant.model_class_name}"
108
61
  end
109
-
110
- def virtual_column_html(column)
111
- input_name = "#{model_class.name.underscore}[#{column.name}]"
112
- input_type = settings.inputs[column.name.to_sym]
113
- fv = column.form_value @record
114
- if input_type
115
- if input_type == :check_box
116
- @action_view.send(:check_box_tag, input_name, '1', fv) +
117
- @action_view.send(:hidden_field_tag, input_name, '0')
118
- end
119
- else
120
- @action_view.send(:text_field_tag, input_name, fv)
121
- end
122
- end
123
62
  end
124
63
  end
@@ -1,7 +1,9 @@
1
1
  class AdminAssistant
2
2
  module Helper
3
- def admin_assistant_includes
4
- stylesheet_link_tag 'admin_assistant'
3
+ def admin_assistant_includes(opts = {})
4
+ theme = opts[:theme] || 'default'
5
+ stylesheet_link_tag("admin_assistant/#{theme}") +
6
+ javascript_include_tag('admin_assistant')
5
7
  end
6
8
  end
7
9
  end
@@ -2,32 +2,37 @@ require 'ar_query'
2
2
 
3
3
  class AdminAssistant
4
4
  class Index
5
- def initialize(admin_assistant, url_params = {})
6
- @admin_assistant = admin_assistant
7
- @url_params = url_params
5
+ attr_reader :controller_methods
6
+
7
+ def initialize(admin_assistant, url_params = {}, controller_methods = {})
8
+ @admin_assistant, @url_params, @controller_methods =
9
+ admin_assistant, url_params, controller_methods
8
10
  end
9
11
 
10
12
  def belongs_to_sort_column
11
- columns.detect { |column|
12
- column.is_a?(BelongsToColumn) && column.name.to_s == @url_params[:sort]
13
+ @admin_assistant.accumulate_belongs_to_columns(column_names).detect { |c|
14
+ c.name.to_s == sort
13
15
  }
14
16
  end
15
17
 
18
+ def column_names
19
+ settings.column_names || model_class.columns.map(&:name)
20
+ end
21
+
16
22
  def columns
17
- column_names = settings.column_names || model_class.columns.map { |c|
18
- @admin_assistant.column_name_or_assoc_name(c.name)
19
- }
20
- @admin_assistant.columns column_names
23
+ @admin_assistant.accumulate_columns column_names
21
24
  end
22
25
 
23
- def conditions
26
+ def conditions_from_settings
24
27
  settings.conditions
25
28
  end
26
29
 
27
30
  def find_include
31
+ fi = settings.include || []
28
32
  if by_assoc = belongs_to_sort_column
29
- by_assoc.name
33
+ fi << by_assoc.name
30
34
  end
35
+ fi
31
36
  end
32
37
 
33
38
  def model_class
@@ -53,25 +58,34 @@ class AdminAssistant
53
58
  :order => order_sql, :include => find_include,
54
59
  :per_page => 25, :page => @url_params[:page]
55
60
  )
56
- search.add_to_query(ar_query)
57
- if conditions
58
- conditions_sql = conditions.call @url_params
61
+ if @controller_methods[:conditions_for_index]
62
+ sql = @controller_methods[:conditions_for_index].call
63
+ ar_query.condition_sqls << sql if sql
64
+ elsif conditions_from_settings
65
+ if conditions_from_settings.respond_to?(:call)
66
+ conditions_sql = conditions_from_settings.call @url_params
67
+ else
68
+ conditions_sql = conditions_from_settings
69
+ end
59
70
  ar_query.condition_sqls << conditions_sql if conditions_sql
60
71
  end
72
+ search.add_to_query(ar_query)
61
73
  if settings.total_entries
62
- ar_query[:total_entries] = settings.total_entries.call
74
+ ar_query.total_entries = settings.total_entries.call
63
75
  end
64
- @records = model_class.paginate :all, ar_query
76
+ @records = model_class.paginate :all, ar_query.to_hash
65
77
  end
66
78
  @records
67
79
  end
68
80
 
69
81
  def search
70
- @search ||= Search.new(@admin_assistant, @url_params['search'])
82
+ @search ||= AdminAssistant::Search.new(
83
+ @admin_assistant, @url_params['search']
84
+ )
71
85
  end
72
86
 
73
- def search_terms
74
- @url_params['search']
87
+ def search_requested?
88
+ !@url_params['search'].blank?
75
89
  end
76
90
 
77
91
  def settings
@@ -79,14 +93,17 @@ class AdminAssistant
79
93
  end
80
94
 
81
95
  def sort
82
- @url_params[:sort]
96
+ @url_params[:sort] ||
97
+ (settings.sort_by.to_s if settings.sort_by.is_a?(Symbol))
83
98
  end
84
99
 
85
100
  def sort_column
86
- if @url_params[:sort]
101
+ if sort
87
102
  columns.detect { |c|
88
- c.name.to_s == @url_params[:sort]
103
+ c.name.to_s == sort
89
104
  } || belongs_to_sort_column
105
+ elsif settings.sort_by.is_a?(Symbol)
106
+ columns.detect { |c| c.name == settings.sort_by.to_s }
90
107
  end
91
108
  end
92
109
 
@@ -95,86 +112,89 @@ class AdminAssistant
95
112
  end
96
113
 
97
114
  def view(action_view)
98
- View.new self, action_view
115
+ @view ||= View.new(self, action_view, @admin_assistant)
99
116
  end
100
117
 
101
- class Search
102
- def initialize(admin_assistant, search_params)
103
- @admin_assistant, @search_params = admin_assistant, search_params
104
- @search_params ||= {}
118
+ class View
119
+ def initialize(index, action_view, admin_assistant)
120
+ @index, @action_view, @admin_assistant =
121
+ index, action_view, admin_assistant
105
122
  end
106
123
 
107
- def [](name)
108
- @search_params[name]
109
- end
110
-
111
- def add_to_query(ar_query)
112
- columns.each do |column|
113
- column.add_to_query ar_query
114
- end
124
+ def ajax_toggle_allowed?
125
+ @admin_assistant.update?
115
126
  end
116
127
 
117
128
  def columns
118
- search_field_names = settings.search_fields
119
- if search_field_names.empty?
120
- [DefaultSearchColumn.new(
121
- default_terms, @admin_assistant.model_class
122
- )]
123
- else
124
- columns = search_field_names.map { |column_name|
125
- @admin_assistant.column column_name.to_s
129
+ unless @columns
130
+ @columns = @index.columns.map { |c|
131
+ c.index_view @action_view, @admin_assistant, :index => @index
126
132
  }
127
- columns.each do |c|
128
- c.search_terms = @search_params[c.name]
129
- end
130
- columns
131
133
  end
134
+ @columns
132
135
  end
133
136
 
134
- def column_views(action_view)
135
- columns.map { |c|
136
- opts = {:search => self}
137
- if c.respond_to?(:name)
138
- opts[:boolean_labels] = settings.boolean_labels[c.name]
139
- end
140
- c.view(action_view, opts)
141
- }
142
- end
143
-
144
- def default_terms
145
- @search_params if @search_params.is_a?(String)
137
+ def destroy?
138
+ @destroy ||= @admin_assistant.destroy?
146
139
  end
147
140
 
148
- def id
141
+ def edit?
142
+ @edit ||= @admin_assistant.edit?
149
143
  end
150
144
 
151
- def method_missing(meth, *args)
152
- if column = columns.detect { |c| c.name == meth.to_s }
153
- column.search_value
145
+ def header
146
+ if block = @index.settings.header
147
+ block.call @action_view.params
154
148
  else
155
- super
149
+ @admin_assistant.model_class_name.pluralize.capitalize
156
150
  end
157
151
  end
158
-
159
- def settings
160
- @admin_assistant.index_settings
152
+
153
+ def right_column?
154
+ edit? or destroy? or show? or !right_column_lambdas.empty?
161
155
  end
162
- end
163
-
164
- class View
165
- def initialize(index, action_view)
166
- @index, @action_view = index, action_view
156
+
157
+ def right_column_lambdas
158
+ @right_column_lambdas ||=
159
+ @admin_assistant.index_settings.right_column_links
167
160
  end
168
161
 
169
- def columns
170
- @index.columns.map { |c|
171
- c.view(
172
- @action_view,
173
- :boolean_labels => @index.settings.boolean_labels[c.name],
174
- :sort_order => (@index.sort_order if c.name == @index.sort),
175
- :link_to_args => @index.settings.link_to_args[c.name.to_sym]
176
- )
177
- }
162
+ def right_column_links(record)
163
+ links = ""
164
+ if edit?
165
+ links << @action_view.link_to(
166
+ 'Edit', :action => 'edit', :id => record.id
167
+ ) << " "
168
+ end
169
+ if destroy?
170
+ links << @action_view.link_to_remote(
171
+ 'Delete',
172
+ :url => {:action => 'destroy', :id => record.id},
173
+ :confirm => 'Are you sure?',
174
+ :success =>
175
+ "Effect.Fade('#{@admin_assistant.model_class.name.underscore}_#{record.id}')",
176
+ :method => :delete
177
+ ) << ' '
178
+ end
179
+ if show?
180
+ links << @action_view.link_to(
181
+ 'Show', :action => 'show', :id => record.id
182
+ ) << ' '
183
+ end
184
+ right_column_lambdas.each do |lambda|
185
+ link_args = lambda.call record
186
+ links << @action_view.link_to(*link_args)
187
+ end
188
+ if @action_view.respond_to?(:extra_right_column_links_for_index)
189
+ links << @action_view.extra_right_column_links_for_index(
190
+ record
191
+ ) || ''
192
+ end
193
+ links
194
+ end
195
+
196
+ def show?
197
+ @show ||= @admin_assistant.show?
178
198
  end
179
199
  end
180
200
  end
@@ -0,0 +1,45 @@
1
+ class AdminAssistant
2
+ class PaperclipColumn < Column
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name.to_s
7
+ end
8
+
9
+ def contains?(column_name)
10
+ column_name.to_s == @name ||
11
+ column_name.to_s =~
12
+ /^#{@name}_(file_name|content_type|file_size|updated_at)$/
13
+ end
14
+
15
+ class View < AdminAssistant::Column::View
16
+ def image_html(record)
17
+ @action_view.image_tag record.send(@column.name).url
18
+ end
19
+ end
20
+
21
+ class FormView < View
22
+ include AdminAssistant::Column::FormViewMethods
23
+
24
+ def default_html(form)
25
+ form.file_field name
26
+ end
27
+ end
28
+
29
+ class IndexView < View
30
+ include AdminAssistant::Column::IndexViewMethods
31
+
32
+ def html(record)
33
+ image_html record
34
+ end
35
+ end
36
+
37
+ class ShowView < View
38
+ include AdminAssistant::Column::ShowViewMethods
39
+
40
+ def html(record)
41
+ image_html record
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,102 @@
1
+ class AdminAssistant
2
+ class PolymorphicBelongsToColumn < Column
3
+ def initialize(belongs_to_assoc)
4
+ @belongs_to_assoc = belongs_to_assoc
5
+ end
6
+
7
+ def add_to_query_condition(ar_query_condition, search)
8
+ kv = (key_value = search.send(association_foreign_key)) &&
9
+ !key_value.blank?
10
+ tv = (type_value = search.send(foreign_type_field)) &&
11
+ !type_value.blank?
12
+ if kv and tv
13
+ ar_query_condition.add_condition do |subcond|
14
+ subcond.boolean_join = :and
15
+ subcond.sqls << "#{association_foreign_key} = ?"
16
+ subcond.bind_vars << key_value
17
+ subcond.sqls << "#{foreign_type_field} = ?"
18
+ subcond.bind_vars << type_value
19
+ end
20
+ end
21
+ end
22
+
23
+ def association_foreign_key
24
+ @belongs_to_assoc.association_foreign_key
25
+ end
26
+
27
+ def attributes_for_search_object(search_params)
28
+ atts = {}
29
+ atts[association_foreign_key.to_sym] =
30
+ search_params[association_foreign_key]
31
+ atts[foreign_type_field.to_sym] = search_params[foreign_type_field]
32
+ if !atts[foreign_type_field.to_sym].blank?
33
+ atts[name.to_sym] = Module.const_get(
34
+ search_params[foreign_type_field]
35
+ ).find_by_id(search_params[association_foreign_key])
36
+ else
37
+ atts[name.to_sym] = nil
38
+ end
39
+ atts
40
+ end
41
+
42
+ def contains?(column_name)
43
+ column_name.to_s == name
44
+ end
45
+
46
+ def foreign_type_field
47
+ @belongs_to_assoc.options[:foreign_type]
48
+ end
49
+
50
+ def match_text_fields_in_search
51
+ false
52
+ end
53
+
54
+ def name
55
+ @belongs_to_assoc.name.to_s
56
+ end
57
+
58
+ class FormView < AdminAssistant::Column::View
59
+ include AdminAssistant::Column::FormViewMethods
60
+
61
+ def default_html(form)
62
+ opts_for_select = @polymorphic_types.map { |t| [t.name, t.name] }
63
+ form.select(name + '_type', opts_for_select, @select_options) + " " +
64
+ form.text_field(name + '_id', :class => 'integer')
65
+ end
66
+ end
67
+
68
+ class IndexView < AdminAssistant::Column::View
69
+ include AdminAssistant::Column::IndexViewMethods
70
+
71
+ def value(record)
72
+ v = record.send name
73
+ if v
74
+ target = AssociationTarget.new v.class
75
+ str = target.name.capitalize
76
+ fv = target.assoc_value v
77
+ if fv
78
+ str << " '#{fv}'"
79
+ else
80
+ str << " #{v.id}"
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ class SearchView < AdminAssistant::Column::View
87
+ include AdminAssistant::Column::SearchViewMethods
88
+
89
+ def html(form)
90
+ @action_view.send(
91
+ :render,
92
+ :file => AdminAssistant.template_file('_polymorphic_field_search'),
93
+ :use_full_path => false,
94
+ :locals => {
95
+ :record => @search, :column => @column,
96
+ :polymorphic_types => @polymorphic_types
97
+ }
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,47 @@
1
+ class AdminAssistant
2
+ module Request
3
+ class Autocomplete < Base
4
+ def associated_class
5
+ @associated_class ||= Module.const_get(
6
+ underscored_assoc_class_name.camelize
7
+ )
8
+ end
9
+
10
+ def call
11
+ render_template_file(
12
+ 'autocomplete', :layout => false,
13
+ :locals => {
14
+ :records => records, :prefix => underscored_assoc_class_name,
15
+ :associated_class => associated_class
16
+ }
17
+ )
18
+ end
19
+
20
+ def records
21
+ action =~ /autocomplete_(.*)/
22
+ associated_class = Module.const_get $1.camelize
23
+ target = AssociationTarget.new associated_class
24
+ field = target.default_name_method
25
+ associated_class.find(
26
+ :all,
27
+ :conditions => [
28
+ "LOWER(#{field}) like ?",
29
+ "%#{search_string.downcase unless search_string.nil?}%"
30
+ ],
31
+ :limit => 10, :order => "length(#{field}), lower(#{field})"
32
+ )
33
+ end
34
+
35
+ def search_string
36
+ @controller.params[
37
+ "#{underscored_assoc_class_name}_autocomplete_input"
38
+ ]
39
+ end
40
+
41
+ def underscored_assoc_class_name
42
+ action =~ /autocomplete_(.*)/
43
+ $1
44
+ end
45
+ end
46
+ end
47
+ end