administrate 0.20.1 → 1.0.0.beta2

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +9 -9
  3. data/app/assets/builds/administrate/application.css +2542 -0
  4. data/app/assets/builds/administrate/application.css.map +1 -0
  5. data/app/assets/builds/administrate/application.js +22169 -0
  6. data/app/assets/builds/administrate/application.js.map +7 -0
  7. data/app/assets/builds/administrate-internal/docs.css +89 -0
  8. data/app/assets/builds/administrate-internal/docs.css.map +1 -0
  9. data/app/assets/config/administrate_manifest.js +2 -0
  10. data/app/assets/javascripts/administrate/add_jquery.js +4 -0
  11. data/app/assets/javascripts/administrate/application.js +8 -4
  12. data/app/assets/javascripts/administrate/controllers/application.js +9 -0
  13. data/app/assets/javascripts/administrate/controllers/index.js +7 -0
  14. data/app/assets/javascripts/administrate/controllers/select_controller.js +8 -0
  15. data/app/assets/javascripts/administrate/{components/table.js → controllers/table_controller.js} +9 -9
  16. data/app/assets/stylesheets/administrate/application.scss +3 -3
  17. data/app/assets/stylesheets/administrate/base/_forms.scss +4 -4
  18. data/app/assets/stylesheets/administrate/base/_layout.scss +5 -0
  19. data/app/assets/stylesheets/administrate/base/_tables.scss +1 -1
  20. data/app/assets/stylesheets/administrate/base/_typography.scss +12 -1
  21. data/app/assets/stylesheets/administrate/components/_attributes.scss +1 -0
  22. data/app/assets/stylesheets/administrate/components/_buttons.scss +12 -12
  23. data/app/assets/stylesheets/administrate/components/_cells.scss +19 -19
  24. data/app/assets/stylesheets/administrate/components/_field-unit.scss +3 -3
  25. data/app/assets/stylesheets/administrate/components/_main-content.scss +1 -1
  26. data/app/assets/stylesheets/administrate/components/_navigation.scss +3 -3
  27. data/app/assets/stylesheets/administrate/components/_search.scss +11 -11
  28. data/app/assets/stylesheets/administrate/library/_variables.scss +7 -3
  29. data/app/assets/stylesheets/administrate/reset/_normalize.scss +7 -1
  30. data/app/controllers/administrate/application_controller.rb +20 -19
  31. data/app/controllers/concerns/administrate/punditize.rb +5 -5
  32. data/app/helpers/administrate/application_helper.rb +4 -4
  33. data/app/views/administrate/application/_collection.html.erb +1 -1
  34. data/app/views/administrate/application/_collection_item_actions.html.erb +3 -3
  35. data/app/views/administrate/application/_index_header.html.erb +4 -0
  36. data/app/views/administrate/application/_javascript.html.erb +1 -1
  37. data/app/views/administrate/application/edit.html.erb +15 -3
  38. data/app/views/administrate/application/index.html.erb +19 -11
  39. data/app/views/administrate/application/new.html.erb +15 -3
  40. data/app/views/administrate/application/show.html.erb +35 -23
  41. data/app/views/fields/belongs_to/_form.html.erb +3 -2
  42. data/app/views/fields/has_many/_form.html.erb +1 -1
  43. data/app/views/fields/polymorphic/_form.html.erb +1 -1
  44. data/app/views/fields/rich_text/_form.html.erb +6 -0
  45. data/app/views/fields/rich_text/_index.html.erb +18 -0
  46. data/app/views/fields/rich_text/_show.html.erb +18 -0
  47. data/app/views/fields/select/_form.html.erb +2 -1
  48. data/app/views/fields/text/_form.html.erb +1 -1
  49. data/app/views/layouts/administrate/application.html.erb +1 -2
  50. data/docs/customizing_dashboards.md +138 -4
  51. data/docs/customizing_page_views.md +25 -0
  52. data/lib/administrate/base_dashboard.rb +6 -5
  53. data/lib/administrate/engine.rb +7 -6
  54. data/lib/administrate/field/associative.rb +5 -1
  55. data/lib/administrate/field/base.rb +21 -3
  56. data/lib/administrate/field/belongs_to.rb +7 -2
  57. data/lib/administrate/field/date.rb +1 -1
  58. data/lib/administrate/field/date_time.rb +2 -2
  59. data/lib/administrate/field/deferred.rb +10 -2
  60. data/lib/administrate/field/has_many.rb +5 -5
  61. data/lib/administrate/field/has_one.rb +10 -6
  62. data/lib/administrate/field/number.rb +2 -2
  63. data/lib/administrate/field/polymorphic.rb +3 -3
  64. data/lib/administrate/field/rich_text.rb +21 -0
  65. data/lib/administrate/field/select.rb +4 -0
  66. data/lib/administrate/generator_helpers.rb +1 -1
  67. data/lib/administrate/namespace/resource.rb +1 -1
  68. data/lib/administrate/order.rb +30 -26
  69. data/lib/administrate/page/base.rb +2 -7
  70. data/lib/administrate/page/collection.rb +2 -2
  71. data/lib/administrate/page/form.rb +1 -1
  72. data/lib/administrate/page/show.rb +1 -1
  73. data/lib/administrate/resource_resolver.rb +1 -1
  74. data/lib/administrate/search.rb +6 -7
  75. data/lib/administrate/version.rb +1 -1
  76. data/lib/administrate/view_generator.rb +3 -3
  77. data/lib/administrate.rb +18 -18
  78. data/lib/generators/administrate/dashboard/dashboard_generator.rb +12 -9
  79. data/lib/generators/administrate/field/field_generator.rb +2 -2
  80. data/lib/generators/administrate/install/install_generator.rb +2 -2
  81. data/lib/generators/administrate/routes/routes_generator.rb +5 -5
  82. data/lib/generators/administrate/views/field_generator.rb +2 -2
  83. data/lib/generators/administrate/views/index_generator.rb +1 -0
  84. data/lib/generators/administrate/views/layout_generator.rb +1 -1
  85. metadata +20 -53
  86. data/app/assets/javascripts/administrate/components/associative.js +0 -5
  87. data/app/assets/javascripts/administrate/components/select.js +0 -3
  88. data/app/assets/stylesheets/administrate/utilities/_text-color.scss +0 -3
  89. data/lib/generators/administrate/assets/assets_generator.rb +0 -12
  90. data/lib/generators/administrate/assets/javascripts_generator.rb +0 -17
  91. data/lib/generators/administrate/assets/stylesheets_generator.rb +0 -17
  92. /data/app/assets/stylesheets/{docs.scss → administrate-internal/docs.scss} +0 -0
