aerogel-admin 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +13 -0
  6. data/Rakefile +1 -0
  7. data/aerogel-admin.gemspec +31 -0
  8. data/app/helpers/admin.rb +54 -0
  9. data/app/helpers/decorators.rb +29 -0
  10. data/app/helpers/icons.rb +11 -0
  11. data/app/helpers/table_builder.rb +8 -0
  12. data/app/helpers/tabs_builder.rb +7 -0
  13. data/app/routes/admin.rb +37 -0
  14. data/app/routes/users.rb +71 -0
  15. data/app/routes/users_access.rb +54 -0
  16. data/app/routes/users_roles.rb +53 -0
  17. data/assets/fonts/aerogel-icons.css +28 -0
  18. data/assets/fonts/aerogel-icons.eot +0 -0
  19. data/assets/fonts/aerogel-icons.svg +11 -0
  20. data/assets/fonts/aerogel-icons.ttf +0 -0
  21. data/assets/fonts/aerogel-icons.woff +0 -0
  22. data/assets/javascripts/.gitkeep +0 -0
  23. data/assets/javascripts/controllers/admin-modal.js.coffee +7 -0
  24. data/assets/javascripts/controllers/admin-modal/admin-modal-form-buttons.js.coffee +56 -0
  25. data/assets/javascripts/controllers/admin.js.coffee +13 -0
  26. data/assets/javascripts/controllers/admin/selectize-inputs.js.coffee +7 -0
  27. data/assets/javascripts/controllers/admin/top-menu-shadow.js.coffee +9 -0
  28. data/assets/javascripts/utils/ajax-spinner.js.coffee +26 -0
  29. data/assets/javascripts/utils/ajax-watcher.js.coffee +18 -0
  30. data/assets/javascripts/utils/bootstrap-modal-reload.js.coffee +8 -0
  31. data/assets/javascripts/utils/form-data-async.js.coffee +27 -0
  32. data/assets/javascripts/utils/i18n.js.coffee +27 -0
  33. data/assets/javascripts/utils/on-future-elements.js.coffee +15 -0
  34. data/assets/stylesheets/admin/bootstrap-settings.css.scss +33 -0
  35. data/assets/stylesheets/admin/global.css.scss +61 -0
  36. data/assets/stylesheets/admin/styles/ajax-indicator.css.scss +26 -0
  37. data/assets/stylesheets/admin/styles/bootstrap-modal.css.scss +24 -0
  38. data/assets/stylesheets/admin/styles/language-selector.css.scss +5 -0
  39. data/assets/stylesheets/admin/styles/page-header.css.scss +7 -0
  40. data/assets/stylesheets/admin/styles/sticky-footer-navbar.css.scss +34 -0
  41. data/assets/stylesheets/admin/styles/table.css.scss +3 -0
  42. data/assets/stylesheets/admin/styles/top-menu.css.scss +3 -0
  43. data/assets/stylesheets/admin/utils/center-absolutely.css.scss +5 -0
  44. data/assets/stylesheets/controllers/admin.css.scss +14 -0
  45. data/assets/vendor/bootstrap-datetimepicker.css.scss +1 -0
  46. data/assets/vendor/bootstrap-datetimepicker.js.coffee +1 -0
  47. data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.min.css +5 -0
  48. data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js +1 -0
  49. data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.ru.js +163 -0
  50. data/assets/vendor/moment.js.coffee +1 -0
  51. data/assets/vendor/momentjs/moment-with-langs.min.js +9 -0
  52. data/assets/vendor/selectize.css.scss +2 -0
  53. data/assets/vendor/selectize.js.coffee +1 -0
  54. data/assets/vendor/selectize/selectize.bootstrap3.css +385 -0
  55. data/assets/vendor/selectize/selectize.css +311 -0
  56. data/assets/vendor/selectize/selectize.default.css +381 -0
  57. data/assets/vendor/selectize/selectize.js +3345 -0
  58. data/assets/vendor/smart-list-table.css.scss +42 -0
  59. data/assets/vendor/smart-list-table.js.coffee +1 -0
  60. data/assets/vendor/smart-list-table/smart-list-table-row.js.coffee +63 -0
  61. data/assets/vendor/smart-list-table/smart-list-table.css.scss +54 -0
  62. data/assets/vendor/smart-list-table/smart-list-table.js.coffee +133 -0
  63. data/assets/vendor/smart-tree-table.css.scss +42 -0
  64. data/assets/vendor/smart-tree-table.js.coffee +1 -0
  65. data/assets/vendor/smart-tree-table/smart-tree-table-drag-n-drop.js.coffee +190 -0
  66. data/assets/vendor/smart-tree-table/smart-tree-table-row.js.coffee +78 -0
  67. data/assets/vendor/smart-tree-table/smart-tree-table.css.scss +54 -0
  68. data/assets/vendor/smart-tree-table/smart-tree-table.js.coffee +267 -0
  69. data/assets/vendor/spin.js +353 -0
  70. data/config/README.md +3 -0
  71. data/config/development/.keep +0 -0
  72. data/config/production/.keep +0 -0
  73. data/db/model/README.md +1 -0
  74. data/db/model/admin/user_new_form.rb +40 -0
  75. data/db/model/user.rb +26 -0
  76. data/db/seed/01_admin_roles.seed +8 -0
  77. data/db/seed/02_admin_access.seed +24 -0
  78. data/db/seed/development/.keep +0 -0
  79. data/db/seed/development/20_users.seed +45 -0
  80. data/db/seed/development/admin_users.seed +38 -0
  81. data/db/seed/production/.keep +0 -0
  82. data/lib/aerogel/admin.rb +25 -0
  83. data/lib/aerogel/admin/core.rb +14 -0
  84. data/lib/aerogel/admin/menu.rb +38 -0
  85. data/lib/aerogel/admin/table_builder.rb +100 -0
  86. data/lib/aerogel/admin/tabs_builder.rb +69 -0
  87. data/lib/aerogel/admin/version.rb +5 -0
  88. data/locales/actions.en.yml +27 -0
  89. data/locales/actions.ru.yml +28 -0
  90. data/locales/admin.en.yml +14 -0
  91. data/locales/admin.ru.yml +14 -0
  92. data/locales/models.en.yml +8 -0
  93. data/locales/models.ru.yml +8 -0
  94. data/locales/views.en.yml +46 -0
  95. data/locales/views.ru.yml +46 -0
  96. data/public/README.md +1 -0
  97. data/rake/README.md +3 -0
  98. data/views/admin/index.html.erb +3 -0
  99. data/views/admin/table_builder/standard/_table_column.html.erb +3 -0
  100. data/views/admin/table_builder/standard/_table_row.html.erb +7 -0
  101. data/views/admin/table_builder/standard/table.html.erb +10 -0
  102. data/views/admin/tabs_builder/standard/_tab.html.erb +3 -0
  103. data/views/admin/tabs_builder/standard/tabs.html.erb +3 -0
  104. data/views/admin/users/_tabs.html.erb +8 -0
  105. data/views/admin/users/access/delete.html.erb +12 -0
  106. data/views/admin/users/access/edit.html.erb +9 -0
  107. data/views/admin/users/access/index.html.erb +23 -0
  108. data/views/admin/users/access/new.html.erb +9 -0
  109. data/views/admin/users/delete.html.erb +12 -0
  110. data/views/admin/users/edit.html.erb +46 -0
  111. data/views/admin/users/index.html.erb +31 -0
  112. data/views/admin/users/new.html.erb +11 -0
  113. data/views/admin/users/roles/delete.html.erb +12 -0
  114. data/views/admin/users/roles/edit.html.erb +9 -0
  115. data/views/admin/users/roles/index.html.erb +21 -0
  116. data/views/admin/users/roles/new.html.erb +7 -0
  117. data/views/form_builder/standard/field_multiselect.erb +14 -0
  118. data/views/form_builder/standard/field_select.erb +17 -0
  119. data/views/layouts/admin.html.erb +63 -0
  120. data/views/layouts/admin/_ajax_indicator.html.erb +4 -0
  121. data/views/layouts/admin/_alerts.html.erb +22 -0
  122. data/views/layouts/admin/_menu.html.erb +50 -0
  123. data/views/layouts/admin/_menu_item.html.erb +5 -0
  124. data/views/layouts/admin/_modals.html.erb +8 -0
  125. data/views/layouts/admin/modal.html.erb +38 -0
  126. metadata +280 -0
