simple_admin 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +2 -2
- data/README.rdoc +2 -2
- data/Rakefile +2 -2
- data/app/assets/stylesheets/simple_admin/active_admin.css +9 -3
- data/app/controllers/simple_admin/admin_controller.rb +13 -6
- data/app/helpers/simple_admin/filter_helper.rb +82 -31
- data/app/helpers/simple_admin/form_helper.rb +7 -1
- data/app/helpers/simple_admin/header_helper.rb +7 -5
- data/app/helpers/simple_admin/path_helper.rb +3 -3
- data/app/helpers/simple_admin/table_helper.rb +5 -4
- data/app/views/layouts/simple_admin.html.erb +3 -0
- data/app/views/simple_admin/admin/index.html.erb +99 -64
- data/config/routes.rb +1 -1
- data/lib/simple_admin.rb +8 -0
- data/lib/simple_admin/attributes.rb +11 -5
- data/lib/simple_admin/engine.rb +1 -1
- data/lib/simple_admin/interface.rb +9 -4
- data/lib/simple_admin/version.rb +1 -1
- data/simple_admin.gemspec +1 -2
- data/spec/controllers/simple_admin/admin_controller_spec.rb +9 -0
- data/spec/simple_admin/interface_spec.rb +9 -1
- metadata +21 -40
data/Gemfile
CHANGED
@@ -3,10 +3,10 @@ source "http://rubygems.org"
|
|
3
3
|
# Gem's dependencies in simple_admin.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
gem "rails", ">= 3.
|
6
|
+
gem "rails", ">= 3.2.0"
|
7
7
|
gem "kaminari", ">= 0"
|
8
8
|
gem "formtastic", ">= 0"
|
9
|
-
gem "
|
9
|
+
gem "ransack"
|
10
10
|
gem "fastercsv" if RUBY_VERSION =~ /^1.8/
|
11
11
|
gem "fuubar"
|
12
12
|
|
data/README.rdoc
CHANGED
@@ -72,7 +72,7 @@ You can also specify to which actions the before block should be
|
|
72
72
|
applied:
|
73
73
|
|
74
74
|
SimpleAdmin.register :post do
|
75
|
-
before :only => [:create, :
|
75
|
+
before :only => [:create, :destroy] do
|
76
76
|
require_admin
|
77
77
|
end
|
78
78
|
end
|
@@ -219,7 +219,7 @@ generated at initialization. Available actions are:
|
|
219
219
|
* new
|
220
220
|
* show
|
221
221
|
* edit
|
222
|
-
*
|
222
|
+
* destroy
|
223
223
|
* create
|
224
224
|
* update
|
225
225
|
|
data/Rakefile
CHANGED
@@ -44,8 +44,8 @@ task :sample do
|
|
44
44
|
# If we are not on 1.9.x we need fastercsv
|
45
45
|
system "echo 'gem \"fastercsv\"' >> Gemfile" if RUBY_VERSION =~ /^1.8/
|
46
46
|
|
47
|
-
# To work with Rails 3.1.x we need
|
48
|
-
system "echo 'gem \"
|
47
|
+
# To work with Rails 3.1.x we need ransack
|
48
|
+
system "echo 'gem \"ransack\"' >> Gemfile"
|
49
49
|
|
50
50
|
# With more gems, comes more bundling
|
51
51
|
system "bundle"
|
@@ -793,7 +793,7 @@ body {
|
|
793
793
|
margin-right: 300px; }
|
794
794
|
#active_admin_content.without_sidebar #main_content_wrapper #main_content {
|
795
795
|
margin-right: 0; }
|
796
|
-
#active_admin_content #sidebar {
|
796
|
+
#active_admin_content #sidebar, #active_admin_content #filters {
|
797
797
|
float: left;
|
798
798
|
width: 270px;
|
799
799
|
margin-left: -270px; }
|
@@ -989,7 +989,7 @@ table {
|
|
989
989
|
.panel_contents table tr.even td {
|
990
990
|
background: #f4f5f5; }
|
991
991
|
|
992
|
-
.sidebar_section {
|
992
|
+
.sidebar_section, .filters_section {
|
993
993
|
background: #f4f4f4;
|
994
994
|
-webkit-border-radius: 4px;
|
995
995
|
-moz-border-radius: 4px;
|
@@ -998,7 +998,7 @@ table {
|
|
998
998
|
-moz-box-shadow: inset 0 1px 4px #dddddd;
|
999
999
|
-webkit-box-shadow: inset 0 1px 4px #dddddd;
|
1000
1000
|
margin-bottom: 20px; }
|
1001
|
-
.sidebar_section h3 {
|
1001
|
+
.sidebar_section h3, .filters_section h3 {
|
1002
1002
|
background: #efefef;
|
1003
1003
|
background: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#dfe1e2));
|
1004
1004
|
background: -moz-linear-gradient(-90deg, #efefef, #dfe1e2);
|
@@ -1015,16 +1015,21 @@ table {
|
|
1015
1015
|
line-height: 140%;
|
1016
1016
|
margin-bottom: 0.5em;
|
1017
1017
|
color: #5e6469; }
|
1018
|
+
.filters_section h3 span.icon svg path, .filters_section h3 span.icon svg polygon, .filters_section h3 span.icon svg rect, .filters_section h3 span.icon svg circle,
|
1018
1019
|
.sidebar_section h3 span.icon svg path, .sidebar_section h3 span.icon svg polygon, .sidebar_section h3 span.icon svg rect, .sidebar_section h3 span.icon svg circle {
|
1019
1020
|
fill: #5e6469 !important; }
|
1021
|
+
.filters_section h3 span.icon,
|
1020
1022
|
.sidebar_section h3 span.icon {
|
1021
1023
|
width: 1em;
|
1022
1024
|
height: 1em; }
|
1025
|
+
.filters_section h3 span.icon svg,
|
1023
1026
|
.sidebar_section h3 span.icon svg {
|
1024
1027
|
width: 1em;
|
1025
1028
|
height: 1em; }
|
1029
|
+
.filters_section h3 span.icon,
|
1026
1030
|
.sidebar_section h3 span.icon {
|
1027
1031
|
margin-right: 5px; }
|
1032
|
+
.filters_section > div,
|
1028
1033
|
.sidebar_section > div {
|
1029
1034
|
padding: 3px 15px 15px 15px; }
|
1030
1035
|
|
@@ -1385,6 +1390,7 @@ table.dashboard {
|
|
1385
1390
|
text-transform: uppercase;
|
1386
1391
|
letter-spacing: 0.2em; }
|
1387
1392
|
|
1393
|
+
.filters_section .attributes_table th,
|
1388
1394
|
.sidebar_section .attributes_table th {
|
1389
1395
|
width: 50px; }
|
1390
1396
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'kaminari'
|
2
|
-
require '
|
2
|
+
require 'ransack'
|
3
3
|
|
4
4
|
module SimpleAdmin
|
5
5
|
class AdminController < ::ApplicationController
|
@@ -7,6 +7,7 @@ module SimpleAdmin
|
|
7
7
|
before_filter :lookup_interface, :except => [:dashboard]
|
8
8
|
before_filter :lookup_before, :except => [:dashboard]
|
9
9
|
before_filter :lookup_resource, :only => [:show, :edit, :update, :destroy]
|
10
|
+
before_filter :check_action, :except => [:dashboard]
|
10
11
|
|
11
12
|
unloadable
|
12
13
|
|
@@ -17,10 +18,12 @@ module SimpleAdmin
|
|
17
18
|
layout 'simple_admin'
|
18
19
|
|
19
20
|
def index
|
20
|
-
@collection = @interface.constant
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
@collection = @interface.constant rescue nil
|
22
|
+
if @collection
|
23
|
+
@collection = @collection.search(clean_search_params(params)).result
|
24
|
+
@collection = @collection.order("#{@interface.constant.table_name}.#{$1} #{$2}") if params[:order] && params[:order] =~ /^([\w\_\.]+)_(desc|asc)$/
|
25
|
+
@collection = @collection.page(params[:page]).per(params[:per_page] || SimpleAdmin.default_per_page) if params[:format].blank? || params[:format] == 'html'
|
26
|
+
end
|
24
27
|
respond_to do |format|
|
25
28
|
format.csv
|
26
29
|
format.html
|
@@ -116,9 +119,13 @@ module SimpleAdmin
|
|
116
119
|
search_params = search_params.dup
|
117
120
|
search_params.delete_if do |key, value|
|
118
121
|
value == "" ||
|
119
|
-
["utf8", "scope", "commit", "action", "order", "interface", "controller", "format", "page", "per_page"].include?(key)
|
122
|
+
["utf8", "scope", "commit", "action", "order", "interface", "controller", "format", "page", "per_page", "editing", "mode"].include?(key)
|
120
123
|
end
|
121
124
|
search_params
|
122
125
|
end
|
126
|
+
|
127
|
+
def check_action
|
128
|
+
raise SimpleAdmin::ActionNotAllowed.new(params[:action]) unless @interface.actions.include?(params[:action].to_sym)
|
129
|
+
end
|
123
130
|
end
|
124
131
|
end
|
@@ -1,10 +1,31 @@
|
|
1
1
|
module SimpleAdmin
|
2
2
|
module FilterHelper
|
3
|
+
def filter_fields(attributes)
|
4
|
+
attributes.map do |col|
|
5
|
+
options = col.options.dup
|
6
|
+
expand_block_options!(options)
|
7
|
+
case col.kind
|
8
|
+
when :attribute, :filter
|
9
|
+
filter_for(col.attribute, @interface.constant, options)
|
10
|
+
when :content
|
11
|
+
instance_exec(self, &col.data)
|
12
|
+
when :fieldset
|
13
|
+
content_tag :fieldset, options do
|
14
|
+
content_tag :legend do
|
15
|
+
options[:legend]
|
16
|
+
end unless options[:legend].blank
|
17
|
+
filter_fields(col.attributes)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
content_tag :div, options do
|
21
|
+
filter_fields(col.attributes)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end.join.html_safe
|
25
|
+
end
|
26
|
+
|
3
27
|
def filter_for(method, klass, options={})
|
4
|
-
options ||= {}
|
5
|
-
options = options.dup
|
6
28
|
options[:as] ||= default_filter_type(klass, method)
|
7
|
-
expand_block_options!(options)
|
8
29
|
return "" unless options[:as]
|
9
30
|
field_type = options.delete(:as)
|
10
31
|
wrapper_options = options[:wrapper_html] || {}
|
@@ -15,22 +36,26 @@ module SimpleAdmin
|
|
15
36
|
end
|
16
37
|
|
17
38
|
def filter_string_input(klass, method, options = {})
|
18
|
-
field_name = "#{method}
|
39
|
+
field_name = "#{method}_cont"
|
40
|
+
label_content = options[:title] || options[:label] || "Search #{method.to_s.titlecase}" unless options[:label] == false
|
19
41
|
|
20
|
-
|
21
|
-
|
22
|
-
]
|
42
|
+
content = []
|
43
|
+
content << label(field_name, label_content) unless label_content.blank?
|
44
|
+
content << text_field_tag(field_name, params[field_name] || '', options[:input_html] || {})
|
45
|
+
content.join("\n").html_safe
|
23
46
|
end
|
24
47
|
|
25
48
|
def filter_date_range_input(klass, method, options = {})
|
26
|
-
gt_field_name = "#{method}
|
27
|
-
lt_field_name = "#{method}
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
49
|
+
gt_field_name = "#{method}_gteq"
|
50
|
+
lt_field_name = "#{method}_lteq"
|
51
|
+
label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
|
52
|
+
|
53
|
+
content = []
|
54
|
+
content << label(gt_field_name, label_content) unless label_content.blank?
|
55
|
+
content << filter_date_text_field(klass, gt_field_name)
|
56
|
+
content << " - "
|
57
|
+
content << filter_date_text_field(klass, lt_field_name)
|
58
|
+
content.join("\n").html_safe
|
34
59
|
end
|
35
60
|
|
36
61
|
def filter_date_text_field(klass, method)
|
@@ -39,12 +64,18 @@ module SimpleAdmin
|
|
39
64
|
end
|
40
65
|
|
41
66
|
def filter_numeric_input(klass, method, options = {})
|
67
|
+
field_name = "#{method}_numeric"
|
42
68
|
filters = numeric_filters_for_method(method, options.delete(:filters) || default_numeric_filters)
|
43
69
|
current_filter = current_numeric_scope(klass, filters)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
70
|
+
label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
|
71
|
+
|
72
|
+
content = []
|
73
|
+
content << label(field_name, label_content) unless label_content.blank?
|
74
|
+
content << select_tag('', options_for_select(filters, current_filter),
|
75
|
+
{:onchange => "document.getElementById('#{method}_numeric').name = '' + this.value + '';"}.merge(options[:input_html] || {}))
|
76
|
+
content << " "
|
77
|
+
content << text_field_tag(current_filter, params[current_filter] || '', {:size => 10, :id => field_name}.merge(options[:input_html] || {}))
|
78
|
+
content.join("\n").html_safe
|
48
79
|
end
|
49
80
|
|
50
81
|
def numeric_filters_for_method(method, filters)
|
@@ -63,7 +94,7 @@ module SimpleAdmin
|
|
63
94
|
|
64
95
|
def filter_select_input(klass, method, options = {})
|
65
96
|
association_name = method.to_s.gsub(/_id$/, '').to_sym
|
66
|
-
|
97
|
+
field_name = if reflection = reflection_for(klass, association_name)
|
67
98
|
if [:has_and_belongs_to_many, :has_many].include?(reflection.macro)
|
68
99
|
"#{association_name.to_s.singularize}_ids"
|
69
100
|
else
|
@@ -72,11 +103,15 @@ module SimpleAdmin
|
|
72
103
|
else
|
73
104
|
association_name
|
74
105
|
end
|
75
|
-
|
106
|
+
field_name = (field_name.to_s + "_eq")
|
76
107
|
collection = find_collection_for_column(klass, association_name, options)
|
77
|
-
|
78
|
-
|
79
|
-
]
|
108
|
+
label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
|
109
|
+
|
110
|
+
content = []
|
111
|
+
content << label(field_name, label_content) unless label_content.blank?
|
112
|
+
content << select_tag(field_name, options_for_select(collection, params[field_name.to_sym]),
|
113
|
+
{:include_blank => options[:include_blank] || 'Any'}.merge(options[:input_html] || {}))
|
114
|
+
content.join("\n").html_safe
|
80
115
|
end
|
81
116
|
|
82
117
|
def generate_association_input_name(klass, method) #:nodoc:
|
@@ -110,26 +145,40 @@ module SimpleAdmin
|
|
110
145
|
end
|
111
146
|
|
112
147
|
def boolean_collection(klass, column, options)
|
113
|
-
[['
|
148
|
+
[['No', false], ['Yes', true]]
|
149
|
+
end
|
150
|
+
|
151
|
+
def filter_boolean_input(klass, method, options = {})
|
152
|
+
field_name = options[:name] || (generate_association_input_name(klass, method).to_s + "_in")
|
153
|
+
label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
|
154
|
+
|
155
|
+
content = []
|
156
|
+
content << check_box_tag(field_name, true, params[field_name.to_sym], options)
|
157
|
+
content << label(field_name, label_content) unless label_content.blank?
|
158
|
+
content.join("\n").html_safe
|
114
159
|
end
|
115
160
|
|
116
161
|
def filter_check_boxes_input(klass, method, options = {})
|
117
|
-
|
162
|
+
field_name = options[:name] || (generate_association_input_name(klass, method).to_s + "_in")
|
118
163
|
collection = find_collection_for_column(klass, method, options)
|
119
|
-
selected_values = params[
|
164
|
+
selected_values = params[field_name.to_sym] || []
|
120
165
|
checkboxes = content_tag :div, :class => "check_boxes_wrapper" do
|
121
166
|
collection.map do |c|
|
122
167
|
label = c.is_a?(Array) ? c.first : c
|
123
168
|
value = c.is_a?(Array) ? c.last : c
|
124
|
-
"<label><input type=\"checkbox\" name=\"#{
|
169
|
+
"<label><input type=\"checkbox\" name=\"#{field_name}[]\" value=\"#{value}\" #{selected_values.include?(value) ? "checked" : ""}/> #{label}</label>"
|
125
170
|
end.join("\n").html_safe
|
126
171
|
end
|
172
|
+
label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
|
127
173
|
|
128
|
-
|
129
|
-
|
130
|
-
|
174
|
+
content = []
|
175
|
+
content << label(field_name, label_content) unless label_content.blank?
|
176
|
+
content << checkboxes
|
177
|
+
content.join("\n").html_safe
|
131
178
|
end
|
132
179
|
|
180
|
+
alias_method :filter_checkboxes_input, :filter_check_boxes_input
|
181
|
+
|
133
182
|
# Returns the default filter type for a given attribute
|
134
183
|
def default_filter_type(klass, method)
|
135
184
|
if column = column_for(klass, method)
|
@@ -143,6 +192,8 @@ module SimpleAdmin
|
|
143
192
|
return :numeric
|
144
193
|
when :float, :decimal
|
145
194
|
return :numeric
|
195
|
+
when :boolean
|
196
|
+
return :boolean
|
146
197
|
end
|
147
198
|
end
|
148
199
|
|
@@ -9,7 +9,7 @@ module SimpleAdmin
|
|
9
9
|
when :attribute
|
10
10
|
form.input col.attribute, options
|
11
11
|
when :content
|
12
|
-
instance_exec(@resource, &col.data)
|
12
|
+
instance_exec(@resource, form, col, &col.data)
|
13
13
|
when :fieldset
|
14
14
|
content_tag :fieldset, options do
|
15
15
|
content_tag :legend do
|
@@ -24,5 +24,11 @@ module SimpleAdmin
|
|
24
24
|
end
|
25
25
|
end.join.html_safe
|
26
26
|
end
|
27
|
+
|
28
|
+
def form_field(form, col)
|
29
|
+
options = (col.options || {}).dup
|
30
|
+
expand_block_options!(options)
|
31
|
+
form.input col.attribute, options
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
@@ -35,12 +35,12 @@ module SimpleAdmin
|
|
35
35
|
content = ""
|
36
36
|
# If we are currently showing, then check for edit and destroy action items
|
37
37
|
if params[:action].to_sym == :show
|
38
|
-
if
|
38
|
+
if @interface.actions.include?(:edit)
|
39
39
|
content << link_to("Edit #{@interface.member.titlecase}",
|
40
40
|
send("edit_simple_admin_#{@interface.member}_path", @object))
|
41
41
|
end
|
42
42
|
content << " "
|
43
|
-
if
|
43
|
+
if @interface.actions.include?(:destroy)
|
44
44
|
content << link_to("Delete #{@interface.member.titlecase}",
|
45
45
|
send("simple_admin_#{@interface.member}_path", @object),
|
46
46
|
:method => :delete, :confirm => "Are you sure you want to delete this?")
|
@@ -48,9 +48,11 @@ module SimpleAdmin
|
|
48
48
|
end
|
49
49
|
# If we are not showing an item or creating a new one, then check for new action items
|
50
50
|
unless [:new, :show, :dashboard].include?(params[:action].to_sym)
|
51
|
-
if
|
52
|
-
|
53
|
-
|
51
|
+
if @interface.constant && @interface.member
|
52
|
+
if @interface.actions.include?(:new)
|
53
|
+
content << link_to("New #{@interface.member.titlecase}",
|
54
|
+
send("new_simple_admin_#{@interface.member}_path"))
|
55
|
+
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
content.html_safe
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module SimpleAdmin
|
2
2
|
module PathHelper
|
3
|
-
def resource_path(res)
|
3
|
+
def resource_path(res, options={})
|
4
4
|
if res.new_record?
|
5
|
-
send("simple_admin_#{@interface.collection}_path")
|
5
|
+
send("simple_admin_#{@interface.collection}_path", options)
|
6
6
|
else
|
7
|
-
send("simple_admin_#{@interface.member}_path", res)
|
7
|
+
send("simple_admin_#{@interface.member}_path", res, options)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -30,10 +30,11 @@ module SimpleAdmin
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def resource_actions(object)
|
33
|
-
links =
|
34
|
-
links += link_to "
|
35
|
-
links += link_to "
|
36
|
-
links
|
33
|
+
links = ""
|
34
|
+
links += link_to "View", send("simple_admin_#{@interface.member}_path", object), :class => "member_link view_link" if @interface.actions.include?(:show)
|
35
|
+
links += link_to "Edit", send("edit_simple_admin_#{@interface.member}_path", object), :class => "member_link edit_link" if @interface.actions.include?(:edit)
|
36
|
+
links += link_to "Delete", send("simple_admin_#{@interface.member}_path", object), :method => :delete, :confirm => "Are you sure you want to delete this?", :class => "member_link delete_link" if @interface.actions.include?(:destroy)
|
37
|
+
raw links
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
@@ -1,82 +1,117 @@
|
|
1
1
|
<% content_for :content do %>
|
2
|
-
|
3
|
-
<div class="
|
4
|
-
|
5
|
-
<% if @collection.
|
6
|
-
|
2
|
+
<% if @collection %>
|
3
|
+
<div class="paginated_collection <%= @interface.collection.to_s %>">
|
4
|
+
<div class="pagination_information">
|
5
|
+
<% if @collection.num_pages < 2 %>
|
6
|
+
<% if @collection.size == 0 %>
|
7
|
+
No <%= @interface.collection.titleize %> found
|
8
|
+
<% elsif @collection.size == 1 %>
|
9
|
+
Displaying <b>1</b> <%= @interface.member.titleize %>
|
10
|
+
<% else %>
|
11
|
+
Displaying <b>all <%= @collection.size %></b> <%= @interface.collection.titleize %>
|
12
|
+
<% end %>
|
7
13
|
<% elsif @collection.size == 1 %>
|
8
|
-
|
14
|
+
Displaying <%= @interface.member.titleize %>: <%= pretty_format(@collection.first) %>;
|
15
|
+
<b><%= @collection.total_count %></b> in total
|
9
16
|
<% else %>
|
10
|
-
|
17
|
+
Displaying <%= @interface.collection.titleize %>
|
18
|
+
<b><%= (@collection.current_page * (params[:per_page] || SimpleAdmin.default_per_page).to_i) - (params[:per_page] || SimpleAdmin.default_per_page).to_i + 1 %> - <%= (@collection.current_page * (params[:per_page] || SimpleAdmin.default_per_page).to_i) > @collection.total_count ? @collection.total_count : (@collection.current_page * (params[:per_page] || SimpleAdmin.default_per_page).to_i) %></b>
|
19
|
+
of <b><%= @collection.total_count %></b> in total
|
11
20
|
<% end %>
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
<% editable = @interface.attributes_for(:index).any? {|a| a.editable} %>
|
22
|
+
<% if editable %>
|
23
|
+
| <%= link_to((params[:editing] ? 'Stop Editing' : 'Edit'), request.query_parameters.merge(:editing => (params[:editing] ? nil : 1))) %>
|
24
|
+
| <%= link_to((params[:mode] == 'approvals' ? 'Rows' : 'Approvals'), request.query_parameters.merge(
|
25
|
+
:mode => (params[:mode] == 'approvals' ? nil : 'approvals'),
|
26
|
+
:per_page => (params[:mode] == 'approvals' ? nil : 1))) %>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
<div class="paginated_collection_contents <%= params[:mode] || 'rows' %> <%= 'editing' if params[:editing] %>">
|
30
|
+
<div class="index_content">
|
31
|
+
<% if params[:mode] == 'approvals' %>
|
32
|
+
<div class="index_as_pages">
|
33
|
+
<%= paginate(@collection) %>
|
34
|
+
<% if @resource = @collection.first %>
|
35
|
+
<%= send(SimpleAdmin.form_for_helper, @resource, :as => @interface.member.to_sym,
|
36
|
+
:url => resource_path(@resource), :id => "#{@interface.member}_#{@resource.id}", :html => {:onsubmit => "return false", :class => "inline simple_form formtastic #{@interface.member}"}) do |form| %>
|
37
|
+
<%= form_fields(form, @interface.attributes_for(:index, 'approvals')) %>
|
29
38
|
<% end %>
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
39
|
+
<% end %>
|
40
|
+
</div>
|
41
|
+
<% else %>
|
42
|
+
<div class="index_as_table">
|
43
|
+
<table border="0" class="index_table" cellspacing="0" cellpadding="0" id="<%= @interface.collection %>">
|
44
|
+
<thead>
|
45
|
+
<tr>
|
36
46
|
<% @interface.attributes_for(:index).each do |col| %>
|
37
|
-
<
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
<%= pretty_format(object.send(col.attribute)) %>
|
42
|
-
<% end %>
|
43
|
-
</td>
|
47
|
+
<th class="<%= sortable_header_classes_for(col) %> <%= col.attribute %>">
|
48
|
+
<%= link_to(col.title,
|
49
|
+
request.query_parameters.merge(:order => "#{col.sort_key}_#{order_for_sort_key(col.sort_key)}").except(:page)) %>
|
50
|
+
</th>
|
44
51
|
<% end %>
|
45
|
-
<
|
52
|
+
<th> </th>
|
46
53
|
</tr>
|
47
|
-
|
48
|
-
|
49
|
-
|
54
|
+
</thead>
|
55
|
+
<tbody>
|
56
|
+
<% @collection.each do |object| %>
|
57
|
+
<tr class="<%= cycle('odd', 'even') %>">
|
58
|
+
<% @interface.attributes_for(:index).each do |col| %>
|
59
|
+
<td class="<%= col.attribute %>">
|
60
|
+
<% if col.editable && params[:editing] %>
|
61
|
+
<%= send(SimpleAdmin.form_for_helper, object, :as => @interface.member.to_sym,
|
62
|
+
:url => resource_path(object), :id => "#{@interface.member}_#{object.id}", :html => {:class => "inline simple_form formtastic #{@interface.member}"}) do |form| %>
|
63
|
+
<%= form_field(form, col) %>
|
64
|
+
<% end %>
|
65
|
+
<% elsif col.data %>
|
66
|
+
<%= instance_exec(object, &col.data) %>
|
67
|
+
<% else %>
|
68
|
+
<%= pretty_format(object.send(col.attribute)) %>
|
69
|
+
<% end %>
|
70
|
+
</td>
|
71
|
+
<% end %>
|
72
|
+
<td><%= resource_actions(object) %></td>
|
73
|
+
</tr>
|
74
|
+
<% end %>
|
75
|
+
</tbody>
|
76
|
+
</table>
|
77
|
+
</div>
|
78
|
+
<% end %>
|
50
79
|
</div>
|
51
80
|
</div>
|
81
|
+
<% if params[:mode] != 'approvals' %>
|
82
|
+
<div id="index_footer">
|
83
|
+
Download:
|
84
|
+
<% [:csv, :xml, :json].each do |format| %>
|
85
|
+
<%= link_to format.to_s.upcase, { :format => format}.merge(request.query_parameters.except(:commit, :format)) %>
|
86
|
+
<% end %>
|
87
|
+
</div>
|
88
|
+
<%= paginate(@collection) %>
|
89
|
+
<% end %>
|
90
|
+
|
52
91
|
</div>
|
53
|
-
|
54
|
-
Download:
|
55
|
-
<% [:csv, :xml, :json].each do |format| %>
|
56
|
-
<%= link_to format.to_s.upcase, { :format => format}.merge(request.query_parameters.except(:commit, :format)) %>
|
57
|
-
<% end %>
|
58
|
-
</div>
|
59
|
-
<%= paginate(@collection) %>
|
60
|
-
</div>
|
92
|
+
<% end %>
|
61
93
|
<% end %>
|
62
94
|
|
63
|
-
<% content_for :
|
64
|
-
|
65
|
-
|
66
|
-
<div class="
|
67
|
-
|
68
|
-
|
69
|
-
|
95
|
+
<% content_for :filters do %>
|
96
|
+
<% filters = @interface.filters_for(:index) %>
|
97
|
+
<% unless filters.blank? %>
|
98
|
+
<div class="panel sidebar_section" id="filters_sidebar_section">
|
99
|
+
<h3>Filters</h3>
|
100
|
+
<div class="panel_contents">
|
101
|
+
<%= form_tag '', :class => "q_search", :id => "q_search", :method => "get" do %>
|
102
|
+
<%= filter_fields(filters) %>
|
103
|
+
<div class="buttons">
|
104
|
+
<input name="commit" type="submit" value="Filter" />
|
105
|
+
<a href="?" class="clear_filters_btn">Clear Filters</a>
|
106
|
+
<input id="order" name="order" type="hidden" value="id_desc" />
|
107
|
+
<input id="scope" name="scope" type="hidden" />
|
108
|
+
</div>
|
70
109
|
<% end %>
|
71
|
-
|
72
|
-
<input name="commit" type="submit" value="Filter" />
|
73
|
-
<a href="?" class="clear_filters_btn">Clear Filters</a>
|
74
|
-
<input id="order" name="order" type="hidden" value="id_desc" />
|
75
|
-
<input id="scope" name="scope" type="hidden" />
|
76
|
-
</div>
|
77
|
-
<% end %>
|
110
|
+
</div>
|
78
111
|
</div>
|
79
|
-
|
112
|
+
<% end %>
|
113
|
+
<% end %>
|
80
114
|
|
115
|
+
<% content_for :sidebar do %>
|
81
116
|
<%= sidebars %>
|
82
117
|
<% end %>
|
data/config/routes.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
SimpleAdmin::Engine.routes.draw do
|
2
2
|
root :to => "admin#dashboard"
|
3
3
|
SimpleAdmin::registered.each do |interface|
|
4
|
-
resources interface.
|
4
|
+
resources interface.route,
|
5
5
|
:controller => :admin,
|
6
6
|
:as => "simple_admin_#{interface.collection}",
|
7
7
|
:defaults => {:interface => interface.collection}
|
data/lib/simple_admin.rb
CHANGED
@@ -111,4 +111,12 @@ module SimpleAdmin
|
|
111
111
|
@message = "SimpleAdmin interface unknown, make sure you mount SimpleAdmin in your routes and that you have registered an interface for this resource"
|
112
112
|
end
|
113
113
|
end
|
114
|
+
|
115
|
+
class ActionNotAllowed < StandardError
|
116
|
+
attr_accessor :message
|
117
|
+
|
118
|
+
def initialize(action)
|
119
|
+
@message = "SimpleAdmin #{action} action undefined, make sure you have allowed this action in your interface definition."
|
120
|
+
end
|
121
|
+
end
|
114
122
|
end
|
@@ -33,7 +33,7 @@ module SimpleAdmin
|
|
33
33
|
# :sort_key a column name used when sorting this column (defaults to the column for this attribute)
|
34
34
|
#
|
35
35
|
def attribute(name, options={}, &block)
|
36
|
-
attr = @attributes.find {|attr| attr.attribute == name.to_sym }
|
36
|
+
attr = @attributes.find {|attr| attr.attribute == name.to_sym && attr.mode == options[:mode]}
|
37
37
|
unless attr
|
38
38
|
attr = OpenStruct.new
|
39
39
|
@attributes << attr
|
@@ -44,6 +44,8 @@ module SimpleAdmin
|
|
44
44
|
attr.data = block
|
45
45
|
attr.title = options[:title] || name.to_s.titleize
|
46
46
|
attr.sortable = options[:sortable].nil? || !(options[:sortable] === false)
|
47
|
+
attr.editable = options[:editable] === true
|
48
|
+
attr.mode = options[:mode]
|
47
49
|
attr.sort_key = (options[:sort_key] || name).to_s
|
48
50
|
attr
|
49
51
|
end
|
@@ -58,6 +60,7 @@ module SimpleAdmin
|
|
58
60
|
cont.kind = :content
|
59
61
|
cont.options = options
|
60
62
|
cont.data = block
|
63
|
+
cont.mode = options[:mode]
|
61
64
|
cont
|
62
65
|
end
|
63
66
|
|
@@ -81,6 +84,7 @@ module SimpleAdmin
|
|
81
84
|
@attributes << sect
|
82
85
|
sect.kind = options.delete(:kind) || :section
|
83
86
|
sect.options = options
|
87
|
+
sect.mode = options[:mode]
|
84
88
|
sect.attributes = []
|
85
89
|
save_attributes = @attributes
|
86
90
|
@attributes = sect.attributes
|
@@ -100,21 +104,23 @@ module SimpleAdmin
|
|
100
104
|
# :except An array of excluded attribute names as symbols
|
101
105
|
# :only An array of attribute names for this view
|
102
106
|
#
|
103
|
-
|
107
|
+
def defaults(options={})
|
104
108
|
clear
|
105
109
|
if @section.section == :form
|
106
|
-
|
110
|
+
reflections = @interface.constant.reflections rescue []
|
111
|
+
association_columns = reflections.map do |name, ref|
|
107
112
|
if ref.macro == :belongs_to && ref.options[:polymorphic] != true
|
108
113
|
name.to_sym
|
109
114
|
end
|
110
115
|
end.compact
|
111
116
|
|
112
|
-
|
117
|
+
content_columns = @interface.constant.content_columns rescue []
|
118
|
+
cols = content_columns.map {|col| col.name.to_sym }
|
113
119
|
cols += association_columns
|
114
120
|
cols -= SKIPPED_COLUMNS
|
115
121
|
cols.compact!
|
116
122
|
else
|
117
|
-
cols = @interface.constant.columns.map{|col| col.name.to_sym }
|
123
|
+
cols = @interface.constant.columns.map{|col| col.name.to_sym } rescue []
|
118
124
|
end
|
119
125
|
cols -= options[:except] if options[:except]
|
120
126
|
cols &= options[:only] if options[:only]
|
data/lib/simple_admin/engine.rb
CHANGED
@@ -6,7 +6,7 @@ module SimpleAdmin
|
|
6
6
|
isolate_namespace SimpleAdmin
|
7
7
|
initializer 'simple_admin' do |app|
|
8
8
|
if Rails.env == "development"
|
9
|
-
simple_admin_reloader = ActiveSupport::FileUpdateChecker.new(Dir["app/admin/**/*"],
|
9
|
+
simple_admin_reloader = ActiveSupport::FileUpdateChecker.new(Dir["app/admin/**/*"], {}) do
|
10
10
|
SimpleAdmin.unregister
|
11
11
|
Rails.application.reload_routes!
|
12
12
|
end
|
@@ -9,24 +9,28 @@ module SimpleAdmin
|
|
9
9
|
@constant = resource
|
10
10
|
@symbol = resource.name.underscore.to_sym
|
11
11
|
else
|
12
|
-
@constant = resource.to_s.camelize.singularize.constantize
|
12
|
+
@constant = resource.to_s.camelize.singularize.constantize rescue nil
|
13
13
|
@symbol = resource.to_sym
|
14
14
|
end
|
15
15
|
@member = @symbol.to_s.singularize
|
16
16
|
@collection = @symbol.to_s.pluralize
|
17
17
|
@options = options
|
18
18
|
@sections = {}
|
19
|
+
@block = block
|
19
20
|
@before = []
|
20
|
-
@builder = SimpleAdmin::Builder.new(self, &block)
|
21
21
|
self
|
22
22
|
end
|
23
23
|
|
24
|
+
def route
|
25
|
+
@options[:route] || @collection
|
26
|
+
end
|
27
|
+
|
24
28
|
def filters_for(sym)
|
25
29
|
options_for(sym)[:filters].attributes
|
26
30
|
end
|
27
31
|
|
28
|
-
def attributes_for(sym)
|
29
|
-
options_for(sym)[:attributes].attributes
|
32
|
+
def attributes_for(sym, mode=nil)
|
33
|
+
options_for(sym)[:attributes].attributes.select{|a| a.mode == mode}
|
30
34
|
end
|
31
35
|
|
32
36
|
def sidebars_for(sym)
|
@@ -48,6 +52,7 @@ module SimpleAdmin
|
|
48
52
|
private
|
49
53
|
|
50
54
|
def section(sym)
|
55
|
+
@builder ||= SimpleAdmin::Builder.new(self, &@block)
|
51
56
|
@sections[sym] ||= SimpleAdmin::Section.new(self, sym)
|
52
57
|
@sections[sym]
|
53
58
|
end
|
data/lib/simple_admin/version.rb
CHANGED
data/simple_admin.gemspec
CHANGED
@@ -20,9 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
|
22
22
|
# Formtastic is a dependency if you are using the default form helper
|
23
|
-
s.add_runtime_dependency("rails", [">= 3.1.
|
23
|
+
s.add_runtime_dependency("rails", [">= 3.1.0"])
|
24
24
|
s.add_runtime_dependency("kaminari", [">= 0"])
|
25
|
-
s.add_runtime_dependency("meta_search", [">= 1.1.0.pre"])
|
26
25
|
|
27
26
|
s.add_development_dependency("rspec", [">= 2.6.0"])
|
28
27
|
s.add_development_dependency("rspec-rails", [">= 2.6.0"])
|
@@ -92,4 +92,13 @@ describe SimpleAdmin::AdminController do
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
context "action limits" do
|
96
|
+
it "should prevent destroy from firing" do
|
97
|
+
SimpleAdmin::Interface.any_instance.expects(:actions).returns([:index, :show, :edit])
|
98
|
+
expect {
|
99
|
+
delete :destroy, :id => @thing.id, :interface => "things"
|
100
|
+
}.to raise_error(SimpleAdmin::ActionNotAllowed)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
95
104
|
end
|
@@ -44,11 +44,19 @@ describe SimpleAdmin::Interface do
|
|
44
44
|
@interface.should_not be_nil
|
45
45
|
end
|
46
46
|
|
47
|
-
it "
|
47
|
+
it "doesn't execute a block using a builder when initialized" do
|
48
|
+
SimpleAdmin::Builder.any_instance.expects(:success).never
|
49
|
+
@interface = SimpleAdmin.register(:things) do
|
50
|
+
success
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "executes a block using a builder if a section is requested" do
|
48
55
|
SimpleAdmin::Builder.any_instance.expects(:success)
|
49
56
|
@interface = SimpleAdmin.register(:things) do
|
50
57
|
success
|
51
58
|
end
|
59
|
+
@interface.send(:section, :custom)
|
52
60
|
end
|
53
61
|
|
54
62
|
it "retrieves the attributes for a section" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeff Rafter
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-03-16 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -26,14 +26,12 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 3
|
30
30
|
segments:
|
31
31
|
- 3
|
32
32
|
- 1
|
33
33
|
- 0
|
34
|
-
|
35
|
-
- 4
|
36
|
-
version: 3.1.0rc4
|
34
|
+
version: 3.1.0
|
37
35
|
type: :runtime
|
38
36
|
version_requirements: *id001
|
39
37
|
- !ruby/object:Gem::Dependency
|
@@ -50,27 +48,10 @@ dependencies:
|
|
50
48
|
version: "0"
|
51
49
|
type: :runtime
|
52
50
|
version_requirements: *id002
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
|
-
name: meta_search
|
55
|
-
prerelease: false
|
56
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
hash: 961915996
|
62
|
-
segments:
|
63
|
-
- 1
|
64
|
-
- 1
|
65
|
-
- 0
|
66
|
-
- pre
|
67
|
-
version: 1.1.0.pre
|
68
|
-
type: :runtime
|
69
|
-
version_requirements: *id003
|
70
51
|
- !ruby/object:Gem::Dependency
|
71
52
|
name: rspec
|
72
53
|
prerelease: false
|
73
|
-
requirement: &
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
74
55
|
none: false
|
75
56
|
requirements:
|
76
57
|
- - ">="
|
@@ -82,11 +63,11 @@ dependencies:
|
|
82
63
|
- 0
|
83
64
|
version: 2.6.0
|
84
65
|
type: :development
|
85
|
-
version_requirements: *
|
66
|
+
version_requirements: *id003
|
86
67
|
- !ruby/object:Gem::Dependency
|
87
68
|
name: rspec-rails
|
88
69
|
prerelease: false
|
89
|
-
requirement: &
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
90
71
|
none: false
|
91
72
|
requirements:
|
92
73
|
- - ">="
|
@@ -98,11 +79,11 @@ dependencies:
|
|
98
79
|
- 0
|
99
80
|
version: 2.6.0
|
100
81
|
type: :development
|
101
|
-
version_requirements: *
|
82
|
+
version_requirements: *id004
|
102
83
|
- !ruby/object:Gem::Dependency
|
103
84
|
name: shoulda
|
104
85
|
prerelease: false
|
105
|
-
requirement: &
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
106
87
|
none: false
|
107
88
|
requirements:
|
108
89
|
- - ">="
|
@@ -112,11 +93,11 @@ dependencies:
|
|
112
93
|
- 0
|
113
94
|
version: "0"
|
114
95
|
type: :development
|
115
|
-
version_requirements: *
|
96
|
+
version_requirements: *id005
|
116
97
|
- !ruby/object:Gem::Dependency
|
117
98
|
name: factory_girl
|
118
99
|
prerelease: false
|
119
|
-
requirement: &
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
120
101
|
none: false
|
121
102
|
requirements:
|
122
103
|
- - ">="
|
@@ -126,11 +107,11 @@ dependencies:
|
|
126
107
|
- 0
|
127
108
|
version: "0"
|
128
109
|
type: :development
|
129
|
-
version_requirements: *
|
110
|
+
version_requirements: *id006
|
130
111
|
- !ruby/object:Gem::Dependency
|
131
112
|
name: mocha
|
132
113
|
prerelease: false
|
133
|
-
requirement: &
|
114
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
134
115
|
none: false
|
135
116
|
requirements:
|
136
117
|
- - ">"
|
@@ -140,11 +121,11 @@ dependencies:
|
|
140
121
|
- 0
|
141
122
|
version: "0"
|
142
123
|
type: :development
|
143
|
-
version_requirements: *
|
124
|
+
version_requirements: *id007
|
144
125
|
- !ruby/object:Gem::Dependency
|
145
126
|
name: capybara
|
146
127
|
prerelease: false
|
147
|
-
requirement: &
|
128
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
148
129
|
none: false
|
149
130
|
requirements:
|
150
131
|
- - ">="
|
@@ -156,11 +137,11 @@ dependencies:
|
|
156
137
|
- 0
|
157
138
|
version: 0.4.0
|
158
139
|
type: :development
|
159
|
-
version_requirements: *
|
140
|
+
version_requirements: *id008
|
160
141
|
- !ruby/object:Gem::Dependency
|
161
142
|
name: sqlite3-ruby
|
162
143
|
prerelease: false
|
163
|
-
requirement: &
|
144
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
164
145
|
none: false
|
165
146
|
requirements:
|
166
147
|
- - ">="
|
@@ -170,7 +151,7 @@ dependencies:
|
|
170
151
|
- 0
|
171
152
|
version: "0"
|
172
153
|
type: :development
|
173
|
-
version_requirements: *
|
154
|
+
version_requirements: *id009
|
174
155
|
description: Use SimpleAdmin to build out filtering, searchable, editable interfaces for your models
|
175
156
|
email:
|
176
157
|
- jeffrafter@gmail.com
|