simple_admin_rails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +22 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/application.js +10 -0
- data/app/assets/javascripts/bootstrap.js +4 -0
- data/app/assets/javascripts/moment.js +3043 -0
- data/app/assets/javascripts/simple_admin/admin.js +8 -0
- data/app/assets/javascripts/simple_admin/application.js +13 -0
- data/app/assets/stylesheets/application.css +7 -0
- data/app/assets/stylesheets/bootstrap_and_overrides.css.less +29 -0
- data/app/assets/stylesheets/simple_admin/application.css +29 -0
- data/app/controllers/simple_admin/admin_controller.rb +118 -0
- data/app/controllers/simple_admin/application_controller.rb +4 -0
- data/app/helpers/simple_admin/application_helper.rb +4 -0
- data/app/models/admin.rb +165 -0
- data/app/views/layouts/simple_admin/application.html.erb +70 -0
- data/app/views/simple_admin/admin/_dashboard.html.erb +19 -0
- data/app/views/simple_admin/admin/index.html.erb +50 -0
- data/app/views/simple_admin/admin/model_edit.html.erb +13 -0
- data/app/views/simple_admin/admin/model_new.html.erb +10 -0
- data/config/initializers/kaminari_config.rb +10 -0
- data/config/initializers/simple_form.rb +166 -0
- data/config/initializers/simple_form_bootstrap.rb +136 -0
- data/config/locales/en.bootstrap.yml +23 -0
- data/config/locales/simple_form.en.yml +31 -0
- data/config/routes.rb +12 -0
- data/lib/simple_admin.rb +4 -0
- data/lib/simple_admin/engine.rb +40 -0
- data/lib/simple_admin/version.rb +3 -0
- data/lib/tasks/simple_admin_tasks.rake +4 -0
- data/lib/templates/erb/scaffold/_form.html.erb +13 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +6 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/simple_admin_test.rb +7 -0
- data/test/test_helper.rb +19 -0
- metadata +244 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,7 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
3
|
+
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
4
|
+
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
5
|
+
*= require_self
|
6
|
+
*= require_tree .
|
7
|
+
*/
|
@@ -0,0 +1,29 @@
|
|
1
|
+
@import "twitter/bootstrap/bootstrap";
|
2
|
+
|
3
|
+
// Set the correct sprite paths
|
4
|
+
@iconSpritePath: image-url("twitter/bootstrap/glyphicons-halflings.png");
|
5
|
+
@iconWhiteSpritePath: image-url("twitter/bootstrap/glyphicons-halflings-white.png");
|
6
|
+
|
7
|
+
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
|
8
|
+
@fontAwesomeEotPath: font-url("fontawesome-webfont.eot");
|
9
|
+
@fontAwesomeEotPath_iefix: font-url("fontawesome-webfont.eot?#iefix");
|
10
|
+
@fontAwesomeWoffPath: font-url("fontawesome-webfont.woff");
|
11
|
+
@fontAwesomeTtfPath: font-url("fontawesome-webfont.ttf");
|
12
|
+
@fontAwesomeSvgPath: font-url("fontawesome-webfont.svg#fontawesomeregular");
|
13
|
+
|
14
|
+
// Font Awesome
|
15
|
+
@import "fontawesome/font-awesome";
|
16
|
+
|
17
|
+
// Glyphicons
|
18
|
+
//@import "twitter/bootstrap/glyphicons.less";
|
19
|
+
|
20
|
+
// Your custom LESS stylesheets goes here
|
21
|
+
//
|
22
|
+
// Since bootstrap was imported above you have access to its mixins which
|
23
|
+
// you may use and inherit here
|
24
|
+
//
|
25
|
+
// If you'd like to override bootstrap's own variables, you can do so here as well
|
26
|
+
// See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation
|
27
|
+
//
|
28
|
+
// Example:
|
29
|
+
// @link-color: #ff0000;
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
16
|
+
|
17
|
+
|
18
|
+
h2.admin-header a {
|
19
|
+
text-decoration: none;
|
20
|
+
}
|
21
|
+
|
22
|
+
.pagination {
|
23
|
+
display:inline-block;
|
24
|
+
}
|
25
|
+
|
26
|
+
td.action {
|
27
|
+
white-space: nowrap;
|
28
|
+
width: 1px;
|
29
|
+
}
|
@@ -0,0 +1,118 @@
|
|
1
|
+
ActionController::Parameters.permit_all_parameters = true
|
2
|
+
|
3
|
+
class SimpleAdmin::AdminController < ApplicationController
|
4
|
+
http_basic_authenticate_with name: SimpleAdmin.config_username, password: SimpleAdmin.config_password
|
5
|
+
|
6
|
+
before_action :admin
|
7
|
+
before_action :setup
|
8
|
+
before_action :restrict
|
9
|
+
|
10
|
+
layout "simple_admin/application"
|
11
|
+
|
12
|
+
def index
|
13
|
+
end
|
14
|
+
|
15
|
+
def model_index
|
16
|
+
@resources = @model_name.constantize.order('updated_at DESC').page(params[:page]) || []
|
17
|
+
render :index
|
18
|
+
end
|
19
|
+
|
20
|
+
def model_edit
|
21
|
+
@resource = @admin.get_resource(params[:model_name], params[:id])
|
22
|
+
end
|
23
|
+
|
24
|
+
def model_new
|
25
|
+
@resource = @admin.new_resource(params[:model_name])
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
form_params = params[@model_name.downcase]
|
30
|
+
@resource = @admin.get_resource(@model_name, params[:id])
|
31
|
+
@resource.assign_attributes form_params.to_h.symbolize_keys!
|
32
|
+
|
33
|
+
if @resource.save
|
34
|
+
redirect_to admin_model_index_path @model_name
|
35
|
+
else
|
36
|
+
flash[:error] = "Could not update #{@model_name}! – #{@resource.errors.messages.to_a.join(", ")}"
|
37
|
+
redirect_to admin_model_edit_path(@model_name, params[:id])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def create
|
42
|
+
form_params = params[@model_name.downcase]
|
43
|
+
@resource = @admin.new_resource(@model_name)
|
44
|
+
@resource.assign_attributes form_params.to_h.symbolize_keys!
|
45
|
+
|
46
|
+
if @resource.save
|
47
|
+
redirect_to admin_model_index_path @model_name
|
48
|
+
else
|
49
|
+
flash[:error] = "Could not create #{@model_name}! – #{@resource.errors.messages.to_a.join(", ")}"
|
50
|
+
redirect_to admin_model_new_path @model_name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def destroy
|
55
|
+
@resource = @admin.get_resource(@model_name, params[:id])
|
56
|
+
@resource.destroy!
|
57
|
+
redirect_to admin_model_index_path @model_name
|
58
|
+
end
|
59
|
+
|
60
|
+
def extension
|
61
|
+
@extension = @admin.extensions.find{ |e| e.constant.name == params[:name].camelize }
|
62
|
+
@extension_instance = @extension.instantiate
|
63
|
+
render path_to_extension_view
|
64
|
+
end
|
65
|
+
|
66
|
+
def extension_post
|
67
|
+
@extension = @admin.extensions.find{ |e| e.constant.name == params[:name].camelize }
|
68
|
+
@extension_instance = @extension.instantiate
|
69
|
+
|
70
|
+
if params[:extension].present?
|
71
|
+
@extension_instance.form_params = params[:extension]
|
72
|
+
|
73
|
+
if @extension_instance.process!
|
74
|
+
# success
|
75
|
+
flash[:notice] = @extension_instance.flash_notice
|
76
|
+
redirect_to admin_extension_path(params[:name])
|
77
|
+
else
|
78
|
+
# failure
|
79
|
+
flash[:alert] = @extension_instance.flash_alert
|
80
|
+
render path_to_extension_view
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def admin
|
88
|
+
@admin = Admin.new admin_config
|
89
|
+
end
|
90
|
+
|
91
|
+
def setup
|
92
|
+
@table_models = @admin.table_models
|
93
|
+
@creatable_resources = @admin.creatable_models_whitelist
|
94
|
+
end
|
95
|
+
|
96
|
+
def restrict
|
97
|
+
# make sure we only try to access what we can
|
98
|
+
return unless params[:model_name]
|
99
|
+
@model_name = params[:model_name]
|
100
|
+
render(text: "Could not access that Model", status: 401) unless @table_models.map(&:name).include?(@model_name)
|
101
|
+
end
|
102
|
+
|
103
|
+
def admin_config
|
104
|
+
{
|
105
|
+
model_blacklist: SimpleAdmin.config_model_blacklist,
|
106
|
+
visible_columns_blacklist: SimpleAdmin.config_visible_columns_blacklist,
|
107
|
+
editable_columns_blacklist: SimpleAdmin.config_editable_columns_blacklist,
|
108
|
+
creatable_models_whitelist: SimpleAdmin.config_creatable_models_whitelist,
|
109
|
+
editable_associations_whitelist: SimpleAdmin.config_editable_associations_whitelist,
|
110
|
+
extensions: SimpleAdmin.config_extensions
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def path_to_extension_view
|
115
|
+
# TODO: make hardcoded path to view configurable
|
116
|
+
"/simple_admin/extensions/#{params[:name]}"
|
117
|
+
end
|
118
|
+
end
|
data/app/models/admin.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# there is no Boolean
|
2
|
+
module Boolean; end
|
3
|
+
class TrueClass; include Boolean; end
|
4
|
+
class FalseClass; include Boolean; end
|
5
|
+
|
6
|
+
class Admin
|
7
|
+
attr_accessor :model_blacklist, :visible_columns_blacklist, :editable_columns_blacklist,
|
8
|
+
:creatable_models_whitelist, :editable_associations_whitelist, :extensions
|
9
|
+
|
10
|
+
def initialize(params)
|
11
|
+
@model_blacklist = []
|
12
|
+
@visible_columns_blacklist = []
|
13
|
+
@editable_columns_blacklist = []
|
14
|
+
@creatable_models_whitelist = []
|
15
|
+
@editable_associations_whitelist = {}
|
16
|
+
@extensions = []
|
17
|
+
|
18
|
+
self.model_blacklist = params[:model_blacklist]
|
19
|
+
self.visible_columns_blacklist = params[:visible_columns_blacklist]
|
20
|
+
self.editable_columns_blacklist = params[:editable_columns_blacklist]
|
21
|
+
self.creatable_models_whitelist = params[:creatable_models_whitelist]
|
22
|
+
self.editable_associations_whitelist = params[:editable_associations_whitelist]
|
23
|
+
self.extensions = params[:extensions]
|
24
|
+
end
|
25
|
+
|
26
|
+
def model_blacklist=(items)
|
27
|
+
raise Exception.new("params must be of type Array") unless items.is_a? Array
|
28
|
+
@model_blacklist << items
|
29
|
+
@model_blacklist.flatten!.uniq!
|
30
|
+
end
|
31
|
+
|
32
|
+
def visible_columns_blacklist=(items)
|
33
|
+
raise Exception.new("params must be of type Array") unless items.is_a? Array
|
34
|
+
@visible_columns_blacklist << items
|
35
|
+
@visible_columns_blacklist.flatten!.uniq!
|
36
|
+
end
|
37
|
+
|
38
|
+
def editable_columns_blacklist=(items)
|
39
|
+
raise Exception.new("params must be of type Array") unless items.is_a? Array
|
40
|
+
@editable_columns_blacklist << items
|
41
|
+
@editable_columns_blacklist.flatten!.uniq!
|
42
|
+
end
|
43
|
+
|
44
|
+
def creatable_models_whitelist=(items)
|
45
|
+
raise Exception.new("params must be of type Array") unless items.is_a? Array
|
46
|
+
@creatable_models_whitelist << items
|
47
|
+
@creatable_models_whitelist.flatten!.uniq!
|
48
|
+
@creatable_models_whitelist = @creatable_models_whitelist.map(&:constantize)
|
49
|
+
end
|
50
|
+
|
51
|
+
def editable_associations_whitelist=(items_hash)
|
52
|
+
# key should be model name, values should be array of permitted editable associations
|
53
|
+
raise Exception.new("param must be of type Hash") unless items_hash.is_a? Hash
|
54
|
+
@editable_associations_whitelist = @editable_associations_whitelist.merge items_hash
|
55
|
+
end
|
56
|
+
|
57
|
+
def table_models
|
58
|
+
@table_models = []
|
59
|
+
ActiveRecord::Base.connection.tables.collect do |table|
|
60
|
+
unless model_blacklist && model_blacklist.include?(table.classify)
|
61
|
+
begin
|
62
|
+
@table_models << table.classify.constantize
|
63
|
+
rescue
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
@table_models.compact! || @table_models
|
69
|
+
end
|
70
|
+
|
71
|
+
def decorate_column_class(col_name)
|
72
|
+
case col_name
|
73
|
+
when 'created_at'
|
74
|
+
'format-date'
|
75
|
+
when 'updated_at'
|
76
|
+
'format-date'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def visible_attributes_for_model(my_model)
|
81
|
+
my_model = my_model.constantize if my_model.is_a? String
|
82
|
+
my_model.new.attribute_names - visible_columns_blacklist
|
83
|
+
end
|
84
|
+
|
85
|
+
def editable_attributes_for_model(my_model)
|
86
|
+
my_model = my_model.constantize if my_model.is_a? String
|
87
|
+
my_model.new.attribute_names - editable_columns_blacklist
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_resource(model_name, id)
|
91
|
+
model_name.constantize.find(id)
|
92
|
+
end
|
93
|
+
|
94
|
+
def new_resource(model_name)
|
95
|
+
model_name.constantize.new
|
96
|
+
end
|
97
|
+
|
98
|
+
def simple_form_field(simple_form_obj, my_model, alternate_input_hash={})
|
99
|
+
editable_attributes = self.editable_attributes_for_model my_model
|
100
|
+
collection_validators = my_model.constantize.validators.select{|v| v.options.keys.include? :in }
|
101
|
+
html = ""
|
102
|
+
|
103
|
+
# table columns
|
104
|
+
editable_attributes.each do |ea|
|
105
|
+
if !alternate_input_hash.blank? && ea =~ Regexp.new(alternate_input_hash.keys.first.to_s)
|
106
|
+
# non simple_form inputs
|
107
|
+
html += "<div class='form-group'>"
|
108
|
+
html += simple_form_obj.send(alternate_input_hash.values.first.to_s, ea.to_sym, {}, { class: "form-control" })
|
109
|
+
html += "</div>"
|
110
|
+
else
|
111
|
+
# simple_form inputs
|
112
|
+
collection_validator = collection_validators.select{|cv| cv.attributes.include? ea.to_sym}.first
|
113
|
+
if collection_validator
|
114
|
+
# treat as a collection
|
115
|
+
html += simple_form_obj.input(ea.to_sym, collection: collection_validator.send(:delimiter), input_html: {class: 'form-control'}, wrapper_html: {class: 'form-group'})
|
116
|
+
else
|
117
|
+
if simple_form_obj.object.send(ea.to_sym).is_a? Boolean
|
118
|
+
class_for_group = nil
|
119
|
+
class_for_control = nil
|
120
|
+
else
|
121
|
+
class_for_group = "form-group"
|
122
|
+
class_for_control = "form-control"
|
123
|
+
end
|
124
|
+
# is not a collection
|
125
|
+
html += simple_form_obj.input(ea.to_sym, input_html: {class: class_for_control}, wrapper_html: {class: class_for_group})
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# model associations
|
131
|
+
my_model.constantize.reflections.keys.each do |associated|
|
132
|
+
if editable_associations_whitelist[my_model] &&
|
133
|
+
editable_associations_whitelist[my_model].include?(associated)
|
134
|
+
# TODO: check association type, one to many one to one,
|
135
|
+
# and switch appropriate form control
|
136
|
+
html += simple_form_obj.association(associated, as: :check_boxes)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
html.html_safe
|
141
|
+
end
|
142
|
+
|
143
|
+
def extensions=(ext_array)
|
144
|
+
raise Exception.new("Extension requires an array of constants: [ ExtensionConstant, AnotherExtension ]") unless ext_array.is_a?(Array)
|
145
|
+
@extensions = ext_array.map{ |ext| Extension.new(ext) }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
Extension = Struct.new(:ext) do
|
150
|
+
def view_name
|
151
|
+
self.ext.name.underscore
|
152
|
+
end
|
153
|
+
|
154
|
+
def title
|
155
|
+
self.ext.name.titleize
|
156
|
+
end
|
157
|
+
|
158
|
+
def instantiate
|
159
|
+
self.ext.new
|
160
|
+
end
|
161
|
+
|
162
|
+
def constant
|
163
|
+
self.ext
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= SimpleAdmin.config_site_name %></title>
|
5
|
+
|
6
|
+
<!-- Bootstrap -->
|
7
|
+
<!-- Latest compiled and minified CSS -->
|
8
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
|
9
|
+
<!-- Optional theme -->
|
10
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
|
11
|
+
<!-- Latest compiled and minified JavaScript -->
|
12
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
|
13
|
+
|
14
|
+
<%= stylesheet_link_tag "simple_admin/application", media: "all" %>
|
15
|
+
<%= javascript_include_tag "simple_admin/application" %>
|
16
|
+
<%= csrf_meta_tags %>
|
17
|
+
</head>
|
18
|
+
<body>
|
19
|
+
|
20
|
+
<div class="container">
|
21
|
+
|
22
|
+
<h2 class="page-header admin-header"><%= link_to SimpleAdmin.config_site_name, admin_path %></h2>
|
23
|
+
|
24
|
+
<% if flash[:notice] %>
|
25
|
+
<p class="alert alert-info"><%= flash[:notice] %></p>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<% if flash[:alert] %>
|
29
|
+
<p class="alert alert-warning"><%= flash[:alert] %></p>
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
<div class="row">
|
33
|
+
<div class="col-sm-4">
|
34
|
+
|
35
|
+
<div class="row">
|
36
|
+
<div class="col-sm-12">
|
37
|
+
<% @table_models.each do |t_model| %>
|
38
|
+
<a href="<%= url_for admin_model_index_path(t_model) %>" class="list-group-item <%= 'active' if t_model.name == @model_name %> ">
|
39
|
+
<span class="badge pull-right"><%= t_model.count %></span>
|
40
|
+
<h4 class="list-group-item-heading"><%= t_model.name %></h4>
|
41
|
+
</a>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<% if @admin.extensions.any? %>
|
47
|
+
<hr>
|
48
|
+
<div class="row">
|
49
|
+
<div class="col-sm-12">
|
50
|
+
<% @admin.extensions.each do |ext| %>
|
51
|
+
<a href="<%= url_for admin_extension_path(ext.view_name) %>" class="list-group-item <%= 'active' if ext.title == @extension.try(:title) %> ">
|
52
|
+
<h4 class="list-group-item-heading"><%= ext.title %></h4>
|
53
|
+
</a>
|
54
|
+
<% end %>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
<% end %>
|
58
|
+
|
59
|
+
</div>
|
60
|
+
|
61
|
+
<div class="col-sm-8">
|
62
|
+
<%= yield %>
|
63
|
+
</div>
|
64
|
+
|
65
|
+
</div>
|
66
|
+
|
67
|
+
</div>
|
68
|
+
|
69
|
+
</body>
|
70
|
+
</html>
|