administrate 0.20.1 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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