admin_it 1.1.0 → 1.2.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/admin_it.gemspec +1 -0
  4. data/app/assets/fonts/admin_it/FontAwesome.otf +0 -0
  5. data/app/assets/fonts/admin_it/fontawesome-webfont.eot +0 -0
  6. data/app/assets/fonts/admin_it/fontawesome-webfont.svg +469 -379
  7. data/app/assets/fonts/admin_it/fontawesome-webfont.ttf +0 -0
  8. data/app/assets/fonts/admin_it/fontawesome-webfont.woff +0 -0
  9. data/app/assets/javascript/admin_it/admin_it.js +64 -7
  10. data/app/assets/stylesheets/admin_it/font-awesome-old.min.css.erb +4 -0
  11. data/app/assets/stylesheets/admin_it/font-awesome.min.css.erb +2 -2
  12. data/app/controllers/admin_it/signed_url_controller.rb +43 -0
  13. data/app/views/admin_it/context/_show.html.slim +17 -11
  14. data/app/views/admin_it/context/_table.html.slim +14 -16
  15. data/app/views/admin_it/context/_tiles.html.slim +42 -21
  16. data/app/views/admin_it/editors/_hidden.html.slim +6 -0
  17. data/app/views/admin_it/editors/_image.html.slim +50 -0
  18. data/app/views/admin_it/editors/_text.html.slim +6 -0
  19. data/app/views/admin_it/shared/_child.html.slim +21 -14
  20. data/app/views/admin_it/shared/_fields.html.slim +36 -15
  21. data/app/views/admin_it/shared/_filters.html.slim +11 -12
  22. data/app/views/admin_it/shared/_form.html.slim +40 -24
  23. data/app/views/admin_it/shared/_pagination.html.slim +10 -18
  24. data/app/views/admin_it/shared/_toolbar.html.slim +19 -11
  25. data/app/views/layouts/admin_it.html.slim +24 -16
  26. data/app/views/layouts/admin_it_dialog.html.slim +4 -4
  27. data/config/routes.rb +3 -0
  28. data/lib/admin_it.rb +1 -0
  29. data/lib/admin_it/config.rb +9 -0
  30. data/lib/admin_it/context/context.rb +65 -19
  31. data/lib/admin_it/context/single_context.rb +19 -9
  32. data/lib/admin_it/controller.rb +6 -1
  33. data/lib/admin_it/data/active_record/field.rb +7 -0
  34. data/lib/admin_it/data/active_record/single_context.rb +0 -1
  35. data/lib/admin_it/engine.rb +2 -0
  36. data/lib/admin_it/field/field.rb +51 -16
  37. data/lib/admin_it/helpers/toolbar.rb +8 -2
  38. data/lib/admin_it/locales/en.yml +4 -0
  39. data/lib/admin_it/locales/ru.yml +4 -0
  40. data/lib/admin_it/middleware.rb +54 -0
  41. data/lib/admin_it/shared.rb +8 -0
  42. data/lib/admin_it/version.rb +1 -1
  43. data/lib/extend_it/dsl.rb +1 -1
  44. metadata +22 -2
@@ -1,19 +1,40 @@
1
1
  - for_context ||= context
2
2
 
3
+ - if for_context.child?
4
+ - parent_name = for_context.parent.resource.name
5
+ - parent_field = for_context.field(parent_name)
6
+ - if parent_field
7
+ input(
8
+ name=parent_name
9
+ value=parent_field.read(for_context.entity)
10
+ type="hidden"
11
+ )
12
+
13
+ / hidden fields
14
+ - fields.each do |field|
15
+ - next if field.visible?
16
+ - locals = { field: field, for_context: for_context }
17
+ = render partial: File.join(%w(admin_it editors hidden)), locals: locals
18
+
19
+ / visible fields
3
20
  - fields.each do |field|
