simple_admin_rails 0.0.1
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.
- 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>
|