administrate 0.17.0 → 0.20.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -2
  3. data/app/assets/javascripts/administrate/application.js +0 -2
  4. data/app/assets/javascripts/administrate/components/select.js +3 -0
  5. data/app/assets/stylesheets/administrate/application.scss +0 -1
  6. data/app/assets/stylesheets/administrate/base/_forms.scss +1 -1
  7. data/app/assets/stylesheets/administrate/components/_buttons.scss +9 -0
  8. data/app/assets/stylesheets/administrate/components/_field-unit.scss +7 -0
  9. data/app/assets/stylesheets/administrate/components/_flashes.scss +2 -2
  10. data/app/assets/stylesheets/administrate/library/_variables.scss +1 -1
  11. data/app/controllers/administrate/application_controller.rb +85 -17
  12. data/app/controllers/concerns/administrate/punditize.rb +44 -20
  13. data/app/helpers/administrate/application_helper.rb +24 -6
  14. data/app/views/administrate/application/_collection.html.erb +5 -6
  15. data/app/views/administrate/application/_collection_header_actions.html.erb +2 -2
  16. data/app/views/administrate/application/_collection_item_actions.html.erb +4 -4
  17. data/app/views/administrate/application/_form.html.erb +19 -4
  18. data/app/views/administrate/application/_index_header.html.erb +2 -2
  19. data/app/views/administrate/application/_navigation.html.erb +2 -2
  20. data/app/views/administrate/application/_pagination.html.erb +1 -0
  21. data/app/views/administrate/application/edit.html.erb +2 -2
  22. data/app/views/administrate/application/index.html.erb +1 -1
  23. data/app/views/administrate/application/new.html.erb +1 -1
  24. data/app/views/administrate/application/show.html.erb +28 -12
  25. data/app/views/fields/belongs_to/_index.html.erb +1 -1
  26. data/app/views/fields/belongs_to/_show.html.erb +1 -1
  27. data/app/views/fields/date/_form.html.erb +1 -3
  28. data/app/views/fields/date_time/_form.html.erb +1 -3
  29. data/app/views/fields/has_many/_index.html.erb +1 -1
  30. data/app/views/fields/has_many/_show.html.erb +2 -1
  31. data/app/views/fields/has_one/_form.html.erb +15 -5
  32. data/app/views/fields/has_one/_index.html.erb +2 -1
  33. data/app/views/fields/has_one/_show.html.erb +20 -13
  34. data/app/views/fields/polymorphic/_index.html.erb +2 -1
  35. data/app/views/fields/polymorphic/_show.html.erb +1 -1
  36. data/app/views/fields/select/_form.html.erb +5 -18
  37. data/app/views/fields/time/_form.html.erb +2 -3
  38. data/app/views/fields/url/_index.html.erb +1 -1
  39. data/app/views/fields/url/_show.html.erb +1 -1
  40. data/app/views/layouts/administrate/application.html.erb +1 -1
  41. data/config/locales/administrate.de.yml +2 -2
  42. data/config/locales/administrate.ja.yml +5 -5
  43. data/config/locales/administrate.sl.yml +30 -0
  44. data/docs/adding_controllers_without_related_model.md +2 -2
  45. data/docs/authorization.md +43 -20
  46. data/docs/customizing_controller_actions.md +25 -6
  47. data/docs/customizing_dashboards.md +88 -10
  48. data/docs/getting_started.md +1 -1
  49. data/docs/guides/customising_search.md +149 -0
  50. data/docs/guides/hiding_dashboards_from_sidebar.md +4 -2
  51. data/docs/guides/scoping_has_many_relations.md +27 -0
  52. data/docs/guides.md +3 -1
  53. data/lib/administrate/base_dashboard.rb +39 -3
  54. data/lib/administrate/engine.rb +2 -2
  55. data/lib/administrate/field/associative.rb +18 -8
  56. data/lib/administrate/field/base.rb +4 -0
  57. data/lib/administrate/field/belongs_to.rb +9 -2
  58. data/lib/administrate/field/deferred.rb +5 -1
  59. data/lib/administrate/field/has_many.rb +20 -6
  60. data/lib/administrate/field/has_one.rb +4 -0
  61. data/lib/administrate/field/number.rb +2 -8
  62. data/lib/administrate/field/polymorphic.rb +2 -1
  63. data/lib/administrate/field/select.rb +19 -9
  64. data/lib/administrate/field/url.rb +4 -0
  65. data/lib/administrate/not_authorized_error.rb +20 -0
  66. data/lib/administrate/order.rb +73 -17
  67. data/lib/administrate/page/base.rb +4 -0
  68. data/lib/administrate/page/collection.rb +1 -0
  69. data/lib/administrate/page/form.rb +9 -8
  70. data/lib/administrate/page/show.rb +10 -2
  71. data/lib/administrate/resource_resolver.rb +2 -1
  72. data/lib/administrate/search.rb +1 -1
  73. data/lib/administrate/version.rb +1 -1
  74. data/lib/administrate/view_generator.rb +6 -1
  75. data/lib/administrate.rb +24 -2
  76. data/lib/generators/administrate/dashboard/dashboard_generator.rb +20 -2
  77. data/lib/generators/administrate/dashboard/templates/controller.rb.erb +2 -2
  78. data/lib/generators/administrate/install/install_generator.rb +6 -1
  79. data/lib/generators/administrate/routes/routes_generator.rb +11 -2
  80. data/lib/generators/administrate/test_record.rb +21 -0
  81. metadata +39 -46
  82. data/app/assets/javascripts/administrate/components/date_time_picker.js +0 -14
  83. data/config/i18n-tasks.yml +0 -18
  84. data/config/routes.rb +0 -2
  85. data/config/unicorn.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a099c28c8a74491759229e6fe82de763f25b9ff59473d5c70b00d38675e3498