4
- - name = "#{for_context.resource.name}[#{field.name}]"
5
- - id = "#{for_context.resource.name}_#{field.name}"
6
- - if field.visible?
7
- div class="form-group"
8
- label for="#{id}" class="col-sm-2 control-label"
9
- = field.display_name
10
- div class="col-sm-10"
11
- input(
12
- type="text"
13
- class="form-control"
14
- id==id
15
- name==name
16
- value==field.read(for_context.entity)
17
- )
21
+ - if field.editor == :image
22
+ - locals = { field: field, for_context: for_context, id: "#{for_context.resource.name}_#{field.name}" }
23
+ = render partial: File.join(%w(admin_it editors hidden)), locals: locals
24
+ - next
25
+ - next unless field.visible?
26
+ - next if field == parent_field
27
+ - locals = { field: field, for_context: for_context }
28
+ - if field.partial.nil?
29
+ .form-group
30
+ - control_width = 'col-sm-12'
31
+ - if field.show_label?
32
+ - id = "#{for_context.resource.name}_#{field.name}"
33
+ label.col-sm-2.control-label for="#{id}"
34
+ = field.display_name
35
+ - control_width = 'col-sm-10'
36
+ div class=control_width
37
+ = render partial: File.join('admin_it', 'editors', field.editor.to_s), locals: locals
18
38
  - else
19
- input type="hidden" name==name id==id value==field.read(for_context.entity)
39
+ = render partial: field.partial, locals: locals
40
+
@@ -1,24 +1,23 @@
1
1
  - for_context ||= context
2
2
 
3
3
  - unless for_context.single? || for_context.filters.empty?
4
- div class="row admin-it-filters"
5
- ul class="nav nav-pills nav-stacked col-md-2"
4
+ .row.admin-it-filters
5
+ ul.nav.nav-pills.nav-stacked.col-md-2
6
6
  - for_context.filters.each do |filter, index|
7
7
  li class="#{filter == for_context.active_filter ? 'active' : ''}" data-toggle="popup"
8
8
  a href="##{filter.name}" data-toggle="pill"
9
9
  = filter.display_name
10
- button(
11
- class="btn btn-xs btn-danger pull-right fade"
10
+ button.btn.btn-xs.btn-danger.pull-right.fade(
12
11
  data-toggle="popup-target"
13
- data-link==url_for(for_context.url_params(filters: "-#{filter.name}"))
12
+ data-link=url_for(for_context.url_params(filters: "-#{filter.name}"))
14
13
  )
15
- i class="fa fa-times-circle"
14
+ i.fa.fa-times-circle
16
15
 
17
- div class="well tab-content admin-it-filter-content col-md-10"
16
+ .well.tab-content.admin-it-filter-content.col-md-10
18
17
  - for_context.filters.each do |filter|
19
- div(
20
- class="tab-pane fade#{filter == for_context.active_filter ? ' in active' : ''}"
21
- id==filter.name
18
+ div.tab-pane.fade(
19
+ class="#{filter == for_context.active_filter ? ' in active' : ''}"
20
+ id=filter.name
22
21
  )
23
22
  - filter.all_values.each do |v|
24
23
  - if filter.values.include?(v[:value])
@@ -33,8 +32,8 @@
33
32
  filters: "!#{filter.name}(+#{filter.value(v[:value])})", \
34
33
  active_filter: filter.name \
35
34
  ))
36
- a class="label #{a_class}" href==href
35
+ a.label class="#{a_class}" href=href
37
36
  = v[:value].to_s + ' '
38
- span class="badge"
37
+ span.badge
39
38
  = v[:count].to_s
40
39
  = ' '
@@ -1,50 +1,66 @@
1
1
  - for_context ||= context
2
2
 
3
+ - parent = for_context.child? ? for_context.parent : for_context
4
+ - resource = for_context.resource
5
+
3
6
  - if for_context.is_a?(AdminIt::NewContext)
4
- - action = for_context.resource.collection_path
7
+ - action = resource.collection_path
5
8
  - elsif for_context.is_a?(AdminIt::EditContext)
6
- - action = for_context.resource.single_path(for_context.entity)
7
-
8
- form(
9
- class="form-horizontal"
10
- role="form"
11
- action==action
12
- method="POST"
13
- )
14
- input name="authenticity_token" type="hidden" value==form_authenticity_token
15
- - if for_context.is_a?(AdminIt::EditContext)
16
- input name="_method" type="hidden" value="patch"
9
+ - action = resource.single_path(for_context.entity)
10
+
11
+ / images
12
+ - for_context.fields(scope: { editor: :image }).each do |field|
13
+ - locals = { field: field, for_context: for_context }
14
+ = render partial: File.join(%w(admin_it editors image)), locals: locals
17
15
 
