avo 2.11.1.pre.2 → 2.11.1.pre.3

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.

Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/stylesheets/avo.css +1 -4
  4. data/app/assets/stylesheets/css/sidebar.css +28 -0
  5. data/app/components/avo/fields/date_field/show_component.html.erb +4 -0
  6. data/app/components/avo/fields/has_one_field/show_component.html.erb +1 -1
  7. data/app/components/avo/fields/has_one_field/show_component.rb +7 -6
  8. data/app/components/avo/fields/text_field/index_component.html.erb +2 -0
  9. data/app/components/avo/fields/text_field/show_component.html.erb +2 -0
  10. data/app/components/avo/index/field_wrapper_component.html.erb +4 -0
  11. data/app/components/avo/index/resource_controls_component.rb +7 -5
  12. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  13. data/app/components/avo/index/resource_table_component.rb +2 -1
  14. data/app/components/avo/index/table_row_component.html.erb +1 -1
  15. data/app/components/avo/index/table_row_component.rb +2 -1
  16. data/app/components/avo/resource_component.rb +11 -5
  17. data/app/components/avo/sidebar_component.html.erb +2 -2
  18. data/app/components/avo/tab_switcher_component.rb +4 -0
  19. data/app/components/avo/views/resource_edit_component.rb +4 -6
  20. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  21. data/app/components/avo/views/resource_index_component.rb +5 -3
  22. data/app/components/avo/views/resource_show_component.html.erb +1 -1
  23. data/app/components/avo/views/resource_show_component.rb +3 -1
  24. data/app/controllers/avo/application_controller.rb +24 -9
  25. data/app/controllers/avo/associations_controller.rb +38 -12
  26. data/app/controllers/avo/base_controller.rb +2 -1
  27. data/app/javascript/js/controllers/sidebar_controller.js +46 -0
  28. data/app/javascript/js/controllers.js +2 -2
  29. data/app/views/avo/base/index.html.erb +1 -0
  30. data/app/views/avo/base/show.html.erb +7 -1
  31. data/app/views/avo/partials/_javascript.html.erb +1 -0
  32. data/app/views/avo/partials/_navbar.html.erb +5 -2
  33. data/app/views/layouts/avo/application.html.erb +3 -3
  34. data/lib/avo/concerns/has_fields.rb +5 -0
  35. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  36. data/lib/avo/fields/has_base_field.rb +11 -0
  37. data/lib/avo/fields/text_field.rb +4 -2
  38. data/lib/avo/services/authorization_service.rb +41 -37
  39. data/lib/avo/version.rb +1 -1
  40. data/lib/avo.rb +1 -0
  41. data/public/avo-assets/avo.css +43 -11
  42. data/public/avo-assets/avo.js +68 -67
  43. data/public/avo-assets/avo.js.map +3 -3
  44. metadata +4 -3
  45. data/app/javascript/js/controllers/mobile_controller.js +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bc7baa68c6256d8c8a26e4a40be36129d1187451015bc72c0ec2be65c3acd8b
4
- data.tar.gz: 57a8ef18222e622ed7d3f45334b46c97183d10ea845b321044648cd1974faf22
3
+ metadata.gz: 794b7326ae2257afd693641ff51ba513d09d4729a77a671a96f1291f0b31b8e3
4
+ data.tar.gz: a3b8b5d2f19e9ac16164ae6cb634a0f53d92da7af1b4bc3c829704327f112c91
5
5
  SHA512:
6
- metadata.gz: 3df946571876e5742a86eb9314fe1122ea2838bedd0c3af3027e754df24ca20a4c9961e62c46f50a399c9265ddbf2b1f2cb9b2fd2ffb43e13bdcf6595634d71c
7
- data.tar.gz: 7d9b4560a2069d752f5e82bc66c0d0eb4e1e9df5d95261371e859c8907741ac80a43a7ba09ce5ed6f59da316723b9f1ab9dbb8de3931f98113fc5de1c619ee74
6
+ metadata.gz: 15b95f8cef9a7ec9af6a272dd2abddbb7d8471f5edefb568b16e84f3b26165f7ac4f913974d70291463c8fcbc4427d7608a41a01c0167864d0368178ebf1e755
7
+ data.tar.gz: 7a7b600d0cc554e8aa30377199dad939e82c8165310b9f81cfe911c06a4d3cac3c72617fdad432475cd61bfcb60a1d00ee5ba4101a2d204940f48f6c6f03ba72
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (2.11.1.pre.2)
4
+ avo (2.11.1.pre.3)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -16,6 +16,7 @@
16
16
  @import './css/breadcrumbs.css';