@@ -1,6 +1,6 @@
1
1
  module Administrate
2
2
  module Punditize
3
- if Object.const_defined?("Pundit")
3
+ if Object.const_defined?(:Pundit)
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  if Pundit.const_defined?(:Authorization)
@@ -28,7 +28,7 @@ module Administrate
28
28
  def authorized_action?(resource, action)
29
29
  namespaced_resource = policy_namespace + [resource]
30
30
  policy = Pundit.policy!(pundit_user, namespaced_resource)
31
- policy.send("#{action}?".to_sym)
31
+ policy.send(:"#{action}?")
32
32
  end
33
33
 
34
34
  def policy_scope!(user, scope)
@@ -38,13 +38,13 @@ module Administrate
38
38
  policy_scope = policy_scope_class.new(user, pundit_model(scope))
39
39
  rescue ArgumentError
40
40
  raise(Pundit::InvalidConstructorError,
41
- "Invalid #<#{policy_scope_class}> constructor is called")
41
+ "Invalid #<#{policy_scope_class}> constructor is called")
42
42
  end
43
43
 
44
44
  if policy_scope.respond_to? :resolve_admin
45
45
  Administrate.deprecator.warn(
46
- "Pundit policy scope `resolve_admin` method is deprecated. " +
47
- "Please use a namespaced pundit policy instead.",
46
+ "Pundit policy scope `resolve_admin` method is deprecated. " \
47
+ "Please use a namespaced pundit policy instead."
48
48
  )
49
49
  policy_scope.resolve_admin
50
50
  else
@@ -50,7 +50,7 @@ module Administrate
50
50
  def display_resource_name(resource_name, opts = {})
51
51
  dashboard_from_resource(resource_name).resource_name(
52
52
  count: opts[:singular] ? SINGULAR_COUNT : PLURAL_MANY_COUNT,
53
- default: default_resource_name(resource_name, opts),
53
+ default: default_resource_name(resource_name, opts)
54
54
  )
55
55
  end
56
56
 
@@ -65,14 +65,14 @@ module Administrate
65
65
  def resource_index_route(resource_name)