4
- data.tar.gz: c24140b9e1bc63eb9ecd5f38cd0cfd3d3c0f125a81370df1dd6663af66121baa
3
+ metadata.gz: eebcadb06ece9944279b1119e950922a388078a5c9ab5b95d6d8863c1e03994d
4
+ data.tar.gz: 96a697e2e10228a5f2772b1602985107f7ed428ca63d77c7d6e964a614659b9d
5
5
  SHA512:
6
- metadata.gz: 8ada94532afa210a359eece24f042fdb7de549cdb6bb94507c044f85df7b26cfbd5855202de2016255d635d2448dcba303d91eac4dc41c896f30e52f95073345
7
- data.tar.gz: 98e115c8c3acfe7dc5cab3a3055b5f06dfa5661f9d2eb135c528b474cbe36e6fb51b36a8c88489860a9fda200758ce37f4d60375705f0e0234af4dc2805e75ff
6
+ metadata.gz: da130cb7cdcfd15321a653a59cfb499411f85324c19216097a6f4a76407268cc6691647e2ccdf86c367bd6c2e1491342324de89e22c9152eddbea8e6ba32b819
7
+ data.tar.gz: 8813f237457fa4e37abf8d5c350e4fb4f8f536583a6919764bc5c2abb0bc3b3fc436431b41e731722045179c45dcfef0d4c45d8c5534e7d26ee52d151252665f
data/Rakefile CHANGED
@@ -28,5 +28,3 @@ if defined? RSpec
28
28
  t.verbose = false
29
29
  end
30
30
  end
31
-
32
- task default: "bundler:audit"
@@ -1,6 +1,4 @@
1
1
  //= require jquery
2
2
  //= require jquery_ujs
3
3
  //= require selectize
4
- //= require moment
5
- //= require datetime_picker
6
4
  //= require_tree .
@@ -0,0 +1,3 @@
1
+ $(function() {
2
+ $('.field-unit--select select').selectize({});
3
+ });
@@ -3,7 +3,6 @@
3
3
  @import "reset/normalize";
4
4
 
5
5
  @import "selectize";
6
- @import "datetime_picker";
7
6
 
8
7
  @import "library/clearfix";
9
8
  @import "library/data-label";
