rails_admin_next 1.0.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 (184) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +46 -0
  3. data/LICENSE.md +21 -0
  4. data/README.md +317 -0
  5. data/Rakefile +25 -0
  6. data/app/assets/javascripts/rails_admin/custom/ui.js +1 -0
  7. data/app/assets/stylesheets/rails_admin/framework.css +2375 -0
  8. data/app/assets/stylesheets/rails_admin/skin.css +472 -0
  9. data/app/assets/stylesheets/rails_admin/tokens.css +281 -0
  10. data/app/assets/stylesheets/rails_admin.css +47 -0
  11. data/app/controllers/rails_admin_next/application_controller.rb +98 -0
  12. data/app/controllers/rails_admin_next/main_controller.rb +185 -0
  13. data/app/helpers/rails_admin_next/application_helper.rb +254 -0
  14. data/app/helpers/rails_admin_next/form_builder.rb +173 -0
  15. data/app/helpers/rails_admin_next/main_helper.rb +63 -0
  16. data/app/views/layouts/rails_admin_next/_head.html.erb +29 -0
  17. data/app/views/layouts/rails_admin_next/_navigation.html.erb +18 -0
  18. data/app/views/layouts/rails_admin_next/_secondary_navigation.html.erb +27 -0
  19. data/app/views/layouts/rails_admin_next/_sidebar_navigation.html.erb +6 -0
  20. data/app/views/layouts/rails_admin_next/application.html.erb +42 -0
  21. data/app/views/layouts/rails_admin_next/content.html.erb +22 -0
  22. data/app/views/layouts/rails_admin_next/modal.js.erb +7 -0
  23. data/app/views/rails_admin_next/main/_dashboard_history.html.erb +46 -0
  24. data/app/views/rails_admin_next/main/_delete_notice.html.erb +36 -0
  25. data/app/views/rails_admin_next/main/_form_action_text.html.erb +2 -0
  26. data/app/views/rails_admin_next/main/_form_boolean.html.erb +15 -0
  27. data/app/views/rails_admin_next/main/_form_colorpicker.html.erb +6 -0
  28. data/app/views/rails_admin_next/main/_form_datetime.html.erb +14 -0
  29. data/app/views/rails_admin_next/main/_form_enumeration.html.erb +26 -0
  30. data/app/views/rails_admin_next/main/_form_field.html.erb +2 -0
  31. data/app/views/rails_admin_next/main/_form_file_upload.html.erb +18 -0
  32. data/app/views/rails_admin_next/main/_form_filtering_multiselect.html.erb +20 -0
  33. data/app/views/rails_admin_next/main/_form_filtering_select.html.erb +22 -0
  34. data/app/views/rails_admin_next/main/_form_multiple_file_upload.html.erb +21 -0
  35. data/app/views/rails_admin_next/main/_form_nested_many.html.erb +20 -0
  36. data/app/views/rails_admin_next/main/_form_nested_one.html.erb +19 -0
  37. data/app/views/rails_admin_next/main/_form_polymorphic_association.html.erb +24 -0
  38. data/app/views/rails_admin_next/main/_form_text.html.erb +2 -0
  39. data/app/views/rails_admin_next/main/_pagination.html.erb +54 -0
  40. data/app/views/rails_admin_next/main/_submit_buttons.html.erb +26 -0
  41. data/app/views/rails_admin_next/main/bulk_delete.html.erb +19 -0
  42. data/app/views/rails_admin_next/main/dashboard.html.erb +64 -0
  43. data/app/views/rails_admin_next/main/delete.html.erb +21 -0
  44. data/app/views/rails_admin_next/main/edit.html.erb +3 -0
  45. data/app/views/rails_admin_next/main/export.html.erb +149 -0
  46. data/app/views/rails_admin_next/main/history.html.erb +80 -0
  47. data/app/views/rails_admin_next/main/index.html.erb +186 -0
  48. data/app/views/rails_admin_next/main/new.html.erb +3 -0
  49. data/app/views/rails_admin_next/main/show.html.erb +30 -0
  50. data/config/brakeman.ignore +145 -0
  51. data/config/importmap.rails_admin.rb +29 -0
  52. data/config/initializers/active_record_extensions.rb +21 -0
  53. data/config/locales/rails_admin_next.en.yml +158 -0
  54. data/config/routes.rb +14 -0
  55. data/lib/generators/rails_admin_next/install_generator.rb +39 -0
  56. data/lib/generators/rails_admin_next/templates/initializer.erb +40 -0
  57. data/lib/generators/rails_admin_next/utils.rb +21 -0
  58. data/lib/rails_admin_next/abstract_model.rb +266 -0
  59. data/lib/rails_admin_next/adapters/active_record/association.rb +130 -0
  60. data/lib/rails_admin_next/adapters/active_record/object_extension.rb +13 -0
  61. data/lib/rails_admin_next/adapters/active_record/property.rb +58 -0
  62. data/lib/rails_admin_next/adapters/active_record.rb +371 -0
  63. data/lib/rails_admin_next/config/actions/base.rb +160 -0
  64. data/lib/rails_admin_next/config/actions/bulk_delete.rb +67 -0
  65. data/lib/rails_admin_next/config/actions/dashboard.rb +62 -0
  66. data/lib/rails_admin_next/config/actions/delete.rb +58 -0
  67. data/lib/rails_admin_next/config/actions/edit.rb +56 -0
  68. data/lib/rails_admin_next/config/actions/export.rb +47 -0
  69. data/lib/rails_admin_next/config/actions/history_index.rb +40 -0
  70. data/lib/rails_admin_next/config/actions/history_show.rb +40 -0
  71. data/lib/rails_admin_next/config/actions/index.rb +108 -0
  72. data/lib/rails_admin_next/config/actions/new.rb +69 -0
  73. data/lib/rails_admin_next/config/actions/show.rb +36 -0
  74. data/lib/rails_admin_next/config/actions/show_in_app.rb +37 -0
  75. data/lib/rails_admin_next/config/actions.rb +122 -0
  76. data/lib/rails_admin_next/config/configurable.rb +107 -0
  77. data/lib/rails_admin_next/config/const_load_suppressor.rb +78 -0
  78. data/lib/rails_admin_next/config/fields/association.rb +169 -0
  79. data/lib/rails_admin_next/config/fields/base.rb +391 -0
  80. data/lib/rails_admin_next/config/fields/collection_association.rb +90 -0
  81. data/lib/rails_admin_next/config/fields/factories/action_text.rb +14 -0
  82. data/lib/rails_admin_next/config/fields/factories/active_storage.rb +34 -0
  83. data/lib/rails_admin_next/config/fields/factories/association.rb +32 -0
  84. data/lib/rails_admin_next/config/fields/factories/encrypted_password.rb +28 -0
  85. data/lib/rails_admin_next/config/fields/factories/enum.rb +23 -0
  86. data/lib/rails_admin_next/config/fields/factories/password.rb +19 -0
  87. data/lib/rails_admin_next/config/fields/group.rb +77 -0
  88. data/lib/rails_admin_next/config/fields/singular_association.rb +62 -0
  89. data/lib/rails_admin_next/config/fields/types/action_text.rb +20 -0
  90. data/lib/rails_admin_next/config/fields/types/active_record_enum.rb +62 -0
  91. data/lib/rails_admin_next/config/fields/types/active_storage.rb +70 -0
  92. data/lib/rails_admin_next/config/fields/types/all.rb +32 -0
  93. data/lib/rails_admin_next/config/fields/types/belongs_to_association.rb +48 -0
  94. data/lib/rails_admin_next/config/fields/types/boolean.rb +70 -0
  95. data/lib/rails_admin_next/config/fields/types/citext.rb +15 -0
  96. data/lib/rails_admin_next/config/fields/types/color.rb +43 -0
  97. data/lib/rails_admin_next/config/fields/types/date.rb +38 -0
  98. data/lib/rails_admin_next/config/fields/types/datetime.rb +79 -0
  99. data/lib/rails_admin_next/config/fields/types/decimal.rb +23 -0
  100. data/lib/rails_admin_next/config/fields/types/enum.rb +52 -0
  101. data/lib/rails_admin_next/config/fields/types/file_upload.rb +87 -0
  102. data/lib/rails_admin_next/config/fields/types/float.rb +23 -0
  103. data/lib/rails_admin_next/config/fields/types/has_and_belongs_to_many_association.rb +16 -0
  104. data/lib/rails_admin_next/config/fields/types/has_many_association.rb +16 -0
  105. data/lib/rails_admin_next/config/fields/types/has_one_association.rb +35 -0
  106. data/lib/rails_admin_next/config/fields/types/hidden.rb +31 -0
  107. data/lib/rails_admin_next/config/fields/types/inet.rb +15 -0
  108. data/lib/rails_admin_next/config/fields/types/integer.rb +20 -0
  109. data/lib/rails_admin_next/config/fields/types/json.rb +37 -0
  110. data/lib/rails_admin_next/config/fields/types/multiple_active_storage.rb +82 -0
  111. data/lib/rails_admin_next/config/fields/types/multiple_file_upload.rb +134 -0
  112. data/lib/rails_admin_next/config/fields/types/numeric.rb +24 -0
  113. data/lib/rails_admin_next/config/fields/types/password.rb +45 -0
  114. data/lib/rails_admin_next/config/fields/types/polymorphic_association.rb +118 -0
  115. data/lib/rails_admin_next/config/fields/types/serialized.rb +28 -0
  116. data/lib/rails_admin_next/config/fields/types/string.rb +50 -0
  117. data/lib/rails_admin_next/config/fields/types/string_like.rb +25 -0
  118. data/lib/rails_admin_next/config/fields/types/text.rb +28 -0
  119. data/lib/rails_admin_next/config/fields/types/time.rb +39 -0
  120. data/lib/rails_admin_next/config/fields/types/timestamp.rb +16 -0
  121. data/lib/rails_admin_next/config/fields/types/uuid.rb +15 -0
  122. data/lib/rails_admin_next/config/fields/types.rb +29 -0
  123. data/lib/rails_admin_next/config/fields.rb +88 -0
  124. data/lib/rails_admin_next/config/groupable.rb +20 -0
  125. data/lib/rails_admin_next/config/has_description.rb +14 -0
  126. data/lib/rails_admin_next/config/has_fields.rb +144 -0
  127. data/lib/rails_admin_next/config/has_groups.rb +27 -0
  128. data/lib/rails_admin_next/config/hideable.rb +30 -0
  129. data/lib/rails_admin_next/config/inspectable.rb +39 -0
  130. data/lib/rails_admin_next/config/lazy_model.rb +74 -0
  131. data/lib/rails_admin_next/config/model.rb +126 -0
  132. data/lib/rails_admin_next/config/proxyable/proxy.rb +42 -0
  133. data/lib/rails_admin_next/config/proxyable.rb +26 -0
  134. data/lib/rails_admin_next/config/sections/base.rb +36 -0
  135. data/lib/rails_admin_next/config/sections/create.rb +13 -0
  136. data/lib/rails_admin_next/config/sections/edit.rb +13 -0
  137. data/lib/rails_admin_next/config/sections/export.rb +13 -0
  138. data/lib/rails_admin_next/config/sections/list.rb +63 -0
  139. data/lib/rails_admin_next/config/sections/modal.rb +12 -0
  140. data/lib/rails_admin_next/config/sections/nested.rb +12 -0
  141. data/lib/rails_admin_next/config/sections/show.rb +12 -0
  142. data/lib/rails_admin_next/config/sections/update.rb +12 -0
  143. data/lib/rails_admin_next/config/sections.rb +39 -0
  144. data/lib/rails_admin_next/config.rb +436 -0
  145. data/lib/rails_admin_next/engine.rb +91 -0
  146. data/lib/rails_admin_next/extension.rb +35 -0
  147. data/lib/rails_admin_next/extensions/cancancan/authorization_adapter.rb +90 -0
  148. data/lib/rails_admin_next/extensions/cancancan.rb +5 -0
  149. data/lib/rails_admin_next/extensions/controller_extension.rb +8 -0
  150. data/lib/rails_admin_next/extensions/paper_trail/auditing_adapter.rb +192 -0
  151. data/lib/rails_admin_next/extensions/paper_trail.rb +5 -0
  152. data/lib/rails_admin_next/extensions/pundit/authorization_adapter.rb +72 -0
  153. data/lib/rails_admin_next/extensions/pundit.rb +5 -0
  154. data/lib/rails_admin_next/extensions/url_for_extension.rb +15 -0
  155. data/lib/rails_admin_next/icons.rb +143 -0
  156. data/lib/rails_admin_next/support/composite_keys_serializer.rb +15 -0
  157. data/lib/rails_admin_next/support/csv_converter.rb +100 -0
  158. data/lib/rails_admin_next/support/hash_helper.rb +31 -0
  159. data/lib/rails_admin_next/support/paginated_collection.rb +79 -0
  160. data/lib/rails_admin_next/version.rb +17 -0
  161. data/lib/rails_admin_next.rb +72 -0
  162. data/lib/tasks/rails_admin.rake +40 -0
  163. data/src/rails_admin/application.js +15 -0
  164. data/src/rails_admin/base.js +12 -0
  165. data/src/rails_admin/controllers/alert_controller.js +28 -0
  166. data/src/rails_admin/controllers/collapse_controller.js +16 -0
  167. data/src/rails_admin/controllers/dropdown_controller.js +110 -0
  168. data/src/rails_admin/controllers/feedback_controller.js +73 -0
  169. data/src/rails_admin/controllers/filter_box_controller.js +346 -0
  170. data/src/rails_admin/controllers/filtering_multiselect_controller.js +386 -0
  171. data/src/rails_admin/controllers/filtering_select_controller.js +509 -0
  172. data/src/rails_admin/controllers/index.js +31 -0
  173. data/src/rails_admin/controllers/modal_controller.js +142 -0
  174. data/src/rails_admin/controllers/nested_form_controller.js +117 -0
  175. data/src/rails_admin/controllers/polymorphic_controller.js +26 -0
  176. data/src/rails_admin/controllers/remote_form_controller.js +173 -0
  177. data/src/rails_admin/controllers/sidescroll_controller.js +23 -0
  178. data/src/rails_admin/dom-ready.js +20 -0
  179. data/src/rails_admin/dom.js +19 -0
  180. data/src/rails_admin/i18n.js +20 -0
  181. data/src/rails_admin/icons.js +64 -0
  182. data/src/rails_admin/interactions.js +221 -0
  183. data/src/rails_admin/select_query.js +65 -0
  184. metadata +346 -0