66
66
  url_for(
67
67
  action: "index",
68
- controller: "/#{namespace}/#{resource_name}",
68
+ controller: "/#{namespace}/#{resource_name}"
69
69
  )
70
70
  end
71
71
 
72
72
  def sanitized_order_params(page, current_field_name)
73
73
  collection_names = page.item_associations + [current_field_name]
74
74
  association_params = collection_names.map do |assoc_name|
75
- { assoc_name => %i[order direction page per_page] }
75
+ {assoc_name => %i[order direction page per_page]}
76
76
  end
77
77
  params.permit(:search, :id, :_page, :per_page, association_params)
78
78
  end
@@ -87,7 +87,7 @@ module Administrate
87
87
 
88
88
  def default_resource_name(name, opts = {})
89
89
  resource_name = (opts[:singular] ? name.to_s : name.to_s.pluralize)
90
- resource_name.gsub("/", "_").titleize
90
+ resource_name.tr("/", "_").titleize
91
91
  end
92
92
  end
93
93
  end
@@ -18,7 +18,7 @@ to display a collection of resources in an HTML table.
18
18
  [1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
19
19
  %>
20
20
 
21
- <table aria-labelledby="<%= table_title %>">
21
+ <table aria-labelledby="<%= table_title %>" data-controller="table" data-action="click->table#visitDataUrl keydown->table#visitDataUrl">
22
22
  <thead>
23
23
  <tr>
24
24
  <% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
@@ -7,11 +7,11 @@
7
7
  <% end %>
8
8
 
9
9
  <% if existing_action?(collection_presenter.resource_name, :destroy) %>
10
- <td><%= link_to(
10
+ <td><%= button_to(
11
11
  t("administrate.actions.destroy"),
12
12
  [namespace, resource],
13
- class: "text-color-red",
13
+ class: "link link--danger",
14
14
  method: :delete,
15
- data: { confirm: t("administrate.actions.confirm") }
15
+ data: { turbo_confirm: t("administrate.actions.confirm") }
16
16
  ) if accessible_action?(resource, :destroy) %></td>
17
17
  <% end %>
@@ -7,6 +7,8 @@
7
7
  <%= content_for(:title) %>
8
8
  </h1>
9
9
 
10
+ <%= content_for(:header_middle) %>
11
+
10
12
  <% if show_search_bar %>
11
13
  <%= render(
12
14
  "search",
@@ -25,4 +27,6 @@
25
27
  class: "button",
26
28
  ) if accessible_action?(new_resource, :new) %>
27
29
  </div>
30
+
31
+ <%= content_for(:header_last) %>
28
32
  </header>
@@ -8,7 +8,7 @@ by providing a `content_for(:javascript)` block.
8
8
  %>
9
9
 
10
10
  <% Administrate::Engine.javascripts.each do |js_path| %>
11
- <%= javascript_include_tag js_path %>
11
+ <%= javascript_include_tag js_path, "data-turbo-track": "reload", defer: true %>
12
12
  <% end %>
13
13
 
14
14
  <%= yield :javascript %>
@@ -22,6 +22,8 @@ It displays a header, and renders the `_form` partial to do the heavy lifting.
22
22
  <%= content_for(:title) %>
23
23
  </h1>
24
24
 
25
+ <%= content_for(:header_middle) %>
26
+
25
27
  <div>
26
28
  <%= link_to(
27
29
  t("administrate.actions.show_resource", name: page.page_title),
@@ -29,8 +31,18 @@ It displays a header, and renders the `_form` partial to do the heavy lifting.
29
31
  class: "button",
30
32
  ) if accessible_action?(page.resource, :show) %>
31
33
  </div>
34
+
35
+ <%= content_for(:header_last) %>
32
36
  </header>
33
37
 
34
- <section class="main-content__body">
35
- <%= render "form", page: page %>
36
- </section>
38
+ <%= content_for(:before_main) %>
39
+
40
+ <% if content_for?(:main) %>
41
+ <%= content_for(:main) %>
42
+ <% else %>
43
+ <section class="main-content__body">
44
+ <%= render "form", page: page %>
45
+ </section>
46
+ <% end %>
47
+
48
+ <%= content_for(:after_main) %>
@@ -32,15 +32,23 @@ It renders the `_table` partial to display details about the resources.
32
32
  )
33
33
  %>
34
34
 
35
- <section class="main-content__body main-content__body--flush">
36
- <%= render(
37
- "collection",
38
- collection_presenter: page,
39
- collection_field_name: resource_name,
40
- page: page,
41
- resources: resources,
42
- table_title: "page-title"
43
- ) %>
35
+ <%= content_for(:before_main) %>
36
+
37
+ <% if content_for?(:main) %>
38
+ <%= content_for(:main) %>
39
+ <% else %>
40
+ <section class="main-content__body main-content__body--flush">
41
+ <%= render(
42
+ "collection",
43
+ collection_presenter: page,
44
+ collection_field_name: resource_name,
45
+ page: page,
46
+ resources: resources,
47
+ table_title: "page-title"
48
+ ) %>
49
+
50
+ <%= render("pagination", resources: resources) %>
51
+ </section>
52
+ <% end %>
44
53
 
45
- <%= render("pagination", resources: resources) %>
46
- </section>
54
+ <%= content_for(:after_main) %>
@@ -27,11 +27,23 @@ to do the heavy lifting.
27
27
  <%= content_for(:title) %>
28
28
  </h1>
29
29
 
30
+ <%= content_for(:header_middle) %>
31
+
30
32
  <div>
31
33
  <%= link_to t("administrate.actions.back"), :back, class: "button" %>
32
34
  </div>
35
+
36
+ <%= content_for(:header_last) %>
33
37
  </header>
34
38
 
35
- <section class="main-content__body">
36
- <%= render "form", page: page %>
37
- </section>
39
+ <%= content_for(:before_main) %>
40
+
41
+ <% if content_for?(:main) %>
42
+ <%= content_for(:main) %>
43
+ <% else %>
44
+ <section class="main-content__body">
45
+ <%= render "form", page: page %>
46
+ </section>
47
+ <% end %>
48
+
49
+ <%= content_for(:after_main) %>
@@ -23,6 +23,8 @@ as well as a link to its edit page.
23
23
  <%= content_for(:title) %>
24
24
  </h1>
25
25
 
26
+ <%= content_for(:header_middle) %>
27
+
26
28
  <div>
27
29
  <%= link_to(
28
30
  t("administrate.actions.edit_resource", name: page.page_title),
@@ -30,36 +32,46 @@ as well as a link to its edit page.
30
32
  class: "button",
31
33
  ) if accessible_action?(page.resource, :edit) %>
32
34
 
33
- <%= link_to(
35
+ <%= button_to(
34
36
  t("administrate.actions.destroy"),
35
37
  [namespace, page.resource],
36
38
  class: "button button--danger",
37
39
  method: :delete,
38
- data: { confirm: t("administrate.actions.confirm") }
40
+ data: { turbo_confirm: t("administrate.actions.confirm") }
39
41
  ) if accessible_action?(page.resource, :destroy) %>
40
42
  </div>
43
+
44
+ <%= content_for(:header_last) %>
41
45
  </header>
42
46
 
43
- <section class="main-content__body">
44
- <dl>
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 %>
47
+ <%= content_for(:before_main) %>
48
+
49
+ <% if content_for?(:main) %>
50
+ <%= content_for(:main) %>
51
+ <% else %>
52
+ <section class="main-content__body">
53
+ <dl>
54
+ <% page.attributes.each do |title, attributes| %>
55
+ <fieldset class="<%= "field-unit--nested" if title.present? %>">
56
+ <% if title.present? %>
57
+ <legend><%= t "helpers.label.#{page.resource_name}.#{title}", default: title %></legend>
58
+ <% end %>
59
+
60
+ <% attributes.each do |attribute| %>
61
+ <dt class="attribute-label" id="<%= attribute.name %>">
62
+ <%= t(
63
+ "helpers.label.#{resource_name}.#{attribute.name}",
64
+ default: page.resource.class.human_attribute_name(attribute.name),
65
+ ) %>
66
+ </dt>
50
67
 
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>
68
+ <dd class="attribute-data attribute-data--<%=attribute.html_class%>"
69
+ ><%= render_field attribute, page: page %></dd>
70
+ <% end %>
71
+ </fieldset>
72
+ <% end %>
73
+ </dl>
74
+ </section>
75
+ <% end %>
58
76
 
59
- <dd class="attribute-data attribute-data--<%=attribute.html_class%>"
60
- ><%= render_field attribute, page: page %></dd>
61
- <% end %>
62
- </fieldset>
63
- <% end %>
64
- </dl>
65
- </section>
77
+ <%= content_for(:after_main) %>
@@ -17,10 +17,11 @@ that displays all possible records to associate with.
17
17
  %>
18
18
 
19
19
  <div class="field-unit__label">
20
- <%= f.label field.permitted_attribute %>
20
+ <%= f.label field.attribute, for: "#{f.object_name}_#{field.permitted_attribute}" %>
21
21
  </div>
22
22
  <div class="field-unit__field">
23
23
  <%= f.select(field.permitted_attribute,
24
24
  options_for_select(field.associated_resource_options, field.selected_option),
25
- include_blank: field.include_blank_option) %>
25
+ {include_blank: field.include_blank_option},
26
+ data: {controller: field.html_controller}) %>
26
27
  </div>
@@ -23,7 +23,7 @@ and is augmented with [Selectize].
23
23
  <%= f.label field.attribute, for: "#{f.object_name}_#{field.attribute_key}" %>
24
24
  </div>
25
25
  <div class="field-unit__field">
26
- <%= f.select(field.attribute_key, nil, {}, multiple: true) do %>
26
+ <%= f.select(field.attribute_key, nil, {}, multiple: true, data: {controller: field.html_controller}) do %>
27
27
  <%= options_for_select(field.associated_resource_options, field.selected_options) %>
28
28
  <% end %>
29
29
  </div>
@@ -22,7 +22,7 @@ This partial renders an input element for polymorphic relationships.
22
22
 
23
23
  <div class="field-unit__field">
24
24
  <%= pf.hidden_field(:type, value: field.class.name) %>
25
- <%= pf.select(:value) do %>
25
+ <%= pf.select(:value, {}, data: {controller: field.html_controller}) do %>
26
26
  <%= grouped_options_for_select(field.associated_resource_grouped_options, field.selected_global_id, prompt: true) %>
27
27
  <% end %>
28
28
  </div>
@@ -0,0 +1,6 @@
1
+ <div class="field-unit__label">
2
+ <%= f.label field.attribute %>
3
+ </div>
4
+ <div class="field-unit__field">
5
+ <%= f.rich_text_area(field.attribute) %>
6
+ </div>
@@ -0,0 +1,18 @@
1
+ <%#
2
+ # RichText Index Partial
3
+
4
+ This partial renders a rich_text attribute
5
+ to be displayed on a resource's index page.
6
+
7
+ By default, the attribute is rendered as a plain text truncated string.
8
+
9
+ ## Local variables:
10
+
11
+ - `field`:
12
+ An instance of [Administrate::Field::RichText][1].
13
+ A wrapper around the RichText pulled from the database.
14
+
15
+ [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/RichText
16
+ %>
17
+
18
+ <%= field.truncate %>
@@ -0,0 +1,18 @@
1
+ <%#
2
+ # RichText Show Partial
3
+
4
+ This partial renders a rich_text attribute,
5
+ to be displayed on a resource's show page.
6
+
7
+ By default, the attribute is rendered as HTML.
8
+
9
+ ## Local variables:
10
+
11
+ - `field`:
12
+ An instance of [Administrate::Field::RichText][1].
13
+ A wrapper around the RichText object pulled from the database.
14
+
15
+ [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/RichText
16
+ %>
17
+
18
+ <%= field.data %>
@@ -26,7 +26,8 @@ to be displayed on a resource's edit form page.
26
26
  field.selectable_options,
27
27
  field.data,
28
28
  ),
29
- include_blank: field.include_blank_option
29
+ {include_blank: field.include_blank_option},
30
+ data: {controller: field.html_controller}
30
31
  )
31
32
  %>
32
33
  </div>
@@ -18,5 +18,5 @@ This partial renders a textarea element for a text attribute.
18
18
  <%= f.label field.attribute %>
19
19
  </div>
20
20
  <div class="field-unit__field">
21
- <%= f.text_area field.attribute %>
21
+ <%= f.text_area field.attribute, field.options.fetch(:input_options, {}) %>
22
22
  </div>
@@ -24,6 +24,7 @@ By default, it renders:
24
24
  <%= render "stylesheet" %>
25
25
  <%= csrf_meta_tags %>
26
26
  <%= csp_meta_tag if defined?(csp_meta_tag) %>
27
+ <%= render "javascript" %>
27
28
  </head>
28
29
  <body>
29
30
  <%= render "icons" %>
@@ -36,7 +37,5 @@ By default, it renders:
36
37
  <%= yield %>
37
38
  </main>
38
39
  </div>
39
-
40
- <%= render "javascript" %>
41
40
  </body>
42
41
  </html>
@@ -82,7 +82,8 @@ the table views and in the dropdown menu on the record forms.
82
82
  You can set multiple columns as well with direction. E.g.: `"name, email DESC"`.
83
83
 
84
84
  `:scope` - Specifies a custom scope inside a callable. Useful for preloading.
85
- Example: `.with_options(scope: -> { MyModel.includes(:rel).limit(5) })`
85
+ Example #1: `.with_options(scope: -> { MyModel.includes(:rel).limit(5) })`
86
+ Example #2: `.with_options(scope: -> (field) { field.resource.my_models.includes(:rel).limit(5) })`
86
87
 
87
88
  `:include_blank` - Specifies if the select element to be rendered should include
88
89
  blank option. Default is `true`.
@@ -113,7 +114,7 @@ association `belongs_to :country`, from your model.
113
114
 
114
115
  **Field::HasMany**
115
116
 
116
- `:collection_attributes` - Set the columns to display in the show view.
117
+ `:collection_attributes` - Set the columns to display in the show view.
117
118
  Default is COLLECTION_ATTRIBUTES in dashboard.
118
119
 
119
120
  `:limit` - The number of resources (paginated) to display in the show view. To disable pagination,
@@ -144,14 +145,14 @@ Default is `false`.
144
145
  For example:
145
146
 
146
147
  ```ruby
147
- cities: Field::HasMany.with_options(
148
+ city: Field::HasOne.with_options(
148
149
  searchable: true,
149
150
  searchable_fields: ['name'],
150
151
  )
151
152
  ```
152
153
 
153
154
  with this, you will be able to search through the column `name` from the
154
- association `has_many :cities`, from your model.
155
+ association `has_one :city`, from your model.
155
156
 
156
157
  `:class_name` (deprecated) - Specifies the name of the associated class.
157
158
 
@@ -266,6 +267,9 @@ Default is `false`.
266
267
  `:truncate` - Set the number of characters to display in the index view.
267
268
  Defaults to `50`.
268
269
 
270
+ `:input_options` - Options to customize the text area in form view.
271
+ Example: `.with_options(input_options: { rows: 20 })`
272
+
269
273
  **Field::Url**
270
274
 
271
275
  `:searchable` - Specify if the attribute should be considered when searching.
@@ -436,3 +440,133 @@ en:
436
440
  ```
437
441
 
438
442
  If not defined (see `SHOW_PAGE_ATTRIBUTES` above), Administrate will default to the given strings.
443
+
444
+ ## Virtual Attributes
445
+
446
+ For all field types, you can use the `getter` option to change where the data is retrieved from or to set the data directly.
447
+
448
+ By using this, you can define an attribute in `ATTRIBUTE_TYPES` that doesn’t exist in the model, and use it for various purposes.
449
+
450
+ ### Attribute Aliases
451
+
452
+ You can create an alias for an attribute. For example:
453
+
454
+ ```ruby
455
+ ATTRIBUTE_TYPES = {
456
+ shipped_at: Field::DateTime,
457
+ shipped_on: Field::Date.with_options(
458
+ getter: :shipped_at
459
+ )
460
+ }
461
+ COLLECTION_ATTRIBUTES = [
462
+ :shipped_on
463
+ }
464
+ SHOW_PAGE_ATTRIBUTES = [
465
+ :shipped_at
466
+ }
467
+ ```
468
+
469
+ In this example, a virtual attribute `shipped_on` based on the value of `shipped_at` is defined as a `Date` type and used for display on the index page (this can help save table cell space).
470
+
471
+ ### Decorated Attributes
472
+
473
+ You can also use this to decorate data. For example:
474
+
475
+ ```ruby
476
+ ATTRIBUTE_TYPES = {
477
+ price: Field::Number,
478
+ price_including_tax: Field::Number.with_options(
479
+ getter: -> (field) {
480
+ field.resource.price * 1.1 if field.resource.price.present?
481
+ }
482
+ )
483
+ }
484
+ ```
485
+
486
+ ### Composite Attributes
487
+
488
+ You can dynamically create a virtual attribute by combining multiple attributes for display. For example:
489
+
490
+ ```ruby
491
+ ATTRIBUTE_TYPES = {
492
+ first_name: Field::String,
493
+ last_name: Field::String,
494
+ full_name: Field::String.with_options(
495
+ getter: -> (field) {
496
+ [
497
+ field.resource.first_name,
498
+ field.resource.last_name
499
+ ].compact_blank.join(' ')
500
+ }
501
+ )
502
+ }
503
+ ```
504
+
505
+ ## Virtual Fields
506
+
507
+ Custom fields can also be defined using virtual fields.
508
+
509
+ ```ruby
510
+ ATTRIBUTE_TYPES = {
511
+ id: Field::Number,
512
+ receipt: Field::ReceiptLink
513
+ }
514
+ ```
515
+
516
+ ```ruby
517
+ module Administrate
518
+ module Field
519
+ class ReceiptLink < Base
520
+ def data
521
+ resource.id
522
+ end
523
+
524
+ def filename
525
+ "receipt-#{data}.pdf"
526
+ end
527
+
528
+ def url
529
+ "/files/receipts/#{filename}"
530
+ end
531
+ end
532
+ end
533
+ end
534
+ ```
535
+
536
+ ```erb
537
+ <%= link_to field.filename, field.url %>
538
+ ```
539
+
540
+ ### Custom Actions via Virtual Field
541
+
542
+ By creating custom fields that are not dependent on specific attributes, you can insert custom views into any screen.
543
+ For example, you can add custom buttons like this:
544
+
545
+ ```ruby
546
+ ATTRIBUTE_TYPES = {
547
+ id: Field::Number,
548
+ custom_index_actions: Field::CustomActionButtons,
549
+ custom_show_actions: Field::CustomActionButtons,
550
+ }
551
+ ```
552
+
553
+ ```ruby
554
+ module Administrate
555
+ module Field
556
+ class CustomActionButtons < Base
557
+ def data
558
+ resource.id
559
+ end
560
+ end
561
+ end
562
+ end
563
+ ```
564
+
565
+ ```erb
566
+ <%# app/views/fields/custom_action_buttons/_index.html.erb %>
567
+ <% if field.data.present? %>
568
+ <%= button_to "some action 1", [:some_action_1, namespace, field.resource] %>
569
+ <%= button_to "some action 2", [:some_action_2, namespace, field.resource] %>
570
+ <%= button_to "some action 3", [:some_action_3, namespace, field.resource] %>
571
+ <% end %>
572
+ ```
@@ -94,3 +94,28 @@ rails generate administrate:views:layout
94
94
  # It only generates the sidebar partial
95
95
  # -> app/views/admin/application/_navigation.html.erb
96
96
  ```
97
+
98
+ ## Customizing for a specific layout
99
+
100
+ You can use several hook points to add elements to specific layouts or specific pages:
101
+
102
+ * header_middle
103
+ * header_last
104
+ * before_main
105
+ * main
106
+ * after_main
107
+
108
+ For example, you can add a button in the middle of the header as follows:
109
+
110
+ ```eruby
111
+ <%# app/views/admin/customers/_index_header.html.erb %>
112
+
113
+ <% content_for(:header_middle) do %>
114
+ <div>
115
+ You are logged in as <em><%= pundit_user.name %></em>.
116
+ <%= link_to("Become the Admin", become_admin_customer_path("admin")) unless pundit_user.admin? %>
117
+ </div>
118
+ <% end %>
119
+
120
+ <%= render template: 'administrate/application/_index_header', locals: local_assigns %>
121
+ ```
@@ -7,6 +7,7 @@ require "administrate/field/has_many"
7
7
  require "administrate/field/has_one"
8
8
  require "administrate/field/number"
9
9
  require "administrate/field/polymorphic"
10
+ require "administrate/field/rich_text"
10
11
  require "administrate/field/select"
11
12
  require "administrate/field/string"
12
13
  require "administrate/field/text"
@@ -79,7 +80,7 @@ module Administrate
79
80
  attribute_types[attr].permitted_attribute(
80
81
  attr,
81
82
  resource_class: self.class.model,
82
- action: action,
83
+ action: action
83
84
  )
84
85
  end.uniq
85
86
  end
@@ -118,10 +119,10 @@ module Administrate
118
119
 
119
120
  def item_associations
120
121
  attributes = if show_page_attributes.is_a?(Hash)
121
- show_page_attributes.values.flatten
122
- else
123
- show_page_attributes
124
- end
122
+ show_page_attributes.values.flatten
123
+ else
124
+ show_page_attributes
125
+ end
125
126
  attribute_associated attributes
126
127
  end
127
128