avo 0.2.3 → 0.4.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 +52 -48
- data/Gemfile.lock +26 -2
- data/README.md +30 -9
- data/app/controllers/avo/application_controller.rb +34 -1
- data/app/controllers/avo/filters_controller.rb +19 -0
- data/app/controllers/avo/relations_controller.rb +34 -0
- data/app/controllers/avo/resource_overview_controller.rb +15 -7
- data/app/controllers/avo/resources_controller.rb +71 -145
- data/app/controllers/avo/search_controller.rb +55 -0
- data/app/helpers/avo/application_helper.rb +6 -2
- data/app/views/layouts/avo/_javascript.html.erb +3 -0
- data/app/views/layouts/avo/_translations.html.erb +5 -0
- data/app/views/layouts/avo/application.html.erb +40 -18
- data/avo.gemspec +5 -2
- data/config/credentials.yml.enc +1 -0
- data/config/routes.rb +11 -7
- data/lib/avo.rb +4 -0
- data/lib/avo/app/action.rb +8 -5
- data/lib/avo/app/app.rb +35 -28
- data/lib/avo/app/fields/belongs_to.rb +2 -2
- data/lib/avo/app/fields/code_field.rb +2 -0
- data/lib/avo/app/fields/country_field.rb +1 -1
- data/lib/avo/app/fields/field.rb +3 -1
- data/lib/avo/app/fields/field_extensions/visible_in_different_views.rb +4 -0
- data/lib/avo/app/fields/has_and_belongs_to_many.rb +1 -0
- data/lib/avo/app/fields/has_many.rb +1 -0
- data/lib/avo/app/fields/has_one.rb +2 -2
- data/lib/avo/app/fields/id_field.rb +4 -4
- data/lib/avo/app/fields/markdown_field.rb +27 -0
- data/lib/avo/app/fields/password_field.rb +3 -1
- data/lib/avo/app/fields/select_field.rb +1 -1
- data/lib/avo/app/licensing/community_license.rb +4 -0
- data/lib/avo/app/licensing/hq.rb +86 -0
- data/lib/avo/app/licensing/license.rb +48 -0
- data/lib/avo/app/licensing/license_manager.rb +25 -0
- data/lib/avo/app/licensing/null_license.rb +12 -0
- data/lib/avo/app/licensing/pro_license.rb +9 -0
- data/lib/avo/app/resource.rb +49 -18
- data/lib/avo/app/services/authorization_service.rb +40 -0
- data/lib/avo/configuration.rb +28 -2
- data/lib/avo/engine.rb +7 -7
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/install_generator.rb +2 -1
- data/lib/generators/avo/templates/{initializer.rb → initializer/avo.rb} +2 -0
- data/lib/generators/avo/templates/locales/avo.en.yml +60 -0
- data/lib/generators/avo/templates/views/_scripts.html.erb +0 -0
- data/public/avo-packs/css/application-2f609d81.css +3 -0
- data/public/avo-packs/css/application-2f609d81.css.br +0 -0
- data/public/avo-packs/css/application-2f609d81.css.gz +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js +3 -0
- data/public/avo-packs/js/{application-9a0dde96ad9918852965.js.LICENSE.txt → application-84e2d573c3c15df1fb7b.js.LICENSE.txt} +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.br +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.gz +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.map +1 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.map.br +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.map.gz +0 -0
- data/public/avo-packs/manifest.json +13 -6
- data/public/avo-packs/manifest.json.br +0 -0
- data/public/avo-packs/manifest.json.gz +0 -0
- data/public/avo-packs/media/font/fontello-068ca2b3.ttf +0 -0
- data/public/avo-packs/media/font/fontello-068ca2b3.ttf.br +0 -0
- data/public/avo-packs/media/font/fontello-068ca2b3.ttf.gz +0 -0
- data/public/avo-packs/media/font/fontello-8d4a4e6f.woff2 +0 -0
- data/public/avo-packs/media/font/fontello-9354499c.svg +72 -0
- data/public/avo-packs/media/font/fontello-9354499c.svg.br +0 -0
- data/public/avo-packs/media/font/fontello-9354499c.svg.gz +0 -0
- data/public/avo-packs/media/font/fontello-a782baa8.woff +0 -0
- data/public/avo-packs/media/font/fontello-e73a0647.eot +0 -0
- data/public/avo-packs/media/font/fontello-e73a0647.eot.br +0 -0
- data/public/avo-packs/media/font/fontello-e73a0647.eot.gz +0 -0
- data/public/avo-packs/media/svgs/arrow-circle-right-1ad1e15ec9a7aa54b67d126566a5aa2d.svg +1 -0
- data/public/avo-packs/media/svgs/arrow-circle-right-1ad1e15ec9a7aa54b67d126566a5aa2d.svg.br +0 -0
- data/public/avo-packs/media/svgs/arrow-circle-right-1ad1e15ec9a7aa54b67d126566a5aa2d.svg.gz +0 -0
- data/public/avo-packs/media/svgs/exclamation-8d1c0baa390a8df9bb52176011eb5892.svg +1 -0
- data/public/avo-packs/media/svgs/exclamation-8d1c0baa390a8df9bb52176011eb5892.svg.br +0 -0
- data/public/avo-packs/media/svgs/exclamation-8d1c0baa390a8df9bb52176011eb5892.svg.gz +0 -0
- metadata +90 -22
- data/public/avo-packs/css/application-5dc4dd78.css +0 -3
- data/public/avo-packs/css/application-5dc4dd78.css.br +0 -0
- data/public/avo-packs/css/application-5dc4dd78.css.gz +0 -0
- data/public/avo-packs/js/application-9a0dde96ad9918852965.js +0 -3
- data/public/avo-packs/js/application-9a0dde96ad9918852965.js.br +0 -0
- data/public/avo-packs/js/application-9a0dde96ad9918852965.js.gz +0 -0
- data/public/avo-packs/js/application-9a0dde96ad9918852965.js.map +0 -1
- data/public/avo-packs/js/application-9a0dde96ad9918852965.js.map.br +0 -0
- data/public/avo-packs/js/application-9a0dde96ad9918852965.js.map.gz +0 -0
@@ -13,7 +13,7 @@ module Avo
|
|
13
13
|
|
14
14
|
hide_on :create
|
15
15
|
|
16
|
-
@placeholder =
|
16
|
+
@placeholder = I18n.t 'avo.choose_an_option'
|
17
17
|
|
18
18
|
@relation_method = name.to_s.parameterize.underscore
|
19
19
|
end
|
@@ -43,7 +43,7 @@ module Avo
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
fields[:
|
46
|
+
fields[:plural_name] = target_resource.plural_name
|
47
47
|
|
48
48
|
fields
|
49
49
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module Avo
|
2
2
|
module Fields
|
3
3
|
class IdField < Field
|
4
|
-
DEFAULT_VALUE = 'id'
|
5
|
-
|
6
4
|
def initialize(name, **args, &block)
|
5
|
+
default_value = 'id'
|
6
|
+
|
7
7
|
if name.nil?
|
8
|
-
@name = name =
|
8
|
+
@name = name = default_value
|
9
9
|
elsif !name.is_a? String and !name.is_a? Symbol
|
10
10
|
args_copy = name
|
11
|
-
@name = name =
|
11
|
+
@name = name = default_value
|
12
12
|
args = args_copy
|
13
13
|
end
|
14
14
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'field'
|
2
|
+
|
3
|
+
module Avo
|
4
|
+
module Fields
|
5
|
+
class MarkdownField < Field
|
6
|
+
def initialize(name, **args, &block)
|
7
|
+
@defaults = {
|
8
|
+
component: 'markdown-field',
|
9
|
+
}
|
10
|
+
|
11
|
+
super(name, **args, &block)
|
12
|
+
|
13
|
+
hide_on :index
|
14
|
+
|
15
|
+
@always_show = args[:always_show].present? ? args[:always_show] : false
|
16
|
+
@height = args[:height].present? ? args[:height].to_s : 'auto'
|
17
|
+
end
|
18
|
+
|
19
|
+
def hydrate_field(fields, model, resource, view)
|
20
|
+
{
|
21
|
+
always_show: @always_show,
|
22
|
+
height: @height
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -12,7 +12,7 @@ module Avo
|
|
12
12
|
@options = args[:options].present? ? args[:options] : {}
|
13
13
|
@enum = args[:enum].present? ? args[:enum] : nil
|
14
14
|
@display_value = args[:display_value].present? ? args[:display_value] : false
|
15
|
-
@placeholder = args[:placeholder].present? ? args[:placeholder].to_s : '
|
15
|
+
@placeholder = args[:placeholder].present? ? args[:placeholder].to_s : I18n.t('avo.choose_an_option')
|
16
16
|
end
|
17
17
|
|
18
18
|
def hydrate_field(fields, model, resource, view)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Avo
|
2
|
+
class HQ
|
3
|
+
attr_accessor :current_request
|
4
|
+
|
5
|
+
ENDPOINT = 'https://avohq.io/api/v1/licenses/check'
|
6
|
+
CACHE_KEY = 'avo.hq.response'
|
7
|
+
REQUEST_TIMEOUT = 5 # seconds
|
8
|
+
|
9
|
+
def initialize(current_request)
|
10
|
+
@current_request = current_request
|
11
|
+
@cache_store = Avo::App.cache_store
|
12
|
+
end
|
13
|
+
|
14
|
+
def response
|
15
|
+
@hq_response or request
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def request
|
20
|
+
return cached_response if has_cached_response
|
21
|
+
|
22
|
+
begin
|
23
|
+
perform_and_cache_request
|
24
|
+
rescue HTTParty::Error => exception
|
25
|
+
cache_and_return_error 'HTTP client error.', exception.message
|
26
|
+
rescue Net::OpenTimeout => exception
|
27
|
+
cache_and_return_error 'Request timeout.', exception.message
|
28
|
+
rescue SocketError => exception
|
29
|
+
cache_and_return_error 'Connection error.', exception.message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def perform_and_cache_request
|
34
|
+
hq_response = perform_request
|
35
|
+
|
36
|
+
return cache_and_return_error 'Avo HQ Internal server error.', hq_response.body if hq_response.code == 500
|
37
|
+
|
38
|
+
cache_response 1.hour.to_i, hq_response.parsed_response if hq_response.code == 200
|
39
|
+
end
|
40
|
+
|
41
|
+
def cache_response(time, response)
|
42
|
+
response.merge!(
|
43
|
+
expiry: time,
|
44
|
+
**payload,
|
45
|
+
).stringify_keys!
|
46
|
+
|
47
|
+
@cache_store.write(CACHE_KEY, response, expires_in: time)
|
48
|
+
|
49
|
+
@hq_response = response
|
50
|
+
|
51
|
+
response
|
52
|
+
end
|
53
|
+
|
54
|
+
def perform_request
|
55
|
+
puts 'Performing request to avohq.io API to check license availability.'.inspect if Rails.env.development?
|
56
|
+
|
57
|
+
HTTParty.post ENDPOINT, body: payload.to_json, headers: { 'Content-type': 'application/json' }, timeout: REQUEST_TIMEOUT
|
58
|
+
end
|
59
|
+
|
60
|
+
def payload
|
61
|
+
{
|
62
|
+
license: Avo.configuration.license,
|
63
|
+
license_key: Avo.configuration.license_key,
|
64
|
+
avo_version: Avo::VERSION,
|
65
|
+
rails_version: Rails::VERSION::STRING,
|
66
|
+
ruby_version: RUBY_VERSION,
|
67
|
+
environment: Rails.env,
|
68
|
+
ip: current_request.ip,
|
69
|
+
host: current_request.host,
|
70
|
+
port: current_request.port,
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def cache_and_return_error(error, exception_message = '')
|
75
|
+
cache_response 5.minutes.to_i, { error: error, exception_message: exception_message }.stringify_keys
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_cached_response
|
79
|
+
@cache_store.exist? CACHE_KEY
|
80
|
+
end
|
81
|
+
|
82
|
+
def cached_response
|
83
|
+
@cache_store.read CACHE_KEY
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Avo
|
2
|
+
class License
|
3
|
+
attr_accessor :id
|
4
|
+
attr_accessor :response
|
5
|
+
attr_accessor :valid
|
6
|
+
|
7
|
+
def initialize(response)
|
8
|
+
@response = response
|
9
|
+
@id = response['id']
|
10
|
+
@valid = response['valid']
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?
|
14
|
+
valid
|
15
|
+
end
|
16
|
+
|
17
|
+
def invalid?
|
18
|
+
!valid?
|
19
|
+
end
|
20
|
+
|
21
|
+
def pro?
|
22
|
+
id == 'pro'
|
23
|
+
end
|
24
|
+
|
25
|
+
def error
|
26
|
+
@response['error']
|
27
|
+
end
|
28
|
+
|
29
|
+
def properties
|
30
|
+
@response.slice 'valid', 'id', 'error'
|
31
|
+
end
|
32
|
+
|
33
|
+
def abilities
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
|
37
|
+
def can(ability)
|
38
|
+
abilities.include? ability
|
39
|
+
end
|
40
|
+
|
41
|
+
def cant(ability)
|
42
|
+
!can ability
|
43
|
+
end
|
44
|
+
|
45
|
+
alias_method :has, :can
|
46
|
+
alias_method :lacks, :cant
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'license'
|
2
|
+
require_relative 'community_license'
|
3
|
+
require_relative 'pro_license'
|
4
|
+
require_relative 'null_license'
|
5
|
+
|
6
|
+
module Avo
|
7
|
+
class LicenseManager
|
8
|
+
def initialize(hq_response)
|
9
|
+
@hq_response = hq_response
|
10
|
+
end
|
11
|
+
|
12
|
+
def license
|
13
|
+
return NullLicense.new if Rails.env.test? and ENV['RUN_WITH_NULL_LICENSE'] == '1'
|
14
|
+
|
15
|
+
case @hq_response['id']
|
16
|
+
when 'community'
|
17
|
+
CommunityLicense.new @hq_response
|
18
|
+
when 'pro'
|
19
|
+
ProLicense.new @hq_response
|
20
|
+
else
|
21
|
+
NullLicense.new @hq_response
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/avo/app/resource.rb
CHANGED
@@ -12,14 +12,17 @@ module Avo
|
|
12
12
|
attr_reader :default_view_type
|
13
13
|
|
14
14
|
class << self
|
15
|
-
def hydrate_resource(model
|
16
|
-
default_panel_name =
|
15
|
+
def hydrate_resource(model:, resource:, view: :index, user:)
|
16
|
+
default_panel_name = I18n.t 'avo.resource_details', name: resource.name
|
17
17
|
|
18
18
|
resource_with_fields = {
|
19
19
|
id: model.id,
|
20
|
-
|
21
|
-
|
20
|
+
authorization: get_authorization(user, model),
|
21
|
+
singular_name: resource.singular_name,
|
22
|
+
plural_name: resource.plural_name,
|
22
23
|
title: model[resource.title],
|
24
|
+
translation_key: resource.translation_key,
|
25
|
+
path: resource.url,
|
23
26
|
fields: [],
|
24
27
|
grid_fields: {},
|
25
28
|
panels: [{
|
@@ -39,6 +42,8 @@ module Avo
|
|
39
42
|
|
40
43
|
furnished_field = field.fetch_for_resource(model, resource, view)
|
41
44
|
|
45
|
+
next unless field_resource_authorized field, furnished_field, user
|
46
|
+
|
42
47
|
next if furnished_field.blank?
|
43
48
|
|
44
49
|
furnished_field[:panel_name] = default_panel_name
|
@@ -74,19 +79,41 @@ module Avo
|
|
74
79
|
|
75
80
|
"/resources/#{url}"
|
76
81
|
end
|
82
|
+
|
83
|
+
def get_authorization(user, model)
|
84
|
+
[:create, :edit, :update, :show, :destroy].map do |action|
|
85
|
+
[action, AuthorizationService::authorize_action(user, model, action)]
|
86
|
+
end.to_h
|
87
|
+
end
|
88
|
+
|
89
|
+
def field_resource_authorized(field, furnished_field, user)
|
90
|
+
if [Avo::Fields::HasManyField, Avo::Fields::HasAndBelongsToManyField].include? field.class
|
91
|
+
return true if furnished_field[:relationship_model].nil?
|
92
|
+
|
93
|
+
AuthorizationService.authorize user, furnished_field[:relationship_model].safe_constantize, Avo.configuration.authorization_methods.stringify_keys['index']
|
94
|
+
else
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end
|
77
98
|
end
|
78
99
|
|
79
100
|
def name
|
80
101
|
return @name if @name.present?
|
81
102
|
|
103
|
+
return I18n.t(@translation_key, count: 1).capitalize if @translation_key
|
104
|
+
|
82
105
|
self.class.name.demodulize.titlecase
|
83
106
|
end
|
84
107
|
|
85
|
-
def
|
108
|
+
def singular_name
|
109
|
+
return I18n.t(@translation_key, count: 1).capitalize if @translation_key
|
110
|
+
|
86
111
|
name
|
87
112
|
end
|
88
113
|
|
89
|
-
def
|
114
|
+
def plural_name
|
115
|
+
return I18n.t(@translation_key, count: 2).capitalize if @translation_key
|
116
|
+
|
90
117
|
name.pluralize
|
91
118
|
end
|
92
119
|
|
@@ -108,6 +135,10 @@ module Avo
|
|
108
135
|
'id'
|
109
136
|
end
|
110
137
|
|
138
|
+
def translation_key
|
139
|
+
@translation_key
|
140
|
+
end
|
141
|
+
|
111
142
|
def model
|
112
143
|
return @model if @model.present?
|
113
144
|
|
@@ -138,26 +169,26 @@ module Avo
|
|
138
169
|
@search
|
139
170
|
end
|
140
171
|
|
141
|
-
def query_search(query: '', via_resource_name: , via_resource_id:)
|
142
|
-
|
172
|
+
def query_search(query: '', via_resource_name: , via_resource_id:, user:)
|
173
|
+
model_class = self.model
|
174
|
+
|
175
|
+
db_query = AuthorizationService.with_policy(user, model_class)
|
143
176
|
|
144
177
|
if via_resource_name.present?
|
145
|
-
|
146
|
-
|
147
|
-
db_query = related_model.find(via_resource_id).public_send(self.
|
178
|
+
related_model = App.get_resource_by_name(via_resource_name).model
|
179
|
+
|
180
|
+
db_query = related_model.find(via_resource_id).public_send(self.plural_name.downcase)
|
148
181
|
end
|
149
182
|
|
183
|
+
new_query = []
|
184
|
+
|
150
185
|
[self.search].flatten.each_with_index do |search_by, index|
|
151
|
-
|
186
|
+
new_query.push 'or' if index != 0
|
152
187
|
|
153
|
-
|
154
|
-
db_query = db_query.where query_string
|
155
|
-
else
|
156
|
-
db_query = db_query.or(self.model.where query_string)
|
157
|
-
end
|
188
|
+
new_query.push "text(#{search_by}) ILIKE '%#{query}%'"
|
158
189
|
end
|
159
190
|
|
160
|
-
db_query.
|
191
|
+
db_query.where(new_query.join(' '))
|
161
192
|
end
|
162
193
|
|
163
194
|
def model
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Avo
|
2
|
+
class AuthorizationService
|
3
|
+
class << self
|
4
|
+
def authorize(user, record, action)
|
5
|
+
return true if skip_authorization
|
6
|
+
|
7
|
+
begin
|
8
|
+
if Pundit.policy user, record
|
9
|
+
Pundit.authorize user, record, action
|
10
|
+
end
|
11
|
+
true
|
12
|
+
rescue Pundit::NotAuthorizedError => error
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def authorize_action(user, record, action)
|
18
|
+
action = Avo.configuration.authorization_methods.stringify_keys[action.to_s]
|
19
|
+
|
20
|
+
return true if action.nil?
|
21
|
+
|
22
|
+
authorize user, record, action
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_policy(user, model)
|
26
|
+
return model if skip_authorization
|
27
|
+
|
28
|
+
begin
|
29
|
+
Pundit.policy_scope! user, model
|
30
|
+
rescue => exception
|
31
|
+
model
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def skip_authorization
|
36
|
+
Avo::App.license.lacks :authorization
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|