simple_admin 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|