avo 2.15.3 → 2.16.1.pre.1.nativefields
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +78 -86
- data/app/components/avo/base_component.rb +7 -1
- data/app/components/avo/field_wrapper_component.html.erb +40 -0
- data/app/components/avo/field_wrapper_component.rb +102 -0
- data/app/components/avo/fields/badge_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +3 -3
- data/app/components/avo/fields/belongs_to_field/index_component.html.erb +2 -1
- data/app/components/avo/fields/belongs_to_field/show_component.html.erb +2 -2
- data/app/components/avo/fields/belongs_to_field/show_component.rb +8 -0
- data/app/components/avo/fields/boolean_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/boolean_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/boolean_group_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/code_field/edit_component.html.erb +2 -2
- data/app/components/avo/fields/code_field/show_component.html.erb +2 -2
- data/app/components/avo/fields/country_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/country_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/date_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/date_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/date_time_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/date_time_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/edit_component.rb +22 -4
- data/app/components/avo/fields/external_image_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/external_image_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/external_image_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/file_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/file_field/index_component.html.erb +2 -2
- data/app/components/avo/fields/file_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/files_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/gravatar_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/gravatar_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/has_one_field/show_component.html.erb +14 -1
- data/app/components/avo/fields/has_one_field/show_component.rb +21 -0
- data/app/components/avo/fields/id_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/id_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/id_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/index_component.rb +9 -4
- data/app/components/avo/fields/key_value_field/edit_component.html.erb +2 -2
- data/app/components/avo/fields/key_value_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/markdown_field/edit_component.html.erb +2 -2
- data/app/components/avo/fields/markdown_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/number_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/number_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/password_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/progress_bar_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/select_field/edit_component.html.erb +1 -2
- data/app/components/avo/fields/select_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/show_component.rb +36 -1
- data/app/components/avo/fields/status_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/status_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/tags_field/edit_component.html.erb +1 -5
- data/app/components/avo/fields/tags_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/text_field/edit_component.html.erb +2 -2
- data/app/components/avo/fields/text_field/index_component.html.erb +1 -1
- data/app/components/avo/fields/text_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/textarea_field/edit_component.html.erb +1 -1
- data/app/components/avo/fields/textarea_field/show_component.html.erb +1 -1
- data/app/components/avo/fields/trix_field/edit_component.html.erb +13 -13
- data/app/components/avo/fields/trix_field/edit_component.rb +19 -1
- data/app/components/avo/fields/trix_field/show_component.html.erb +1 -1
- data/app/components/avo/index/grid_item_component.html.erb +6 -6
- data/app/components/avo/index/grid_item_component.rb +15 -0
- data/app/components/avo/index/resource_controls_component.rb +3 -0
- data/app/components/avo/index/resource_grid_component.rb +1 -1
- data/app/components/avo/resource_component.rb +5 -1
- data/app/components/avo/sidebar/item_switcher_component.html.erb +3 -3
- data/app/components/avo/sidebar/link_component.html.erb +2 -2
- data/app/components/avo/sidebar/link_component.rb +3 -1
- data/app/components/avo/views/resource_edit_component.html.erb +47 -28
- data/app/components/avo/views/resource_edit_component.rb +26 -9
- data/app/components/avo/views/resource_show_component.rb +0 -8
- data/app/controllers/avo/actions_controller.rb +7 -5
- data/app/controllers/avo/application_controller.rb +22 -3
- data/app/controllers/avo/associations_controller.rb +2 -2
- data/app/controllers/avo/base_controller.rb +24 -11
- data/app/helpers/avo/application_helper.rb +31 -3
- data/app/helpers/avo/resources_helper.rb +4 -8
- data/app/helpers/avo/url_helpers.rb +8 -0
- data/app/javascript/js/controllers/action_controller.js +3 -1
- data/app/javascript/js/controllers/fields/date_field_controller.js +21 -1
- data/app/javascript/js/controllers/search_controller.js +122 -118
- data/app/views/avo/actions/show.html.erb +5 -1
- data/avo.gemspec +1 -1
- data/config/master.key +1 -0
- data/lib/avo/base_action.rb +25 -6
- data/lib/avo/base_resource.rb +6 -4
- data/lib/avo/base_resource_tool.rb +1 -1
- data/lib/avo/concerns/fetches_things.rb +2 -0
- data/lib/avo/configuration.rb +2 -0
- data/lib/avo/fields/base_field.rb +5 -1
- data/lib/avo/fields/belongs_to_field.rb +1 -1
- data/lib/avo/fields/has_and_belongs_to_many_field.rb +1 -1
- data/lib/avo/fields/has_base_field.rb +5 -1
- data/lib/avo/fields/has_many_field.rb +1 -1
- data/lib/avo/fields/select_field.rb +1 -0
- data/lib/avo/menu/base_item.rb +1 -0
- data/lib/avo/menu/builder.rb +4 -2
- data/lib/avo/menu/menu.rb +2 -0
- data/lib/avo/services/authorization_service.rb +24 -20
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/templates/field/components/edit_component.html.erb.tt +1 -1
- data/lib/generators/avo/templates/field/components/show_component.html.erb.tt +1 -1
- data/public/avo-assets/avo.base.css +26 -8
- data/public/avo-assets/avo.base.js +79 -79
- data/public/avo-assets/avo.base.js.map +3 -3
- metadata +8 -11
- data/app/components/avo/common_field_wrapper_component.html.erb +0 -26
- data/app/components/avo/common_field_wrapper_component.rb +0 -46
- data/app/components/avo/edit/field_wrapper_component.html.erb +0 -11
- data/app/components/avo/edit/field_wrapper_component.rb +0 -21
- data/app/components/avo/show/field_wrapper_component.html.erb +0 -9
- data/app/components/avo/show/field_wrapper_component.rb +0 -12
@@ -25,6 +25,8 @@ export default class extends Controller {
|
|
25
25
|
|
26
26
|
debouncedFetch = debouncePromise(fetch, this.searchDebounce);
|
27
27
|
|
28
|
+
destroyMethod;
|
29
|
+
|
28
30
|
get dataset() {
|
29
31
|
return this.autocompleteTarget.dataset
|
30
32
|
}
|
@@ -56,97 +58,52 @@ export default class extends Controller {
|
|
56
58
|
return this.dataset.searchResource === 'global'
|
57
59
|
}
|
58
60
|
|
59
|
-
|
60
|
-
const
|
61
|
-
|
62
|
-
return url.segment(this.searchSegments()).search(this.searchParams(query)).toString()
|
63
|
-
}
|
64
|
-
|
65
|
-
searchSegments() {
|
66
|
-
let segments = [
|
67
|
-
window.Avo.configuration.root_path,
|
68
|
-
'avo_api',
|
69
|
-
this.dataset.searchResource,
|
70
|
-
'search',
|
71
|
-
]
|
72
|
-
|
73
|
-
if (this.isGlobalSearch) {
|
74
|
-
segments = [window.Avo.configuration.root_path, 'avo_api', 'search']
|
75
|
-
}
|
76
|
-
|
77
|
-
return segments
|
78
|
-
}
|
79
|
-
|
80
|
-
searchParams(query) {
|
81
|
-
let params = {
|
82
|
-
q: query,
|
83
|
-
global: false,
|
84
|
-
}
|
85
|
-
|
86
|
-
if (this.isGlobalSearch) {
|
87
|
-
params.global = true
|
88
|
-
}
|
61
|
+
connect() {
|
62
|
+
const that = this
|
89
63
|
|
90
|
-
|
91
|
-
params = this.addAssociationParams(params)
|
92
|
-
params = this.addReflectionParams(params)
|
64
|
+
this.buttonTarget.onclick = () => this.showSearchPanel()
|
93
65
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
// eslint-disable-next-line camelcase
|
98
|
-
via_parent_resource_id: this.dataset.viaParentResourceId,
|
99
|
-
// eslint-disable-next-line camelcase
|
100
|
-
via_parent_resource_class: this.dataset.viaParentResourceClass,
|
101
|
-
// eslint-disable-next-line camelcase
|
102
|
-
via_relation: this.dataset.viaRelation,
|
103
|
-
}
|
66
|
+
this.clearValueTargets.forEach((target) => {
|
67
|
+
if (target.getAttribute('value') && this.hasClearButtonTarget) {
|
68
|
+
this.clearButtonTarget.classList.remove('hidden')
|
104
69
|
}
|
105
|
-
}
|
106
|
-
|
107
|
-
return params
|
108
|
-
}
|
70
|
+
})
|
109
71
|
|
110
|
-
|
111
|
-
|
112
|
-
...params,
|
113
|
-
// eslint-disable-next-line camelcase
|
114
|
-
via_association: this.dataset.viaAssociation,
|
115
|
-
// eslint-disable-next-line camelcase
|
116
|
-
via_association_id: this.dataset.viaAssociationId,
|
72
|
+
if (this.isGlobalSearch) {
|
73
|
+
Mousetrap.bind(['command+k', 'ctrl+k'], () => this.showSearchPanel())
|
117
74
|
}
|
118
75
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
76
|
+
autocomplete({
|
77
|
+
container: this.autocompleteTarget,
|
78
|
+
placeholder: this.translationKeys.placeholder,
|
79
|
+
translations: {
|
80
|
+
detachedCancelButtonText: this.translationKeys.cancel_button,
|
81
|
+
},
|
82
|
+
autoFocus: true,
|
83
|
+
openOnFocus: true,
|
84
|
+
detachedMediaQuery: '',
|
85
|
+
getSources: ({ query }) => {
|
86
|
+
document.body.classList.add('search-loading')
|
87
|
+
const endpoint = that.searchUrl(query)
|
130
88
|
|
131
|
-
|
132
|
-
|
89
|
+
return that
|
90
|
+
.debouncedFetch(endpoint)
|
91
|
+
.then((response) => {
|
92
|
+
document.body.classList.remove('search-loading')
|
133
93
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
94
|
+
return response.json()
|
95
|
+
})
|
96
|
+
.then((data) => Object.keys(data).map((resourceName) => that.addSource(resourceName, data[resourceName])))
|
97
|
+
},
|
98
|
+
})
|
138
99
|
|
139
|
-
|
100
|
+
// document.addEventListener('turbo:before-render', destroy)
|
101
|
+
// this.destroyMethod = destroy
|
140
102
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
} else {
|
145
|
-
Turbo.visit(item._url, { action: 'advance' })
|
103
|
+
// When using search for belongs-to
|
104
|
+
if (this.buttonTarget.dataset.shouldBeDisabled !== 'true') {
|
105
|
+
this.buttonTarget.removeAttribute('disabled')
|
146
106
|
}
|
147
|
-
|
148
|
-
// On searchable belongs to the class `aa-Detached` remains on the body making it unscrollable
|
149
|
-
document.body.classList.remove('aa-Detached')
|
150
107
|
}
|
151
108
|
|
152
109
|
addSource(resourceName, data) {
|
@@ -221,59 +178,106 @@ export default class extends Controller {
|
|
221
178
|
}
|
222
179
|
}
|
223
180
|
|
224
|
-
|
225
|
-
this.
|
181
|
+
handleOnSelect({ item }) {
|
182
|
+
if (this.isBelongsToSearch) {
|
183
|
+
this.updateFieldAttribute(this.hiddenIdTarget, 'value', item._id)
|
184
|
+
this.updateFieldAttribute(this.buttonTarget, 'value', item._label)
|
185
|
+
|
186
|
+
document.querySelector('.aa-DetachedOverlay').remove()
|
187
|
+
|
188
|
+
if (this.hasClearButtonTarget) {
|
189
|
+
this.clearButtonTarget.classList.remove('hidden')
|
190
|
+
}
|
191
|
+
} else {
|
192
|
+
Turbo.visit(item._url, { action: 'advance' })
|
193
|
+
}
|
194
|
+
|
195
|
+
// On searchable belongs to the class `aa-Detached` remains on the body making it unscrollable
|
196
|
+
document.body.classList.remove('aa-Detached')
|
226
197
|
}
|
227
198
|
|
228
|
-
|
229
|
-
|
230
|
-
|
199
|
+
searchUrl(query) {
|
200
|
+
const url = URI()
|
201
|
+
|
202
|
+
return url.segment(this.searchSegments()).search(this.searchParams(query)).toString()
|
231
203
|
}
|
232
204
|
|
233
|
-
|
234
|
-
|
205
|
+
searchSegments() {
|
206
|
+
let segments = [
|
207
|
+
window.Avo.configuration.root_path,
|
208
|
+
'avo_api',
|
209
|
+
this.dataset.searchResource,
|
210
|
+
'search',
|
211
|
+
]
|
235
212
|
|
236
|
-
|
213
|
+
if (this.isGlobalSearch) {
|
214
|
+
segments = [window.Avo.configuration.root_path, 'avo_api', 'search']
|
215
|
+
}
|
237
216
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
217
|
+
return segments
|
218
|
+
}
|
219
|
+
|
220
|
+
searchParams(query) {
|
221
|
+
let params = {
|
222
|
+
q: query,
|
223
|
+
global: false,
|
224
|
+
}
|
243
225
|
|
244
226
|
if (this.isGlobalSearch) {
|
245
|
-
|
227
|
+
params.global = true
|
246
228
|
}
|
247
229
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
translations: {
|
252
|
-
detachedCancelButtonText: this.translationKeys.cancel_button,
|
253
|
-
},
|
254
|
-
openOnFocus: true,
|
255
|
-
detachedMediaQuery: '',
|
256
|
-
getSources: ({ query }) => {
|
257
|
-
document.body.classList.add('search-loading')
|
258
|
-
const endpoint = that.searchUrl(query)
|
230
|
+
if (this.isBelongsToSearch || this.isHasManySearch) {
|
231
|
+
params = this.addAssociationParams(params)
|
232
|
+
params = this.addReflectionParams(params)
|
259
233
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
234
|
+
if (this.isBelongsToSearch) {
|
235
|
+
params = {
|
236
|
+
...params,
|
237
|
+
// eslint-disable-next-line camelcase
|
238
|
+
via_parent_resource_id: this.dataset.viaParentResourceId,
|
239
|
+
// eslint-disable-next-line camelcase
|
240
|
+
via_parent_resource_class: this.dataset.viaParentResourceClass,
|
241
|
+
// eslint-disable-next-line camelcase
|
242
|
+
via_relation: this.dataset.viaRelation,
|
243
|
+
}
|
244
|
+
}
|
245
|
+
}
|
264
246
|
|
265
|
-
|
266
|
-
|
267
|
-
.then((data) => Object.keys(data).map((resourceName) => that.addSource(resourceName, data[resourceName])))
|
268
|
-
},
|
269
|
-
})
|
247
|
+
return params
|
248
|
+
}
|
270
249
|
|
271
|
-
|
250
|
+
addAssociationParams(params) {
|
251
|
+
params = {
|
252
|
+
...params,
|
253
|
+
// eslint-disable-next-line camelcase
|
254
|
+
via_association: this.dataset.viaAssociation,
|
255
|
+
// eslint-disable-next-line camelcase
|
256
|
+
via_association_id: this.dataset.viaAssociationId,
|
257
|
+
}
|
272
258
|
|
273
|
-
|
274
|
-
|
275
|
-
|
259
|
+
return params
|
260
|
+
}
|
261
|
+
|
262
|
+
addReflectionParams(params) {
|
263
|
+
params = {
|
264
|
+
...params,
|
265
|
+
// eslint-disable-next-line camelcase
|
266
|
+
via_reflection_class: this.dataset.viaReflectionClass,
|
267
|
+
// eslint-disable-next-line camelcase
|
268
|
+
via_reflection_id: this.dataset.viaReflectionId,
|
276
269
|
}
|
270
|
+
|
271
|
+
return params
|
272
|
+
}
|
273
|
+
|
274
|
+
showSearchPanel() {
|
275
|
+
this.autocompleteTarget.querySelector('button').click()
|
276
|
+
}
|
277
|
+
|
278
|
+
clearValue() {
|
279
|
+
this.clearValueTargets.map((target) => this.updateFieldAttribute(target, 'value', ''))
|
280
|
+
this.clearButtonTarget.classList.add('hidden')
|
277
281
|
}
|
278
282
|
|
279
283
|
// Private
|
@@ -25,7 +25,11 @@
|
|
25
25
|
<% if @action.get_fields.present? %>
|
26
26
|
<div class="mt-4">
|
27
27
|
<% @action.get_fields.each_with_index do |field, index| %>
|
28
|
-
<%= render field
|
28
|
+
<%= render field
|
29
|
+
.hydrate(resource: @resource, model: @resource.model, user: @resource.user, view: @view)
|
30
|
+
.component_for_view(:edit)
|
31
|
+
.new(field: field, resource: @resource, index: index, form: form, displayed_in_modal: true)
|
32
|
+
%>
|
29
33
|
<% end %>
|
30
34
|
</div>
|
31
35
|
<% end %>
|
data/avo.gemspec
CHANGED
@@ -41,7 +41,7 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_dependency "active_link_to"
|
42
42
|
spec.add_dependency "image_processing"
|
43
43
|
spec.add_dependency "view_component", "2.60"
|
44
|
-
spec.add_dependency "
|
44
|
+
spec.add_dependency "turbo-rails"
|
45
45
|
spec.add_dependency "addressable"
|
46
46
|
spec.add_dependency "meta-tags"
|
47
47
|
spec.add_dependency "breadcrumbs_on_rails"
|
data/config/master.key
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2aeb23d82b909d9c6b5abb62f7058c2a
|
data/lib/avo/base_action.rb
CHANGED
@@ -73,26 +73,36 @@ module Avo
|
|
73
73
|
def get_attributes_for_action
|
74
74
|
get_fields.map do |field|
|
75
75
|
[field.id, field.value]
|
76
|
-
end
|
77
|
-
.to_h
|
76
|
+
end.to_h
|
78
77
|
end
|
79
78
|
|
80
79
|
def handle_action(**args)
|
81
80
|
models, fields, current_user, resource = args.values_at(:models, :fields, :current_user, :resource)
|
82
|
-
|
81
|
+
# Fetching the field definitions and not the actual fields (get_fields) because they will break if the user uses a `visible` block and adds a condition using the `params` variable. The params are different in the show method and the handle method.
|
82
|
+
action_fields = get_field_definitions.map { |field| [field.id, field] }.to_h
|
83
|
+
|
84
|
+
# For some fields, like belongs_to, the id and database_id differ (user vs user_id).
|
85
|
+
# That's why we need to fetch the database_id for when we process the action.
|
86
|
+
action_fields_by_database_id = action_fields.map do |id, value|
|
87
|
+
[value.database_id.to_sym, value]
|
88
|
+
end.to_h
|
83
89
|
|
84
90
|
if fields.present?
|
85
91
|
processed_fields = fields.to_unsafe_h.map do |name, value|
|
86
|
-
|
92
|
+
field = action_fields_by_database_id[name.to_sym]
|
93
|
+
|
94
|
+
next if field.blank?
|
95
|
+
|
96
|
+
[name, field.resolve_attribute(value)]
|
87
97
|
end
|
88
98
|
|
89
|
-
processed_fields = processed_fields.to_h
|
99
|
+
processed_fields = processed_fields.reject(&:blank?).to_h
|
90
100
|
else
|
91
101
|
processed_fields = {}
|
92
102
|
end
|
93
103
|
|
94
104
|
args = {
|
95
|
-
fields: processed_fields,
|
105
|
+
fields: processed_fields.with_indifferent_access,
|
96
106
|
current_user: current_user,
|
97
107
|
resource: resource
|
98
108
|
}
|
@@ -175,6 +185,15 @@ module Avo
|
|
175
185
|
self
|
176
186
|
end
|
177
187
|
|
188
|
+
# We're overriding this method to hydrate with the proper resource attribute.
|
189
|
+
def hydrate_fields(model: nil, view: nil)
|
190
|
+
fields.map do |field|
|
191
|
+
field.hydrate(model: @model, view: @view, resource: resource)
|
192
|
+
end
|
193
|
+
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
178
197
|
private
|
179
198
|
|
180
199
|
def add_message(body, type = :info)
|
data/lib/avo/base_resource.rb
CHANGED
@@ -30,6 +30,7 @@ module Avo
|
|
30
30
|
class_attribute :search_query, default: nil
|
31
31
|
class_attribute :search_query_help, default: ""
|
32
32
|
class_attribute :includes, default: []
|
33
|
+
class_attribute :authorization_policy
|
33
34
|
class_attribute :translation_key
|
34
35
|
class_attribute :default_view_type, default: :table
|
35
36
|
class_attribute :devise_password_optional, default: false
|
@@ -92,7 +93,7 @@ module Avo
|
|
92
93
|
end
|
93
94
|
|
94
95
|
def authorization
|
95
|
-
Avo::Services::AuthorizationService.new Avo::App.current_user
|
96
|
+
Avo::Services::AuthorizationService.new Avo::App.current_user, model_class, policy_class: authorization_policy
|
96
97
|
end
|
97
98
|
|
98
99
|
def order_actions
|
@@ -263,7 +264,7 @@ module Avo
|
|
263
264
|
field.computed
|
264
265
|
end
|
265
266
|
.map do |field|
|
266
|
-
[field.database_id
|
267
|
+
[field.database_id.to_s, field]
|
267
268
|
end
|
268
269
|
.to_h
|
269
270
|
|
@@ -285,8 +286,9 @@ module Avo
|
|
285
286
|
model
|
286
287
|
end
|
287
288
|
|
288
|
-
def authorization
|
289
|
-
|
289
|
+
def authorization(user: nil)
|
290
|
+
current_user = user || Avo::App.current_user
|
291
|
+
Avo::Services::AuthorizationService.new(current_user, model || model_class, policy_class: authorization_policy)
|
290
292
|
end
|
291
293
|
|
292
294
|
def file_hash
|
@@ -48,9 +48,11 @@ module Avo
|
|
48
48
|
end
|
49
49
|
|
50
50
|
# Returns the Avo resource by singular snake_cased name
|
51
|
+
# From all the resources that use the same model_class, it will fetch the first one in alphabetical order
|
51
52
|
#
|
52
53
|
# get_resource_by_name('User') => UserResource
|
53
54
|
# get_resource_by_name(User) => UserResource
|
55
|
+
|
54
56
|
def get_resource_by_model_name(klass)
|
55
57
|
# Fetch the mappings imposed by the user.
|
56
58
|
# If they are present, use those ones.
|
data/lib/avo/configuration.rb
CHANGED
@@ -36,6 +36,7 @@ module Avo
|
|
36
36
|
attr_accessor :profile_menu
|
37
37
|
attr_accessor :model_resource_mapping
|
38
38
|
attr_accessor :tabs_style
|
39
|
+
attr_accessor :resource_default_view
|
39
40
|
attr_writer :branding
|
40
41
|
|
41
42
|
def initialize
|
@@ -83,6 +84,7 @@ module Avo
|
|
83
84
|
@profile_menu = nil
|
84
85
|
@model_resource_mapping = {}
|
85
86
|
@tabs_style = :tabs
|
87
|
+
@resource_default_view = :show
|
86
88
|
end
|
87
89
|
|
88
90
|
def current_user_method(&block)
|
@@ -76,6 +76,8 @@ module Avo
|
|
76
76
|
@as_description = args[:as_description] || false
|
77
77
|
@index_text_align = args[:index_text_align] || :left
|
78
78
|
@html = args[:html] || nil
|
79
|
+
@view = args[:view] || nil
|
80
|
+
@value = args[:value] || nil
|
79
81
|
|
80
82
|
@args = args
|
81
83
|
|
@@ -155,6 +157,8 @@ module Avo
|
|
155
157
|
end
|
156
158
|
|
157
159
|
def value(property = nil)
|
160
|
+
return @value if @value.present?
|
161
|
+
|
158
162
|
property ||= id
|
159
163
|
|
160
164
|
# Get model value
|
@@ -191,7 +195,7 @@ module Avo
|
|
191
195
|
end
|
192
196
|
|
193
197
|
# Try to see if the field has a different database ID than it's name
|
194
|
-
def database_id
|
198
|
+
def database_id
|
195
199
|
foreign_key
|
196
200
|
rescue
|
197
201
|
id
|
@@ -210,7 +210,7 @@ module Avo
|
|
210
210
|
model
|
211
211
|
end
|
212
212
|
|
213
|
-
def database_id
|
213
|
+
def database_id
|
214
214
|
# If the field is a polymorphic value, return the polymorphic_type as key and pre-fill the _id in fill_field.
|
215
215
|
return "#{polymorphic_as}_type" if polymorphic_as.present?
|
216
216
|
|
@@ -30,7 +30,7 @@ module Avo
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def resource
|
33
|
-
Avo::App.get_resource_by_model_name
|
33
|
+
@resource || Avo::App.get_resource_by_model_name(@model.class)
|
34
34
|
end
|
35
35
|
|
36
36
|
def turbo_frame
|
@@ -108,6 +108,10 @@ module Avo
|
|
108
108
|
def frame_id
|
109
109
|
use_resource.present? ? use_resource.route_key.to_sym : @id
|
110
110
|
end
|
111
|
+
|
112
|
+
def default_view
|
113
|
+
Avo.configuration.skip_show_view ? :edit : :show
|
114
|
+
end
|
111
115
|
end
|
112
116
|
end
|
113
117
|
end
|
data/lib/avo/menu/base_item.rb
CHANGED
data/lib/avo/menu/builder.rb
CHANGED
@@ -22,9 +22,11 @@ class Avo::Menu::Builder
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Adds a link
|
25
|
-
def link(name, **args)
|
26
|
-
|
25
|
+
def link(name, path = nil, **args)
|
26
|
+
path ||= args[:path]
|
27
|
+
@menu.items << Avo::Menu::Link.new(name: name, path: path, **args)
|
27
28
|
end
|
29
|
+
alias_method :link_to, :link
|
28
30
|
|
29
31
|
# Validates and adds a resource
|
30
32
|
def resource(name, **args)
|
data/lib/avo/menu/menu.rb
CHANGED
@@ -5,13 +5,14 @@ module Avo
|
|
5
5
|
attr_accessor :record
|
6
6
|
|
7
7
|
class << self
|
8
|
-
def authorize(user, record, action, **args)
|
8
|
+
def authorize(user, record, action, policy_class: nil, **args)
|
9
9
|
return true if skip_authorization
|
10
10
|
return true if user.nil?
|
11
11
|
|
12
|
+
policy_class ||= Pundit.policy(user, record)&.class
|
12
13
|
begin
|
13
|
-
if
|
14
|
-
Pundit.authorize user, record, action
|
14
|
+
if policy_class&.new(user, record)
|
15
|
+
Pundit.authorize user, record, action, policy_class: policy_class
|
15
16
|
end
|
16
17
|
|
17
18
|
true
|
@@ -28,7 +29,7 @@ module Avo
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
def authorize_action(user, record, action, **args)
|
32
|
+
def authorize_action(user, record, action, policy_class: nil, **args)
|
32
33
|
action = Avo.configuration.authorization_methods.stringify_keys[action.to_s] || action
|
33
34
|
|
34
35
|
# If no action passed we should raise error if the user wants that.
|
@@ -41,16 +42,18 @@ module Avo
|
|
41
42
|
|
42
43
|
# Add the question mark if it's missing
|
43
44
|
action = "#{action}?" unless action.end_with? "?"
|
44
|
-
|
45
|
-
authorize user, record, action, **args
|
45
|
+
authorize(user, record, action, policy_class: policy_class, **args)
|
46
46
|
end
|
47
47
|
|
48
|
-
def apply_policy(user, model)
|
49
|
-
return model if skip_authorization
|
50
|
-
return model if user.nil?
|
48
|
+
def apply_policy(user, model, policy_class: nil)
|
49
|
+
return model if skip_authorization || user.nil?
|
51
50
|
|
52
51
|
begin
|
53
|
-
|
52
|
+
if policy_class
|
53
|
+
policy_class::Scope.new(user, model).resolve
|
54
|
+
else
|
55
|
+
Pundit.policy_scope! user, model
|
56
|
+
end
|
54
57
|
rescue Pundit::NotDefinedError => e
|
55
58
|
return model unless Avo.configuration.raise_error_on_missing_policy
|
56
59
|
|
@@ -68,12 +71,12 @@ module Avo
|
|
68
71
|
end.to_h
|
69
72
|
end
|
70
73
|
|
71
|
-
def
|
72
|
-
Pundit.policy
|
73
|
-
end
|
74
|
+
def defined_methods(user, record, policy_class: nil, **args)
|
75
|
+
return Pundit.policy!(user, record).methods if policy_class.nil?
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
+
# I'm aware this will not raise a Pundit error.
|
78
|
+
# Should the policy not exist, it will however raise an uninitialized constant error, which is probably what we want when specifying a custom policy
|
79
|
+
policy_class.new(user, record).methods
|
77
80
|
rescue Pundit::NotDefinedError => e
|
78
81
|
return [] unless Avo.configuration.raise_error_on_missing_policy
|
79
82
|
|
@@ -87,13 +90,14 @@ module Avo
|
|
87
90
|
end
|
88
91
|
end
|
89
92
|
|
90
|
-
def initialize(user = nil, record = nil)
|
93
|
+
def initialize(user = nil, record = nil, policy_class: nil)
|
91
94
|
@user = user
|
92
95
|
@record = record
|
96
|
+
@policy_class = policy_class || Pundit.policy(user, record)&.class
|
93
97
|
end
|
94
98
|
|
95
99
|
def authorize(action, **args)
|
96
|
-
self.class.authorize(user, record, action, **args)
|
100
|
+
self.class.authorize(user, record, action, policy_class: @policy_class, **args)
|
97
101
|
end
|
98
102
|
|
99
103
|
def set_record(record)
|
@@ -109,15 +113,15 @@ module Avo
|
|
109
113
|
end
|
110
114
|
|
111
115
|
def authorize_action(action, **args)
|
112
|
-
self.class.authorize_action(user, record, action, **args)
|
116
|
+
self.class.authorize_action(user, record, action, policy_class: @policy_class, **args)
|
113
117
|
end
|
114
118
|
|
115
119
|
def apply_policy(model)
|
116
|
-
self.class.apply_policy(user, model)
|
120
|
+
self.class.apply_policy(user, model, policy_class: @policy_class)
|
117
121
|
end
|
118
122
|
|
119
123
|
def defined_methods(model, **args)
|
120
|
-
self.class.defined_methods(user, model, **args)
|
124
|
+
self.class.defined_methods(user, model, policy_class: @policy_class, **args)
|
121
125
|
end
|
122
126
|
|
123
127
|
def has_method?(method, **args)
|