16
+ form.form-horizontal role="form" action=action method="POST"
17
+ input name="authenticity_token" type="hidden" value=form_authenticity_token
18
+ input name="section" type="hidden" value=parent.section
18
19
  - if for_context.child?
19
- input name="redirect" type="hidden" value="test"
20
+ input name="parent" type="hidden" value=for_context.parent.to_link
21
+ - if for_context.is_a?(AdminIt::EditContext)
22
+ input name="_method" type="hidden" value="patch"
20
23
 
21
24
  - if for_context.sections.size > 0
22
- ul class="nav nav-tabs"
25
+ ul.nav.nav-tabs
23
26
  - for_context.sections.each do |section|
24
27
  li class="#{section.name == for_context.section ? 'active' : ''}"
25
28
  a href="##{section.name}" data-toggle="tab"
29
+ - if section.icon?
30
+ i.fa> class="fa-#{section.icon}"
26
31
  = section.display_name
27
- div class="tab-content"
32
+ .tab-content
28
33
  - for_context.sections.each do |section|
29
- div class="tab-pane #{section.name == for_context.section ? 'active' : ''}" id==section.name
30
- - if section.fields.size > 0
34
+ .tab-pane class="#{section.name == for_context.section ? 'active' : ''}" id=section.name
35
+ - if !section.renderable?
31
36
  - fields = for_context.fields.select do |f|
32
37
  - section.fields.include?(f.name)
33
38
  = render partial: File.join(%w(admin_it shared fields)), \
34
39
  locals: { fields: fields }
35
40
  - else
36
- - c = section.render(for_context.entity, for_context)
37
- - if c.is_a?(AdminIt::Context)
38
- = render partial: File.join(%W(admin_it shared child)), \
39
- locals: { for_context: for_context, child: c }
41
+ - rendered = section.render(for_context.entity, for_context)
42
+ - rendered = [rendered] unless rendered.is_a?(Array)
43
+ - rendered.each do |item|
44
+ - if item.is_a?(AdminIt::Context)
45
+ = render partial: File.join(%W(admin_it shared child)), \
46
+ locals: { for_context: for_context, child: item }
47
+ - elsif item.is_a?(AdminIt::Partial)
48
+ - locals = item.locals.merge(for_context: for_context)
49
+ = render partial: item.name, locals: locals
50
+ - elsif item.is_a?(String)
51
+ == item
40
52
  - else
41
53
  = render partial: File.join(%w(admin_it shared fields)), \
42
54
  locals: { fields: for_context.fields(scope: :all) }
43
55
 
44
56
  - content_for :buttons do
45
- button class="btn btn-primary" = t('admin_it.action.save')
57
+ button.btn.btn-primary type="submit" = t('admin_it.action.save')
46
58
 
47
59
  - if for_context.layout.empty?
48
- div class="form-group"
49
- div class="col-sm-10 col-sm-offset-2"
60
+ .form-group
61
+ .col-sm-10.col-sm-offset-2
50
62
  = yield :buttons
63
+
64
+ .modal.fade#child_modal
65
+ .modal-dialog.modal-lg
66
+ .modal-content
@@ -1,18 +1,14 @@
1
1
  - for_context ||= context
2
2
 
3
3
  - if for_context.pages > 1
4
- div class="text-center"
5
- ul class="pagination"
4
+ .text-center
5
+ ul.pagination
6
6
  - if for_context.page == 1
7
- li.disabled: a href="#"
8
- i class="fa fa-angle-double-left"
9
- li.disabled: a href="#"
10
- i class="fa fa-angle-left"
7
+ li.disabled: a href="#": i.fa.fa-angle-double-left
8
+ li.disabled: a href="#": i.fa.fa-angle-left
11
9
  - else
12
- li: a href="#{url_for(page: 'first')}"
13
- i class="fa fa-angle-double-left"
14
- li: a href="#{url_for(page: 'prev')}"
15
- i class="fa fa-angle-left"
10
+ li: a href="#{url_for(page: 'first')}": i.fa.fa-angle-double-left
11
+ li: a href="#{url_for(page: 'prev')}": i.fa.fa-angle-left
16
12
  - start = 1