@@ -85,7 +85,7 @@ textarea {
85
85
  [type="checkbox"],
86
86
  [type="radio"] {
87
87
  display: inline;
88
- margin-right: $small-spacing / 2;
88
+ margin-right: $small-spacing * 0.5;
89
89
  }
90
90
 
91
91
  [type="file"] {
@@ -49,6 +49,15 @@ input[type="submit"],
49
49
  color: $blue;
50
50
  }
51
51
 
52
+ .button--danger {
53
+ background-color: $red;
54
+
55
+ &:hover {
56
+ background-color: mix($black, $red, 20%);
57
+ color: $white;
58
+ }
59
+ }
60
+
52
61
  .button--nav {
53
62
  margin-bottom: $base-spacing;
54
63
  }
@@ -2,6 +2,7 @@
2
2
  @include administrate-clearfix;
3
3
  align-items: center;
4
4
  display: flex;
5
+ flex-wrap: wrap;
5
6
  margin-bottom: $base-spacing;
6
7
  position: relative;
7
8
  width: 100%;
@@ -25,6 +26,12 @@
25
26
  }
26
27
  }
27
28
 
29
+ .field-unit__hint {
30
+ font-size: 90%;
31
+ margin-left: calc(15% + 2rem);
32
+ width: 100%;
33
+ }
34
+
28
35
  .field-unit--nested {
29
36
  border: $base-border;
30
37
  margin-left: 7.5%;
@@ -3,8 +3,8 @@
3
3
  background-color: $color;
4
4
  color: mix($black, $color, 60%);
5
5
  display: block;
6
- margin-bottom: $base-spacing / 2;
7
- padding: $base-spacing / 2;
6
+ margin-bottom: $base-spacing * 0.5;
7
+ padding: $base-spacing * 0.5;
8
8
  text-align: center;
9
9
 
10
10
  a {
@@ -14,7 +14,7 @@ $heading-line-height: 1.2 !default;
14
14
  // Other Sizes
15
15
  $base-border-radius: 4px !default;
16
16
  $base-spacing: $base-line-height * 1em !default;
17
- $small-spacing: $base-spacing / 2 !default;
17
+ $small-spacing: $base-spacing * 0.5 !default;
18
18
 
19
19
  // Colors
20
20
  $white: #fff !default;
@@ -5,12 +5,10 @@ module Administrate
5
5
  def index
6
6
  authorize_resource(resource_class)
7
7
  search_term = params[:search].to_s.strip
8
- resources = Administrate::Search.new(scoped_resource,
9
- dashboard,
10
- search_term).run
8
+ resources = filter_resources(scoped_resource, search_term: search_term)
11
9
  resources = apply_collection_includes(resources)
12
10
  resources = order.apply(resources)
13
- resources = resources.page(params[:_page]).per(records_per_page)
11
+ resources = paginate_resources(resources)
14
12
  page = Administrate::Page::Collection.new(dashboard, order: order)
15
13
 
16
14
  render locals: {
@@ -42,10 +40,11 @@ module Administrate
42
40
  end
43
41
 
44
42
  def create
45
- resource = resource_class.new(resource_params)
43
+ resource = new_resource(resource_params)
46
44
  authorize_resource(resource)
47
45
 
48
46
  if resource.save
47
+ yield(resource) if block_given?
49
48
  redirect_to(
50
49
  after_resource_created_path(resource),
51
50
  notice: translate_with_resource("create.success"),
@@ -81,6 +80,14 @@ module Administrate
81
80
 
82
81
  private
83
82
 
83
+ def filter_resources(resources, search_term:)
84
+ Administrate::Search.new(
85
+ resources,
86
+ dashboard,
87
+ search_term,
88
+ ).run
89
+ end
90
+
84
91
  def after_resource_destroyed_path(_requested_resource)
85
92
  { action: :index }
86
93
  end
@@ -99,15 +106,30 @@ module Administrate
99
106
  resource_name.to_s.pluralize == underscore_resource ? :active : :inactive
100
107
  end
101
108
 
102
- helper_method :valid_action?
103
- def valid_action?(name, resource = resource_class)
104
- !!routes.detect do |controller, action|
105
- controller == resource.to_s.underscore.pluralize && action == name.to_s
106
- end
109
+ # Whether the named action route exists for the resource class.
110
+ #
111
+ # @param resource [Class, String, Symbol] A class of resources, or the name
112
+ # of a class of resources.
113
+ # @param action_name [String, Symbol] The name of an action that might be
114
+ # possible to perform on a resource or resource class.
115
+ # @return [Boolean] `true` if a route exists for the resource class and the
116
+ # action. `false` otherwise.
117
+ def existing_action?(resource, action_name)
118
+ routes.include?([resource.to_s.underscore.pluralize, action_name.to_s])
119
+ end
120
+ helper_method :existing_action?
121
+
122
+ # @deprecated Use {#existing_action} instead. Note that, in
123
+ # {#existing_action}, the order of parameters is reversed and
124
+ # there is no default value for the `resource` parameter.
125
+ def valid_action?(action_name, resource = resource_class)
126
+ Administrate.warn_of_deprecated_authorization_method(__method__)
127
+ existing_action?(resource, action_name)
107
128
  end
129
+ helper_method :valid_action?
108
130
 
109
131
  def routes
110
- @routes ||= Namespace.new(namespace).routes
132
+ @routes ||= Namespace.new(namespace).routes.to_set
111
133
  end
112
134
 
113
135
  def records_per_page
@@ -115,7 +137,23 @@ module Administrate
115
137
  end
116
138
 
117
139
  def order
118
- @order ||= Administrate::Order.new(sorting_attribute, sorting_direction)
140
+ @order ||= Administrate::Order.new(
141
+ sorting_attribute,
142
+ sorting_direction,
143
+ association_attribute: order_by_field(
144
+ dashboard_attribute(sorting_attribute),
145
+ ),
146
+ )
147
+ end
148
+
149
+ def order_by_field(dashboard)
150
+ return unless dashboard.try(:options)
151
+
152
+ dashboard.options.fetch(:order, nil)
153
+ end
154
+
155
+ def dashboard_attribute(attribute)
156
+ dashboard.attribute_types[attribute.to_sym] if attribute
119
157
  end
120
158
 
121
159
  def sorting_attribute
@@ -164,7 +202,7 @@ module Administrate
164
202
 
165
203
  def resource_params
166
204
  params.require(resource_class.model_name.param_key).
167
- permit(dashboard.permitted_attributes).
205
+ permit(dashboard.permitted_attributes(action_name)).
168
206
  transform_values { |v| read_param_value(v) }
169
207
  end
170
208
 
@@ -177,6 +215,8 @@ module Administrate
177
215
  end
178
216
  elsif data.is_a?(ActionController::Parameters)
179
217
  data.transform_values { |v| read_param_value(v) }
218
+ elsif data.is_a?(String) && data.blank?
219
+ nil
180
220
  else
181
221
  data
182
222
  end
@@ -206,18 +246,46 @@ module Administrate
206
246
  ).any? { |_name, attribute| attribute.searchable? }
207
247
  end
208
248
 
209
- def show_action?(_action, _resource)
249
+ # Whether the current user is authorized to perform the named action on the
250
+ # resource.
251
+ #
252
+ # @param _resource [ActiveRecord::Base, Class, String, Symbol] The
253
+ # temptative target of the action, or the name of its class.
254
+ # @param _action_name [String, Symbol] The name of an action that might be
255
+ # possible to perform on a resource or resource class.
256
+ # @return [Boolean] `true` if the current user is authorized to perform the
257
+ # action on the resource. `false` otherwise.
258
+ def authorized_action?(_resource, _action_name)
210
259
  true
211
260
  end
261
+ helper_method :authorized_action?
262
+
263
+ # @deprecated Use {#authorized_action} instead. Note that the order of
264
+ # parameters is reversed in {#authorized_action}.
265
+ def show_action?(action, resource)
266
+ Administrate.warn_of_deprecated_authorization_method(__method__)
267
+ authorized_action?(resource, action)
268
+ end
212
269
  helper_method :show_action?
213
270
 
214
- def new_resource
215
- resource_class.new
271
+ def new_resource(params = {})
272
+ resource_class.new(params)
216
273
  end
217
274
  helper_method :new_resource
218
275
 
219
276
  def authorize_resource(resource)
220
- resource
277
+ if authorized_action?(resource, action_name)
278
+ resource
279
+ else
280
+ raise Administrate::NotAuthorizedError.new(
281
+ action: action_name,
282
+ resource: resource,
283
+ )
284
+ end
285
+ end
286
+
287
+ def paginate_resources(resources)
288
+ resources.page(params[:_page]).per(records_per_page)
221
289
  end
222
290
  end
223
291
  end
@@ -2,35 +2,59 @@ module Administrate
2
2
  module Punditize
3
3
  if Object.const_defined?("Pundit")
4
4
  extend ActiveSupport::Concern
5
- include Pundit
6
5
 
7
- included do
8
- def scoped_resource
9
- policy_scope_admin super
10
- end
6
+ if Pundit.const_defined?(:Authorization)
7
+ include Pundit::Authorization
8
+ else
9
+ include Pundit
10
+ end
11
11
 
12
- def authorize_resource(resource)
13
- authorize resource
14
- end
12
+ private
15
13
 
16
- def show_action?(action, resource)
17
- Pundit.policy!(pundit_user, resource).send("#{action}?".to_sym)
18
- end
14
+ def policy_namespace
15
+ []
19
16
  end
20
17
 
21
- private
18
+ def scoped_resource
19
+ namespaced_scope = policy_namespace + [super]
20
+ policy_scope!(pundit_user, namespaced_scope)
21
+ end
22
+
23
+ def authorize_resource(resource)
24
+ namespaced_resource = policy_namespace + [resource]
25
+ authorize namespaced_resource
26
+ end
27
+
28
+ def authorized_action?(resource, action)
29
+ namespaced_resource = policy_namespace + [resource]
30
+ policy = Pundit.policy!(pundit_user, namespaced_resource)
31
+ policy.send("#{action}?".to_sym)
32
+ end
33
+
34
+ def policy_scope!(user, scope)
35
+ policy_scope_class = Pundit::PolicyFinder.new(scope).scope!
22
36
 
23
- # Like the policy_scope method in stock Pundit, but allows the 'resolve'
24
- # to be overridden by 'resolve_admin' for a different index scope in Admin
25
- # controllers.
26
- def policy_scope_admin(scope)
27
- ps = Pundit::PolicyFinder.new(scope).scope!.new(pundit_user, scope)
28
- if ps.respond_to? :resolve_admin
29
- ps.resolve_admin
37
+ begin
38
+ policy_scope = policy_scope_class.new(user, pundit_model(scope))
39
+ rescue ArgumentError
40
+ raise(Pundit::InvalidConstructorError,
41
+ "Invalid #<#{policy_scope_class}> constructor is called")
42
+ end
43
+
44
+ if policy_scope.respond_to? :resolve_admin
45
+ Administrate.deprecator.warn(
46
+ "Pundit policy scope `resolve_admin` method is deprecated. " +
47
+ "Please use a namespaced pundit policy instead.",
48
+ )
49
+ policy_scope.resolve_admin
30
50
  else
31
- ps.resolve
51
+ policy_scope.resolve
32
52
  end
33
53
  end
54
+
55
+ def pundit_model(record)
56
+ record.is_a?(Array) ? record.last : record
57
+ end
34
58
  end
35
59
  end
36
60
  end
@@ -4,11 +4,7 @@ module Administrate
4
4
  SINGULAR_COUNT = 1
5
5
 
6
6
  def application_title
7
- if Rails::VERSION::MAJOR <= 5
8
- Rails.application.class.parent_name.titlecase
9
- else
10
- Rails.application.class.module_parent_name.titlecase
11
- end
7
+ Rails.application.class.module_parent_name.titlecase
12
8
  end
13
9
 
14
10
  def render_field(field, locals = {})
@@ -29,6 +25,28 @@ module Administrate
29
25
  dashboard.try(:model) || resource_name.to_sym
30
26
  end
31
27
 
28
+ # Unification of
29
+ # {Administrate::ApplicationController#existing_action? existing_action?}
30
+ # and
31
+ # {Administrate::ApplicationController#authorized_action?
32
+ # authorized_action?}
33
+ #
34
+ # @param target [ActiveRecord::Base, Class, Symbol, String] A resource,
35
+ # a class of resources, or the name of a class of resources.
36
+ # @param action_name [String, Symbol] The name of an action that might be
37
+ # possible to perform on a resource or resource class.
38
+ # @return [Boolean] Whether the action both (a) exists for the record class,
39
+ # and (b) the current user is authorized to perform it on the record
40
+ # instance or class.
41
+ def accessible_action?(target, action_name)
42
+ target = target.to_sym if target.is_a?(String)
43
+ target_class_or_class_name =
44
+ target.is_a?(ActiveRecord::Base) ? target.class : target
45
+
46
+ existing_action?(target_class_or_class_name, action_name) &&
47
+ authorized_action?(target, action_name)
48
+ end
49
+
32
50
  def display_resource_name(resource_name, opts = {})
33
51
  dashboard_from_resource(resource_name).resource_name(
34
52
  count: opts[:singular] ? SINGULAR_COUNT : PLURAL_MANY_COUNT,
@@ -52,7 +70,7 @@ module Administrate
52
70
  end
53
71
 
54
72
  def sanitized_order_params(page, current_field_name)
55
- collection_names = page.item_includes + [current_field_name]
73
+ collection_names = page.item_associations + [current_field_name]
56
74
  association_params = collection_names.map do |assoc_name|
57
75
  { assoc_name => %i[order direction page per_page] }
58
76
  end
@@ -25,17 +25,16 @@ to display a collection of resources in an HTML table.
25
25
  <th class="cell-label
26
26
  cell-label--<%= attr_type.html_class %>
27
27
  cell-label--<%= collection_presenter.ordered_html_class(attr_name) %>
28
- cell-label--<%= "#{resource_name}_#{attr_name}" %>"
28
+ cell-label--<%= "#{collection_presenter.resource_name}_#{attr_name}" %>"
29
29
  scope="col"
30
- role="columnheader"
31
30
  aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
32
31
  <%= link_to(sanitized_order_params(page, collection_field_name).merge(
33
32
  collection_presenter.order_params_for(attr_name, key: collection_field_name)
34
33
  )) do %>
35
34
  <%= t(
36
35
  "helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
37
- default: resource_class.human_attribute_name(attr_name),
38
- ).titleize %>
36
+ default: resource_class.human_attribute_name(attr_name).titleize,
37
+ ) %>
39
38
  <% if collection_presenter.ordered_by?(attr_name) %>
40
39
  <span class="cell-label__sort-indicator cell-label__sort-indicator--<%= collection_presenter.ordered_html_class(attr_name) %>">
41
40
  <svg aria-hidden="true">
@@ -59,13 +58,13 @@ to display a collection of resources in an HTML table.
59
58
  <tbody>
60
59
  <% resources.each do |resource| %>
61
60
  <tr class="js-table-row"
62
- <% if show_action? :show, resource %>
61
+ <% if accessible_action?(resource, :show) %>
63
62
  <%= %(tabindex=0 role=link data-url=#{polymorphic_path([namespace, resource])}) %>
64
63
  <% end %>
65
64
  >
66
65
  <% collection_presenter.attributes_for(resource).each do |attribute| %>
67
66
  <td class="cell-data cell-data--<%= attribute.html_class %>">
68
- <% if show_action? :show, resource -%>
67
+ <% if accessible_action?(resource, :show) -%>
69
68
  <a href="<%= polymorphic_path([namespace, resource]) -%>"
70
69
  tabindex="-1"
71
70
  class="action-show"
@@ -1,4 +1,4 @@
1
- <% [valid_action?(:edit, collection_presenter.resource_name),
2
- valid_action?(:destroy, collection_presenter.resource_name)].count(true).times do %>
1
+ <% [existing_action?(collection_presenter.resource_name, :edit),
2
+ existing_action?(collection_presenter.resource_name, :destroy)].count(true).times do %>
3
3
  <th scope="col"></th>
4
4
  <% end %>
@@ -1,17 +1,17 @@
1
- <% if valid_action?(:edit, collection_presenter.resource_name) %>
1
+ <% if existing_action?(collection_presenter.resource_name, :edit) %>
2
2
  <td><%= link_to(
3
3
  t("administrate.actions.edit"),
4
4
  [:edit, namespace, resource],
5
5
  class: "action-edit",
6
- ) if show_action?(:edit, resource) %></td>
6
+ ) if accessible_action?(resource, :edit) %></td>
7
7
  <% end %>
8
8
 
9
- <% if valid_action?(:destroy, collection_presenter.resource_name) %>
9
+ <% if existing_action?(collection_presenter.resource_name, :destroy) %>
10
10
  <td><%= link_to(
11
11
  t("administrate.actions.destroy"),
12
12
  [namespace, resource],
13
13
  class: "text-color-red",
14
14
  method: :delete,
15
15
  data: { confirm: t("administrate.actions.confirm") }
16
- ) if show_action?(:destroy, resource) %></td>
16
+ ) if accessible_action?(resource, :destroy) %></td>
17
17
  <% end %>
@@ -33,10 +33,25 @@ and renders all form fields for a resource's editable attributes.
33
33
  </div>
34
34
  <% end %>
35
35
 
36
- <% page.attributes(controller.action_name).each do |attribute| -%>
37
- <div class="field-unit field-unit--<%= attribute.html_class %> field-unit--<%= requireness(attribute) %>">
38
- <%= render_field attribute, f: f %>
39
- </div>
36
+ <% page.attributes(controller.action_name).each do |title, attributes| -%>
37
+ <fieldset class="<%= "field-unit--nested" if title.present? %>">
38
+ <% if title.present? %>
39
+ <legend><%= t "helpers.label.#{f.object_name}.#{title}", default: title %></legend>
40
+ <% end %>
41
+
42
+ <% attributes.each do |attribute| %>
43
+ <div class="field-unit field-unit--<%= attribute.html_class %> field-unit--<%= requireness(attribute) %>">
44
+ <%= render_field attribute, f: f %>
45
+
46
+ <% hint_key = "administrate.field_hints.#{page.resource_name}.#{attribute.name}" %>
47
+ <% if I18n.exists?(hint_key) -%>
48
+ <div class="field-unit__hint">
49
+ <%= I18n.t(hint_key) %>
50
+ </div>
51
+ <% end -%>
52
+ </div>
53
+ <% end %>
54
+ </fieldset>
40
55
  <% end -%>
41
56
 
42
57
  <div class="form-actions">
@@ -2,7 +2,7 @@
2
2
  <%= display_resource_name(page.resource_name) %>
3
3
  <% end %>
4
4
 
5
- <header class="main-content__header" role="banner">
5
+ <header class="main-content__header">
6
6
  <h1 class="main-content__page-title" id="page-title">
7
7
  <%= content_for(:title) %>
8
8
  </h1>
@@ -23,6 +23,6 @@
23
23
  ),
24
24
  [:new, namespace, page.resource_path.to_sym],
25
25
  class: "button",
26
- ) if valid_action?(:new) && show_action?(:new, new_resource) %>
26
+ ) if accessible_action?(new_resource, :new) %>
27
27
  </div>