17
17
  @import './css/search.css';
18
18
  @import './css/active-storage.css';
19
+ @import './css/sidebar.css';
19
20
  @import './css/spinner.css';
20
21
  @import './css/tags.css';
21
22
 
@@ -69,10 +70,6 @@ body {
69
70
  @apply opacity-0 translate-y-1;
70
71
  }
71
72
 
72
- .application-sidebar .active:hover,
73
- .application-sidebar .active {
74
- @apply bg-blue-100 text-blue-500;
75
- }
76
73
 
77
74
  .turbo-progress-bar {
78
75
  @apply bg-blue-400;
@@ -0,0 +1,28 @@
1
+ .application-sidebar .active:hover,
2
+ .application-sidebar .active {
3
+ @apply bg-blue-100 text-blue-500;
4
+ }
5
+
6
+ .content-area {
7
+ /* remove the left padding. */
8
+ .main-content-area {
9
+ @apply lg:pl-0;
10
+ }
11
+
12
+ /* Hide the sidebar by default. */
13
+ .avo-sidebar {
14
+ @apply lg:hidden;
15
+ }
16
+
17
+ &.sidebar-open {
18
+ /* Add padding to the main area to allow for the sidebar to expand. */
19
+ .main-content-area {
20
+ @apply lg:pl-64;
21
+ }
22
+
23
+ /* Show the sidebar. */
24
+ .avo-sidebar {
25
+ @apply lg:block;
26
+ }
27
+ }
28
+ }
@@ -5,5 +5,9 @@
5
5
  date_field_format_value: @field.format,
6
6
  } do %>
7
7
  <%= @field.formatted_value %>
8
+ <% if params[:avo_debug].present? %>
9
+ <!-- Raw value: -->
10
+ <!-- <%= @field.formatted_value.inspect %> -->
11
+ <% end %>
8
12
  <% end %>
9
13
  <% end %>
@@ -7,7 +7,7 @@
7
7
  <% c.tools do %>
8
8
  <% if !@field.readonly && can_attach? %>
9
9
  <%= a_link attach_path, icon: 'heroicons/outline/link', color: :primary, 'data-turbo-frame': 'attach_modal' do %>
10
- <%= t('avo.attach_item', item: @field.name) %>
10
+ <%= t('avo.attach_item', item: @field.name.downcase) %>
11
11
  <% end %>
12
12
  <% end %>
13
13
  <% end %>
@@ -4,19 +4,20 @@ class Avo::Fields::HasOneField::ShowComponent < Avo::Fields::ShowComponent
4
4
  include Avo::ApplicationHelper
5
5
 
6
6
  def can_attach?
7
- attach_policy = true
7
+ policy_result = true
8
+
8
9
  if @field.present?
9
10
  reflection_resource = @field.target_resource
10
11
  if reflection_resource.present? && @resource.present?
11
- method_name = ("attach_#{reflection_resource.model_key}?").to_sym
12
- defined_policy_methods = @resource.authorization.defined_methods(@resource.model_class, raise_exception: false)
12
+ method_name = "attach_#{@field.id}?".to_sym
13
13
 
14
- if defined_policy_methods.present? && defined_policy_methods.include?(method_name)
15
- attach_policy = @resource.authorization.authorize_action(method_name, raise_exception: false)
14
+ if @resource.authorization.has_method?(method_name, raise_exception: false)
15
+ policy_result = @resource.authorization.authorize_action(method_name, raise_exception: false)
16
16
  end
17
17
  end
18
18
  end
19
- attach_policy
19
+
20
+ policy_result
20
21
  end
21
22
 
22
23
  def attach_path
@@ -1,6 +1,8 @@
1
1
  <%= index_field_wrapper field: @field, resource: @resource do %>
2
2
  <% if @field.as_html %>
3
3
  <%== @field.value %>
4
+ <% elsif @field.protocol.present? %>
5
+ <%= link_to @field.value, "#{@field.protocol}:#{@field.value}" %>
4
6
  <% else %>
5
7
  <%= link_to_if @field.link_to_resource, @field.value, resource_path %>
6
8
  <% end %>