17
13
  - if for_context.pages > 10
18
14
  - start = for_context.page - 5 if for_context.page > 5
@@ -30,12 +26,8 @@
30
26
  for_context.page < for_context.pages - 5
31
27
  li.disabled: a href="#" ...
32
28
  - if for_context.page == for_context.pages
33
- li.disabled: a href="#"
34
- i class="fa fa-angle-right"
35
- li.disabled: a href="#"
36
- i class="fa fa-angle-double-right"
29
+ li.disabled: a href="#": i.fa.fa-angle-right
30
+ li.disabled: a href="#": i.fa.fa-angle-double-right
37
31
  - else
38
- li: a href="#{url_for(page: 'next')}"
39
- i class="fa fa-angle-right"
40
- li: a href="#{url_for(page: 'last')}"
41
- i class="fa fa-angle-double-right"
32
+ li: a href="#{url_for(page: 'next')}": i.fa.fa-angle-right
33
+ li: a href="#{url_for(page: 'last')}": i.fa.fa-angle-double-right
@@ -1,15 +1,22 @@
1
1
  - for_context ||= context
2
2
 
3
- <!-- toolbar -->
3
+ /! admin_it_#{for_context.resource.name}_toolbar
4
4
  - if for_context.collection?
5
5
  - new_context = for_context.resource.contexts.find do |c|
6
6
  - c <= AdminIt::NewContext
7
+
8
+ / add button
7
9
  - unless new_context.nil?
8
- = for_context.toolbar.button t('admin_it.action.create'), \
9
- add_class: 'btn-success', \
10
- link: new_context.path, \
11
- icon: 'plus'
12
- - if resource.collections.size > 1
10
+ - opts = { add_class: 'btn-success', icon: 'plus' }
11
+ - if for_context.child?
12
+ - opts[:dialog] = '#child_modal'
13
+ - opts[:link] = new_context.url(parent: for_context.parent, layout: :dialog)
14
+ - else
15
+ - opts[:link] = new_context.url
16
+ = for_context.toolbar.button t('admin_it.action.create'), opts
17
+
18
+ / collection context select
19
+ - if resource.collections.size > 1 && !for_context.child?
13
20
  - for_context.toolbar.buttons(class: 'pull-right') do |buttons|
14
21
  - resource.collections.each do |collection|
15
22
  - btn_class = ['btn-default']
@@ -18,14 +25,15 @@
18
25
  link: collection.path, \
19
26
  icon: collection.icon
20
27
 
21
- - unless for_context.all_filters.empty?
28
+ / filters
29
+ - unless for_context.all_filters.empty? || for_context.child?
22
30
  - for_context.toolbar.buttons(class: 'pull-right admin-it-filters') do |buttons|
23
31
  = buttons.button add_class: 'btn-info dropdown-toggle', \
24
32
  add_data: { toggle: 'dropdown' }, \
25
33
  link: '#', icon: 'filter' do |button|
26
- = t('admin_it.filters') + ' '
27
- span class="caret"
28
- ul class="dropdown-menu" role="menu"
34
+ = t('admin_it.filters')
35
+ span.caret<
36
+ ul.dropdown-menu role="menu"
29
37
  - for_context.all_filters.each do |filter|
30
- li: a href==for_context.url_for(filters: filter.filter_name)
38
+ li: a href=for_context.url_for(filters: filter.filter_name)
31
39
  = filter.display_name
@@ -17,21 +17,29 @@ html
17
17
  = javascript_include_tag 'jquery', 'jquery_ujs', 'turbolinks'
18
18
  = javascript_include_tag 'admin_it/bootstrap.min', \
19
19
  'data-turbolinks-track' => true
20
+
21
+ / JQuery FileUpload
22
+ = stylesheet_link_tag 'jquery.fileupload'
23
+ = stylesheet_link_tag 'jquery.fileupload-ui'
24
+ = javascript_include_tag 'jquery.ui.widget'
25
+ = javascript_include_tag 'jquery.fileupload'
26
+ = javascript_include_tag 'jquery.fileupload-ui'
27
+ = javascript_include_tag 'jquery.fileupload-process'
28
+
20
29
  = javascript_include_tag 'admin_it/admin_it'
21
30
 
