avo 4.2.6 → 4.2.7
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -6
- data/app/components/avo/modal_component.html.erb +1 -1
- data/app/components/avo/u_i/panel_component.html.erb +1 -1
- data/app/controllers/avo/associations_controller.rb +3 -0
- data/app/controllers/avo/search_controller.rb +4 -0
- data/app/views/avo/partials/_confirm_dialog.html.erb +2 -2
- data/lib/avo/base_action.rb +5 -2
- data/lib/avo/current.rb +19 -1
- data/lib/avo/fields/location_field.rb +6 -0
- data/lib/avo/resources/base.rb +33 -2
- data/lib/avo/services/telemetry_service.rb +15 -1
- data/lib/avo/version.rb +1 -1
- data/lib/avo/view_types/view_type_manager.rb +6 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06a99742dcd8d96855fb10577085da6acf2fb2eabebc8ba66d02b653e404f962
|
|
4
|
+
data.tar.gz: 7d63ef33110cdf91bb3188ecf82abac3ecf9f56fd3c1edc53605737da96b2557
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4dec169c3e7a227e2fd6baffd9a8766c91fe0fba55a04fb3bcf9cd1691c12479a5579450e98cf27a90c1cfab99970d9b5498a9b26e3a745c443878b4f1504006
|
|
7
|
+
data.tar.gz: 8903fb5e47c672f02254cf44e90fb3df88775e0d5b382131b7ca70c3567d5d6b35815b09e6a129b827111ef447d6cc6dc1dba99655b4bf628d59b9eb114dd69d
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
avo (4.2.
|
|
4
|
+
avo (4.2.7)
|
|
5
5
|
actionview (>= 6.1)
|
|
6
6
|
active_link_to
|
|
7
7
|
activerecord (>= 6.1)
|
|
@@ -139,7 +139,7 @@ GEM
|
|
|
139
139
|
avo-record_link_field (0.0.2)
|
|
140
140
|
avo-rhino_field (4.1.0)
|
|
141
141
|
aws-eventstream (1.4.0)
|
|
142
|
-
aws-partitions (1.
|
|
142
|
+
aws-partitions (1.1289.0)
|
|
143
143
|
aws-sdk-core (3.257.0)
|
|
144
144
|
aws-eventstream (~> 1, >= 1.3.0)
|
|
145
145
|
aws-partitions (~> 1, >= 1.992.0)
|
|
@@ -432,7 +432,7 @@ GEM
|
|
|
432
432
|
addressable (~> 2.8)
|
|
433
433
|
childprocess (~> 5.0)
|
|
434
434
|
logger (~> 1.6)
|
|
435
|
-
lexxy (0.9.
|
|
435
|
+
lexxy (0.9.33)
|
|
436
436
|
railties (>= 8.0.2)
|
|
437
437
|
lint_roller (1.1.0)
|
|
438
438
|
listen (3.10.0)
|
|
@@ -514,7 +514,7 @@ GEM
|
|
|
514
514
|
openssl (4.0.2)
|
|
515
515
|
orm_adapter (0.5.0)
|
|
516
516
|
ostruct (0.6.3)
|
|
517
|
-
pagy (43.6.
|
|
517
|
+
pagy (43.6.3)
|
|
518
518
|
json
|
|
519
519
|
uri
|
|
520
520
|
yaml
|
|
@@ -596,7 +596,7 @@ GEM
|
|
|
596
596
|
zeitwerk (~> 2.6)
|
|
597
597
|
rainbow (3.1.1)
|
|
598
598
|
rake (13.4.2)
|
|
599
|
-
ransack (
|
|
599
|
+
ransack (5.0.2)
|
|
600
600
|
activerecord (>= 7.2)
|
|
601
601
|
activesupport (>= 7.2)
|
|
602
602
|
i18n
|
|
@@ -624,7 +624,7 @@ GEM
|
|
|
624
624
|
parser (~> 3.3.0)
|
|
625
625
|
rainbow (>= 2.0, < 4.0)
|
|
626
626
|
rexml (~> 3.1)
|
|
627
|
-
regexp_parser (2.
|
|
627
|
+
regexp_parser (2.13.0)
|
|
628
628
|
reline (0.7.0)
|
|
629
629
|
io-console (~> 0.5)
|
|
630
630
|
responders (3.2.1)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
aria-modal="true"
|
|
18
18
|
class="modal__card-container"
|
|
19
19
|
>
|
|
20
|
-
<div class="modal__card" data
|
|
20
|
+
<div class="modal__card" <%= tag.attributes(data: {"#{ctrl}-target": "card"}) %>>
|
|
21
21
|
<div class="card">
|
|
22
22
|
<div class="card__wrapper">
|
|
23
23
|
<% if has_header? %>
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<% end %>
|
|
37
37
|
|
|
38
38
|
<div class="panel__content">
|
|
39
|
-
<div class="panel__body" <%= "
|
|
39
|
+
<div class="panel__body" <%= tag.attributes(content_focusable? ? {data: {"content-focus": ""}, tabindex: -1} : {}) %>>
|
|
40
40
|
<% if card? %>
|
|
41
41
|
<div class="panel__card"><%= card %></div>
|
|
42
42
|
<% else %>
|
|
@@ -412,6 +412,9 @@ module Avo
|
|
|
412
412
|
def checkbox_list_option(record)
|
|
413
413
|
resource = @attachment_resource.new(record:, view: Avo::ViewInquirer.new(:new), params:)
|
|
414
414
|
search_item = (@attachment_resource.fetch_search(:item, record:) || {}).with_indifferent_access
|
|
415
|
+
# Same split as the global search controller: `record_title` is the fallback, and a
|
|
416
|
+
# declared `self.search[:item]` supplies title, description and image itself. Those
|
|
417
|
+
# values are app-authored and outside what a title-attribute rule governs.
|
|
415
418
|
title = search_item[:title] || resource.record_title
|
|
416
419
|
|
|
417
420
|
{
|
|
@@ -160,6 +160,10 @@ module Avo
|
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def fetch_result_information(record, resource, item)
|
|
163
|
+
# `record_title` is the fallback here, not the source: a resource declaring
|
|
164
|
+
# `self.search[:item]` supplies the title itself, and that block is app-authored.
|
|
165
|
+
# Anything that governs which attributes a title may expose therefore reaches this
|
|
166
|
+
# surface through `record_title` only — the item half is the app's own.
|
|
163
167
|
title = item&.dig(:title) || resource.record_title
|
|
164
168
|
highlighted_title = highlight(title&.to_s, CGI.escapeHTML(params[:q] || ""))
|
|
165
169
|
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
<form method="dialog">
|
|
3
3
|
<div class="card relative">
|
|
4
4
|
<button value="cancel" class="absolute top-3 inset-e-2.5 text-content-secondary bg-transparent hover:bg-tertiary hover:text-content rounded-lg text-sm size-8 ms-auto inline-flex justify-center items-center" data-modal-hide="popup-modal" tabindex="3">
|
|
5
|
-
<%= svg "x
|
|
5
|
+
<%= svg "x", class: "size-6" %>
|
|
6
6
|
<span class="sr-only"><%= t "avo.close_modal" %></span>
|
|
7
7
|
</button>
|
|
8
8
|
<div class="card__wrapper">
|
|
9
9
|
<div class="card__body text-center items-center justify-between min-h-56">
|
|
10
10
|
<div class="flex flex-col items-center justify-center p-4 md:p-5">
|
|
11
11
|
<svg class="mt-4 mx-auto mb-4 text-content-secondary size-12" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
|
12
|
-
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
|
12
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
|
13
13
|
</svg>
|
|
14
14
|
<p class="text-md font-normal text-content">
|
|
15
15
|
<%= t "avo.are_you_sure" %>
|
data/lib/avo/base_action.rb
CHANGED
|
@@ -186,8 +186,11 @@ module Avo
|
|
|
186
186
|
|
|
187
187
|
# For some fields, like belongs_to, the id and database_id differ (user vs user_id).
|
|
188
188
|
# That's why we need to fetch the database_id for when we process the action.
|
|
189
|
-
action_fields_by_database_id = action_fields.
|
|
190
|
-
|
|
189
|
+
action_fields_by_database_id = action_fields.values.flat_map do |field|
|
|
190
|
+
# A polymorphic belongs_to submits both `<name>_type` and `<name>_id`, but its database_id only covers the type.
|
|
191
|
+
database_ids = field.try(:is_polymorphic?) ? field.to_permitted_param : [field.database_id]
|
|
192
|
+
|
|
193
|
+
database_ids.map { |database_id| [database_id.to_sym, field] }
|
|
191
194
|
end.to_h
|
|
192
195
|
|
|
193
196
|
args[:fields].to_unsafe_h.map do |name, value|
|
data/lib/avo/current.rb
CHANGED
|
@@ -27,13 +27,31 @@ class Avo::Current < ActiveSupport::CurrentAttributes
|
|
|
27
27
|
|
|
28
28
|
attribute :appearance_settings
|
|
29
29
|
|
|
30
|
+
# Which Avo surface this request arrived through: `:ui` for the admin screens, `:api`,
|
|
31
|
+
# `:ai`, `:mcp`. It names the client, not the screen — `view` already means index/show/
|
|
32
|
+
# edit/new, which is a screen within one surface. Consumers that resolve an answer per
|
|
33
|
+
# user can differ per surface without a second declaration.
|
|
34
|
+
attribute :interface
|
|
35
|
+
|
|
36
|
+
# Set by the callers that map over every resource with no user in scope — this gem's
|
|
37
|
+
# telemetry, and avo-licensing's debug service. Consumers that restrict what a user may
|
|
38
|
+
# reach read this to opt out explicitly, so the exemption can never be inferred from a
|
|
39
|
+
# nil user (which is a legitimate state on the API surface).
|
|
40
|
+
attribute :enumerating_all_resources
|
|
41
|
+
|
|
30
42
|
# Rails 7.1 CurrentAttributes#attribute is only `def attribute(*names)` — no `default:` keyword.
|
|
31
43
|
# `attribute :x, default: {}` is passed as a second positional `{ default: {} }`, so `names.map(&:to_sym)` raises.
|
|
32
|
-
resets
|
|
44
|
+
resets do
|
|
45
|
+
self.appearance_settings = {}
|
|
46
|
+
self.interface = :ui
|
|
47
|
+
self.enumerating_all_resources = false
|
|
48
|
+
end
|
|
33
49
|
|
|
34
50
|
def initialize
|
|
35
51
|
super
|
|
36
52
|
self.appearance_settings = {}
|
|
53
|
+
self.interface = :ui
|
|
54
|
+
self.enumerating_all_resources = false
|
|
37
55
|
end
|
|
38
56
|
|
|
39
57
|
# Protect from error #<RuntimeError: Missing rack.input> when request is ActionDispatch::Request.empty
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
module Avo
|
|
4
4
|
module Fields
|
|
5
5
|
class LocationField < BaseField
|
|
6
|
+
# The two columns a location is stored across, when it is stored across two. Read
|
|
7
|
+
# by anything that has to know which columns back a field — `to_permitted_param`
|
|
8
|
+
# cannot answer that here, since it yields the field id and a hash rather than the
|
|
9
|
+
# column names.
|
|
10
|
+
attr_reader :stored_as
|
|
11
|
+
|
|
6
12
|
def initialize(id, **args, &block)
|
|
7
13
|
hide_on :index
|
|
8
14
|
super
|
data/lib/avo/resources/base.rb
CHANGED
|
@@ -516,21 +516,52 @@ module Avo
|
|
|
516
516
|
fetch_record_title.to_s
|
|
517
517
|
end
|
|
518
518
|
|
|
519
|
+
# Whether this resource may expose `attribute` inside a record title. Core always
|
|
520
|
+
# answers true; a plugin that restricts what a user may reach overrides this and
|
|
521
|
+
# answers from its own rules. That is what keeps a restricted attribute out of
|
|
522
|
+
# global search results, breadcrumbs, record links and the association picker
|
|
523
|
+
# without teaching each of those surfaces separately — they all render a title.
|
|
524
|
+
#
|
|
525
|
+
# Deliberately a question about one attribute rather than a field lookup: a bare
|
|
526
|
+
# `Resource.new(record:)` is built once per option in a belongs_to lookup list, and
|
|
527
|
+
# its items holder is empty until `detect_fields` runs, so resolving a title through
|
|
528
|
+
# the field pipeline would both cost a detection per option and answer nothing on
|
|
529
|
+
# the resources that never detect.
|
|
530
|
+
def title_attribute_reachable?(attribute)
|
|
531
|
+
true
|
|
532
|
+
end
|
|
533
|
+
|
|
519
534
|
def fetch_record_title
|
|
520
535
|
return name if @record.nil?
|
|
521
536
|
|
|
522
537
|
# Get the title from the record if title is not set, try to get the name, title or label, or fallback to the to_param
|
|
523
|
-
return
|
|
538
|
+
return reachable_title_fallback || @record.to_param if title.nil?
|
|
524
539
|
|
|
525
540
|
# If the title is a symbol, get the value from the record else execute the block/string
|
|
526
541
|
case title
|
|
527
542
|
when Symbol
|
|
528
|
-
@record.send
|
|
543
|
+
title_attribute_reachable?(title) ? @record.send(title) : @record.to_param
|
|
529
544
|
when Proc
|
|
545
|
+
# App-authored code. The app's own code is outside this boundary, so the
|
|
546
|
+
# reachability question is not asked of it.
|
|
530
547
|
Avo::ExecutionContext.new(target: title, resource: self, record: @record).handle
|
|
531
548
|
end
|
|
532
549
|
end
|
|
533
550
|
|
|
551
|
+
# Preserves the original `||` chain exactly — an unreachable candidate is skipped
|
|
552
|
+
# the same way a blank one is.
|
|
553
|
+
def reachable_title_fallback
|
|
554
|
+
reachable_title_attribute(:name) ||
|
|
555
|
+
reachable_title_attribute(:title) ||
|
|
556
|
+
reachable_title_attribute(:label)
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def reachable_title_attribute(attribute)
|
|
560
|
+
return unless title_attribute_reachable?(attribute)
|
|
561
|
+
|
|
562
|
+
@record.try(attribute)
|
|
563
|
+
end
|
|
564
|
+
|
|
534
565
|
def record_icon
|
|
535
566
|
fetch_record_icon.to_s
|
|
536
567
|
end
|
|
@@ -24,7 +24,10 @@ class Avo::Services::TelemetryService
|
|
|
24
24
|
def avo_metadata
|
|
25
25
|
resources = Avo.resource_manager.all
|
|
26
26
|
dashboards = Avo::Current.app.dashboard_manager.all
|
|
27
|
-
|
|
27
|
+
# Counting fields across every resource with no user in scope. Declared explicitly so a
|
|
28
|
+
# consumer that restricts fields per user opts out here rather than inferring it from a
|
|
29
|
+
# nil user — nil is a legitimate user on the API surface, where restriction must still apply.
|
|
30
|
+
field_definitions = enumerating_all_resources { resources.map(&:get_field_definitions) }
|
|
28
31
|
fields_count = field_definitions.map(&:count).sum
|
|
29
32
|
fields_per_resource = sprintf("%0.01f", fields_count / (resources.count + 0.0))
|
|
30
33
|
|
|
@@ -69,6 +72,17 @@ class Avo::Services::TelemetryService
|
|
|
69
72
|
"error: #{error.message}"
|
|
70
73
|
end
|
|
71
74
|
|
|
75
|
+
# Marks a block as enumerating every resource without a user, restoring whatever the
|
|
76
|
+
# surrounding request had set rather than assuming it was unset.
|
|
77
|
+
def enumerating_all_resources
|
|
78
|
+
previous = Avo::Current.enumerating_all_resources
|
|
79
|
+
Avo::Current.enumerating_all_resources = true
|
|
80
|
+
|
|
81
|
+
yield
|
|
82
|
+
ensure
|
|
83
|
+
Avo::Current.enumerating_all_resources = previous
|
|
84
|
+
end
|
|
85
|
+
|
|
72
86
|
def other_metadata(type = :actions)
|
|
73
87
|
resources = Avo.resource_manager.all
|
|
74
88
|
|
data/lib/avo/version.rb
CHANGED
|
@@ -40,24 +40,26 @@ module Avo
|
|
|
40
40
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
|
+
# Names, not constants: plugins register view types from Avo.boot, and loading a component there loads
|
|
44
|
+
# Action View while the app initializes, which Rails 8.2 flags (rails/rails#57224).
|
|
43
45
|
def register_defaults
|
|
44
46
|
register :table,
|
|
45
|
-
component: Avo::ViewTypes::TableComponent,
|
|
47
|
+
component: "Avo::ViewTypes::TableComponent",
|
|
46
48
|
icon: "tabler/outline/layout-list",
|
|
47
49
|
active_icon: "tabler/filled/layout-list"
|
|
48
50
|
|
|
49
51
|
register :grid,
|
|
50
|
-
component: Avo::ViewTypes::GridComponent,
|
|
52
|
+
component: "Avo::ViewTypes::GridComponent",
|
|
51
53
|
icon: "tabler/outline/layout-grid",
|
|
52
54
|
active_icon: "tabler/filled/layout-grid"
|
|
53
55
|
|
|
54
56
|
register :map,
|
|
55
|
-
component: Avo::ViewTypes::MapComponent,
|
|
57
|
+
component: "Avo::ViewTypes::MapComponent",
|
|
56
58
|
icon: "tabler/outline/compass",
|
|
57
59
|
active_icon: "tabler/filled/compass"
|
|
58
60
|
|
|
59
61
|
register :list,
|
|
60
|
-
component: Avo::ViewTypes::TableComponent,
|
|
62
|
+
component: "Avo::ViewTypes::TableComponent",
|
|
61
63
|
icon: "tabler/outline/layout-list",
|
|
62
64
|
active_icon: "tabler/filled/layout-list"
|
|
63
65
|
end
|