padrino-admin 0.16.2 → 0.18.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.
- checksums.yaml +4 -4
- data/README.rdoc +3 -3
- data/Rakefile +0 -21
- data/lib/padrino-admin/access_control.rb +17 -4
- data/lib/padrino-admin/generators/actions.rb +1 -1
- data/lib/padrino-admin/generators/admin_app.rb +9 -11
- data/lib/padrino-admin/generators/admin_page.rb +12 -2
- data/lib/padrino-admin/generators/orm.rb +30 -47
- data/lib/padrino-admin/generators/templates/account/seeds.rb.tt +7 -2
- data/lib/padrino-admin/generators/templates/app/controllers/sessions.rb.tt +0 -4
- data/lib/padrino-admin/generators/templates/app.rb.tt +4 -1
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/affix.js +63 -36
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/alert.js +18 -11
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/bootstrap.min.js +4 -4
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/button.js +41 -23
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/carousel.js +90 -49
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/collapse.js +86 -44
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/dropdown.js +66 -48
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/modal.js +167 -52
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/popover.js +33 -20
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/scrollspy.js +58 -39
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/tab.js +55 -25
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/tooltip.js +370 -92
- data/lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/transition.js +22 -11
- data/lib/padrino-admin/generators/templates/assets/javascripts/jquery-3.7.1.min.js +2 -0
- data/lib/padrino-admin/generators/templates/assets/stylesheets/application.css +14 -0
- data/lib/padrino-admin/generators/templates/assets/stylesheets/bootstrap.css +6 -4
- data/lib/padrino-admin/generators/templates/erb/app/base/index.erb.tt +1 -1
- data/lib/padrino-admin/generators/templates/erb/app/layouts/application.erb.tt +1 -1
- data/lib/padrino-admin/generators/templates/erb/app/sessions/new.erb.tt +1 -1
- data/lib/padrino-admin/generators/templates/erb/page/index.erb.tt +2 -2
- data/lib/padrino-admin/generators/templates/haml/app/base/index.haml.tt +1 -1
- data/lib/padrino-admin/generators/templates/haml/app/layouts/application.haml.tt +1 -1
- data/lib/padrino-admin/generators/templates/haml/app/sessions/new.haml.tt +1 -1
- data/lib/padrino-admin/generators/templates/slim/app/base/index.slim.tt +1 -1
- data/lib/padrino-admin/generators/templates/slim/app/layouts/application.slim.tt +1 -1
- data/lib/padrino-admin/generators/templates/slim/app/sessions/new.slim.tt +1 -1
- data/lib/padrino-admin/generators/templates/slim/page/index.slim.tt +2 -2
- data/lib/padrino-admin/helpers/authentication_helpers.rb +1 -1
- data/lib/padrino-admin/helpers/view_helpers.rb +3 -3
- data/lib/padrino-admin/locale/orm/pl.yml +0 -11
- data/lib/padrino-admin.rb +1 -1
- data/padrino-admin.gemspec +0 -2
- data/test/generators/test_account_model_generator.rb +1 -60
- data/test/generators/test_admin_app_generator.rb +159 -25
- data/test/generators/test_admin_page_generator.rb +110 -9
- data/test/test_admin_application.rb +123 -0
- metadata +9 -124
- data/lib/padrino-admin/bootstrap-less/alerts.less +0 -67
- data/lib/padrino-admin/bootstrap-less/badges.less +0 -55
- data/lib/padrino-admin/bootstrap-less/bootstrap.less +0 -49
- data/lib/padrino-admin/bootstrap-less/breadcrumbs.less +0 -26
- data/lib/padrino-admin/bootstrap-less/button-groups.less +0 -226
- data/lib/padrino-admin/bootstrap-less/buttons.less +0 -155
- data/lib/padrino-admin/bootstrap-less/carousel.less +0 -232
- data/lib/padrino-admin/bootstrap-less/close.less +0 -33
- data/lib/padrino-admin/bootstrap-less/code.less +0 -63
- data/lib/padrino-admin/bootstrap-less/component-animations.less +0 -29
- data/lib/padrino-admin/bootstrap-less/dropdowns.less +0 -213
- data/lib/padrino-admin/bootstrap-less/font-awesome/bordered-pulled.less +0 -16
- data/lib/padrino-admin/bootstrap-less/font-awesome/core.less +0 -12
- data/lib/padrino-admin/bootstrap-less/font-awesome/fixed-width.less +0 -6
- data/lib/padrino-admin/bootstrap-less/font-awesome/font-awesome.less +0 -17
- data/lib/padrino-admin/bootstrap-less/font-awesome/icons.less +0 -412
- data/lib/padrino-admin/bootstrap-less/font-awesome/larger.less +0 -13
- data/lib/padrino-admin/bootstrap-less/font-awesome/list.less +0 -19
- data/lib/padrino-admin/bootstrap-less/font-awesome/mixins.less +0 -20
- data/lib/padrino-admin/bootstrap-less/font-awesome/path.less +0 -14
- data/lib/padrino-admin/bootstrap-less/font-awesome/rotated-flipped.less +0 -9
- data/lib/padrino-admin/bootstrap-less/font-awesome/spinning.less +0 -30
- data/lib/padrino-admin/bootstrap-less/font-awesome/stacked.less +0 -20
- data/lib/padrino-admin/bootstrap-less/font-awesome/variables.less +0 -381
- data/lib/padrino-admin/bootstrap-less/forms.less +0 -419
- data/lib/padrino-admin/bootstrap-less/glyphicons.less +0 -233
- data/lib/padrino-admin/bootstrap-less/grid.less +0 -100
- data/lib/padrino-admin/bootstrap-less/input-groups.less +0 -157
- data/lib/padrino-admin/bootstrap-less/jumbotron.less +0 -44
- data/lib/padrino-admin/bootstrap-less/labels.less +0 -64
- data/lib/padrino-admin/bootstrap-less/list-group.less +0 -110
- data/lib/padrino-admin/bootstrap-less/media.less +0 -56
- data/lib/padrino-admin/bootstrap-less/mixins.less +0 -926
- data/lib/padrino-admin/bootstrap-less/modals.less +0 -138
- data/lib/padrino-admin/bootstrap-less/navbar.less +0 -616
- data/lib/padrino-admin/bootstrap-less/navs.less +0 -242
- data/lib/padrino-admin/bootstrap-less/normalize.less +0 -423
- data/lib/padrino-admin/bootstrap-less/padrino-admin.less +0 -18
- data/lib/padrino-admin/bootstrap-less/pager.less +0 -55
- data/lib/padrino-admin/bootstrap-less/pagination.less +0 -88
- data/lib/padrino-admin/bootstrap-less/panels.less +0 -230
- data/lib/padrino-admin/bootstrap-less/popovers.less +0 -133
- data/lib/padrino-admin/bootstrap-less/print.less +0 -101
- data/lib/padrino-admin/bootstrap-less/progress-bars.less +0 -80
- data/lib/padrino-admin/bootstrap-less/responsive-utilities.less +0 -93
- data/lib/padrino-admin/bootstrap-less/scaffolding.less +0 -134
- data/lib/padrino-admin/bootstrap-less/tables.less +0 -233
- data/lib/padrino-admin/bootstrap-less/theme.less +0 -247
- data/lib/padrino-admin/bootstrap-less/thumbnails.less +0 -36
- data/lib/padrino-admin/bootstrap-less/tooltip.less +0 -95
- data/lib/padrino-admin/bootstrap-less/type.less +0 -296
- data/lib/padrino-admin/bootstrap-less/utilities.less +0 -56
- data/lib/padrino-admin/bootstrap-less/variables.less +0 -827
- data/lib/padrino-admin/bootstrap-less/wells.less +0 -29
- data/lib/padrino-admin/generators/templates/account/couchrest.rb.tt +0 -67
- data/lib/padrino-admin/generators/templates/account/datamapper.rb.tt +0 -61
- data/lib/padrino-admin/generators/templates/account/dynamoid.rb.tt +0 -55
- data/lib/padrino-admin/generators/templates/account/minirecord.rb.tt +0 -44
- data/lib/padrino-admin/generators/templates/account/mongomapper.rb.tt +0 -47
- data/lib/padrino-admin/generators/templates/account/ohm.rb.tt +0 -70
- data/lib/padrino-admin/generators/templates/assets/javascripts/jquery-1.11.0.min.js +0 -4
- data/lib/padrino-admin/locale/orm/cs.yml +0 -12
- data/lib/padrino-admin/locale/orm/da.yml +0 -12
- data/lib/padrino-admin/locale/orm/de.yml +0 -12
- data/lib/padrino-admin/locale/orm/en.yml +0 -12
- data/lib/padrino-admin/locale/orm/es.yml +0 -12
- data/lib/padrino-admin/locale/orm/fr.yml +0 -12
- data/lib/padrino-admin/locale/orm/hu.yml +0 -12
- data/lib/padrino-admin/locale/orm/it.yml +0 -12
- data/lib/padrino-admin/locale/orm/ja.yml +0 -12
- data/lib/padrino-admin/locale/orm/lv.yml +0 -12
- data/lib/padrino-admin/locale/orm/nl.yml +0 -12
- data/lib/padrino-admin/locale/orm/no.yml +0 -12
- data/lib/padrino-admin/locale/orm/pt_br.yml +0 -12
- data/lib/padrino-admin/locale/orm/ro.yml +0 -12
- data/lib/padrino-admin/locale/orm/ru.yml +0 -12
- data/lib/padrino-admin/locale/orm/sv.yml +0 -12
- data/lib/padrino-admin/locale/orm/tr.yml +0 -12
- data/lib/padrino-admin/locale/orm/uk.yml +0 -12
- data/lib/padrino-admin/locale/orm/zh_cn.yml +0 -12
- data/lib/padrino-admin/locale/orm/zh_tw.yml +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c033740234fd9e4af9f6c01e8af2090eed03e89faaaf5ba0a1dd9f49d2b53701
|
|
4
|
+
data.tar.gz: b94058c0063610c456ca57fbc873074c21ebf5e968a644a63cf8ef2ea1decfd8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1fb1835134b7dea8f57e7d575f0e46d9512ebaf167e8305c16143c2c0c072af41c63f61fc154ab359ee3ad32f71860da728e766a6c79cd413027e035a457531b
|
|
7
|
+
data.tar.gz: 2076c252e57b13dcdfb6a66ee01219114aa1ab6fbf758036fea60b6b3c9e20582072b9c2fe5f298c6acbc6ee979983f4fe1a26d0a37a28a05bd3243f0a9a6f85
|
data/README.rdoc
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Padrino has a beautiful Admin management dashboard with these features:
|
|
6
6
|
|
|
7
|
-
Orm Agnostic:: Data Adapters for
|
|
7
|
+
Orm Agnostic:: Data Adapters for Activerecord, Sequel, Mongoid
|
|
8
8
|
Template Agnostic:: Erb, Erubis and Haml Renderer
|
|
9
9
|
Authentication:: Support for Account authentication, Account Permission management
|
|
10
10
|
Scaffold:: You can simply create a new "admin interface" by providing a Model
|
|
@@ -27,7 +27,7 @@ Create the admin subapplication:
|
|
|
27
27
|
Next, follow the admin setup steps:
|
|
28
28
|
|
|
29
29
|
* configure your <tt>config/database.rb</tt> to connect to the correct data.
|
|
30
|
-
* run <tt>padrino rake
|
|
30
|
+
* run <tt>padrino rake ar:migrate</tt> # if you use activerecord
|
|
31
31
|
* run <tt>padrino rake seed</tt>
|
|
32
32
|
|
|
33
33
|
Your admin panel now is ready and you can start your server with <tt>padrino start</tt> and point your browser to <tt>/admin</tt>!
|
|
@@ -35,7 +35,7 @@ Your admin panel now is ready and you can start your server with <tt>padrino sta
|
|
|
35
35
|
To create a new "scaffold" you need to provide only a Model name to the command:
|
|
36
36
|
|
|
37
37
|
demo$ padrino-gen model post --skip-migration # edit your post.rb model and add some fields
|
|
38
|
-
demo$ padrino-gen rake
|
|
38
|
+
demo$ padrino-gen rake ar:migrate
|
|
39
39
|
demo$ padrino-gen admin_page post
|
|
40
40
|
demo$ padrino start # and go to http://localhost:3000/admin
|
|
41
41
|
|
data/Rakefile
CHANGED
|
@@ -1,22 +1 @@
|
|
|
1
1
|
require_relative '../gem_rake_helper'
|
|
2
|
-
|
|
3
|
-
namespace :bootstrap do
|
|
4
|
-
desc 'Compile bootstrap assets'
|
|
5
|
-
task :compile do
|
|
6
|
-
puts 'Implement'
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
desc 'Compile css'
|
|
10
|
-
task :css do
|
|
11
|
-
require 'less'
|
|
12
|
-
src = File.join(__dir__, 'lib', 'padrino-admin', 'bootstrap-less', 'padrino-admin.less')
|
|
13
|
-
out = File.join(__dir__, 'lib', 'padrino-admin', 'generators', 'templates', 'assets', 'stylesheets', 'bootstrap.css')
|
|
14
|
-
css = Less::Parser.new(paths: [File.dirname(src)]).parse(File.read(src)).to_css(compress: true)
|
|
15
|
-
File.open(out, 'w') { |file| file.write css }
|
|
16
|
-
puts "Generated #{out}"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
desc 'Compile js'
|
|
20
|
-
task :js do
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -44,7 +44,7 @@ module Padrino
|
|
|
44
44
|
# We map project modules for a given role or roles.
|
|
45
45
|
#
|
|
46
46
|
def roles_for(*roles, &block)
|
|
47
|
-
raise Padrino::Admin::AccessControlError, "Role #{
|
|
47
|
+
raise Padrino::Admin::AccessControlError, "Role #{roles.inspect} must be present and must be a symbol!" if roles.any? { |r| !r.is_a?(Symbol) } || roles.empty?
|
|
48
48
|
raise Padrino::Admin::AccessControlError, "You can't merge :any with other roles" if roles.size > 1 && roles.any? { |r| r == :any }
|
|
49
49
|
|
|
50
50
|
@roles += roles
|
|
@@ -104,6 +104,8 @@ module Padrino
|
|
|
104
104
|
#
|
|
105
105
|
def allowed?(account = nil, path = nil)
|
|
106
106
|
path = '/' if path.nil? || path.empty?
|
|
107
|
+
# Compare the decoded path; scrub invalid UTF-8 ('/%ff') so Regexp rules can match it.
|
|
108
|
+
path = Rack::Utils.unescape_path(path.b).force_encoding(Encoding::UTF_8).scrub
|
|
107
109
|
role = account.role.to_sym rescue nil
|
|
108
110
|
authorizations = @authorizations.find_all { |auth| auth.roles.include?(:any) }
|
|
109
111
|
allowed_paths = authorizations.map(&:allowed).flatten.uniq
|
|
@@ -118,10 +120,21 @@ module Padrino
|
|
|
118
120
|
denied_paths += authorizations.map(&:allowed).flatten.uniq # remove paths explicitly allowed for other roles
|
|
119
121
|
denied_paths += authorizations.map(&:denied).flatten.uniq # remove paths explicitly denied to other roles
|
|
120
122
|
end
|
|
121
|
-
return true if allowed_paths.any? { |p| path
|
|
122
|
-
return false if denied_paths.any? { |p| path
|
|
123
|
+
return true if allowed_paths.any? { |p| path_within?(path, p) }
|
|
124
|
+
return false if denied_paths.any? { |p| path.start_with?(p) } # loose on purpose: fails closed
|
|
123
125
|
true
|
|
124
126
|
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
# An allowed path covers itself, its sub-paths and its formats ('/posts' allows '/posts',
|
|
131
|
+
# '/posts/1' and '/posts.json'), but not unrelated paths that merely start with it ('/posts_admin').
|
|
132
|
+
# A Regexp matches at the start of the path, as on the deny side.
|
|
133
|
+
def path_within?(path, prefix)
|
|
134
|
+
return path.start_with?(prefix) if prefix.is_a?(Regexp) || prefix.end_with?('/', '.')
|
|
135
|
+
|
|
136
|
+
path == prefix || path.start_with?("#{prefix}/", "#{prefix}.")
|
|
137
|
+
end
|
|
125
138
|
end
|
|
126
139
|
|
|
127
140
|
###
|
|
@@ -175,7 +188,7 @@ module Padrino
|
|
|
175
188
|
# Returns the name of the project module humanize them for you.
|
|
176
189
|
#
|
|
177
190
|
def human_name
|
|
178
|
-
@name
|
|
191
|
+
Padrino::Inflections.humanize(@name)
|
|
179
192
|
end
|
|
180
193
|
|
|
181
194
|
##
|
|
@@ -34,7 +34,6 @@ module Padrino
|
|
|
34
34
|
class_option :renderer, type: :string, aliases: '-e', desc: 'Rendering engine (erb, haml, slim)'
|
|
35
35
|
class_option :admin_model, type: :string, aliases: '-m', default: 'Account', desc: 'The name of model for access controlling'
|
|
36
36
|
class_option :admin_name, type: :string, aliases: '-a', default: 'admin', desc: 'The admin application name and path'
|
|
37
|
-
# class_option :models_path, desc: 'The models destination path', default: '.', type: :string
|
|
38
37
|
|
|
39
38
|
# Copies over the Padrino base admin application.
|
|
40
39
|
def create_admin
|
|
@@ -42,13 +41,13 @@ module Padrino
|
|
|
42
41
|
if in_app_root?
|
|
43
42
|
unless supported_orm.include?(orm)
|
|
44
43
|
say "<= At the moment, Padrino only supports #{supported_orm.join(' or ')}. Sorry!", :yellow
|
|
45
|
-
|
|
44
|
+
exit 1
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
tmp_ext = options[:renderer] || fetch_component_choice(:renderer)
|
|
49
48
|
unless supported_ext.include?(tmp_ext.to_sym)
|
|
50
49
|
say "<= You are using '#{tmp_ext}' and for admin we only support '#{supported_ext.join(', ')}'. Please use #{supported_ext.map { |ext| "-e #{ext}" }.join(' or ')}", :yellow
|
|
51
|
-
|
|
50
|
+
exit 1
|
|
52
51
|
end
|
|
53
52
|
|
|
54
53
|
# Get the app's namespace.
|
|
@@ -77,19 +76,19 @@ module Padrino
|
|
|
77
76
|
RUBY
|
|
78
77
|
|
|
79
78
|
unless options[:destroy]
|
|
80
|
-
insert_middleware 'ConnectionPoolManagement', @admin_path if
|
|
81
|
-
insert_middleware 'IdentityMap', @admin_path if orm == :datamapper
|
|
79
|
+
insert_middleware 'ConnectionPoolManagement', @admin_path if orm == :activerecord
|
|
82
80
|
end
|
|
83
81
|
|
|
84
82
|
params = [
|
|
85
83
|
@model_singular, 'name:string', 'surname:string', 'email:string', 'crypted_password:string', 'role:string',
|
|
86
|
-
"-a=#{options[:models_path]}",
|
|
87
84
|
"-r=#{options[:root]}"
|
|
88
85
|
]
|
|
89
86
|
params << '-s' if options[:skip_migration]
|
|
90
87
|
params << '-d' if options[:destroy]
|
|
91
88
|
|
|
92
|
-
|
|
89
|
+
# An existing model is reused: the model generator would refuse it and exit 1.
|
|
90
|
+
model_exists = already_exists?(@model_name, fetch_component_choice(:namespace))
|
|
91
|
+
Padrino::Generators::Model.start(params) if options[:destroy] || !model_exists
|
|
93
92
|
column = Struct.new(:name, :type)
|
|
94
93
|
columns = %i[id name surname email].map { |col| column.new(col) }
|
|
95
94
|
column_fields = [
|
|
@@ -102,7 +101,7 @@ module Padrino
|
|
|
102
101
|
]
|
|
103
102
|
|
|
104
103
|
unless options[:destroy]
|
|
105
|
-
admin_app = Padrino::Generators::AdminPage.new([@model_singular], root: options[:root], destroy: options[:destroy], admin_model: @model_singular, admin_name:
|
|
104
|
+
admin_app = Padrino::Generators::AdminPage.new([@model_singular], root: options[:root], destroy: options[:destroy], admin_model: @model_singular, admin_name: options[:admin_name])
|
|
106
105
|
admin_app.default_orm = Padrino::Admin::Generators::Orm.new(@model_singular, orm, columns, column_fields)
|
|
107
106
|
admin_app.invoke_all
|
|
108
107
|
end
|
|
@@ -147,11 +146,10 @@ module Padrino
|
|
|
147
146
|
|
|
148
147
|
instructions = []
|
|
149
148
|
instructions << "Run 'bundle'"
|
|
150
|
-
if %i[activerecord
|
|
149
|
+
if %i[activerecord sequel].include?(orm)
|
|
151
150
|
instructions << "Run 'bundle exec rake db:create' if you have not created a database yet"
|
|
152
151
|
instructions << "Run 'bundle exec rake db:migrate'"
|
|
153
152
|
end
|
|
154
|
-
instructions << "Now repeat after me... 'ohm mani padme hum', 'ohm mani padme hum'... :)" if orm == :ohm
|
|
155
153
|
instructions << "Run 'bundle exec rake db:seed'"
|
|
156
154
|
instructions << "Visit the admin panel in the browser at '/#{@admin_path}'"
|
|
157
155
|
instructions.map! { |i| " #{instructions.index(i) + 1}) #{i}" }
|
|
@@ -164,7 +162,7 @@ module Padrino
|
|
|
164
162
|
say '=' * 65, :green
|
|
165
163
|
say
|
|
166
164
|
else
|
|
167
|
-
|
|
165
|
+
exit_outside_app_root!
|
|
168
166
|
end
|
|
169
167
|
end
|
|
170
168
|
end
|
|
@@ -41,13 +41,23 @@ module Padrino
|
|
|
41
41
|
def create_controller
|
|
42
42
|
self.destination_root = options[:root]
|
|
43
43
|
if in_app_root?
|
|
44
|
+
unless supported_orm.include?(orm)
|
|
45
|
+
say "<= At the moment, Padrino only supports #{supported_orm.join(' or ')}. Sorry!", :yellow
|
|
46
|
+
exit 1
|
|
47
|
+
end
|
|
48
|
+
|
|
44
49
|
@app_name = fetch_app_name
|
|
45
50
|
@admin_name = options[:admin_name].classify
|
|
46
51
|
@admin_path = options[:admin_name].underscore
|
|
52
|
+
check_app_existence(@admin_path)
|
|
47
53
|
@admin_model = options[:admin_model]
|
|
48
54
|
models.each do |model|
|
|
49
55
|
@orm = default_orm || Padrino::Admin::Generators::Orm.new(model, adapter)
|
|
50
|
-
|
|
56
|
+
if options[:destroy]
|
|
57
|
+
self.behavior = :revoke
|
|
58
|
+
else
|
|
59
|
+
@orm.check_model_orm
|
|
60
|
+
end
|
|
51
61
|
empty_directory destination_root("#{@admin_path}/views/#{@orm.name_plural}")
|
|
52
62
|
|
|
53
63
|
template 'templates/page/controller.rb.tt', destination_root("#{@admin_path}/controllers/#{@orm.name_plural}.rb")
|
|
@@ -59,7 +69,7 @@ module Padrino
|
|
|
59
69
|
options[:destroy] ? remove_project_module(@orm.name_plural) : add_project_module(@orm.name_plural)
|
|
60
70
|
end
|
|
61
71
|
else
|
|
62
|
-
|
|
72
|
+
exit_outside_app_root!
|
|
63
73
|
end
|
|
64
74
|
end
|
|
65
75
|
end
|
|
@@ -4,12 +4,19 @@ module Padrino
|
|
|
4
4
|
# Contains all admin related generator functionality.
|
|
5
5
|
#
|
|
6
6
|
module Generators
|
|
7
|
-
# Defines a generic exception for the admin ORM handler.
|
|
8
|
-
|
|
7
|
+
# Defines a generic exception for the admin ORM handler. A Thor::Error, so the
|
|
8
|
+
# generators print its message without a backtrace and exit with status 1.
|
|
9
|
+
class OrmError < Thor::Error; end
|
|
9
10
|
|
|
10
11
|
##
|
|
11
12
|
# Defines the generic ORM management functions used to manipulate data for admin.
|
|
12
13
|
class Orm
|
|
14
|
+
MODEL_BASES = {
|
|
15
|
+
activerecord: 'ActiveRecord::Base',
|
|
16
|
+
mongoid: 'Mongoid::Document',
|
|
17
|
+
sequel: 'Sequel::Model'
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
13
20
|
attr_reader :klass_name, :klass, :name_plural, :name_singular, :orm, :name_param
|
|
14
21
|
|
|
15
22
|
def initialize(name, orm, columns = nil, column_fields = nil)
|
|
@@ -22,18 +29,26 @@ module Padrino
|
|
|
22
29
|
@orm = orm.to_sym
|
|
23
30
|
@columns = columns
|
|
24
31
|
@column_fields = column_fields
|
|
25
|
-
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Raises OrmError when the model is missing or does not belong to the project's orm. Only
|
|
35
|
+
# needed when reading the model's columns to generate a page, so destroying a page skips it.
|
|
36
|
+
def check_model_orm
|
|
37
|
+
return unless @columns.nil?
|
|
38
|
+
raise OrmError, "Model '#{klass_name}' could not be found!\nPerhaps you would like to run 'bundle exec padrino g model #{klass_name}' to create it first?" if @klass.nil?
|
|
39
|
+
|
|
40
|
+
base = MODEL_BASES[orm]
|
|
41
|
+
return if base.nil? || @klass.ancestors.any? { |mod| mod.name == base }
|
|
42
|
+
|
|
43
|
+
raise OrmError, "Model '#{klass_name}' is not based on #{base} but the project orm is #{orm}!\nCheck the orm in your .components file."
|
|
26
44
|
end
|
|
27
45
|
|
|
28
46
|
def activerecord?
|
|
29
|
-
|
|
30
|
-
when :activerecord, :minirecord then true
|
|
31
|
-
else false
|
|
32
|
-
end
|
|
47
|
+
orm == :activerecord
|
|
33
48
|
end
|
|
34
49
|
|
|
35
50
|
def field_type(type)
|
|
36
|
-
type = :string if type.nil?
|
|
51
|
+
type = :string if type.nil?
|
|
37
52
|
type = type.to_s.demodulize.downcase.to_sym unless type.is_a?(Symbol)
|
|
38
53
|
|
|
39
54
|
case type
|
|
@@ -51,37 +66,12 @@ module Padrino
|
|
|
51
66
|
@columns ||=
|
|
52
67
|
case orm
|
|
53
68
|
when :activerecord then @klass.columns
|
|
54
|
-
when :minirecord then @klass.columns
|
|
55
|
-
when :datamapper then @klass.properties.map { |p| dm_column(p) }
|
|
56
|
-
when :couchrest then @klass.properties
|
|
57
69
|
when :mongoid then @klass.fields.values.reject { |col| %w[_id _type].include?(col.name) }
|
|
58
|
-
when :mongomapper then @klass.keys.values.reject { |key| key.name == '_id' } # On MongoMapper keys are an hash
|
|
59
70
|
when :sequel then @klass.db_schema.map { |k, v| v[:type] = :text if v[:db_type] =~ /^text/i; Column.new(k, v[:type]) }
|
|
60
|
-
when :ohm then @klass.attributes.map { |a| Column.new(a.to_s, :string) } # ohm has strings
|
|
61
|
-
when :dynamoid then @klass.attributes.map { |k, v| Column.new(k.to_s, v[:type]) }
|
|
62
71
|
else raise OrmError, "Adapter #{orm} is not yet supported!"
|
|
63
72
|
end
|
|
64
73
|
end
|
|
65
74
|
|
|
66
|
-
def dm_column(property)
|
|
67
|
-
case property
|
|
68
|
-
when DataMapper::Property::Text
|
|
69
|
-
Column.new(property.name, :text)
|
|
70
|
-
when DataMapper::Property::Boolean
|
|
71
|
-
Column.new(property.name, :boolean)
|
|
72
|
-
when DataMapper::Property::Integer
|
|
73
|
-
Column.new(property.name, :integer)
|
|
74
|
-
when DataMapper::Property::Decimal
|
|
75
|
-
Column.new(property.name, :decimal)
|
|
76
|
-
when DataMapper::Property::Float
|
|
77
|
-
Column.new(property.name, :float)
|
|
78
|
-
when DataMapper::Property::String
|
|
79
|
-
Column.new(property.name, :string)
|
|
80
|
-
else # if all fails, lets assume its string-ish
|
|
81
|
-
Column.new(property.name, :string)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
75
|
def column_fields
|
|
86
76
|
excluded_columns = %w[created_at updated_at] << (orm == :mongoid ? '_id' : 'id')
|
|
87
77
|
column_fields = columns.dup
|
|
@@ -97,9 +87,8 @@ module Padrino
|
|
|
97
87
|
|
|
98
88
|
def find(params = nil)
|
|
99
89
|
case orm
|
|
100
|
-
when :activerecord, :
|
|
101
|
-
when :
|
|
102
|
-
when :sequel, :ohm then "#{klass_name}[#{params}]"
|
|
90
|
+
when :activerecord, :mongoid then "#{klass_name}.find(#{params})"
|
|
91
|
+
when :sequel then "#{klass_name}[#{params}]"
|
|
103
92
|
else raise OrmError, "Adapter #{orm} is not yet supported!"
|
|
104
93
|
end
|
|
105
94
|
end
|
|
@@ -118,42 +107,36 @@ module Padrino
|
|
|
118
107
|
|
|
119
108
|
def update_attributes(params = nil)
|
|
120
109
|
case orm
|
|
121
|
-
when :
|
|
122
|
-
when :activerecord
|
|
110
|
+
when :mongoid then "@#{name_singular}.update_attributes(#{params})"
|
|
111
|
+
when :activerecord then "@#{name_singular}.update(#{params})"
|
|
123
112
|
when :sequel then "@#{name_singular}.modified! && @#{name_singular}.update(#{params})"
|
|
124
113
|
else raise OrmError, "Adapter #{orm} is not yet supported!"
|
|
125
114
|
end
|
|
126
115
|
end
|
|
127
116
|
|
|
128
117
|
def destroy
|
|
129
|
-
|
|
118
|
+
"#{name_singular}.destroy"
|
|
130
119
|
end
|
|
131
120
|
|
|
132
121
|
def find_by_ids(params = nil)
|
|
133
122
|
case orm
|
|
134
|
-
when :ohm then "#{klass_name}.fetch(#{params})"
|
|
135
|
-
when :datamapper then "#{klass_name}.all(id: #{params})"
|
|
136
123
|
when :sequel then "#{klass_name}.where(id: #{params})"
|
|
137
124
|
when :mongoid then "#{klass_name}.find(#{params})"
|
|
138
|
-
when :couchrest then "#{klass_name}.all(keys: #{params})"
|
|
139
|
-
when :dynamoid then "#{klass_name}.find(#{params})"
|
|
140
125
|
else find(params)
|
|
141
126
|
end
|
|
142
127
|
end
|
|
143
128
|
|
|
144
129
|
def multiple_destroy(params = nil)
|
|
145
130
|
case orm
|
|
146
|
-
when :ohm then "#{params}.each(&:delete)"
|
|
147
131
|
when :sequel then "#{params}.destroy"
|
|
148
|
-
when :
|
|
149
|
-
when :couchrest, :mongoid, :mongomapper, :dynamoid then "#{params}.each(&:destroy)"
|
|
132
|
+
when :mongoid then "#{params}.each(&:destroy)"
|
|
150
133
|
else "#{klass_name}.destroy #{params}"
|
|
151
134
|
end
|
|
152
135
|
end
|
|
153
136
|
|
|
154
137
|
def has_error(field)
|
|
155
138
|
case orm
|
|
156
|
-
when :
|
|
139
|
+
when :sequel then "@#{name_singular}.errors.key?(:#{field}) && @#{name_singular}.errors[:#{field}].count > 0"
|
|
157
140
|
else "@#{name_singular}.errors.include?(:#{field})"
|
|
158
141
|
end
|
|
159
142
|
end
|
|
@@ -5,8 +5,13 @@
|
|
|
5
5
|
# name = shell.ask("What's your name?")
|
|
6
6
|
# shell.say name
|
|
7
7
|
#
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
if $stdin.tty?
|
|
9
|
+
email = shell.ask "Which email do you want use for logging into admin?"
|
|
10
|
+
password = shell.ask "Tell me the password to use:", echo: false
|
|
11
|
+
else
|
|
12
|
+
# Piped input (CI, Docker): the email and the password on two lines, not echoed.
|
|
13
|
+
email, password = 2.times.map { $stdin.gets.to_s.strip }
|
|
14
|
+
end
|
|
10
15
|
|
|
11
16
|
shell.say ""
|
|
12
17
|
|
|
@@ -8,11 +8,7 @@
|
|
|
8
8
|
set_current_account(account)
|
|
9
9
|
redirect url(:base, :index)
|
|
10
10
|
elsif Padrino.env == :development && params[:bypass]
|
|
11
|
-
<%- if @orm == :ohm -%>
|
|
12
|
-
account = <%= @model_name %>.all.first
|
|
13
|
-
<%- else -%>
|
|
14
11
|
account = <%= @model_name %>.first
|
|
15
|
-
<%- end -%>
|
|
16
12
|
set_current_account(account)
|
|
17
13
|
redirect url(:base, :index)
|
|
18
14
|
else
|
|
@@ -26,7 +26,10 @@ module <%= @app_name %>
|
|
|
26
26
|
set :admin_model, '<%= @model_name %>'
|
|
27
27
|
set :login_page, '/sessions/new'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# The session cookie is Secure (sent over HTTPS only) in production. Over plain HTTP
|
|
30
|
+
# the app then sends no session cookie, so logins and CSRF-protected forms fail. Behind
|
|
31
|
+
# a proxy that terminates TLS, have it send X-Forwarded-Proto: https, or set secure: false.
|
|
32
|
+
set :sessions, same_site: :lax, secure: Padrino.env == :production
|
|
30
33
|
disable :store_location
|
|
31
34
|
|
|
32
35
|
access_control.roles_for :any do |role|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* ========================================================================
|
|
2
|
-
* Bootstrap: affix.js v3.1
|
|
3
|
-
*
|
|
2
|
+
* Bootstrap: affix.js v3.4.1
|
|
3
|
+
* https://getbootstrap.com/docs/3.4/javascript/#affix
|
|
4
4
|
* ========================================================================
|
|
5
|
-
* Copyright 2011-
|
|
5
|
+
* Copyright 2011-2019 Twitter, Inc.
|
|
6
6
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|
7
7
|
* ======================================================================== */
|
|
8
8
|
|
|
@@ -15,28 +15,56 @@
|
|
|
15
15
|
|
|
16
16
|
var Affix = function (element, options) {
|
|
17
17
|
this.options = $.extend({}, Affix.DEFAULTS, options)
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
var target = this.options.target === Affix.DEFAULTS.target ? $(this.options.target) : $(document).find(this.options.target)
|
|
20
|
+
|
|
21
|
+
this.$target = target
|
|
19
22
|
.on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
|
|
20
23
|
.on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
|
|
21
24
|
|
|
22
25
|
this.$element = $(element)
|
|
23
|
-
this.affixed =
|
|
24
|
-
this.unpin =
|
|
26
|
+
this.affixed = null
|
|
27
|
+
this.unpin = null
|
|
25
28
|
this.pinnedOffset = null
|
|
26
29
|
|
|
27
30
|
this.checkPosition()
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
Affix.
|
|
33
|
+
Affix.VERSION = '3.4.1'
|
|
34
|
+
|
|
35
|
+
Affix.RESET = 'affix affix-top affix-bottom'
|
|
31
36
|
|
|
32
37
|
Affix.DEFAULTS = {
|
|
33
|
-
offset: 0
|
|
38
|
+
offset: 0,
|
|
39
|
+
target: window
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
|
|
43
|
+
var scrollTop = this.$target.scrollTop()
|
|
44
|
+
var position = this.$element.offset()
|
|
45
|
+
var targetHeight = this.$target.height()
|
|
46
|
+
|
|
47
|
+
if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
|
|
48
|
+
|
|
49
|
+
if (this.affixed == 'bottom') {
|
|
50
|
+
if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
|
|
51
|
+
return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var initializing = this.affixed == null
|
|
55
|
+
var colliderTop = initializing ? scrollTop : position.top
|
|
56
|
+
var colliderHeight = initializing ? targetHeight : height
|
|
57
|
+
|
|
58
|
+
if (offsetTop != null && scrollTop <= offsetTop) return 'top'
|
|
59
|
+
if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
|
|
60
|
+
|
|
61
|
+
return false
|
|
34
62
|
}
|
|
35
63
|
|
|
36
64
|
Affix.prototype.getPinnedOffset = function () {
|
|
37
65
|
if (this.pinnedOffset) return this.pinnedOffset
|
|
38
66
|
this.$element.removeClass(Affix.RESET).addClass('affix')
|
|
39
|
-
var scrollTop = this.$
|
|
67
|
+
var scrollTop = this.$target.scrollTop()
|
|
40
68
|
var position = this.$element.offset()
|
|
41
69
|
return (this.pinnedOffset = position.top - scrollTop)
|
|
42
70
|
}
|
|
@@ -48,43 +76,41 @@
|
|
|
48
76
|
Affix.prototype.checkPosition = function () {
|
|
49
77
|
if (!this.$element.is(':visible')) return
|
|
50
78
|
|
|
51
|
-
var
|
|
52
|
-
var scrollTop = this.$window.scrollTop()
|
|
53
|
-
var position = this.$element.offset()
|
|
79
|
+
var height = this.$element.height()
|
|
54
80
|
var offset = this.options.offset
|
|
55
81
|
var offsetTop = offset.top
|
|
56
82
|
var offsetBottom = offset.bottom
|
|
57
|
-
|
|
58
|
-
if (this.affixed == 'top') position.top += scrollTop
|
|
83
|
+
var scrollHeight = Math.max($(document).height(), $(document.body).height())
|
|
59
84
|
|
|
60
85
|
if (typeof offset != 'object') offsetBottom = offsetTop = offset
|
|
61
86
|
if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
|
|
62
87
|
if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
|
|
63
88
|
|
|
64
|
-
var affix = this.
|
|
65
|
-
offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
|
|
66
|
-
offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
|
|
89
|
+
var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
|
|
67
90
|
|
|
68
|
-
if (this.affixed
|
|
69
|
-
|
|
91
|
+
if (this.affixed != affix) {
|
|
92
|
+
if (this.unpin != null) this.$element.css('top', '')
|
|
70
93
|
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
var affixType = 'affix' + (affix ? '-' + affix : '')
|
|
95
|
+
var e = $.Event(affixType + '.bs.affix')
|
|
73
96
|
|
|
74
|
-
|
|
97
|
+
this.$element.trigger(e)
|
|
75
98
|
|
|
76
|
-
|
|
99
|
+
if (e.isDefaultPrevented()) return
|
|
77
100
|
|
|
78
|
-
|
|
79
|
-
|
|
101
|
+
this.affixed = affix
|
|
102
|
+
this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
|
|
80
103
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
this.$element
|
|
105
|
+
.removeClass(Affix.RESET)
|
|
106
|
+
.addClass(affixType)
|
|
107
|
+
.trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
|
|
108
|
+
}
|
|
85
109
|
|
|
86
110
|
if (affix == 'bottom') {
|
|
87
|
-
this.$element.offset({
|
|
111
|
+
this.$element.offset({
|
|
112
|
+
top: scrollHeight - height - offsetBottom
|
|
113
|
+
})
|
|
88
114
|
}
|
|
89
115
|
}
|
|
90
116
|
|
|
@@ -92,9 +118,7 @@
|
|
|
92
118
|
// AFFIX PLUGIN DEFINITION
|
|
93
119
|
// =======================
|
|
94
120
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
$.fn.affix = function (option) {
|
|
121
|
+
function Plugin(option) {
|
|
98
122
|
return this.each(function () {
|
|
99
123
|
var $this = $(this)
|
|
100
124
|
var data = $this.data('bs.affix')
|
|
@@ -105,6 +129,9 @@
|
|
|
105
129
|
})
|
|
106
130
|
}
|
|
107
131
|
|
|
132
|
+
var old = $.fn.affix
|
|
133
|
+
|
|
134
|
+
$.fn.affix = Plugin
|
|
108
135
|
$.fn.affix.Constructor = Affix
|
|
109
136
|
|
|
110
137
|
|
|
@@ -127,10 +154,10 @@
|
|
|
127
154
|
|
|
128
155
|
data.offset = data.offset || {}
|
|
129
156
|
|
|
130
|
-
if (data.offsetBottom) data.offset.bottom = data.offsetBottom
|
|
131
|
-
if (data.offsetTop)
|
|
157
|
+
if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
|
|
158
|
+
if (data.offsetTop != null) data.offset.top = data.offsetTop
|
|
132
159
|
|
|
133
|
-
$spy
|
|
160
|
+
Plugin.call($spy, data)
|
|
134
161
|
})
|
|
135
162
|
})
|
|
136
163
|
|