22
- - html = capture do
23
- body style="padding-top: 70px"
24
- nav class="navbar navbar-fixed-top navbar-inverse" role="navigation"
25
- div class="container"
26
- <!-- top-menu -->
27
-
28
- div class="container admin-it-#{context.name}"
29
- = render File.join(%w(admin_it shared toolbar))
30
- = render File.join(%w(admin_it shared filters))
31
- = yield
32
-
33
- - top_menu_html = capture { context.top_menu.render }
34
- - toolbar_html = capture { context.toolbar.render }
35
- = html.gsub('<!-- top-menu -->', top_menu_html) \
36
- .gsub('<!-- toolbar -->', toolbar_html) \
37
- .html_safe
31
+ = yield :head
32
+
33
+ body style="padding-top: 70px"
34
+ nav.navbar.navbar-fixed-top.navbar-inverse role="navigation"
35
+ .container
36
+ <!-- admin_it_top_menu -->
37
+
38
+ .container class="admin-it-#{context.name}"
39
+ = render File.join(%w(admin_it shared toolbar))
40
+ = render File.join(%w(admin_it shared filters))
41
+ = yield
42
+
43
+ = yield :footer
44
+
45
+ - context.end_render(self)
@@ -1,8 +1,8 @@
1
- div class="modal-header"
1
+ .modal-header
2
2
  h4.modal-title = yield :title
3
- div class="modal-body admin-it-#{context.name}"
3
+ .modal-body class="admin-it-#{context.name}"
4
4
  = yield
5
- div class="modal-footer"
6
- button type="button" class="btn btn-default" data-dismiss="modal"
5
+ .modal-footer
6
+ button.btn.btn-default type="button" data-dismiss="modal"
7
7
  = t('admin_it.action.close')
8
8
  = yield :buttons
@@ -18,4 +18,7 @@ AdminIt::Engine.routes.draw do
18
18
  controller: "admin_it/#{name}",
19
19
  action: resource.default_context)
20
20
  end
21
+
22
+ # generates signed urls for S3
23
+ resources :signed_url, only: :index
21
24
  end
@@ -22,5 +22,6 @@ require File.join %w(admin_it filters)
22
22
  require File.join %w(admin_it resource)
23
23
  require File.join %w(admin_it context)
24
24
  require File.join %w(admin_it controller)
25
+ require File.join %w(admin_it middleware)
25
26
  require File.join %w(admin_it engine)
26
27
  require File.join %w(admin_it helpers)
@@ -21,5 +21,14 @@ module AdminIt
21
21
  end
22
22
  @controller = value
23
23
  end
24
+
25
+ def self.s3
26
+ @s3 ||= {}
27
+ end
28
+
29
+ def self.s3=(value)
30
+ fail ArgumentError, 'Wrong S3 options' unless value.is_a?(Hash)
31
+ @s3 = value
32
+ end
24
33
  end
25
34
  end
@@ -7,6 +7,14 @@ require File.join %w(extend_it callbacks)
7
7
  module AdminIt
8
8
  using EnsureIt if EnsureIt.refined?
9
9
 
10
+ class Partial
11
+ attr_reader :name, :locals
12
+ def initialize(name, **locals)
13
+ @name = name
14
+ @locals = locals
15
+ end
16
+ end
17
+
10
18
  #
11
19
  class Context
12
20
  extend ExtendIt::Base
@@ -63,9 +71,14 @@ module AdminIt
63
71
  false
64
72
  end
65
73
 
66
- def self.url(context = nil, **params)
67
- url = context.nil? ? path : context.path
74
+ def self.url(*args, **params)
75
+ context = nil
76
+ args.reject! { |arg| arg.is_a?(Context) ? context = arg : false }
77
+ url = context.nil? ? path(*args) : context.path(*args)
68
78
  params = context.nil? ? params : context.url_params(**params)
79
+ if params.key?(:parent) && params[:parent].is_a?(Context)
80
+ params[:parent] = params[:parent].to_link
81
+ end
69
82
  unless params.empty?
70
83
  url << '?' << params.map { |k, v| "#{k}=#{v}" }.join('&')
71
84
  url = URI.escape(url)
@@ -84,12 +97,17 @@ module AdminIt
84
97
  \z/x
85
98
 
86
99
  def initialize(from, params: nil, store: nil, parent_init: false)