28
28
  </header>
@@ -7,7 +7,7 @@ for all resources in the admin dashboard,
7
7
  as defined by the routes in the `admin/` namespace
8
8
  %>
9
9
 
10
- <nav class="navigation" role="navigation">
10
+ <nav class="navigation">
11
11
  <%= link_to(t("administrate.navigation.back_to_app"), root_url, class: "button button--alt button--nav") if defined?(root_url) %>
12
12
 
13
13
  <% Administrate::Namespace.new(namespace).resources_with_index_route.each do |resource| %>
@@ -15,6 +15,6 @@ as defined by the routes in the `admin/` namespace
15
15
  display_resource_name(resource),
16
16
  resource_index_route(resource),
17
17
  class: "navigation__link navigation__link--#{nav_link_state(resource)}"
18
- ) if valid_action?(:index, resource) && show_action?(:index, model_from_resource(resource)) %>
18
+ ) if accessible_action?(model_from_resource(resource), :index) %>
19
19
  <% end %>
20
20
  </nav>
@@ -0,0 +1 @@
1
+ <%= paginate resources, param_name: local_assigns.fetch(:param_name, "_page") %>
@@ -17,7 +17,7 @@ It displays a header, and renders the `_form` partial to do the heavy lifting.
17
17
 
18
18
  <% content_for(:title) { t("administrate.actions.edit_resource", name: page.page_title) } %>