@@ -1,6 +1,8 @@
1
1
  <%= show_field_wrapper field: @field, resource: @resource, index: @index do %>
2
2
  <% if @field.as_html %>
3
3
  <%== @field.value %>
4
+ <% elsif @field.protocol.present? %>
5
+ <%= link_to @field.value, "#{@field.protocol}:#{@field.value}" %>
4
6
  <% else %>
5
7
  <%= @field.value %>
6
8
  <% end %>
@@ -17,4 +17,8 @@
17
17
  <%= content %>
18
18
  <% end %>
19
19
  <% end %>
20
+ <% if params[:avo_debug].present? %>
21
+ <!-- Raw value: -->
22
+ <!-- <%= @field.value.inspect %> -->
23
+ <% end %>
20
24
  <% end %>
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
4
- def initialize(resource: nil, reflection: nil, parent_model: nil, view_type: :table)
4
+ def initialize(resource: nil, reflection: nil, parent_model: nil, parent_resource: nil, view_type: :table)
5
5
  @resource = resource
6
6
  @reflection = reflection
7
7
  @parent_model = parent_model
8
+ @parent_resource = parent_resource
8
9
  @view_type = view_type
9
10
  end
10
11
 
@@ -12,7 +13,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
12
13
  @reflection.present? &&
13
14
  @resource.model.present? &&
14
15
  is_has_many_association &&
15
- authorize_association_for("detach")
16
+ authorize_association_for(:detach)
16
17
  end
17
18
 
18
19
  def can_edit?
@@ -22,8 +23,9 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
22
23
  end
23
24
 
24
25
  def can_view?
25
- return authorize_association_for(:view) if @reflection.present?
26
+ return authorize_association_for(:show) if @reflection.present?
26
27
 
28
+ # Even if there's a @reflection object present, for show we're going to fallback to the original policy.
27
29
  @resource.authorization.authorize_action(:show, raise_exception: false)
28
30
  end
29
31
 
@@ -42,7 +44,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
42
44
 
43
45
  def edit_path
44
46
  # Add the `view` param to let Avo know where to redirect back when the user clicks the `Cancel` button.
45
- args = {via_view: 'index'}
47
+ args = {via_view: "index"}
46
48
 
47
49
  if @parent_model.present?
48
50
  args = {
@@ -73,6 +75,6 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
73
75
  end
74
76
 
75
77
  def referrer_path
76
- Avo::App.root_path(paths: ['resources', params[:resource_name], params[:id], params[:related_name]], query: request.query_parameters.to_h)
78
+ Avo::App.root_path(paths: ["resources", params[:resource_name], params[:id], params[:related_name]], query: request.query_parameters.to_h)
77
79
  end
78
80
  end
@@ -4,7 +4,7 @@
4
4
  <tbody class="divide-y">
5
5
  <% @resources.each_with_index do |resource, index| %>
6
6
  <% cache_if Avo.configuration.cache_resources_on_index_view, resource.cache_hash(@parent_model), expires_in: 1.day do %>
7
- <%= render Avo::Index::TableRowComponent.new(resource: resource, reflection: @reflection, parent_model: @parent_model) %>
7
+ <%= render Avo::Index::TableRowComponent.new(resource: resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource) %>
8
8
  <% end %>
9
9
  <% end %>
10
10
  </tbody>
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Index::ResourceTableComponent < ViewComponent::Base
4
- def initialize(resources: nil, resource: nil, reflection: nil, parent_model: nil)
4
+ def initialize(resources: nil, resource: nil, reflection: nil, parent_model: nil, parent_resource: nil)
5
5
  @resources = resources
6
6
  @resource = resource
7
7
  @reflection = reflection
8
8
  @parent_model = parent_model
9
+ @parent_resource = parent_resource
9
10
  end
10
11
  end
@@ -14,7 +14,7 @@
14
14
  <% end %>
15
15
  <td class="text-right whitespace-nowrap px-2">
16
16
  <div class="flex items-center justify-end flex-grow-0 h-full w-full">
17
- <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, view_type: :table)) %>
17
+ <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, view_type: :table)) %>
18
18
  </div>
19
19
  </td>
20
20
  </tr>
@@ -3,9 +3,10 @@
3
3
  class Avo::Index::TableRowComponent < ViewComponent::Base
4
4
  include Avo::ResourcesHelper
5
5
 