@@ -0,0 +1,149 @@
1
+ <% params = request.params.except(:action, :controller, :utf8, :page, :per_page, :format, :authenticity_token) %>
2
+ <% visible_fields = @model_config.export.with(view: self, object: @abstract_model.model.new, controller: self.controller).visible_fields %>
3
+ <%= form_tag export_path(params.merge(all: true)), method: 'post', class: "main", data: {turbo: false} do %>
4
+ <input name="send_data" type="hidden" value="true" />
5
+ <fieldset id="fields_to_export" class="mb-3">
6
+ <legend>
7
+ <%= rails_admin_icon(:collapse) %>
8
+ <%= t('admin.export.select') %>
9
+ </legend>
10
+ <div class="form-group control-group">
11
+ <div class="col-sm-12">
12
+ <div class="checkbox">
13
+ <label for="check_all">
14
+ <%= check_box_tag 'all', 'all', true, { id: 'check_all' } %>
15
+ <b>
16
+ <%= t('admin.export.select_all_fields') %>
17
+ </b>
18
+ </label>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ <div class="control-group row">
23
+ <div class="col-sm-12">
24
+ <div class="card bg-light my-2 reverse-selection" rel="tooltip" role="button" title="<%= t('admin.export.click_to_reverse_selection') %>">
25
+ <div class="card-body p-2">
26
+ <b><%= t('admin.export.fields_from', name: @model_config.label_plural.downcase) %></b>
27
+ </div>
28
+ </div>
29
+ <div class="controls">
30
+ <div class="row">
31
+ <% visible_fields.select{ |f| !f.association? || f.association.polymorphic? }.each do |field| %>
32
+ <% list = field.virtual? ? 'methods' : 'only' %>
33
+ <div class="checkbox col-sm-3 my-1">
34
+ <% if field.association? && field.association.polymorphic? %>
35
+ <label for="schema_<%= list %>_<%= field.method_name %>">
36
+ <%= check_box_tag "schema[#{list}][]", field.method_name, true, { id: "schema_#{list}_#{field.method_name}" } %>
37
+ <%= field.label + " [id]" %>
38
+ </label>
39
+ <% polymorphic_type_column_name = @abstract_model.properties.detect {|p| field.association.foreign_type == p.name }.name %>
40
+ <label for="schema_<%= list %>_<%= polymorphic_type_column_name %>">
41
+ <%= check_box_tag "schema[#{list}][]", polymorphic_type_column_name, true, { id: "schema_#{list}_#{polymorphic_type_column_name}" } %>
42
+ <%= field.label + " [type]" %>
43
+ </label>
44
+ <% else %>
45
+ <label for="schema_<%= list %>_<%= field.name %>">
46
+ <%= check_box_tag "schema[#{list}][]", field.name, true, { id: "schema_#{list}_#{field.name}" } %>
47
+ <%= field.label %>
48
+ </label>
49
+ <% end %>
50
+ </div>
51
+ <% end %>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ <% visible_fields.select{ |f| f.association? && !f.association.polymorphic? }.each do |field| %>
57
+ <% fields = field.associated_model_config.export.with(controller: self.controller, view: self, object: (associated_model = field.associated_model_config.abstract_model.model).new).visible_fields.select{ |f| !f.association? } %>
58
+ <div class="control-group row">
59
+ <div class="col-sm-12">
60
+ <div class="card bg-light my-2 reverse-selection" rel="tooltip" role="button" title="<%= t('admin.export.click_to_reverse_selection') %>">
61
+ <div class="card-body p-2">
62
+ <b><%= t('admin.export.fields_from_associated', name: field.label.downcase) %></b>
63
+ </div>
64
+ </div>
65
+ <div class="controls">
66
+ <div class="row">
67
+ <% fields.each do |associated_model_field| %>
68
+ <% list = associated_model_field.virtual? ? 'methods' : 'only' %>
69
+ <div class="checkbox col-sm-3 my-1">
70
+ <label for="schema_include_<%= field.name %>_<%= list %>_<%= associated_model_field.name %>">
71
+ <%= check_box_tag "schema[include][#{field.name}][#{list}][]", associated_model_field.name, true, { id: "schema_include_#{field.name}_#{list}_#{associated_model_field.name}" } %>
72
+ <%= associated_model_field.label %>
73
+ </label>
74
+ </div>
75
+ <% end %>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ <% end %>
81
+ </fieldset>
82
+ <fieldset>
83
+ <legend>
84
+ <%= rails_admin_icon(:collapse) %>
85
+ <%= t('admin.export.options_for', name: 'csv') %>
86
+ </legend>
87
+ <div class="control-group row">
88
+ <% guessed_encoding = @abstract_model.encoding %>
89
+ <label class="col-sm-2 col-form-label text-md-end" for="csv_options_encoding_to">
90
+ <%= t('admin.export.csv.encoding_to') %>
91
+ </label>
92
+ <div class="col-sm-10 controls">
93
+ <div class="w-50">
94
+ <%= select_tag 'csv_options[encoding_to]', options_for_select(Encoding.name_list.sort), include_blank: true, placeholder: t('admin.misc.search'), data: { controller: 'filtering-select' } %>
95
+ </div>
96
+ <p class="form-text">
97
+ <%= t('admin.export.csv.encoding_to_help', name: guessed_encoding) %>
98
+ </p>
99
+ </div>
100
+ </div>
101
+ <div class="control-group row">
102
+ <label class="col-sm-2 col-form-label text-md-end" for="csv_options_skip_header">
103
+ <%= t('admin.export.csv.skip_header') %>
104
+ </label>
105
+ <div class="col-sm-10 controls">
106
+ <div class="col-form-label">
107
+ <label>
108
+ <%= check_box_tag 'csv_options[skip_header]', 'true' %>
109
+ </label>
110
+ </div>
111
+ <p class="form-text">
112
+ <%= t('admin.export.csv.skip_header_help') %>
113
+ </p>
114
+ </div>
115
+ </div>
116
+ <div class="control-group row">
117
+ <label class="col-sm-2 col-form-label text-md-end" for="csv_options_generator_col_sep">
118
+ <%= t('admin.export.csv.col_sep') %>
119
+ </label>
120
+ <div class="col-sm-10 controls">
121
+ <div class="w-50">
122
+ <%= select_tag 'csv_options[generator][col_sep]', options_for_select({ '' => t('admin.export.csv.default_col_sep'), "<comma> ','" => ',', "<semicolon> ';'" => ';', '<tabs>' => "'\t'" }), placeholder: t('admin.misc.search'), data: { controller: 'filtering-select' } %>
123
+ </div>
124
+ <p class="form-text">
125
+ <%= t('admin.export.csv.col_sep_help', value: t('admin.export.csv.default_col_sep')) %>
126
+ </p>
127
+ </div>
128
+ </div>
129
+ </fieldset>
130
+ <div class="form-actions row justify-content-end mb-3">
131
+ <div class="col-sm-offset-2 col-sm-10">
132
+ <input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>" />
133
+ <button class="btn btn-primary" name="csv" type="submit">
134
+ <%= rails_admin_icon(:check) %>
135
+ <%= t("admin.export.confirmation", name: 'csv') %>
136
+ </button>
137
+ <button class="btn btn-info" name="json" type="submit">
138
+ <%= t("admin.export.confirmation", name: 'json') %>
139
+ </button>
140
+ <button class="btn btn-info" name="xml" type="submit">
141
+ <%= t("admin.export.confirmation", name: 'xml') %>
142
+ </button>
143
+ <button class="btn btn-light" name="_continue" type="submit">
144
+ <%= rails_admin_icon(:cancel) %>
145
+ <%= t("admin.form.cancel") %>
146
+ </button>
147
+ </div>
148
+ </div>
149
+ <% end %>
@@ -0,0 +1,80 @@
1
+ <% params = request.params.except(:action, :controller, :model_name) %>
2
+ <% query = params[:query] %>
3
+ <% filter = params[:filter] %>
4
+ <% sort = params[:sort] %>
5
+ <% sort_reverse = params[:sort_reverse] %>
6
+ <% path_method = params[:id] ? "history_show_path" : "history_index_path" %>
7
+ <%= form_tag("", method: "get", class: "search form-inline") do %>
8
+ <div class="card mb-3 p-3 bg-light">
9
+ <div class="row">
10
+ <div class="col-sm-6">
11
+ <div class="input-group">
12
+ <input class="form-control" name="query" placeholder="<%= t("admin.misc.filter") %>" type="search" value="<%= query %>" />
13
+ <button class="btn btn-primary" data-disable-with="<%= RailsAdminNext::Icons.svg(:refresh) + ' ' + t('admin.misc.refresh') %>" type="submit">
14
+ <%= rails_admin_icon(:refresh) %>
15
+ <%= t("admin.misc.refresh") %>
16
+ </button>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ <% end %>
22
+ <table class="table table-condensed table-striped table-hover" id="history">
23
+ <thead>
24
+ <tr>
25
+ <% columns = [] %>
26
+ <% columns << { property_name: "created_at", css_class: "created_at",link_text: t('admin.table_headers.created_at') } %>
27
+ <% columns << { property_name: "username", css_class: "username", link_text: t('admin.table_headers.username') } %>
28
+ <% columns << { property_name: "item", css_class: "item", link_text: t('admin.table_headers.item') } if @general %>
29
+ <% columns << { property_name: "message", css_class: "message", link_text: t('admin.table_headers.message') } %>
30
+ <% columns.each do |column| %>
31
+ <% property_name = column[:property_name] %>
32
+ <% selected = (sort == property_name) %>
33
+ <% sort_direction = (sort_reverse ? "headerSortUp" : "headerSortDown" if selected) %>
34
+ <% sort_location = send(path_method, params.except("sort_reverse").merge(model_name: @abstract_model.to_param, sort: property_name).merge(selected && sort_reverse != "true" ? {sort_reverse: "true"} : {})) %>
35
+ <th class="header <%= column[:css_class] %> <%= sort_direction if selected %>" data-href="<%= sort_location %>">
36
+ <%= column[:link_text] %>
37
+ </th>
38
+ <% end %>
39
+ </tr>
40
+ </thead>
41
+ <tbody class="table-group-divider">
42
+ <% @history.each_with_index do |object, index| %>
43
+ <tr>
44
+ <% unless object.created_at.nil? %>
45
+ <td>
46
+ <%= l(object.created_at, format: :long, default: l(object.created_at, format: :long)) %>
47
+ </td>
48
+ <% end %>
49
+ <td>
50
+ <%= object.username %>
51
+ </td>
52
+ <% if @general %>
53
+ <% if o = @abstract_model.get(object.item) %>
54
+ <% label = o.send(@abstract_model.config.object_label_method) %>
55
+ <% if show_action = action(:show, @abstract_model, o) %>
56
+ <td>
57
+ <%= link_to(label, url_for(action: show_action.action_name, model_name: @abstract_model.to_param, id: o.id)) %>
58
+ </td>
59
+ <% else %>
60
+ <td>
61
+ <%= label %>
62
+ </td>
63
+ <% end %>
64
+ <% else %>
65
+ <td>
66
+ <%= "#{@abstract_model.config.label} ##{object.item}" %>
67
+ </td>
68
+ <% end %>
69
+ <% end %>
70
+ <td>
71
+ <%= object.message.in?(['delete', 'new']) ? t("admin.actions.#{object.message}.done").capitalize : object.message %>
72
+ </td>
73
+ </tr>
74
+ <% end %>
75
+ </tbody>
76
+ </table>
77
+ <% unless params[:all] || !@history.respond_to?(:current_page) %>
78
+ <%= render 'pagination', collection: @history, mode: :numbered, url_proc: ->(page) { send(path_method, params.except('page', :page).merge(page: page)) } %>
79
+ <%= link_to(t("admin.misc.show_all"), send(path_method, params.merge(all: true)), class: "show-all btn btn-light") unless (tc = @history.total_count) <= @history.size || tc > 100 %>
80
+ <% end %>
@@ -0,0 +1,186 @@
1
+ <%
2
+ query = params[:query]
3
+ params = request.params.except(:authenticity_token, :action, :controller, :utf8, :bulk_export)
4
+ params.delete(:query) if params[:query].blank?
5
+ params.delete(:sort_reverse) unless params[:sort_reverse] == 'true'
6
+ sort_reverse = params[:sort_reverse]
7
+ sort = params[:sort]
8
+ params.delete(:sort) if params[:sort] == @model_config.list.sort_by.to_s
9
+ export_action = RailsAdminNext::Config::Actions.find(:export, { controller: self.controller, abstract_model: @abstract_model })
10
+ export_action = nil unless export_action && authorized?(export_action.authorization_key, @abstract_model)
11
+ description = RailsAdminNext.config(@abstract_model.model_name).description
12
+ properties = @model_config.list.with(controller: self.controller, view: self, object: @abstract_model.model.new).fields_for_table
13
+ checkboxes = @model_config.list.checkboxes?
14
+ table_table_header_count = begin
15
+ count = checkboxes ? 1 : 0
16
+ count = count + properties.count
17
+ end
18
+ %>
19
+
20
+ <% content_for :contextual_tabs do %>
21
+ <% if filterable_fields.present? %>
22
+ <li class="nav-item dropdown">
23
+ <button type="button" id="filters_toggle" class="nav-link dropdown-toggle" popovertarget="filters" data-controller="dropdown" data-action="keydown->dropdown#onKeydown">
24
+ <%= t('admin.misc.add_filter') %>
25
+ <b class="caret"></b>
26
+ </button>
27
+ <ul class="dropdown-menu dropdown-menu-end" id="filters" role="menu" popover="auto">
28
+ <% filterable_fields.each do |field| %>
29
+ <li role="none">
30
+ <a
31
+ href="#"
32
+ class="dropdown-item"
33
+ role="menuitem"
34
+ tabindex="-1"
35
+ data-options="<%= field.with(view: self).filter_options.to_json %>"
36
+ >
37
+ <%= field.label %>
38
+ </a>
39
+ </li>
40
+ <% end %>
41
+ </ul>
42
+ </li>
43
+ <% end %>
44
+ <% if checkboxes %>
45
+ <%= bulk_menu %>
46
+ <% end %>
47
+ <% end %>
48
+
49
+ <style nonce="<%= content_security_policy_nonce %>">
50
+ <% properties.select{ |p| p.column_width.present? }.each do |property| %>
51
+ <%= "#list th.#{property.css_class} { width: #{property.column_width}px; min-width: #{property.column_width}px; }" %>
52
+ <%= "#list td.#{property.css_class} { max-width: #{property.column_width}px;}" %>
53
+ <% end %>
54
+ </style>
55
+
56
+ <div id="list">
57
+ <%= form_tag(index_path(params.except(*%w[page f query])), method: :get) do %>
58
+ <div class="card mb-3 p-3 bg-light">
59
+ <div class="row" data-controller="filter-box" data-options="<%= ordered_filter_options.to_json %>" id="filters_box"></div>
60
+ <hr class="filters_box"/>
61
+ <div class="row">
62
+ <div class="col-sm-6">
63
+ <div class="input-group">
64
+ <input class="form-control" name="query" placeholder="<%= t("admin.misc.filter") %>" type="search" value="<%= query %>" />
65
+ <button class="btn btn-primary" data-disable-with="<%= RailsAdminNext::Icons.svg(:refresh) + t('admin.misc.refresh') %>" type="submit">
66
+ <%= rails_admin_icon(:refresh) %>
67
+ <%= t('admin.misc.refresh') %>
68
+ </button>
69
+ <button class="btn btn-info" id="remove_filter" title="<%= t('admin.misc.reset_filters') %>">
70
+ <%= rails_admin_icon(:cancel) %>
71
+ </button>
72
+ </div>
73
+ <% if @model_config.list.search_help.present? %>
74
+ <div class="form-text"><%= @model_config.list.search_help %></div>
75
+ <% end %>
76
+ </div>
77
+ <div class="col-sm-6 text-end">
78
+ <% if export_action %>
79
+ <%= link_to wording_for(:link, export_action), export_path(params.except('page')), class: 'btn btn-info' %>
80
+ <% end %>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ <% end %>
85
+ <% unless @model_config.list.scopes.empty? %>
86
+ <ul class="nav nav-tabs" id="scope_selector">
87
+ <% @model_config.list.scopes.each_with_index do |scope, index| %>
88
+ <% scope = '_all' if scope.nil? %>
89
+ <li class="nav-item">
90
+ <a href="<%= index_path(params.merge(scope: scope, page: nil)) %>" class="nav-link <%= 'active' if scope.to_s == params[:scope] || (params[:scope].blank? && index == 0) %>">
91
+ <%= I18n.t("admin.scopes.#{@abstract_model.to_param}.#{scope}", default: I18n.t("admin.scopes.#{scope}", default: scope.to_s.titleize)) %>
92
+ </a>
93
+ </li>
94
+ <% end %>
95
+ </ul>
96
+ <% end %>
97
+ <%= form_tag bulk_action_path(model_name: @abstract_model.to_param), method: :post, id: "bulk_form", class: ["form", "mb-3"] do %>
98
+ <%= hidden_field_tag :bulk_action %>
99
+ <% if description.present? %>
100
+ <p>
101
+ <strong>
102
+ <%= description %>
103
+ </strong>
104
+ </p>
105
+ <% end %>
106
+ <div id="sidescroll" data-controller="sidescroll">
107
+ <table class="table table-condensed table-striped table-hover">
108
+ <thead>
109
+ <tr>
110
+ <% if checkboxes %>
111
+ <th class="shrink sticky">
112
+ <input class="toggle" type="checkbox" />
113
+ </th>
114
+ <% end %>
115
+ <% properties.each do |property| %>
116
+ <% selected = (sort == property.name.to_s) %>
117
+ <% if property.sortable %>
118
+ <% sort_location = index_path params.except('sort_reverse').except('page').merge(sort: property.name).merge(selected && sort_reverse != "true" ? {sort_reverse: "true"} : {}) %>
119
+ <% sort_direction = (sort_reverse == 'true' ? "headerSortUp" : "headerSortDown" if selected) %>
120
+ <% end %>
121
+ <th class="<%= [property.sortable && "header", property.sortable && sort_direction, property.sticky? && 'sticky', property.css_class, property.type_css_class].select(&:present?).join(' ') %>" data-href="<%= property.sortable && sort_location %>" rel="tooltip" title="<%= property.hint %>">
122
+ <%= property.label %>
123
+ </th>
124
+ <% end %>
125
+ <th class="last shrink"></th>
126
+ </tr>
127
+ </thead>
128
+ <tbody class="table-group-divider">
129
+ <% @objects.each do |object| %>
130
+ <tr class="<%= @abstract_model.param_key %>_row <%= @model_config.list.with(object: object).row_css_class %>">
131
+ <% if checkboxes %>
132
+ <td class="sticky">
133
+ <%= check_box_tag "bulk_ids[]", object.id.to_s, false %>
134
+ </td>
135
+ <% end %>
136
+ <% properties.map{ |property| property.bind(:object, object) }.each do |property| %>
137
+ <% value = property.pretty_value %>
138
+ <%= content_tag(:td, class: [property.sticky? && 'sticky', property.css_class, property.type_css_class].select(&:present?), title: strip_tags(value.to_s)) do %>
139
+ <%= value %>
140
+ <% end %>
141
+ <% end %>
142
+ <td class="last links ra-sidescroll-frozen">
143
+ <ul class="nav d-inline list-inline">
144
+ <%= menu_for :member, @abstract_model, object, true %>
145
+ </ul>
146
+ </td>
147
+ </tr>
148
+ <% end %>
149
+ <% if @objects.empty? %>
150
+ <tr class="empty_row">
151
+ <td colspan="<%= table_table_header_count %>">
152
+ <%= I18n.t('admin.actions.index.no_records') %>
153
+ </td>
154
+ </tr>
155
+ <% end %>
156
+ </tbody>
157
+ </table>
158
+ </div>
159
+ <% if @model_config.list.limited_pagination %>
160
+ <div class="row">
161
+ <div class="col-md-6">
162
+ <%= render 'pagination', collection: @objects, mode: :limited, url_proc: ->(page) { index_path(params.except('page', :page).merge(page: page)) } %>
163
+ </div>
164
+ </div>
165
+ <% elsif @objects.respond_to?(:total_count) %>
166
+ <% total_count = @objects.total_count.to_i %>
167
+ <div class="row">
168
+ <div class="col-md-6">
169
+ <%= render 'pagination', collection: @objects, mode: :numbered, url_proc: ->(page) { index_path(params.except('page', :page).merge(page: page)) } %>
170
+ </div>
171
+ </div>
172
+ <div class="row">
173
+ <div class="col-md-6">
174
+ <%= link_to(t("admin.misc.show_all"), index_path(params.merge(all: true)), class: "show-all btn btn-light clearfix") unless total_count > 100 || total_count <= @objects.to_a.size %>
175
+ </div>
176
+ </div>
177
+ <div class="clearfix total-count">
178
+ <%= "#{total_count} #{@model_config.pluralize(total_count).downcase}" %>
179
+ </div>
180
+ <% else %>
181
+ <div class="clearfix total-count">
182
+ <%= "#{@objects.size} #{@model_config.pluralize(@objects.size).downcase}" %>
183
+ </div>
184
+ <% end %>
185
+ <% end %>
186
+ </div>
@@ -0,0 +1,3 @@
1
+ <%= rails_admin_form_for @object, url: new_path(model_name: @abstract_model.to_param), as: @abstract_model.param_key, html: { multipart: true, class: "main", data: { title: @page_name } } do |form| %>
2
+ <%= form.generate action: :create %>
3
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <% @model_config.show.with(object: @object, view: self, controller: self.controller).visible_groups.each do |fieldset| %>
2
+ <% unless (fields = fieldset.with(object: @object, view: self, controller: self.controller).visible_fields).empty? %>
3
+ <% unless (fields = fields.reject{ |f| RailsAdminNext::config.compact_show_view && (f.formatted_value.nil? || f.formatted_value == '') }).empty? %>
4
+ <div class="fieldset">
5
+ <h4>
6
+ <%= fieldset.label %>
7
+ </h4>
8
+ <% if fieldset.help %>
9
+ <p>
10
+ <%= fieldset.help %>
11
+ </p>
12
+ <% end %>
13
+ <div class="list-group">
14
+ <% fields.each_with_index do |field, index| %>
15
+ <div class="list-group-item border-0 <%= field.type_css_class %> <%= field.css_class %>">
16
+ <div class="card">
17
+ <h5 class="card-header bg-light">
18
+ <%= field.label %>
19
+ </h5>
20
+ <div class="card-body">
21
+ <%= field.pretty_value %>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ <% end %>
26
+ </dl>
27
+ </div>
28
+ <% end %>
29
+ <% end %>
30
+ <% end %>
@@ -0,0 +1,145 @@
1
+ {
2
+ "ignored_warnings": [
3
+ {
4
+ "warning_type": "Weak Hash",
5
+ "warning_code": 90,
6
+ "fingerprint": "7ae45391f647eaa39c6ab4ea081fdd4c928c38064850cacd3115a017ce0fc5ef",
7
+ "check_name": "WeakHash",
8
+ "message": "Weak hashing algorithm used: MD5",
9
+ "file": "app/helpers/rails_admin_next/application_helper.rb",
10
+ "line": 230,
11
+ "link": "https://brakemanscanner.org/docs/warning_types/weak_hash/",
12
+ "code": "Digest::MD5.hexdigest(email)",
13
+ "render_path": null,
14
+ "location": {
15
+ "type": "method",
16
+ "class": "RailsAdminNext::ApplicationHelper",
17
+ "method": "gravatar_url"
18
+ },
19
+ "user_input": null,
20
+ "confidence": "Medium",
21
+ "cwe_id": [
22
+ 328
23
+ ],
24
+ "note": "False positive. MD5 implements the Gravatar avatar URL scheme (Gravatar keys avatars by the MD5 of the email). It is a non-cryptographic identifier, not used for passwords or integrity."
25
+ },
26
+ {
27
+ "warning_type": "Weak Hash",
28
+ "warning_code": 90,
29
+ "fingerprint": "ade4b22042c4f44d36b2a203a27252138eb53e191ff15f8e822c0623d8565311",
30
+ "check_name": "WeakHash",
31
+ "message": "Weak hashing algorithm used: MD5",
32
+ "file": "app/helpers/rails_admin_next/application_helper.rb",
33
+ "line": 236,
34
+ "link": "https://brakemanscanner.org/docs/warning_types/weak_hash/",
35
+ "code": "Digest::MD5.hexdigest(label)",
36
+ "render_path": null,
37
+ "location": {
38
+ "type": "method",
39
+ "class": "RailsAdminNext::ApplicationHelper",
40
+ "method": "collapsible_stack"
41
+ },
42
+ "user_input": null,
43
+ "confidence": "Medium",
44
+ "cwe_id": [
45
+ 328
46
+ ],
47
+ "note": "False positive. MD5 builds a short, stable DOM/CSS key from a sidebar label (collapse-key). It is a non-cryptographic identifier for client-side targeting, not used for security."
48
+ },
49
+ {
50
+ "warning_type": "SQL Injection",
51
+ "warning_code": 0,
52
+ "fingerprint": "d15e1c138bacf89995ddd7472443a7cc493afca1b4d7c90cb4c4c1f828a94bec",
53
+ "check_name": "SQL",
54
+ "message": "Possible SQL injection",
55
+ "file": "lib/rails_admin_next/adapters/active_record.rb",
56
+ "line": 195,
57
+ "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
58
+ "code": "scope.where(\"#{statement}\", *[value1, value2])",
59
+ "render_path": null,
60
+ "location": {
61
+ "type": "method",
62
+ "class": "RailsAdminNext::Adapters::ActiveRecord::WhereBuilder",
63
+ "method": "build"
64
+ },
65
+ "user_input": "statement",
66
+ "confidence": "Weak",
67
+ "cwe_id": [
68
+ 89
69
+ ],
70
+ "note": "False positive. @statements are built by StatementBuilder from schema-derived column names and a fixed operator set using ? placeholders; every user-supplied value is bound positionally via *@values. No user input is interpolated into the SQL string."
71
+ },
72
+ {
73
+ "warning_type": "Dangerous Eval",
74
+ "warning_code": 13,
75
+ "fingerprint": "7cba47560dcc1117f0679d149479586da5c01b563d5974388d00ec644e6a6773",
76
+ "check_name": "Evaluation",
77
+ "message": "Dynamic string evaluated as code",
78
+ "file": "lib/rails_admin_next/config/actions.rb",
79
+ "line": 52,
80
+ "link": "https://brakemanscanner.org/docs/warning_types/dangerous_eval/",
81
+ "code": "\"RailsAdminNext::Config::Actions::#{parent_class.to_s.camelize}\".constantize.new.instance_eval(\"\\n #{parent} true\\n def key\\n :#{key}\\n end\\n \", \"lib/rails_admin_next/config/actions.rb\", 52)",
82
+ "render_path": null,
83
+ "location": {
84
+ "type": "method",
85
+ "class": "RailsAdminNext::Config::Actions",
86
+ "method": "self.add_action"
87
+ },
88
+ "user_input": null,
89
+ "confidence": "Weak",
90
+ "cwe_id": [
91
+ 913,
92
+ 95
93
+ ],
94
+ "note": "False positive. Internal action-registry DSL: the instance_eval'd template interpolates only gem-defined action keys and scope kinds (:base/:collection/:member/:root), never request data."
95
+ },
96
+ {
97
+ "warning_type": "Dangerous Eval",
98
+ "warning_code": 13,
99
+ "fingerprint": "f25a967de73c369963f8999f96afcc6327d4fd49a6ee15725fe6a1dc3fd91a48",
100
+ "check_name": "Evaluation",
101
+ "message": "Dynamic string evaluated as code",
102
+ "file": "lib/rails_admin_next/config/actions.rb",
103
+ "line": 71,
104
+ "link": "https://brakemanscanner.org/docs/warning_types/dangerous_eval/",
105
+ "code": "instance_eval(\"\\n def #{name.to_s.demodulize.underscore.to_sym}(&block)\\n action = #{name.to_s.demodulize.underscore.to_sym}.new\\n add_action_custom_key(action, &block)\\n end\\n \", \"lib/rails_admin_next/config/actions.rb\", 71)",
106
+ "render_path": null,
107
+ "location": {
108
+ "type": "method",
109
+ "class": "RailsAdminNext::Config::Actions",
110
+ "method": "self.register"
111
+ },
112
+ "user_input": null,
113
+ "confidence": "Weak",
114
+ "cwe_id": [
115
+ 913,
116
+ 95
117
+ ],
118
+ "note": "False positive. Internal action-registry DSL: the instance_eval'd template interpolates only gem-defined action class names, never request data."
119
+ },
120
+ {
121
+ "warning_type": "Mass Assignment",
122
+ "warning_code": 70,
123
+ "fingerprint": "c787e046c0932c7b107eb081bffa9cc6796d415358bf3614b6f601573f62488e",
124
+ "check_name": "MassAssignment",
125
+ "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys",
126
+ "file": "lib/rails_admin_next/config/actions/index.rb",
127
+ "line": 89,
128
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
129
+ "code": "params[:csv_options].permit!",
130
+ "render_path": null,
131
+ "location": {
132
+ "type": "method",
133
+ "class": "Index",
134
+ "method": null
135
+ },
136
+ "user_input": null,
137
+ "confidence": "Medium",
138
+ "cwe_id": [
139
+ 915
140
+ ],
141
+ "note": "False positive. The permitted hash is CSV export formatting options (encoding_to, skip_header, and the generator kwargs passed to CSV.generate), consumed by RailsAdmin::CSVConverter#to_csv. It is never assigned to a model, so there is no attribute mass-assignment; unrecognised keys merely raise in Encoding.find / CSV.generate."
142
+ }
143
+ ],
144
+ "brakeman_version": null
145
+ }
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Engine-owned importmap, drawn by lib/rails_admin_next/engine.rb and distinct from the host's
4
+ # config/importmap.rb. Rendered inline (CSP-nonce threaded) on admin pages by
5
+ # app/views/layouts/rails_admin_next/_head.html.erb.
6
+
7
+ # rails_admin's own browser-native ESM modules — served and fingerprinted by Propshaft from src/.
8
+ # pin_all_from covers the controllers/ manifest and every controller too (rails_admin/controllers/*).
9
+ pin "rails_admin", to: "rails_admin/base.js", preload: true
10
+ pin_all_from File.expand_path("../src/rails_admin", __dir__), under: "rails_admin"
11
+
12
+ # Self-hosted survivors. Stimulus (a genuinely new dependency) and Turbo ship single, pre-bundled
13
+ # browser-native ESM files from their gems, served and fingerprinted by Propshaft off each gem's
14
+ # auto-registered asset path — no jspm, no build step.
15
+ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
16
+ pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
17
+
18
+ # The @rails/* and trix modules are self-hosted too: each is a single pre-bundled browser-native ESM
19
+ # file shipped inside its Rails gem (actioncable/activestorage/actiontext) or action_text-trix, served
20
+ # and fingerprinted by Propshaft off the gem's auto-registered asset path — zero external network, JS
21
+ # versioned in lockstep with the gems (no jspm Dependabot blind spot). actioncable's asset path is
22
+ # registered by the `require 'action_cable/engine'` in engine.rb (the @rails/activestorage/actiontext
23
+ # and trix paths come from the host's ActiveStorage/ActionText, which load action_text-trix). There is
24
+ # no jQuery, jQuery-UI, or @rails/ujs pin: destructive actions use Turbo's data-turbo-method/
25
+ # data-turbo-confirm attributes, and date/time fields use native HTML5 inputs (no JS library).
26
+ pin "@rails/actioncable", to: "actioncable.esm.js"
27
+ pin "@rails/activestorage", to: "activestorage.esm.js"
28
+ pin "@rails/actiontext", to: "actiontext.esm.js"
29
+ pin "trix", to: "trix.js"
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The on_load(:active_record) block is class_eval'd in ActiveRecord::Base's
4
+ # context, so these definitions land directly on ActiveRecord::Base.
5
+ ActiveSupport.on_load(:active_record) do
6
+ def self.rails_admin_next(&)
7
+ RailsAdminNext.config(self, &)
8
+ end
9
+
10
+ def rails_admin_default_object_label_method
11
+ new_record? ? "new #{self.class}" : "#{self.class} ##{id}"
12
+ end
13
+
14
+ def safe_send(value)
15
+ if has_attribute?(value)
16
+ read_attribute(value)
17
+ else
18
+ send(value)
19
+ end
20
+ end
21
+ end