avo 1.22.1 → 1.22.4
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.lock +1 -1
- data/app/components/avo/navigation_link_component.rb +1 -1
- data/app/controllers/avo/application_controller.rb +7 -0
- data/app/controllers/avo/base_controller.rb +0 -1
- data/app/javascript/js/application.js +1 -1
- data/app/javascript/js/controllers/action_controller.js +1 -1
- data/app/javascript/js/controllers/actions_picker_controller.js +1 -1
- data/app/javascript/js/controllers/alerts_controller.js +1 -1
- data/app/javascript/js/controllers/attachments_controller.js +1 -1
- data/app/javascript/js/controllers/fields/belongs_to_field_controller.js +1 -1
- data/app/javascript/js/controllers/fields/code_field_controller.js +1 -1
- data/app/javascript/js/controllers/fields/date_field_controller.js +1 -1
- data/app/javascript/js/controllers/fields/key_value_controller.js +3 -3
- data/app/javascript/js/controllers/fields/simple_mde_controller.js +1 -1
- data/app/javascript/js/controllers/fields/trix_field_controller.js +1 -1
- data/app/javascript/js/controllers/filter_controller.js +3 -7
- data/app/javascript/js/controllers/hidden_input_controller.js +1 -1
- data/app/javascript/js/controllers/item_select_all_controller.js +4 -4
- data/app/javascript/js/controllers/item_selector_controller.js +1 -1
- data/app/javascript/js/controllers/loading_button_controller.js +1 -1
- data/app/javascript/js/controllers/modal_controller.js +10 -1
- data/app/javascript/js/controllers/per_page_controller.js +1 -1
- data/app/javascript/js/controllers/search_controller.js +7 -2
- data/app/javascript/js/controllers/tippy_controller.js +1 -1
- data/app/javascript/js/controllers/toggle_panel_controller.js +1 -1
- data/app/views/avo/actions/show.html.erb +7 -3
- data/db/factories.rb +2 -2
- data/lib/avo/base_action.rb +21 -0
- data/lib/avo/base_resource.rb +18 -12
- data/lib/avo/fields/belongs_to_field.rb +9 -0
- data/lib/avo/licensing/h_q.rb +55 -1
- data/lib/avo/version.rb +1 -1
- data/public/avo-assets/avo.js +2569 -1926
- data/public/avo-assets/avo.js.map +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 472b0c4d748a2cc35c595170f3abd2c83e8ef62ea2f41ec15ba2fac1b4fba4eb
|
4
|
+
data.tar.gz: 28de6b788064fb476e80234f058e192dae5bc1866bb544c7db197a53adf801dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a89890b81a8d26b13f7ace5e3854b21b503bf7ae3e61c08e4ba0a52257b4644cb29f3c8a03789726aa9e4479b2a0c5f334163536198f8685f9df516539191467
|
7
|
+
data.tar.gz: 14402ad63c9918b69789dd0161124a06f74829f400138ff8cf972a36df7ac677b9d62319355b26f6a8da9db38c686d28728431a4d6c81ab3202e7df3c4779df3
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Avo::NavigationLinkComponent < ViewComponent::Base
|
4
|
-
def initialize(label: nil, path: nil, active: :inclusive, size: :md, target:
|
4
|
+
def initialize(label: nil, path: nil, active: :inclusive, size: :md, target: nil)
|
5
5
|
@label = label
|
6
6
|
@path = path
|
7
7
|
@active = active
|
@@ -13,6 +13,7 @@ module Avo
|
|
13
13
|
protect_from_forgery with: :exception
|
14
14
|
before_action :init_app
|
15
15
|
before_action :check_avo_license
|
16
|
+
before_action :set_locale
|
16
17
|
before_action :set_authorization
|
17
18
|
before_action :_authenticate!
|
18
19
|
before_action :set_container_classes
|
@@ -245,5 +246,11 @@ module Avo
|
|
245
246
|
def model_param_key
|
246
247
|
@resource.form_scope
|
247
248
|
end
|
249
|
+
|
250
|
+
def set_locale
|
251
|
+
I18n.locale = params[:locale] || I18n.default_locale
|
252
|
+
|
253
|
+
I18n.default_locale = I18n.locale
|
254
|
+
end
|
248
255
|
end
|
249
256
|
end
|
@@ -84,7 +84,6 @@ module Avo
|
|
84
84
|
def new
|
85
85
|
@model = @resource.model_class.new
|
86
86
|
@resource = @resource.hydrate(model: @model, view: :new, user: _current_user)
|
87
|
-
# abort @model.course.inspect
|
88
87
|
|
89
88
|
@page_title = @resource.default_panel_name
|
90
89
|
add_breadcrumb resource_name.humanize, resources_path(resource: @resource)
|
@@ -14,7 +14,7 @@ import 'codemirror/mode/vue/vue'
|
|
14
14
|
import 'codemirror/mode/xml/xml'
|
15
15
|
import 'codemirror/mode/yaml/yaml'
|
16
16
|
|
17
|
-
import { Controller } from 'stimulus'
|
17
|
+
import { Controller } from '@hotwired/stimulus'
|
18
18
|
import CodeMirror from 'codemirror'
|
19
19
|
|
20
20
|
import { castBoolean } from '../../helpers/cast_boolean'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/* eslint-disable max-len */
|
2
|
-
import { Controller } from 'stimulus'
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
3
3
|
import { castBoolean } from '../../helpers/cast_boolean'
|
4
4
|
|
5
5
|
export default class extends Controller {
|
@@ -39,7 +39,7 @@ export default class extends Controller {
|
|
39
39
|
|
40
40
|
deleteRow(event) {
|
41
41
|
if (this.options.disable_deleting_rows || !this.options.editable) return
|
42
|
-
const { index } = event.
|
42
|
+
const { index } = event.params
|
43
43
|
this.fieldValue.splice(index, 1)
|
44
44
|
this.updateTextareaInput()
|
45
45
|
this.updateKeyValueComponent()
|
@@ -93,7 +93,7 @@ export default class extends Controller {
|
|
93
93
|
if (this.options.editable) {
|
94
94
|
result += `<a
|
95
95
|
href="javascript:void(0);"
|
96
|
-
data-index="${index}"
|
96
|
+
data-key-value-index-param="${index}"
|
97
97
|
data-action="click->key-value#deleteRow"
|
98
98
|
title="${this.options.delete_text}"
|
99
99
|
data-tippy="tooltip"
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Controller } from 'stimulus'
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
2
|
import URI from 'urijs'
|
3
3
|
|
4
4
|
export default class extends Controller {
|
@@ -26,16 +26,12 @@ export default class extends Controller {
|
|
26
26
|
// then we convert the percent encodings into raw bytes which
|
27
27
|
// can be fed into btoa.
|
28
28
|
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
|
29
|
-
|
30
|
-
return String.fromCharCode('0x' + p1);
|
31
|
-
}));
|
29
|
+
(match, p1) => String.fromCharCode(`0x${p1}`)))
|
32
30
|
}
|
33
31
|
|
34
32
|
b64DecodeUnicode(str) {
|
35
33
|
// Going backwards: from bytestream, to percent-encoding, to original string.
|
36
|
-
return decodeURIComponent(atob(str).split('').map(
|
37
|
-
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
38
|
-
}).join(''));
|
34
|
+
return decodeURIComponent(atob(str).split('').map((c) => `%${(`00${c.charCodeAt(0).toString(16)}`).slice(-2)}`).join(''))
|
39
35
|
}
|
40
36
|
|
41
37
|
changeFilter() {
|
@@ -1,20 +1,20 @@
|
|
1
|
-
import { Controller } from 'stimulus'
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
2
|
|
3
3
|
export default class extends Controller {
|
4
|
-
static targets = [
|
4
|
+
static targets = ['itemCheckbox', 'checkbox']
|
5
5
|
|
6
6
|
connect() {
|
7
7
|
this.resourceName = this.element.dataset.resourceName
|
8
8
|
}
|
9
9
|
|
10
10
|
toggle(event) {
|
11
|
-
|
11
|
+
const value = !!event.target.checked
|
12
12
|
document.querySelectorAll(`[data-controller="item-selector"][data-resource-name="${this.resourceName}"] input[type=checkbox]`)
|
13
13
|
.forEach((checkbox) => checkbox.checked != value && checkbox.click())
|
14
14
|
}
|
15
15
|
|
16
16
|
update() {
|
17
|
-
|
17
|
+
let allSelected = true
|
18
18
|
this.itemCheckboxTargets.forEach((checkbox) => allSelected = allSelected && checkbox.checked)
|
19
19
|
this.checkboxTarget.checked = allSelected
|
20
20
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Controller } from 'stimulus'
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
2
|
|
3
3
|
export default class extends Controller {
|
4
4
|
static targets = ['modal']
|
@@ -8,4 +8,13 @@ export default class extends Controller {
|
|
8
8
|
|
9
9
|
document.dispatchEvent(new Event('actions-modal:close'))
|
10
10
|
}
|
11
|
+
|
12
|
+
delayedClose() {
|
13
|
+
const vm = this
|
14
|
+
|
15
|
+
setTimeout(() => {
|
16
|
+
vm.modalTarget.remove()
|
17
|
+
document.dispatchEvent(new Event('actions-modal:close'))
|
18
|
+
}, 500)
|
19
|
+
}
|
11
20
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/* eslint-disable no-underscore-dangle */
|
2
2
|
import * as Mousetrap from 'mousetrap'
|
3
|
-
import { Controller } from 'stimulus'
|
3
|
+
import { Controller } from '@hotwired/stimulus'
|
4
4
|
import { Turbo } from '@hotwired/turbo-rails'
|
5
5
|
import { autocomplete } from '@algolia/autocomplete-js'
|
6
6
|
import URI from 'urijs'
|
@@ -177,11 +177,16 @@ export default class extends Controller {
|
|
177
177
|
openOnFocus: true,
|
178
178
|
detachedMediaQuery: '',
|
179
179
|
getSources: ({ query }) => {
|
180
|
+
document.body.classList.add('search-loading')
|
180
181
|
const endpoint = that.searchUrl(query)
|
181
182
|
|
182
183
|
return that
|
183
184
|
.debouncedFetch(endpoint)
|
184
|
-
.then((response) =>
|
185
|
+
.then((response) => {
|
186
|
+
document.body.classList.remove('search-loading')
|
187
|
+
|
188
|
+
return response.json()
|
189
|
+
})
|
185
190
|
.then((data) => Object.keys(data).map((resourceName) => that.addSource(resourceName, data[resourceName])))
|
186
191
|
},
|
187
192
|
})
|
@@ -7,7 +7,11 @@
|
|
7
7
|
data-resource-id="<%= params[:id] %>"
|
8
8
|
class="hidden text-slate-800"
|
9
9
|
>
|
10
|
-
<%= form_with model: @model,
|
10
|
+
<%= form_with model: @model,
|
11
|
+
scope: 'fields',
|
12
|
+
url: "#{@resource.records_path}/actions/#{@action.param_id}",
|
13
|
+
data: @action.class.form_data_attributes do |form|
|
14
|
+
%>
|
11
15
|
<%= render Avo::ModalComponent.new do |c| %>
|
12
16
|
<% c.heading do %>
|
13
17
|
<%= @action.action_name %>
|
@@ -26,8 +30,8 @@
|
|
26
30
|
<% end %>
|
27
31
|
|
28
32
|
<% c.controls do %>
|
29
|
-
<%= a_button @action.cancel_button_label,
|
30
|
-
<%= a_button @action.confirm_button_label, type: :submit, color: :green, size: :sm %>
|
33
|
+
<%= a_button @action.cancel_button_label, data: { action: 'click->modal#close' }, size: :sm %>
|
34
|
+
<%= a_button @action.confirm_button_label, type: :submit, color: :green, size: :sm, data: @action.class.submit_button_data_attributes %>
|
31
35
|
<% end %>
|
32
36
|
<% end %>
|
33
37
|
<% end %>
|
data/db/factories.rb
CHANGED
@@ -42,11 +42,11 @@ FactoryBot.define do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
factory :comment do
|
45
|
-
body { Faker::Lorem.paragraphs(number: rand(4...10)) }
|
45
|
+
body { Faker::Lorem.paragraphs(number: rand(4...10)).join(' ') }
|
46
46
|
end
|
47
47
|
|
48
48
|
factory :review do
|
49
|
-
body { Faker::Lorem.paragraphs(number: rand(4...10)) }
|
49
|
+
body { Faker::Lorem.paragraphs(number: rand(4...10)).join(' ') }
|
50
50
|
end
|
51
51
|
|
52
52
|
factory :person do
|
data/lib/avo/base_action.rb
CHANGED
@@ -15,6 +15,7 @@ module Avo
|
|
15
15
|
class_attribute :fields
|
16
16
|
class_attribute :standalone, default: false
|
17
17
|
class_attribute :visible
|
18
|
+
class_attribute :may_download_file, default: false
|
18
19
|
|
19
20
|
attr_accessor :response
|
20
21
|
attr_accessor :model
|
@@ -22,6 +23,26 @@ module Avo
|
|
22
23
|
attr_accessor :user
|
23
24
|
attr_accessor :fields_loader
|
24
25
|
|
26
|
+
class << self
|
27
|
+
def form_data_attributes
|
28
|
+
# We can't respond with a file download from Turbo se we disable it on the form
|
29
|
+
if may_download_file
|
30
|
+
{ 'turbo': false }
|
31
|
+
else
|
32
|
+
{ 'turbo-frame': '_top', 'action-target': 'form' }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# We can't respond with a file download from Turbo se we disable close the modal manually after a while (it's a hack, we know)
|
37
|
+
def submit_button_data_attributes
|
38
|
+
if may_download_file
|
39
|
+
{ action: 'click->modal#delayedClose' }
|
40
|
+
else
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
25
46
|
def action_name
|
26
47
|
return name if name.present?
|
27
48
|
|
data/lib/avo/base_resource.rb
CHANGED
@@ -127,23 +127,29 @@ module Avo
|
|
127
127
|
field.visible?
|
128
128
|
end
|
129
129
|
.select do |field|
|
130
|
+
is_valid = true
|
131
|
+
|
130
132
|
# Strip out the reflection field in index queries with a parent association.
|
131
133
|
if reflection.present?
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
134
|
+
# regular non-polymorphic association
|
135
|
+
# we're matching the reflection inverse_of foriegn key with the field's foreign_key
|
136
|
+
if field.is_a?(Avo::Fields::BelongsToField)
|
137
|
+
if field.respond_to?(:foreign_key) &&
|
138
|
+
reflection.inverse_of.foreign_key == field.foreign_key
|
139
|
+
is_valid = false
|
140
|
+
end
|
141
|
+
|
142
|
+
# polymorphic association
|
143
|
+
if field.respond_to?(:foreign_key) &&
|
144
|
+
field.is_polymorphic? &&
|
145
|
+
reflection.respond_to?(:polymorphic?) &&
|
146
|
+
reflection.inverse_of.foreign_key == field.reflection.foreign_key
|
147
|
+
is_valid = false
|
148
|
+
end
|
143
149
|
end
|
144
150
|
end
|
145
151
|
|
146
|
-
|
152
|
+
is_valid
|
147
153
|
end
|
148
154
|
|
149
155
|
if panel.present?
|
@@ -136,6 +136,8 @@ module Avo
|
|
136
136
|
|
137
137
|
def is_polymorphic?
|
138
138
|
polymorphic_as.present?
|
139
|
+
rescue
|
140
|
+
false
|
139
141
|
end
|
140
142
|
|
141
143
|
def foreign_key
|
@@ -154,6 +156,13 @@ module Avo
|
|
154
156
|
nil
|
155
157
|
end
|
156
158
|
|
159
|
+
# Get the model reflection instance
|
160
|
+
def reflection
|
161
|
+
reflection_for_key(id)
|
162
|
+
rescue
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
|
157
166
|
def relation_model_class
|
158
167
|
@resource.model_class
|
159
168
|
end
|
data/lib/avo/licensing/h_q.rb
CHANGED
@@ -23,6 +23,12 @@ module Avo
|
|
23
23
|
|
24
24
|
begin
|
25
25
|
perform_and_cache_request
|
26
|
+
rescue Errno::EHOSTUNREACH => exception
|
27
|
+
cache_and_return_error "HTTP host not reachable error.", exception.message
|
28
|
+
rescue Errno::ECONNRESET => exception
|
29
|
+
cache_and_return_error "HTTP connection reset error.", exception.message
|
30
|
+
rescue Errno::ECONNREFUSED => exception
|
31
|
+
cache_and_return_error "HTTP connection refused error.", exception.message
|
26
32
|
rescue HTTParty::Error => exception
|
27
33
|
cache_and_return_error "HTTP client error.", exception.message
|
28
34
|
rescue Net::OpenTimeout => exception
|
@@ -71,7 +77,55 @@ module Avo
|
|
71
77
|
environment: Rails.env,
|
72
78
|
ip: current_request.ip,
|
73
79
|
host: current_request.host,
|
74
|
-
port: current_request.port
|
80
|
+
port: current_request.port,
|
81
|
+
app_name: app_name
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def app_name
|
86
|
+
Rails.application.class.to_s.split("::").first
|
87
|
+
rescue
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def avo_metadata
|
92
|
+
resources = App.resources
|
93
|
+
field_definitions = resources.map(&:get_field_definitions)
|
94
|
+
fields_count = field_definitions.map(&:count).sum
|
95
|
+
fields_per_resource = sprintf("%0.01f", fields_count / (resources.count + 0.0))
|
96
|
+
|
97
|
+
field_types = {}
|
98
|
+
custom_fields_count = 0
|
99
|
+
field_definitions.each do |fields|
|
100
|
+
fields.each do |field|
|
101
|
+
field_types[field.type] ||= 0
|
102
|
+
field_types[field.type] += 1
|
103
|
+
|
104
|
+
custom_fields_count += 1 if field.custom?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
{
|
109
|
+
resources_count: resources.count,
|
110
|
+
fields_count: fields_count,
|
111
|
+
fields_per_resource: fields_per_resource,
|
112
|
+
custom_fields_count: custom_fields_count,
|
113
|
+
field_types: field_types,
|
114
|
+
**other_metadata(:actions),
|
115
|
+
**other_metadata(:filters),
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def other_metadata(type = :actions)
|
120
|
+
resources = App.resources
|
121
|
+
|
122
|
+
types = resources.map(&:"get_#{type}")
|
123
|
+
type_count = types.flatten.uniq.count
|
124
|
+
type_per_resource = sprintf("%0.01f", types.map(&:count).sum / (resources.count + 0.0))
|
125
|
+
|
126
|
+
{
|
127
|
+
"#{type}_count": type_count,
|
128
|
+
"#{type}_per_resource": type_per_resource,
|
75
129
|
}
|
76
130
|
end
|
77
131
|
|
data/lib/avo/version.rb
CHANGED