6
- def initialize(resource: nil, reflection: nil, parent_model: nil)
6
+ def initialize(resource: nil, reflection: nil, parent_model: nil, parent_resource: nil)
7
7
  @resource = resource
8
8
  @reflection = reflection
9
9
  @parent_model = parent_model
10
+ @parent_resource = parent_resource
10
11
  end
11
12
  end
@@ -39,23 +39,29 @@ class Avo::ResourceComponent < Avo::BaseComponent
39
39
  end
40
40
 
41
41
  def authorize_association_for(policy_method)
42
- association_policy = true
42
+ policy_result = true
43
43
 
44
44
  if @reflection.present?
45
+ # Fetch the appropiate resource
45
46
  reflection_resource = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name)
47
+ # Fetch the model
48
+ # Hydrate the resource with the model if we have one
46
49
  reflection_resource.hydrate(model: @parent_model) if @parent_model.present?
47
- association_name = params["related_name"]
50
+ # Use the related_name as the base of the association
51
+ association_name = @reflection.name
48
52
 
49
53
  if association_name.present?
50
54
  method_name = "#{policy_method}_#{association_name}?".to_sym
55
+ # Prepare the authorization service
56
+ service = reflection_resource.authorization
51
57
 
52
- if reflection_resource.authorization.has_method?(method_name, raise_exception: false)
53
- association_policy = reflection_resource.authorization.authorize_action(method_name, raise_exception: false)
58
+ if service.has_method?(method_name, raise_exception: false)
59
+ policy_result = service.authorize_action(method_name, raise_exception: false)
54
60
  end
55
61
  end
56
62
  end
57
63
 
58
- association_policy
64
+ policy_result
59
65
  end
60
66
 
61
67
  def main_panel
@@ -1,6 +1,6 @@
1
1
  <div
2
- class="fixed z-[60] t-0 application-sidebar w-64 hidden lg:flex flex-1 border-r lg:border-none bg-none h-[calc(100vh-4rem)] bg-gray-25 lg:bg-transparent <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
3
- data-mobile-target="sidebar"
2
+ class="avo-sidebar fixed z-[60] t-0 application-sidebar w-64 hidden lg:flex flex-1 border-r lg:border-none bg-none h-[calc(100vh-4rem)] bg-gray-25 lg:bg-transparent <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>";
3
+ data-sidebar-target="sidebar"
4
4
  >
5
5
  <div class="flex flex-col w-full h-full">
6
6
  <div class="flex-1 flex flex-col justify-between overflow-auto h-full pt-3 scroll-shadows">
@@ -80,6 +80,10 @@ class Avo::TabSwitcherComponent < Avo::BaseComponent
80
80
  visible = item.visible?
81
81
  end
82
82
 
83
+ if item.respond_to?(:authorized?)
84
+ visible = item.authorized?
85
+ end
86
+
83
87
  visible
84
88
  end
85
89
  end
@@ -22,12 +22,10 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
22
22
  helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
23
23
  elsif via_index?
24
24
  helpers.resources_path(resource: @resource)
25
- else # via resource show page
26
- if is_edit?
27
- helpers.resource_path(model: @resource.model, resource: @resource)
28
- else
29
- helpers.resources_path(resource: @resource)
30
- end
25
+ elsif is_edit? # via resource show page
26
+ helpers.resource_path(model: @resource.model, resource: @resource)
27
+ else
28
+ helpers.resources_path(resource: @resource)
31
29
  end
32
30
  end
33
31
 
@@ -51,7 +51,7 @@
51
51
  <% if @resources.present? %>
52
52
  <div class="w-full overflow-auto flex flex-col mt-0">
53
53
  <div class="relative flex-1 flex">
54
- <%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model)) %>
54
+ <%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource)) %>
55
55
  </div>
56
56
  </div>
57
57
  <% else %>
@@ -15,6 +15,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
15
15
  reflection: nil,
16
16
  turbo_frame: "",
17
17
  parent_model: nil,
18
+ parent_resource: nil,
18
19
  applied_filters: []
19
20
  )
20
21
  @resource = resource
@@ -27,6 +28,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
27
28
  @reflection = reflection
28
29
  @turbo_frame = turbo_frame
29
30
  @parent_model = parent_model
31
+ @parent_resource = parent_resource
30
32
  @applied_filters = applied_filters
31
33
  @view = :index
32
34
  end