@@ -0,0 +1,3 @@
1
+ A place for your ```*.conf``` files.
2
+
3
+ Environment specific configs go into folders 'production' or 'development'.
File without changes
File without changes
@@ -0,0 +1 @@
1
+ A place for your models.
@@ -0,0 +1,40 @@
1
+ module Admin
2
+ class UserNewForm
3
+ include Model::NonPersistent
4
+
5
+ field :full_name, type: String
6
+ field :email, type: String
7
+ field :roles, type: Array, default: [:user]
8
+
9
+ validates_presence_of :full_name, :email, :roles
10
+ validates_format_of :email, with: /@/, message: :invalid_format
11
+
12
+ validate do |record|
13
+ # validate email
14
+ if User.elem_match( :authentications => { :provider => :password, :uid => record.email } ).count > 0
15
+ record.errors.add :email, :taken
16
+ elsif User.elem_match( :emails => { :email => record.email } ).count > 0
17
+ record.errors.add :email, :taken
18
+ end
19
+ # validate roles
20
+ if record.roles_changed?
21
+ unless Role.slugs.contains? record.roles
22
+ record.errors.add :roles, :invalid_roles
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ # accessors:
29
+ def roles=( value )
30
+ if value.is_a? Array
31
+ self[:roles] = value.map(&:to_sym)
32
+ elsif value.is_a? String
33
+ self[:roles] = value.split(",").map{|v| v.strip.to_sym}
34
+ else
35
+ raise ArgumentError.new "Invalid value of class #{value.class} passed to roles= setter"
36
+ end
37
+ end
38
+
39
+ end # class UserNewForm
40
+ end # module Admin
@@ -0,0 +1,26 @@
1
+ class User
2
+
3
+ # Creates a User from Admin::UserNewForm.
4
+ #
5
+ def self.create_from_admin_user_new_form( object )
6
+ raise "Cannot create User from #{object.class}" unless object.is_a? Admin::UserNewForm
7
+
8
+ tmp_password = self.generate_confirmation_token # generates random password
9
+ self.new(
10
+ full_name: object.full_name,
11
+ roles: object.roles,
12
+ emails: [{
13
+ email: object.email,
14
+ confirmed: false
15
+ }],
16
+ authentications: [{
17
+ provider: :password,
18
+ uid: object.email,
19
+ email_id: object.email,
20
+ password: tmp_password,
21
+ password_confirmation: tmp_password
22
+ }]
23
+ )
24
+ end
25
+
26
+ end # class User
@@ -0,0 +1,8 @@
1
+ model Role
2
+
3
+ find_by [:slug]
4
+
5
+ seeds [
6
+ { slug: :admin, name: "Administrator" }
7
+ ]
8
+
@@ -0,0 +1,24 @@
1
+ # A .seed file is a .conf file which stores
2
+ # database seed data and rules.
3
+ #
4
+
5
+ # Model class to use
6
+ #
7
+ # ! required
8
+ model Access
9
+
10
+ # Attribute name or a list of attribute names to be used as key(s)
11
+ # when finding objects
12
+ #
13
+ # Usage:
14
+ # find_by :name
15
+ # or
16
+ # find_by [:first_name, :last_name]
17
+ #
18
+ # ! required
19
+ find_by [:path, :role]
20
+
21
+ seeds [
22
+ { path: '/admin/**', access: :RW, role: :admin }
23
+ ]
24
+
File without changes
@@ -0,0 +1,45 @@
1
+ model User
2
+
3
+ find_by :full_name
4
+
5
+ # Values of attributes listed here will be set even
6
+ # if they exist already in the matching database object.
7
+ #
8
+ # Other attributes in the seed data will be treated as a default value,
9
+ # i.e. they will only be set if the matching database object does not exist
10
+ # or its corresponding attribute is not set.
11
+ #
12
+ # Usage:
13
+ # force :value
14
+ # or:
15
+ # force [:value, :description]
16
+ #
17
+ # default is: no fields are forced
18
+
19
+ #force :value
20
+
21
+ def create_test_user( name )
22
+ email = "#{name}@#{name}"
23
+ {
24
+ full_name: name,
25
+ roles: [:user],
26
+ emails:[{
27
+ email: email,
28
+ confirmed: true
29
+ }],
30
+ authentications:[{
31
+ provider: :password,
32
+ uid: email,
33
+ email_id: email,
34
+ password: 'test',
35
+ password_confirmation: 'test'
36
+ }]
37
+ }
38
+ end
39
+
40
+ user_seeds = []
41
+ 20.times do |i|
42
+ user_seeds << create_test_user( "user#{i}" )
43
+ end
44
+
45
+ seeds user_seeds
@@ -0,0 +1,38 @@
1
+ model User
2
+
3
+ find_by :full_name
4
+
5
+ # Values of attributes listed here will be set even
6
+ # if they exist already in the matching database object.
7
+ #
8
+ # Other attributes in the seed data will be treated as a default value,
9
+ # i.e. they will only be set if the matching database object does not exist
10
+ # or its corresponding attribute is not set.
11
+ #
12
+ # Usage:
13
+ # force :value
14
+ # or:
15
+ # force [:value, :description]
16
+ #
17
+ # default is: no fields are forced
18
+
19
+ #force :value
20
+
21
+ seeds [
22
+ {
23
+ full_name: 'admin',
24
+ roles: [:user, :admin],
25
+ emails:[{
26
+ email: 'admin@admin',
27
+ confirmed: true
28
+ }],
29
+ authentications:[{
30
+ provider: :password,
31
+ uid: 'admin@admin',
32
+ email_id: 'admin@admin',
33
+ password: 'admin',
34
+ password_confirmation: 'admin'
35
+ }]
36
+ }
37
+ ]
38
+
File without changes
@@ -0,0 +1,25 @@
1
+ require 'aerogel/core'
2
+ require 'aerogel/forms'
3
+ require 'aerogel/users'
4
+ require 'aerogel/media'
5
+ require 'aerogel/bootstrap'
6
+ require 'aerogel/font_awesome'
7
+
8
+ require "aerogel/admin/version"
9
+ require "aerogel/admin/core"
10
+ require "aerogel/admin/menu"
11
+ require "aerogel/admin/table_builder"
12
+ require "aerogel/admin/tabs_builder"
13
+
14
+ module Aerogel
15
+
16
+ # Finally, register module's root folder
17
+ register_path File.join( File.dirname(__FILE__), '..', '..' )
18
+
19
+ # configure module
20
+ on_load do |app|
21
+ app.register Aerogel::Admin
22
+ end
23
+
24
+ end
25
+
@@ -0,0 +1,14 @@
1
+ module Aerogel::Admin
2
+
3
+ def self.registered(app)
4
+ setup_reloader(app) if Aerogel.config.aerogel.reloader?
5
+
6
+ # module initialization
7
+ end
8
+
9
+ def self.setup_reloader(app)
10
+ app.use Aerogel::Reloader, :routes, before: true do
11
+ Aerogel::Admin::Menu.reset!
12
+ end
13
+ end
14
+ end # module Aerogel::Admin
@@ -0,0 +1,38 @@
1
+ module Aerogel::Admin
2
+ class Menu
3
+ attr_accessor :items
4
+ def initialize()
5
+ @items = []
6
+ end
7
+
8
+ def self.instance
9
+ @instance ||= self.new
10
+ end
11
+
12
+ def self.reset!
13
+ instance.items.clear
14
+ end
15
+
16
+ class Item
17
+ attr_accessor :url, :icon, :label, :priority
18
+ def initialize( url, opts = {} )
19
+ self.url = url
20
+ self.icon = opts[:icon]
21
+ self.label = opts[:label] || url
22
+ self.priority = opts[:priority] || 50
23
+ end
24
+
25
+ def self.create( url, opts = {} )
26
+ Menu.instance.items << self.new( url, opts )
27
+ end
28
+
29
+ def human_label
30
+ if label.is_a? Symbol
31
+ I18n.t label
32
+ else
33
+ label.humanize
34
+ end
35
+ end
36
+ end # class Item
37
+ end # class Menu
38
+ end # module Aerogel::Admin
@@ -0,0 +1,100 @@
1
+ module Aerogel::Admin
2
+
3
+ # TableBuilder constructs and displays a table populated with data obtained
4
+ # from passed object.
5
+ #
6
+ # Example:
7
+ # table User.all do
8
+ # column :full_name
9
+ # column :roles
10
+ # end
11
+ #
12
+ class TableBuilder < Aerogel::Render::BlockHelper
13
+
14
+ attr_accessor :object, :options, :columns, :style
15
+
16
+ DEFAULT_OPTIONS = {
17
+ class: 'table-striped',
18
+ style: 'standard',
19
+ html_params: {}
20
+ }
21
+
22
+ def initialize( object, options = {}, &block )
23
+ super( &block )
24
+ self.object = object
25
+ self.options = DEFAULT_OPTIONS.deep_merge( options )
26
+ self.style = self.options[:style]
27
+ self.columns = []
28
+ end
29
+
30
+ def column( *args, &block )
31
+ self.columns << Column.new( self, *args, &block )
32
+ nil
33
+ end
34
+
35
+ def template( name )
36
+ "admin/table_builder/#{style}/#{name}".to_sym
37
+ end
38
+
39
+ def wrap( content )
40
+ erb template("table.html"), locals: { table: self }, layout: false
41
+ end
42
+
43
+ # Renders html params for <table ...>
44
+ #
45
+ def html_params
46
+ attrs = @options[:html_params].dup
47
+ attrs.merge!({
48
+ # :action => @options[:action]
49
+ })
50
+ attrs.map{|n, v| v.nil? ? "#{n}" : "#{n}=\"#{v}\""}.join(" ")
51
+ end
52
+
53
+
54
+ private
55
+
56
+ class Column
57
+
58
+ attr_accessor :table, :field, :label, :options, :block
59
+
60
+ KNOWN_OPTIONS = [ :label ]
61
+
62
+ def initialize( table, field, options = {}, &block )
63
+ self.table = table
64
+ self.field = field
65
+ self.options = options
66
+ self.block = block
67
+ self.label = self.options[:label] || self.field.to_sym
68
+ end
69
+
70
+ def human_label
71
+ if label.is_a? Symbol
72
+ if table.object.respond_to? :human_attribute_name
73
+ table.object.human_attribute_name label, default: label
74
+ elsif table.object.respond_to?( :first ) && table.object.first.class.respond_to?( :human_attribute_name )
75
+ table.object.first.class.human_attribute_name label, default: label
76
+ else
77
+ I18n.t label
78
+ end
79
+ elsif label.is_a? String
80
+ label
81
+ else
82
+ label.to_s.humanize
83
+ end
84
+ end
85
+
86
+ # Renders html params for column cells
87
+ #
88
+ def html_params
89
+ attrs = @options.except( *KNOWN_OPTIONS )
90
+ attrs = attrs.deep_merge( @options[:html_params] ) if @options.key? :html_params
91
+ attrs.to_html_params
92
+ end
93
+
94
+ end # class Column
95
+
96
+ end # class TableBuilder
97
+
98
+ end # module Aerogel::Admin
99
+
100
+
@@ -0,0 +1,69 @@
1
+ module Aerogel::Admin
2
+
3
+ # TabsBuilder constructs and displays a tab navigation.
4
+ # from passed object.
5
+ #
6
+ # Example:
7
+ # tabs do
8
+ # tab "/url/to/page1", label: "Page 1"
9
+ # tab "/url/to/page2", label: "Page 2"
10
+ # end
11
+ #
12
+ class TabsBuilder < Aerogel::Render::BlockHelper
13
+
14
+ attr_accessor :options, :tabs, :style
15
+
16
+ DEFAULT_OPTIONS = {
17
+ style: 'standard'
18
+ }
19
+
20
+ def initialize( options = {}, &block )
21
+ super( &block )
22
+ self.options = DEFAULT_OPTIONS.deep_merge( options )
23
+ self.style = self.options[:style]
24
+ self.tabs = []
25
+ end
26
+
27
+ def tab( *args, &block )
28
+ self.tabs << Tab.new( *args, &block )
29
+ nil
30
+ end
31
+
32
+ def template( name )
33
+ "admin/tabs_builder/#{style}/#{name}".to_sym
34
+ end
35
+
36
+ def wrap( content )
37
+ erb template("tabs.html"), locals: { tabs: self }, layout: false
38
+ end
39
+
40
+ private
41
+
42
+ class Tab
43
+
44
+ attr_accessor :url, :label, :options, :block
45
+
46
+ def initialize( url, options = {}, &block )
47
+ self.url = url
48
+ self.options = options
49
+ self.label = self.options[:label] || self.url
50
+ self.block = block
51
+ end
52
+
53
+ def human_label
54
+ if label.is_a? String
55
+ label
56
+ elsif label.is_a? Symbol
57
+ I18n.t label
58
+ else
59
+ label.humanize
60
+ end
61
+ end
62
+
63
+ end # class Tab
64
+
65
+ end # class TabsBuilder
66
+
67
+ end # module Aerogel::Admin
68
+
69
+