19
19
 
20
- <header class="main-content__header" role="banner">
20
+ <header class="main-content__header">
21
21
  <h1 class="main-content__page-title">
22
22
  <%= content_for(:title) %>
23
23
  </h1>
@@ -27,7 +27,7 @@ It displays a header, and renders the `_form` partial to do the heavy lifting.
27
27
  t("administrate.actions.show_resource", name: page.page_title),
28
28
  [namespace, page.resource],
29
29
  class: "button",
30
- ) if valid_action?(:show) && show_action?(:show, page.resource) %>
30
+ ) if accessible_action?(page.resource, :show) %>
31
31
  </div>
32
32
  </header>
33
33
 
@@ -42,5 +42,5 @@ It renders the `_table` partial to display details about the resources.
42
42
  table_title: "page-title"
43
43
  ) %>
44
44
 
45
- <%= paginate resources, param_name: '_page' %>
45
+ <%= render("pagination", resources: resources) %>
46
46
  </section>
@@ -22,7 +22,7 @@ to do the heavy lifting.
22
22
  ) %>
23
23
  <% end %>
24
24
 
25
- <header class="main-content__header" role="banner">
25
+ <header class="main-content__header">
26
26
  <h1 class="main-content__page-title">
27
27
  <%= content_for(:title) %>