@@ -52,7 +54,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
52
54
  # The Create button is dependent on the new? policy method.
53
55
  # The create? should be called only when the user clicks the Save button so the developers gets access to the params from the form.
54
56
  def can_see_the_create_button?
55
- return authorize_association_for("create") if @reflection.present?
57
+ return authorize_association_for(:create) if @reflection.present?
56
58
 
57
59
  @resource.authorization.authorize_action(:new, raise_exception: false) && !has_reflection_and_is_read_only
58
60
  end
@@ -60,7 +62,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
60
62
  def can_see_the_actions_button?
61
63
  return false if @actions.blank?
62
64
 
63
- return authorize_association_for("act_on") if @reflection.present?
65
+ return authorize_association_for(:act_on) if @reflection.present?
64
66
 
65
67
  @resource.authorization.authorize_action(:act_on, raise_exception: false) && !has_reflection_and_is_read_only
66
68
  end
@@ -69,7 +71,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
69
71
  klass = @reflection
70
72
  klass = @reflection.through_reflection if klass.is_a? ::ActiveRecord::Reflection::ThroughReflection
71
73
 
72
- @reflection.present? && klass.is_a?(::ActiveRecord::Reflection::HasManyReflection) && !has_reflection_and_is_read_only && authorize_association_for("attach")
74
+ @reflection.present? && klass.is_a?(::ActiveRecord::Reflection::HasManyReflection) && !has_reflection_and_is_read_only && authorize_association_for(:attach)
73
75
  end
74
76
 
75
77
  def has_reflection_and_is_read_only
@@ -19,8 +19,8 @@
19
19
  } do %>
20
20
  <%= t('avo.detach_item', item: title).capitalize %>
21
21
  <% end %>
22
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
23
22
  <% end %>
23
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
24
24
  <% if can_see_the_edit_button? %>
25
25
  <%= a_link edit_path,
26
26
  color: :primary,
@@ -4,11 +4,13 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
4
4
  include Avo::ResourcesHelper
5
5
  include Avo::ApplicationHelper
6
6
 
7
- def initialize(resource: nil, reflection: nil, parent_model: nil, resource_panel: nil, actions: [])
7
+ def initialize(resource: nil, reflection: nil, parent_resource: nil, parent_model: nil, resource_panel: nil, actions: [])
8
8
  @resource = resource
9
9
  @reflection = reflection
10
10
  @resource_panel = resource_panel
11
11
  @actions = actions
12
+ @parent_model = parent_model
13
+ @parent_resource = parent_resource
12
14
  @view = :show
13
15
  end
14
16
 
@@ -21,6 +21,7 @@ module Avo
21
21
  before_action :set_container_classes
22
22
  before_action :add_initial_breadcrumbs
23
23
  before_action :set_view
24
+ before_action :set_sidebar_open
24
25
 
25
26
  rescue_from Pundit::NotAuthorizedError, with: :render_unauthorized
26
27
  rescue_from ActiveRecord::RecordInvalid, with: :exception_logger
@@ -129,7 +130,11 @@ module Avo
129
130
  end
130
131
 
131
132
  def set_related_model
132
- @related_model = eager_load_files(@related_resource, @related_resource.class.find_scope).find params[:related_id]
133
+ @related_model = if @field.is_a? Avo::Fields::HasOneField
134
+ @model.send params[:related_name]
135
+ else
136
+ eager_load_files(@related_resource, @model.send(params[:related_name])).find params[:related_id]
137
+ end
133
138
  end
134
139
 
135
140
  def set_view
@@ -155,15 +160,20 @@ module Avo
155
160
  end
156
161
 
157
162
  def hydrate_related_resource
158
- @related_resource.hydrate(view: action_name.to_sym, user: _current_user)
163
+ @related_resource.hydrate(view: action_name.to_sym, user: _current_user, model: @model)
159
164
  end
160
165
 
161
- def authorize_action
162
- if @model.present?
163
- @authorization.set_record(@model).authorize_action action_name.to_sym
164
- else
165
- @authorization.set_record(@resource.model_class).authorize_action action_name.to_sym
166
- end
166
+ def authorize_base_action
167
+ class_to_authorize = @model || @resource.model_class
168
+
169
+ authorize_action class_to_authorize
170
+ end
171
+
172
+ def authorize_action(class_to_authorize, action = nil)
173
+ # Use the provided action or figure it out from the request
174
+ action_to_authorize = action || action_name
175
+
176
+ @authorization.set_record(class_to_authorize).authorize_action action_to_authorize.to_sym
167
177
  end
