avo 1.19.1.pre.9 → 1.21.0.pre.1
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 +1 -0
- data/Gemfile.lock +4 -1
- data/app/components/avo/fields/common/files_list_viewer_component.html.erb +1 -1
- data/app/components/avo/fields/common/multiple_file_viewer_component.html.erb +2 -0
- data/app/components/avo/fields/common/multiple_file_viewer_component.rb +2 -1
- data/app/components/avo/fields/common/single_file_viewer_component.html.erb +2 -0
- data/app/components/avo/fields/common/single_file_viewer_component.rb +2 -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 +3 -1
- data/app/components/avo/fields/file_field/show_component.html.erb +1 -1
- data/app/components/avo/index/resource_controls_component.html.erb +4 -0
- data/app/components/avo/index/resource_controls_component.rb +10 -2
- data/app/components/avo/panel_component.html.erb +4 -1
- data/app/components/avo/panel_component.rb +8 -1
- data/app/components/avo/resource_component.rb +27 -4
- data/app/components/avo/views/resource_edit_component.html.erb +6 -2
- data/app/components/avo/views/resource_index_component.html.erb +6 -4
- data/app/components/avo/views/resource_index_component.rb +20 -2
- data/app/components/avo/views/resource_new_component.html.erb +9 -2
- data/app/components/avo/views/resource_show_component.html.erb +1 -1
- data/app/components/avo/views/resource_show_component.rb +12 -1
- data/app/controllers/avo/base_controller.rb +1 -1
- data/app/controllers/avo/relations_controller.rb +5 -0
- data/config/routes.rb +1 -1
- data/db/factories.rb +8 -0
- data/lib/avo/app.rb +0 -18
- data/lib/avo/base_resource.rb +18 -1
- data/lib/avo/dynamic_router.rb +18 -0
- data/lib/avo/fields/base_field.rb +6 -0
- data/lib/avo/fields/file_field.rb +2 -0
- data/lib/avo/fields/files_field.rb +1 -0
- data/lib/avo/fields/has_base_field.rb +2 -0
- data/lib/avo/services/authorization_service.rb +8 -1
- data/lib/avo/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2009c14b639e720209ef1c2d56c84ac7936cfa96cb71dd4306b02695cae3ade5
|
4
|
+
data.tar.gz: 4e99a479bc58b1da03e2e13f63e1921b1e652724b2d5b8d394964dad6a43a8ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91a4af05f9cc03bdf76751a140697a0ef6d5782f5a049e39023d7dffba3074c16d01204f36608c2f8d776b6f3f72d885bd91f49b6e38ab14ac7b951df13667c9
|
7
|
+
data.tar.gz: 18d41c5e64b507ab27025b4af7fe7b99987d45897b6e717022fcebe8e5debd32dab26aa96a24611a8534ffcaa0f6744932d277d37421e1865722b0f728234516
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (1.
|
4
|
+
avo (1.21.0.pre.1)
|
5
5
|
active_link_to
|
6
6
|
addressable
|
7
7
|
breadcrumbs_on_rails
|
@@ -83,6 +83,8 @@ GEM
|
|
83
83
|
zeitwerk (~> 2.3)
|
84
84
|
addressable (2.8.0)
|
85
85
|
public_suffix (>= 2.0.2, < 5.0)
|
86
|
+
ap (0.1.1)
|
87
|
+
httparty (>= 0.7.7)
|
86
88
|
appraisal (2.4.1)
|
87
89
|
bundler
|
88
90
|
rake
|
@@ -397,6 +399,7 @@ PLATFORMS
|
|
397
399
|
DEPENDENCIES
|
398
400
|
active_link_to
|
399
401
|
addressable
|
402
|
+
ap
|
400
403
|
appraisal
|
401
404
|
avo!
|
402
405
|
aws-sdk-s3
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="relative p-3 bg-slate-200 grid grid-cols-3 xl:grid-cols-4 gap-3 rounded-xl">
|
2
2
|
<% @field.value.attachments.each do |file| %>
|
3
|
-
<%= render Avo::Fields::Common::MultipleFileViewerComponent.new id: @field.id, file: file, is_image: @field.is_image, button_size: :xs, resource: @resource %>
|
3
|
+
<%= render Avo::Fields::Common::MultipleFileViewerComponent.new id: @field.id, file: file, is_image: @field.is_image, is_audio: @field.is_audio, button_size: :xs, resource: @resource %>
|
4
4
|
<% end %>
|
5
5
|
</div>
|
@@ -2,6 +2,8 @@
|
|
2
2
|
<% if @file.present? %>
|
3
3
|
<% if @file.representable? && @is_image %>
|
4
4
|
<%= image_tag helpers.main_app.url_for(@file), class: 'rounded-lg max-h-168 max-w-full' %>
|
5
|
+
<% elsif @is_audio %>
|
6
|
+
<%= audio_tag(helpers.main_app.url_for(@file), controls: true, preload: false, class: 'w-full')%>
|
5
7
|
<% else %>
|
6
8
|
<div class="relative flex flex-col justify-evenly items-center px-2 rounded-lg border bg-white border-gray-500 py-6 flex-1">
|
7
9
|
<div class="flex flex-col justify-center items-center w-full">
|
@@ -3,10 +3,11 @@
|
|
3
3
|
class Avo::Fields::Common::MultipleFileViewerComponent < ViewComponent::Base
|
4
4
|
include Avo::ApplicationHelper
|
5
5
|
|
6
|
-
def initialize(id:, file:, is_image:, direct_upload: false, resource:, button_size: :md)
|
6
|
+
def initialize(id:, file:, is_image:, is_audio:, direct_upload: false, resource:, button_size: :md)
|
7
7
|
@id = id
|
8
8
|
@file = file
|
9
9
|
@is_image = is_image
|
10
|
+
@is_audio = is_audio
|
10
11
|
@direct_upload = direct_upload
|
11
12
|
@button_size = button_size
|
12
13
|
@resource = resource
|
@@ -2,6 +2,8 @@
|
|
2
2
|
<% if @file.present? %>
|
3
3
|
<% if @file.representable? && @is_image %>
|
4
4
|
<%= image_tag helpers.main_app.url_for(@file), class: 'rounded-lg max-h-168 max-w-full' %>
|
5
|
+
<% elsif @is_audio %>
|
6
|
+
<%= audio_tag(helpers.main_app.url_for(@file), controls: true, preload: false, class: 'w-full')%>
|
5
7
|
<% else %>
|
6
8
|
<div class="relative flex flex-col justify-evenly items-center px-2 rounded-lg border bg-white border-gray-500 min-h-48">
|
7
9
|
<div class="flex flex-col justify-center items-center w-full">
|
@@ -3,10 +3,11 @@
|
|
3
3
|
class Avo::Fields::Common::SingleFileViewerComponent < ViewComponent::Base
|
4
4
|
include Avo::ApplicationHelper
|
5
5
|
|
6
|
-
def initialize(id:, file:, is_image:, direct_upload: false, resource:, button_size: :md)
|
6
|
+
def initialize(id:, file:, is_image:, is_audio:, direct_upload: false, resource:, button_size: :md)
|
7
7
|
@id = id
|
8
8
|
@file = file
|
9
9
|
@is_image = is_image
|
10
|
+
@is_audio = is_audio
|
10
11
|
@direct_upload = direct_upload
|
11
12
|
@button_size = button_size
|
12
13
|
@resource = resource
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
2
2
|
<% if @field.value.present? %>
|
3
3
|
<div class="mb-2">
|
4
|
-
<%= render Avo::Fields::Common::SingleFileViewerComponent.new resource: @resource, id: @field.id, file: @field.value, is_image: @field.is_image, button_size: :md %>
|
4
|
+
<%= render Avo::Fields::Common::SingleFileViewerComponent.new resource: @resource, id: @field.id, file: @field.value, is_image: @field.is_image, is_audio: @field.is_audio, button_size: :md %>
|
5
5
|
</div>
|
6
6
|
<% end %>
|
7
7
|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
<%= index_field_wrapper field: @field do %>
|
2
2
|
<% if @field.value.present? %>
|
3
3
|
<% if @field.value.attached? && @field.value.representable? && @field.is_image %>
|
4
|
-
<%= link_to_if @field.link_to_resource, image_tag(helpers.main_app.url_for(@field.value), class: 'max-h-full'), resource_path, class: 'block
|
4
|
+
<%= link_to_if @field.link_to_resource, image_tag(helpers.main_app.url_for(@field.value), class: 'max-h-full'), resource_path, class: 'block' %>
|
5
|
+
<% elsif @field.value.attached? && @field.is_audio %>
|
6
|
+
<%= link_to_if @field.link_to_resource, audio_tag(helpers.main_app.url_for(@field.value), controls: true, preload: false, class: 'max-h-full'), resource_path, class: 'block h-8' %>
|
5
7
|
<% else %>
|
6
8
|
<%= @field.value.filename %>
|
7
9
|
<% end %>
|
@@ -1,3 +1,3 @@
|
|
1
1
|
<%= show_field_wrapper field: @field do %>
|
2
|
-
<%= render Avo::Fields::Common::SingleFileViewerComponent.new resource: @resource, id: @field.id, file: @field.value.attachment, is_image: @field.is_image, button_size: :md %>
|
2
|
+
<%= render Avo::Fields::Common::SingleFileViewerComponent.new resource: @resource, id: @field.id, file: @field.value.attachment, is_image: @field.is_image, is_audio: @field.is_audio, button_size: :md %>
|
3
3
|
<% end %>
|
@@ -4,6 +4,7 @@
|
|
4
4
|
show_path,
|
5
5
|
title: t('avo.view_item', item: singular_resource_name).capitalize,
|
6
6
|
data: {
|
7
|
+
target: 'control:view',
|
7
8
|
control: :show,
|
8
9
|
'tippy': 'tooltip',
|
9
10
|
}
|
@@ -15,6 +16,7 @@
|
|
15
16
|
edit_path,
|
16
17
|
title: t('avo.edit_item', item: singular_resource_name).capitalize,
|
17
18
|
data: {
|
19
|
+
target: 'control:edit',
|
18
20
|
control: :edit,
|
19
21
|
'resource-id': @resource.model.id,
|
20
22
|
'tippy': 'tooltip',
|
@@ -30,6 +32,7 @@
|
|
30
32
|
title: t('avo.detach_item', item: singular_resource_name).capitalize,
|
31
33
|
type: :submit,
|
32
34
|
data: {
|
35
|
+
target: 'control:detach',
|
33
36
|
confirm: t('avo.are_you_sure_detach_item', item: singular_resource_name),
|
34
37
|
control: :detach,
|
35
38
|
'resource-id': @resource.model.id,
|
@@ -49,6 +52,7 @@
|
|
49
52
|
title: t('avo.delete_item', item: singular_resource_name).capitalize,
|
50
53
|
type: :submit,
|
51
54
|
data: {
|
55
|
+
target: 'control:destroy',
|
52
56
|
confirm: t('avo.are_you_sure', item: singular_resource_name),
|
53
57
|
control: :destroy,
|
54
58
|
'resource-id': @resource.model.id,
|
@@ -10,15 +10,19 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
|
|
10
10
|
def can_detach?
|
11
11
|
@reflection.present? &&
|
12
12
|
@resource.model.present? &&
|
13
|
-
|
13
|
+
is_has_many_association &&
|
14
14
|
authorize_association_for("detach")
|
15
15
|
end
|
16
16
|
|
17
17
|
def can_edit?
|
18
|
+
return authorize_association_for(:edit) if @reflection.present?
|
19
|
+
|
18
20
|
@resource.authorization.authorize_action(:edit, raise_exception: false)
|
19
21
|
end
|
20
22
|
|
21
23
|
def can_view?
|
24
|
+
return authorize_association_for(:view) if @reflection.present?
|
25
|
+
|
22
26
|
@resource.authorization.authorize_action(:show, raise_exception: false)
|
23
27
|
end
|
24
28
|
|
@@ -50,7 +54,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
|
|
50
54
|
|
51
55
|
def singular_resource_name
|
52
56
|
if @reflection.present?
|
53
|
-
|
57
|
+
reflection_resource.name
|
54
58
|
else
|
55
59
|
@resource.singular_name.present? ? @resource.singular_name : @resource.model_class.model_name.name.downcase
|
56
60
|
end
|
@@ -61,4 +65,8 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
|
|
61
65
|
|
62
66
|
::Avo::App.get_resource_by_model_name @parent_model.class
|
63
67
|
end
|
68
|
+
|
69
|
+
def is_has_many_association
|
70
|
+
@reflection.is_a?(::ActiveRecord::Reflection::HasManyReflection) || @reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection)
|
71
|
+
end
|
64
72
|
end
|
@@ -6,9 +6,12 @@
|
|
6
6
|
<%= helpers.render_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
|
7
7
|
</div>
|
8
8
|
<% end %>
|
9
|
-
<div class="text-2xl tracking-normal font-bold text-gray-800 truncate">
|
9
|
+
<div class="text-2xl tracking-normal font-bold text-gray-800 truncate" data-target="title">
|
10
10
|
<%= @title %>
|
11
11
|
</div>
|
12
|
+
<div class="text-sm tracking-normal text-gray-600" data-target="description">
|
13
|
+
<%== description %>
|
14
|
+
</div>
|
12
15
|
</div>
|
13
16
|
|
14
17
|
<div>
|
@@ -6,8 +6,9 @@ class Avo::PanelComponent < ViewComponent::Base
|
|
6
6
|
renders_one :bare_content
|
7
7
|
renders_one :footer
|
8
8
|
|
9
|
-
def initialize(title: nil, body_classes: nil, data: {}, display_breadcrumbs: false, index: nil)
|
9
|
+
def initialize(title: nil, description: nil, body_classes: nil, data: {}, display_breadcrumbs: false, index: nil)
|
10
10
|
@title = title
|
11
|
+
@description = description
|
11
12
|
@body_classes = body_classes
|
12
13
|
@data = data
|
13
14
|
@display_breadcrumbs = display_breadcrumbs
|
@@ -25,4 +26,10 @@ class Avo::PanelComponent < ViewComponent::Base
|
|
25
26
|
def display_breadcrumbs?
|
26
27
|
@display_breadcrumbs
|
27
28
|
end
|
29
|
+
|
30
|
+
def description
|
31
|
+
return @description if @description.present?
|
32
|
+
|
33
|
+
' '
|
34
|
+
end
|
28
35
|
end
|
@@ -1,20 +1,28 @@
|
|
1
1
|
class Avo::ResourceComponent < ViewComponent::Base
|
2
2
|
def can_create?
|
3
|
+
return authorize_association_for(:create) if @reflection.present?
|
4
|
+
|
3
5
|
@resource.authorization.authorize_action(:create, raise_exception: false)
|
4
6
|
end
|
5
7
|
|
6
8
|
def can_delete?
|
9
|
+
return authorize_association_for(:destroy) if @reflection.present?
|
10
|
+
|
7
11
|
@resource.authorization.authorize_action(:destroy, raise_exception: false)
|
8
12
|
end
|
9
13
|
|
10
14
|
def authorize_association_for(policy_method)
|
11
15
|
association_policy = true
|
16
|
+
|
12
17
|
if @reflection.present?
|
13
18
|
reflection_resource = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name)
|
14
|
-
if
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
reflection_resource.hydrate(model: @parent_model) if @parent_model.present?
|
20
|
+
association_name = params['related_name']
|
21
|
+
|
22
|
+
if association_name.present?
|
23
|
+
method_name = "#{policy_method}_#{association_name}?".to_sym
|
24
|
+
|
25
|
+
if reflection_resource.authorization.has_method?(method_name, raise_exception: false)
|
18
26
|
association_policy = reflection_resource.authorization.authorize_action(method_name, raise_exception: false)
|
19
27
|
end
|
20
28
|
end
|
@@ -25,7 +33,22 @@ class Avo::ResourceComponent < ViewComponent::Base
|
|
25
33
|
|
26
34
|
private
|
27
35
|
|
36
|
+
# Figure out what is the corresponding field for this @reflection
|
37
|
+
def field
|
38
|
+
fields = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name).get_field_definitions
|
39
|
+
fields.find { |f| f.id == @reflection.name }
|
40
|
+
rescue
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
28
44
|
def relation_resource
|
29
45
|
::Avo::App.get_resource_by_model_name params[:via_resource_class].safe_constantize
|
30
46
|
end
|
47
|
+
|
48
|
+
# Get the resource for the resource using the klass attribute so we get the namespace too
|
49
|
+
def reflection_resource
|
50
|
+
::Avo::App.get_resource_by_model_name(@reflection.klass.to_s)
|
51
|
+
rescue
|
52
|
+
nil
|
53
|
+
end
|
31
54
|
end
|
@@ -1,9 +1,13 @@
|
|
1
1
|
<div data-model-id="<%= @resource.model.id %>">
|
2
2
|
<% @resource.panels.each do |resource_panel| %>
|
3
|
-
<%= form_with model: @resource.model,
|
3
|
+
<%= form_with model: @resource.model,
|
4
|
+
scope: @resource.form_scope,
|
5
|
+
url: helpers.resource_path(model: @resource.model, resource: @resource),
|
6
|
+
method: :put,
|
7
|
+
multipart: true do |form| %>
|
4
8
|
<%= hidden_field_tag :referrer, back_path if params[:via_resource_class] %>
|
5
9
|
|
6
|
-
<%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: true) do |c| %>
|
10
|
+
<%= render Avo::PanelComponent.new(title: resource_panel[:name], description: @resource.resource_description, display_breadcrumbs: true) do |c| %>
|
7
11
|
<% c.tools do %>
|
8
12
|
<div class="flex justify-end space-x-2">
|
9
13
|
<%= a_link back_path do %>
|
@@ -1,16 +1,18 @@
|
|
1
1
|
<div>
|
2
|
-
<%= render Avo::PanelComponent.new(title: title, body_classes: 'py-4', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
|
2
|
+
<%= render Avo::PanelComponent.new(title: title, description: description, body_classes: 'py-4', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
|
3
3
|
<% c.tools do %>
|
4
|
-
|
4
|
+
<% if can_see_the_actions_button? %>
|
5
|
+
<%= render 'actions' %>
|
6
|
+
<% end %>
|
5
7
|
|
6
8
|
<% if can_see_the_create_button? %>
|
7
|
-
<%= a_link create_path do %>
|
9
|
+
<%= a_link create_path, 'data-target': 'create' do %>
|
8
10
|
<%= svg 'plus' %> <%= t('avo.create_new_item', item: singular_resource_name.downcase ) %>
|
9
11
|
<% end %>
|
10
12
|
<% end %>
|
11
13
|
|
12
14
|
<% if can_attach? %>
|
13
|
-
<%= a_link attach_path, color: 'indigo', 'data-turbo-frame': 'attach_modal' do %>
|
15
|
+
<%= a_link attach_path, color: 'indigo', 'data-turbo-frame': 'attach_modal', 'data-target': 'attach' do %>
|
14
16
|
<%= svg 'view-grid-add' %> <%= t('avo.attach_item', item: singular_resource_name).capitalize %>
|
15
17
|
<% end %>
|
16
18
|
<% end %>
|
@@ -30,7 +30,9 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
|
|
30
30
|
|
31
31
|
def title
|
32
32
|
if @reflection.present?
|
33
|
-
|
33
|
+
return field.plural_name if field.present?
|
34
|
+
|
35
|
+
reflection_resource.plural_name
|
34
36
|
else
|
35
37
|
@resource.plural_name
|
36
38
|
end
|
@@ -47,9 +49,19 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
|
|
47
49
|
# The Create button is dependent on the new? policy method.
|
48
50
|
# The create? should be called only when the user clicks the Save button so the developers gets access to the params from the form.
|
49
51
|
def can_see_the_create_button?
|
52
|
+
return authorize_association_for("create") if @reflection.present?
|
53
|
+
|
50
54
|
@resource.authorization.authorize_action(:new, raise_exception: false) && !has_reflection_and_is_read_only
|
51
55
|
end
|
52
56
|
|
57
|
+
def can_see_the_actions_button?
|
58
|
+
return false if @actions.blank?
|
59
|
+
|
60
|
+
return authorize_association_for("act_on") if @reflection.present?
|
61
|
+
|
62
|
+
@resource.authorization.authorize_action(:act_on, raise_exception: false) && !has_reflection_and_is_read_only
|
63
|
+
end
|
64
|
+
|
53
65
|
def can_attach?
|
54
66
|
klass = @reflection
|
55
67
|
klass = @reflection.through_reflection if klass.is_a? ::ActiveRecord::Reflection::ThroughReflection
|
@@ -107,12 +119,18 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
|
|
107
119
|
|
108
120
|
def singular_resource_name
|
109
121
|
if @reflection.present?
|
110
|
-
|
122
|
+
reflection_resource.name
|
111
123
|
else
|
112
124
|
@resource.singular_name || @resource.model_class.model_name.name.downcase
|
113
125
|
end
|
114
126
|
end
|
115
127
|
|
128
|
+
def description
|
129
|
+
return if @reflection.present?
|
130
|
+
|
131
|
+
@resource.resource_description
|
132
|
+
end
|
133
|
+
|
116
134
|
private
|
117
135
|
|
118
136
|
def reflection_model_class
|
@@ -1,7 +1,14 @@
|
|
1
1
|
<div>
|
2
2
|
<% @resource.panels.each do |resource_panel| %>
|
3
|
-
<%= form_with model: @resource.model,
|
4
|
-
|
3
|
+
<%= form_with model: @resource.model,
|
4
|
+
scope: @resource.form_scope,
|
5
|
+
url: helpers.resources_path(resource: @resource,
|
6
|
+
via_relation_class: params[:via_relation_class],
|
7
|
+
via_relation: params[:via_relation],
|
8
|
+
via_resource_id: params[:via_resource_id]),
|
9
|
+
local: true,
|
10
|
+
multipart: true do |form| %>
|
11
|
+
<%= render Avo::PanelComponent.new(title: resource_panel[:name], description: @resource.resource_description, display_breadcrumbs: true) do |c| %>
|
5
12
|
<% c.tools do %>
|
6
13
|
<div class="flex justify-end space-x-2">
|
7
14
|
<%= a_link back_path do %>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
data-selected-resources='["<%= @resource.model.id %>"]'
|
4
4
|
>
|
5
5
|
<% @resource.panels.each_with_index do |resource_panel, index| %>
|
6
|
-
<%= render Avo::PanelComponent.new(title:
|
6
|
+
<%= render Avo::PanelComponent.new(title: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: index) do |c| %>
|
7
7
|
<% c.tools do %>
|
8
8
|
<% if resource_panel[:name] == @resource.default_panel_name %>
|
9
9
|
<%= render 'actions' %>
|
@@ -6,13 +6,24 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
|
|
6
6
|
|
7
7
|
attr_reader :fields_by_panel, :has_one_panels, :has_many_panels, :has_as_belongs_to_many_panels
|
8
8
|
|
9
|
-
def initialize(resource: nil, reflection: nil, parent_model: nil)
|
9
|
+
def initialize(resource: nil, reflection: nil, parent_model: nil, resource_panel: nil)
|
10
10
|
@resource = resource
|
11
11
|
@reflection = reflection
|
12
|
+
@resource_panel = resource_panel
|
12
13
|
|
13
14
|
split_panel_fields
|
14
15
|
end
|
15
16
|
|
17
|
+
def title
|
18
|
+
if @reflection.present?
|
19
|
+
return field.name if field.present?
|
20
|
+
|
21
|
+
reflection_resource.name
|
22
|
+
else
|
23
|
+
@resource.panels.first[:name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
def back_path
|
17
28
|
if via_resource?
|
18
29
|
helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
|
@@ -18,6 +18,11 @@ module Avo
|
|
18
18
|
@parent_model = @parent_resource.class.find_scope.find(params[:id])
|
19
19
|
@parent_resource.hydrate(model: @parent_model)
|
20
20
|
@query = @authorization.apply_policy @parent_model.public_send(params[:related_name])
|
21
|
+
@association_field = @parent_resource.get_field params[:related_name]
|
22
|
+
|
23
|
+
if @association_field.present? && @association_field.scope.present?
|
24
|
+
@query = @query.instance_exec(&@association_field.scope)
|
25
|
+
end
|
21
26
|
|
22
27
|
super
|
23
28
|
end
|
data/config/routes.rb
CHANGED
@@ -23,7 +23,7 @@ Avo::Engine.routes.draw do
|
|
23
23
|
|
24
24
|
# Generate resource routes as below:
|
25
25
|
# resources :posts
|
26
|
-
|
26
|
+
Avo::DynamicRouter::routes(self)
|
27
27
|
|
28
28
|
# Relations
|
29
29
|
get "/:resource_name/:id/:related_name/new", to: "relations#new", as: "associations_new"
|
data/db/factories.rb
CHANGED
data/lib/avo/app.rb
CHANGED
@@ -155,24 +155,6 @@ module Avo
|
|
155
155
|
filename
|
156
156
|
end
|
157
157
|
end
|
158
|
-
|
159
|
-
def draw_routes
|
160
|
-
# We should eager load all the classes so we find all descendants
|
161
|
-
Rails.application.eager_load!
|
162
|
-
|
163
|
-
proc do
|
164
|
-
BaseResource.descendants
|
165
|
-
.select do |resource|
|
166
|
-
resource != :BaseResource
|
167
|
-
end
|
168
|
-
.select do |resource|
|
169
|
-
resource.is_a? Class
|
170
|
-
end
|
171
|
-
.map do |resource|
|
172
|
-
resources resource.new.model_key
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
158
|
end
|
177
159
|
end
|
178
160
|
end
|
data/lib/avo/base_resource.rb
CHANGED
@@ -19,6 +19,7 @@ module Avo
|
|
19
19
|
|
20
20
|
class_attribute :id, default: :id
|
21
21
|
class_attribute :title, default: :id
|
22
|
+
class_attribute :description, default: :id
|
22
23
|
class_attribute :search_query, default: nil
|
23
24
|
class_attribute :search_query_help, default: ''
|
24
25
|
class_attribute :includes, default: []
|
@@ -154,6 +155,12 @@ module Avo
|
|
154
155
|
fields
|
155
156
|
end
|
156
157
|
|
158
|
+
def get_field(id)
|
159
|
+
get_field_definitions.find do |f|
|
160
|
+
f.id == id.to_sym
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
157
164
|
def get_grid_fields
|
158
165
|
return if self.class.grid_loader.blank?
|
159
166
|
|
@@ -224,6 +231,16 @@ module Avo
|
|
224
231
|
name
|
225
232
|
end
|
226
233
|
|
234
|
+
def resource_description
|
235
|
+
return instance_exec(&self.class.description) if self.class.description.respond_to? :call
|
236
|
+
|
237
|
+
# Show the description only on the resource index view.
|
238
|
+
# If the user wants to conditionally it on all pages, they should use a block.
|
239
|
+
if view == :index
|
240
|
+
return self.class.description if self.class.description.is_a? String
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
227
244
|
def translation_key
|
228
245
|
return "avo.resource_translations.#{class_name_without_resource.underscore}" if self.class.translation_enabled
|
229
246
|
|
@@ -442,7 +459,7 @@ module Avo
|
|
442
459
|
end
|
443
460
|
|
444
461
|
def form_scope
|
445
|
-
|
462
|
+
model_class.base_class.to_s.underscore.downcase
|
446
463
|
end
|
447
464
|
end
|
448
465
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Avo
|
2
|
+
module DynamicRouter
|
3
|
+
def self.routes(router)
|
4
|
+
Rails.application.eager_load!
|
5
|
+
|
6
|
+
BaseResource.descendants
|
7
|
+
.select do |resource|
|
8
|
+
resource != :BaseResource
|
9
|
+
end
|
10
|
+
.select do |resource|
|
11
|
+
resource.is_a? Class
|
12
|
+
end
|
13
|
+
.map do |resource|
|
14
|
+
router.resources resource.new.model_key
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -102,6 +102,12 @@ module Avo
|
|
102
102
|
@id.to_s.humanize(keep_id_suffix: true)
|
103
103
|
end
|
104
104
|
|
105
|
+
def plural_name
|
106
|
+
return I18n.t(translation_key, count: 2).capitalize if translation_key
|
107
|
+
|
108
|
+
name.pluralize
|
109
|
+
end
|
110
|
+
|
105
111
|
def placeholder
|
106
112
|
return @placeholder if @placeholder.present?
|
107
113
|
|
@@ -4,6 +4,7 @@ module Avo
|
|
4
4
|
attr_accessor :link_to_resource
|
5
5
|
attr_accessor :is_avatar
|
6
6
|
attr_accessor :is_image
|
7
|
+
attr_accessor :is_audio
|
7
8
|
attr_accessor :direct_upload
|
8
9
|
|
9
10
|
def initialize(id, **args, &block)
|
@@ -12,6 +13,7 @@ module Avo
|
|
12
13
|
@link_to_resource = args[:link_to_resource].present? ? args[:link_to_resource] : false
|
13
14
|
@is_avatar = args[:is_avatar].present? ? args[:is_avatar] : false
|
14
15
|
@is_image = args[:is_image].present? ? args[:is_image] : @is_avatar
|
16
|
+
@is_audio = args[:is_audio].present? ? args[:is_audio] : false
|
15
17
|
@direct_upload = args[:direct_upload].present? ? args[:direct_upload] : false
|
16
18
|
end
|
17
19
|
|
@@ -8,6 +8,7 @@ module Avo
|
|
8
8
|
super(id, **args, &block)
|
9
9
|
|
10
10
|
@is_image = args[:is_image].present? ? args[:is_image] : @is_avatar
|
11
|
+
@is_audio = args[:is_audio].present? ? args[:is_audio] : false
|
11
12
|
@direct_upload = args[:direct_upload].present? ? args[:direct_upload] : false
|
12
13
|
end
|
13
14
|
|
@@ -2,11 +2,13 @@ module Avo
|
|
2
2
|
module Fields
|
3
3
|
class HasBaseField < BaseField
|
4
4
|
attr_accessor :display
|
5
|
+
attr_accessor :scope
|
5
6
|
|
6
7
|
def initialize(id, **args, &block)
|
7
8
|
super(id, **args, &block)
|
8
9
|
|
9
10
|
@display = args[:display].present? ? args[:display] : :show
|
11
|
+
@scope = args[:scope].present? ? args[:scope] : nil
|
10
12
|
end
|
11
13
|
|
12
14
|
def resource
|
@@ -37,6 +37,10 @@ module Avo
|
|
37
37
|
self.class.defined_methods(user, model, **args)
|
38
38
|
end
|
39
39
|
|
40
|
+
def has_method?(method, **args)
|
41
|
+
self.class.defined_methods(user, record, **args).include? method.to_sym
|
42
|
+
end
|
43
|
+
|
40
44
|
class << self
|
41
45
|
def authorize(user, record, action, **args)
|
42
46
|
return true if skip_authorization
|
@@ -64,6 +68,9 @@ module Avo
|
|
64
68
|
def authorize_action(user, record, action, **args)
|
65
69
|
action = Avo.configuration.authorization_methods.stringify_keys[action.to_s] || action
|
66
70
|
|
71
|
+
# Add the question mark if it's missing
|
72
|
+
action = "#{action}?" unless action.end_with? '?'
|
73
|
+
|
67
74
|
return true if action.nil?
|
68
75
|
|
69
76
|
authorize user, record, action, **args
|
@@ -101,7 +108,7 @@ module Avo
|
|
101
108
|
Pundit.policy!(user, record).methods
|
102
109
|
rescue => error
|
103
110
|
if args[:raise_exception] == false
|
104
|
-
|
111
|
+
[]
|
105
112
|
else
|
106
113
|
raise error
|
107
114
|
end
|
data/lib/avo/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: avo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.21.0.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrian Marin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-02-
|
12
|
+
date: 2022-02-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -583,6 +583,7 @@ files:
|
|
583
583
|
- lib/avo/base_action.rb
|
584
584
|
- lib/avo/base_resource.rb
|
585
585
|
- lib/avo/configuration.rb
|
586
|
+
- lib/avo/dynamic_router.rb
|
586
587
|
- lib/avo/engine.rb
|
587
588
|
- lib/avo/fields/badge_field.rb
|
588
589
|
- lib/avo/fields/base_field.rb
|