28
28
  </h1>
@@ -18,7 +18,7 @@ as well as a link to its edit page.
18
18
 
19
19
  <% content_for(:title) { t("administrate.actions.show_resource", name: page.page_title) } %>
20
20
 
21
- <header class="main-content__header" role="banner">
21
+ <header class="main-content__header">
22
22
  <h1 class="main-content__page-title">
23
23
  <%= content_for(:title) %>
24
24
  </h1>
@@ -28,22 +28,38 @@ as well as a link to its edit page.
28
28
  t("administrate.actions.edit_resource", name: page.page_title),
29
29
  [:edit, namespace, page.resource],
30
30
  class: "button",
31
- ) if valid_action?(:edit) && show_action?(:edit, page.resource) %>
31
+ ) if accessible_action?(page.resource, :edit) %>
32
+
33
+ <%= link_to(
34
+ t("administrate.actions.destroy"),
35
+ [namespace, page.resource],
36
+ class: "button button--danger",
37
+ method: :delete,
38
+ data: { confirm: t("administrate.actions.confirm") }
39
+ ) if accessible_action?(page.resource, :destroy) %>
32
40
  </div>
33
41
  </header>
34
42
 
35
43
  <section class="main-content__body">
36
44
  <dl>
37
- <% page.attributes.each do |attribute| %>
38
- <dt class="attribute-label" id="<%= attribute.name %>">
39
- <%= t(
40
- "helpers.label.#{resource_name}.#{attribute.name}",
41
- default: page.resource.class.human_attribute_name(attribute.name),
42
- ) %>
43
- </dt>
44
-
45
- <dd class="attribute-data attribute-data--<%=attribute.html_class%>"
46
- ><%= render_field attribute, page: page %></dd>
45
+ <% page.attributes.each do |title, attributes| %>
46
+ <fieldset class="<%= "field-unit--nested" if title.present? %>">
47
+ <% if title.present? %>
48
+ <legend><%= t "helpers.label.#{page.resource_name}.#{title}", default: title %></legend>
49
+ <% end %>
50
+
51
+ <% attributes.each do |attribute| %>
52
+ <dt class="attribute-label" id="<%= attribute.name %>">
53
+ <%= t(
54
+ "helpers.label.#{resource_name}.#{attribute.name}",
55
+ default: page.resource.class.human_attribute_name(attribute.name),
56
+ ) %>
57
+ </dt>
58
+
59
+ <dd class="attribute-data attribute-data--<%=attribute.html_class%>"
60
+ ><%= render_field attribute, page: page %></dd>
61
+ <% end %>
62
+ </fieldset>
47
63
  <% end %>
48
64
  </dl>
49
65
  </section>
@@ -16,7 +16,7 @@ By default, the relationship is rendered as a link to the associated object.
16
16
  %>
17
17
 
18
18
  <% if field.data %>
19
- <% if valid_action?(:show, field.associated_class) && show_action?(:show, field.associated_class) %>
19
+ <% if accessible_action?(field.data, :show) %>
20
20
  <%= link_to(
21
21
  field.display_associated_resource,
22
22
  [namespace, field.data],