168
178
 
169
179
  # Get the pluralized resource name for this request
@@ -310,10 +320,15 @@ module Avo
310
320
 
311
321
  def default_url_options
312
322
  if params[:force_locale].present?
313
- { **super, force_locale: params[:force_locale] }
323
+ {**super, force_locale: params[:force_locale]}
314
324
  else
315
325
  super
316
326
  end
317
327
  end
328
+
329
+ def set_sidebar_open
330
+ value = cookies["#{Avo::COOKIES_KEY}.sidebar.open"]
331
+ @sidebar_open = value.blank? || value == '1'
332
+ end
318
333
  end
319
334
  end
@@ -5,12 +5,16 @@ module Avo
5
5
  before_action :set_model, only: [:show, :index, :new, :create, :destroy, :order]
6
6
  before_action :set_related_resource_name
7
7
  before_action :set_related_resource, only: [:show, :index, :new, :create, :destroy, :order]
8
- before_action :hydrate_related_resource, only: [:show, :index, :new, :create, :destroy, :order]
8
+ before_action :set_reflection_field
9
+ before_action :hydrate_related_resource, only: [:show, :index, :create, :destroy, :order]
9
10
  before_action :set_related_model, only: [:show, :order]
11
+ before_action :set_reflection
10
12
  before_action :set_attachment_class, only: [:show, :index, :new, :create, :destroy, :order]
11
13
  before_action :set_attachment_resource, only: [:show, :index, :new, :create, :destroy, :order]
12
14
  before_action :set_attachment_model, only: [:create, :destroy, :order]
13
- before_action :set_reflection, only: [:index, :show, :order]
15
+ before_action :authorize_index_action, only: :index
16
+ before_action :authorize_attach_action, only: :new
17
+ before_action :authorize_detach_action, only: :destroy
14
18
 
15
19
  def index
16
20
  @parent_resource = @resource.dup
@@ -28,6 +32,8 @@ module Avo
28
32
  end
29
33
 
30
34
  def show
35
+ @parent_resource, @parent_model = @resource, @model
36
+
31
37
  @resource, @model = @related_resource, @related_model
32
38
 
33
39
  super
@@ -36,12 +42,6 @@ module Avo
36
42
  def new
37
43
  @resource.hydrate(model: @model)
38
44
 
39
- begin
40
- @field = @resource.get_field_definitions.find { |f| f.id == @related_resource_name.to_sym }
41
- @field.hydrate(resource: @resource, model: @model, view: :new)
42
- rescue
43
- end
44
-
45
45
  if @field.present? && !@field.searchable
46
46
  query = @authorization.apply_policy @attachment_class
47
47
 
@@ -93,8 +93,12 @@ module Avo
93
93
 
94
94
  private
95
95
 
96
+ def set_reflection
97
+ @reflection = @model._reflections[params[:related_name].to_s]
98
+ end
99
+
96
100
  def set_attachment_class
97
- @attachment_class = @model._reflections[params[:related_name].to_s].klass
101
+ @attachment_class = @reflection.klass
98
102
  end
99
103
 
100
104
  def set_attachment_resource
@@ -102,11 +106,13 @@ module Avo
102
106
  end
103
107
 
104
108
  def set_attachment_model
105
- @attachment_model = @model._reflections[params[:related_name].to_s].klass.find attachment_id
109
+ @attachment_model = @attachment_class.find attachment_id
106
110
  end
107
111
 
108
- def set_reflection
109
- @reflection = @model._reflections[params[:related_name].to_s]
112
+ def set_reflection_field
113
+ @field = @resource.get_field_definitions.find { |f| f.id == @related_resource_name.to_sym }
114
+ @field.hydrate(resource: @resource, model: @model, view: :new)
115
+ rescue
110
116
  end
111
117
 
112
118
  def attachment_id
@@ -121,5 +127,25 @@ module Avo
121
127
 
122
128
  klass
123
129
  end