100
+ @children = []
101
+
87
102
  run_callbacks :initialize do
88
103
  if from.is_a?(self.class.controller_class)
89
104
  @controller = from
90
105
  elsif from.is_a?(Context)
91
106
  @controller = from.controller
92
- self.parent = from unless parent_init == true
107
+ unless parent_init == true
108
+ self.parent = from
109
+ from.instance_variable_get(:@children) << self
110
+ end
93
111
  params ||= {}
94
112
  store ||= {}
95
113
  end
@@ -103,7 +121,7 @@ module AdminIt
103
121
  store = store[name] ||= {}
104
122
  end
105
123
 
106
- params = controller.request.query_parameters if params.nil?
124
+ params = controller.request.params if params.nil?
107
125
  params = Hash[params.map { |k, v| [k.to_sym, v] }]
108
126
 
109
127
  run_callbacks :load, arguments: { params: params, store: store } do
@@ -125,22 +143,33 @@ module AdminIt
125
143
  @name ||= self.class.context_name
126
144
  end
127
145
 
128
- def field(name)
129
- @fields.find { |f| f.name == name }
130
- end
131
-
132
146
  def fields(scope: :visible)
147
+ values = @fields
148
+ if scope.is_a?(Hash)
149
+ if scope.key?(:editor)
150
+ return values.select { |f| f.editor == scope[:editor] }
151
+ end
152
+ end
133
153
  case scope
134
- when nil, :all then @fields
135
- when :visible then @fields.select { |f| f.visible? }
136
- when :hidden then @fields.select { |f| !f.visible? }
137
- when :readable then @fields.select { |f| f.readable? }
138
- when :writable then @fields.select { |f| f.writable? }
139
- when Field::TYPES then @fields.select { |f| f.type == scope }
140
- else @fields
154
+ when nil, :all then values
155
+ when :visible then values.select { |f| f.visible? }
156
+ when :hidden then values.select { |f| !f.visible? }
157
+ when :readable then values.select { |f| f.readable? }
158
+ when :writable then values.select { |f| f.writable? }
159
+ when :sortable then values.select { |f| f.sortable? }
160
+ when :with_labels then values.select { |f| f.show_label? }
161
+ when :without_labels then values.select { |f| !f.show_label? }
162
+ when Field::TYPES then values.select { |f| f.type == scope }
163
+ else values
141
164
  end
142
165
  end
143
166
 
167
+ def field(name)
168
+ name = name.ensure_symbol
169
+ @fields.find { |f| f.name == name }
170
+ end
171
+
172
+
144
173
  def save(**params)
145
174
  return if controller.nil?
146
175
  session = controller.session
@@ -193,11 +222,24 @@ module AdminIt
193
222
  def begin_render(template)
194
223
  @template = template
195
224
  @toolbar = Helpers::Toolbar.new(template)
196
- @top_menu = Helpers::TopMenu.new(template, class: 'navbar-nav')
225
+ unless child?
226
+ @top_menu = Helpers::TopMenu.new(template, class: 'navbar-nav')
227
+ end
228
+ end
229
+
230
+ def end_render(template)
231
+ request = AdminIt::Request.get(controller.request)
232
+ request["admin_it_#{resource.name}_toolbar"] = template.capture do
233
+ @toolbar.render
234
+ end
235
+ unless child?
236
+ request['admin_it_top_menu'] = template.capture { @top_menu.render }
237
+ # @children.each { |c| c.end_render(template) }
238
+ end
197
239
  end
198
240
 
199
241
  def url_params(**params)
200
- params.merge!(parent: @parent.send(:context_param)) unless @parent.nil?
242
+ params.merge!(parent: @parent.to_link) unless @parent.nil?
201
243
  params
202
244
  end
203
245
 
@@ -206,12 +248,16 @@ module AdminIt
206
248
  @template.url_for(*args, url_params(**params))
207
249
  end
208
250
 
209
- protected
251
+ def partial(name, **locals)
252
+ Partial.new(name, **locals)
253
+ end
210
254
 
211
- def context_param
255
+ def to_link
212
256
  "#{resource.name}/#{name}"
213
257
  end
214
258
 
259
+ protected
260
+
215
261
  def load_context; end
216
262
  end
217
263
  end