130
+
131
+ def authorize_if_defined(method)
132
+ @authorization.set_record(@model)
133
+
134
+ if @authorization.has_method?(method.to_sym)
135
+ @authorization.authorize_action method.to_sym
136
+ end
137
+ end
138
+
139
+ def authorize_index_action
140
+ authorize_if_defined "view_#{@field.id}?"
141
+ end
142
+
143
+ def authorize_attach_action
144
+ authorize_if_defined "attach_#{@field.id}?"
145
+ end
146
+
147
+ def authorize_detach_action
148
+ authorize_if_defined "detach_#{@field.id}?"
149
+ end
124
150
  end
125
151
  end
@@ -10,7 +10,8 @@ module Avo
10
10
  before_action :set_model_to_fill
11
11
  before_action :set_edit_title_and_breadcrumbs, only: [:edit, :update]
12
12
  before_action :fill_model, only: [:create, :update]
13
- before_action :authorize_action
13
+ # Don't run base authorizations for associations
14
+ before_action :authorize_base_action, if: -> {controller_name != "associations"}
14
15
 
15
16
  def index
16
17
  @page_title = @resource.plural_name.humanize
@@ -0,0 +1,46 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import Cookies from 'js-cookie'
3
+
4
+ export default class extends Controller {
5
+ static targets = ['sidebar', 'mainArea']
6
+
7
+ static values = {
8
+ open: Boolean,
9
+ }
10
+
11
+ get cookieKey() {
12
+ return `${window.Avo.configuration.cookies_key}.sidebar.open`
13
+ }
14
+
15
+ get sidebarOpen() {
16
+ return Cookies.get(this.cookieKey) === '1'
17
+ }
18
+
19
+ set cookie(state) {
20
+ Cookies.set(this.cookieKey, state === true ? 1 : 0)
21
+ }
22
+
23
+ markSidebarClosed() {
24
+ Cookies.set(this.cookieKey, '0')
25
+ this.openValue = false
26
+ this.mainAreaTarget.classList.remove('sidebar-open')
27
+ }
28
+
29
+ markSidebarOpen() {
30
+ Cookies.set(this.cookieKey, '1')
31
+ this.openValue = true
32
+ this.mainAreaTarget.classList.add('sidebar-open')
33
+ }
34
+
35
+ toggleSidebar() {
36
+ if (this.openValue) {
37
+ this.markSidebarClosed()
38
+ } else {
39
+ this.markSidebarOpen()
40
+ }
41
+ }
42
+
43
+ toggleSidebarOnMobile() {
44
+ this.sidebarTarget.classList.toggle('hidden')
45
+ }
46
+ }
@@ -16,7 +16,6 @@ import ItemSelectorController from './controllers/item_selector_controller'
16
16
  import KeyValueController from './controllers/fields/key_value_controller'
17
17
  import LoadingButtonController from './controllers/loading_button_controller'
18
18
  import MenuController from './controllers/menu_controller'
19
- import MobileController from './controllers/mobile_controller'
20
19
  import ModalController from './controllers/modal_controller'
21
20
  import MultipleSelectFilterController from './controllers/multiple_select_filter_controller'
22
21
  import PerPageController from './controllers/per_page_controller'
@@ -26,6 +25,7 @@ import ResourceShowController from './controllers/resource_show_controller'
26
25
  import SearchController from './controllers/search_controller'
27
26
  import SelectController from './controllers/select_controller'
28
27
  import SelectFilterController from './controllers/select_filter_controller'
28
+ import SidebarController from './controllers/sidebar_controller'
29
29
  import SimpleMdeController from './controllers/fields/simple_mde_controller'
30
30
  import TabsController from './controllers/tabs_controller'
31
31
  import TagsFieldController from './controllers/fields/tags_field_controller'
@@ -46,7 +46,6 @@ application.register('item-select-all', ItemSelectAllController)
46
46
  application.register('item-selector', ItemSelectorController)
47
47
  application.register('loading-button', LoadingButtonController)
48
48
  application.register('menu', MenuController)
49
- application.register('mobile', MobileController)
50
49
  application.register('modal', ModalController)
51
50
  application.register('multiple-select-filter', MultipleSelectFilterController)
52
51
  application.register('per-page', PerPageController)
@@ -56,6 +55,7 @@ application.register('resource-show', ResourceShowController)
56
55
  application.register('search', SearchController)
57
56
  application.register('select', SelectController)
58
57
  application.register('select-filter', SelectFilterController)
58
+ application.register('sidebar', SidebarController)
59
59
  application.register('tabs', TabsController)
60
60
  application.register('tags-field', TagsFieldController)
61
61
  application.register('text-filter', TextFilterController)