avo 0.3.2 → 0.4.5

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -2
  3. data/Gemfile.lock +8 -1
  4. data/app/controllers/avo/application_controller.rb +2 -2
  5. data/app/controllers/avo/relations_controller.rb +3 -3
  6. data/app/controllers/avo/resource_overview_controller.rb +1 -0
  7. data/app/controllers/avo/resources_controller.rb +8 -6
  8. data/app/views/layouts/avo/_javascript.html.erb +4 -2
  9. data/app/views/layouts/avo/_translations.html.erb +5 -0
  10. data/app/views/layouts/avo/application.html.erb +9 -4
  11. data/avo.gemspec +1 -0
  12. data/lib/avo.rb +2 -0
  13. data/lib/avo/app/action.rb +8 -5
  14. data/lib/avo/app/app.rb +31 -3
  15. data/lib/avo/app/fields/belongs_to.rb +14 -3
  16. data/lib/avo/app/fields/code_field.rb +2 -0
  17. data/lib/avo/app/fields/country_field.rb +1 -1
  18. data/lib/avo/app/fields/field.rb +3 -1
  19. data/lib/avo/app/fields/field_extensions/visible_in_different_views.rb +4 -0
  20. data/lib/avo/app/fields/has_many.rb +4 -2
  21. data/lib/avo/app/fields/has_one.rb +2 -2
  22. data/lib/avo/app/fields/markdown_field.rb +27 -0
  23. data/lib/avo/app/fields/password_field.rb +3 -1
  24. data/lib/avo/app/fields/select_field.rb +1 -1
  25. data/lib/avo/app/filter.rb +1 -12
  26. data/lib/avo/app/licensing/hq.rb +4 -3
  27. data/lib/avo/app/resource.rb +27 -9
  28. data/lib/avo/app/{authorization_service.rb → services/authorization_service.rb} +0 -0
  29. data/lib/avo/configuration.rb +14 -2
  30. data/lib/avo/engine.rb +5 -5
  31. data/lib/avo/version.rb +1 -1
  32. data/lib/generators/avo/install_generator.rb +2 -1
  33. data/lib/generators/avo/templates/{initializer.rb → initializer/avo.rb} +0 -0
  34. data/lib/generators/avo/templates/locales/avo.en.yml +60 -0
  35. data/public/avo-packs/css/application-c9595d99.css +3 -0
  36. data/public/avo-packs/css/application-c9595d99.css.br +0 -0
  37. data/public/avo-packs/css/application-c9595d99.css.gz +0 -0
  38. data/public/avo-packs/js/application-54cdc48985a045c704ef.js +3 -0
  39. data/public/avo-packs/js/{application-725ee54e3adbcd950843.js.LICENSE.txt → application-54cdc48985a045c704ef.js.LICENSE.txt} +0 -0
  40. data/public/avo-packs/js/application-54cdc48985a045c704ef.js.br +0 -0
  41. data/public/avo-packs/js/application-54cdc48985a045c704ef.js.gz +0 -0
  42. data/public/avo-packs/js/application-54cdc48985a045c704ef.js.map +1 -0
  43. data/public/avo-packs/js/application-54cdc48985a045c704ef.js.map.br +0 -0
  44. data/public/avo-packs/js/application-54cdc48985a045c704ef.js.map.gz +0 -0
  45. data/public/avo-packs/manifest.json +11 -6
  46. data/public/avo-packs/manifest.json.br +0 -0
  47. data/public/avo-packs/manifest.json.gz +0 -0
  48. data/public/avo-packs/media/font/fontello-068ca2b3.ttf +0 -0
  49. data/public/avo-packs/media/font/fontello-068ca2b3.ttf.br +0 -0
  50. data/public/avo-packs/media/font/fontello-068ca2b3.ttf.gz +0 -0
  51. data/public/avo-packs/media/font/fontello-8d4a4e6f.woff2 +0 -0
  52. data/public/avo-packs/media/font/fontello-9354499c.svg +72 -0
  53. data/public/avo-packs/media/font/fontello-9354499c.svg.br +0 -0
  54. data/public/avo-packs/media/font/fontello-9354499c.svg.gz +0 -0
  55. data/public/avo-packs/media/font/fontello-a782baa8.woff +0 -0
  56. data/public/avo-packs/media/font/fontello-e73a0647.eot +0 -0
  57. data/public/avo-packs/media/font/fontello-e73a0647.eot.br +0 -0
  58. data/public/avo-packs/media/font/fontello-e73a0647.eot.gz +0 -0
  59. metadata +42 -14
  60. data/public/avo-packs/css/application-73e568bc.css +0 -3
  61. data/public/avo-packs/css/application-73e568bc.css.br +0 -0
  62. data/public/avo-packs/css/application-73e568bc.css.gz +0 -0
  63. data/public/avo-packs/js/application-725ee54e3adbcd950843.js +0 -3
  64. data/public/avo-packs/js/application-725ee54e3adbcd950843.js.br +0 -0
  65. data/public/avo-packs/js/application-725ee54e3adbcd950843.js.gz +0 -0
  66. data/public/avo-packs/js/application-725ee54e3adbcd950843.js.map +0 -1
  67. data/public/avo-packs/js/application-725ee54e3adbcd950843.js.map.br +0 -0
  68. data/public/avo-packs/js/application-725ee54e3adbcd950843.js.map.gz +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7fc8f29ce910c3eeb8bb2d80e61bc7b65385fd0b4bbb71ef9cf85556cf943bc3
4
- data.tar.gz: 277415ac5daec5c8985d3ee62e2ec3dd779ea23df8b5fbc0993fb64da774a54a
3
+ metadata.gz: 9cab03834ddaad490fcd3f0eb64f9ac2006dacb235c2ffb34a451139da31444a
4
+ data.tar.gz: 20c9770b7b259b0207857e4b70450b73748c5d30b7dd405662b720e5f97499ba
5
5
  SHA512:
6
- metadata.gz: e44bf33db4a90366e85780d500d0d476b22b74a8cdec88bb39c1da5e86e5dad6c866bef3f5ab8386f6a51b382aa1ec19ec2f8d20e3aa940ced316d8ca3558701
7
- data.tar.gz: 2365d6464818c443ccb4808c041c084d1cfa5a795e63419e90524be642c64c817249e59b69b27d569ba97247c50089a58511c8f45d2106716f1d028bb506c3d6
6
+ metadata.gz: e024dbbb67133f849c0b45b65694ba6322bf8e07d5ae399280e9e233b54bf26ee954aaabf8eebdfd81e33db79b007ce78a73be5fc99e5fb3b5089b4a75121d81
7
+ data.tar.gz: 1d52cf19ca6a8a831222fd36762862de3e88f8f02c4469eaa19e98844da6f707677120e0f92d13bb47caed37fa3e7cf3f24b6f7f10a049805cbe222a3653fb42
data/Gemfile CHANGED
@@ -59,8 +59,7 @@ gem 'spring'
59
59
  gem 'spring-watcher-listen', '~> 2.0.0'
60
60
 
61
61
  gem 'factory_bot_rails'
62
- gem 'faker'
63
-
62
+ gem 'faker', require: false
64
63
  # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
65
64
  gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
66
65
 
@@ -94,3 +93,7 @@ gem 'zeitwerk', '~> 2.3'
94
93
  gem 'kaminari'
95
94
 
96
95
  gem 'httparty'
96
+
97
+ gem 'iso'
98
+
99
+ gem 'i18n-js'
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (0.3.2)
4
+ avo (0.4.5)
5
5
  countries
6
6
  httparty
7
+ i18n-js
7
8
  inline_svg
8
9
  kaminari
9
10
  pundit
@@ -132,10 +133,14 @@ GEM
132
133
  multi_xml (>= 0.5.2)
133
134
  i18n (1.8.5)
134
135
  concurrent-ruby (~> 1.0)
136
+ i18n-js (3.8.0)
137
+ i18n (>= 0.6.6)
135
138
  i18n_data (0.10.0)
136
139
  inline_svg (1.7.1)
137
140
  activesupport (>= 3.0)
138
141
  nokogiri (>= 1.6)
142
+ iso (0.3.0)
143
+ i18n
139
144
  jbuilder (2.10.1)
140
145
  activesupport (>= 5.0.0)
141
146
  kaminari (1.2.1)
@@ -341,7 +346,9 @@ DEPENDENCIES
341
346
  fuubar
342
347
  gem-release
343
348
  httparty
349
+ i18n-js
344
350
  inline_svg
351
+ iso
345
352
  jbuilder (~> 2.7)
346
353
  kaminari
347
354
  listen (>= 3.0.5, < 3.2)
@@ -5,7 +5,7 @@ module Avo
5
5
  before_action :init_app
6
6
 
7
7
  def init_app
8
- Avo::App.boot unless Rails.env.production?
8
+ Avo::App.boot if Avo::IN_DEVELOPMENT
9
9
  Avo::App.init request
10
10
 
11
11
  @license = Avo::App.license
@@ -58,7 +58,7 @@ module Avo
58
58
  end
59
59
 
60
60
  def render_unauthorized
61
- render json: { message: 'Unauthorized' }, status: 403
61
+ render json: { message: I18n.t('avo.unauthorized') }, status: 403
62
62
  end
63
63
  end
64
64
  end
@@ -9,7 +9,7 @@ module Avo
9
9
 
10
10
  render json: {
11
11
  success: true,
12
- message: "#{attachment_class} attached.",
12
+ message: I18n.t('avo.attachment_class_attached', attachment_class: attachment_class),
13
13
  }
14
14
  end
15
15
 
@@ -18,7 +18,7 @@ module Avo
18
18
 
19
19
  render json: {
20
20
  success: true,
21
- message: "#{attachment_class} attached.",
21
+ message: I18n.t('avo.attachment_class_detached', attachment_class: attachment_class),
22
22
  }
23
23
  end
24
24
 
@@ -28,7 +28,7 @@ module Avo
28
28
  end
29
29
 
30
30
  def attachment_model
31
- attachment_class.safe_constantize.find params[:attachment_id]
31
+ resource._reflections[params[:attachment_name].to_s].klass.find params[:attachment_id]
32
32
  end
33
33
  end
34
34
  end
@@ -5,6 +5,7 @@ module Avo
5
5
  def index
6
6
  resources = App.get_resources
7
7
  .select { |resource| AuthorizationService::authorize session_user, resource.model, Avo.configuration.authorization_methods.stringify_keys['index'] }
8
+ .sort_by(&:name)
8
9
  .map do |resource|
9
10
  {
10
11
  name: resource.name,
@@ -95,7 +95,7 @@ module Avo
95
95
  render json: {
96
96
  success: true,
97
97
  resource: Avo::Resources::Resource.hydrate_resource(model: resource, resource: avo_resource, view: :show, user: current_user),
98
- message: 'Resource updated',
98
+ message: I18n.t('avo.resource_updated'),
99
99
  }
100
100
  end
101
101
 
@@ -118,7 +118,7 @@ module Avo
118
118
  render json: {
119
119
  success: true,
120
120
  resource: Avo::Resources::Resource.hydrate_resource(model: resource, resource: avo_resource, view: :create, user: current_user),
121
- message: 'Resource created',
121
+ message: I18n.t('avo.resource_created'),
122
122
  }
123
123
  end
124
124
 
@@ -132,7 +132,7 @@ module Avo
132
132
  resource.destroy!
133
133
 
134
134
  render json: {
135
- message: 'Resource destroyed',
135
+ message: I18n.t('avo.resource_destroyed'),
136
136
  }
137
137
  end
138
138
 
@@ -224,8 +224,8 @@ module Avo
224
224
  avo_resource.get_filters.each do |filter_class|
225
225
  filter = filter_class.new
226
226
 
227
- if filter.default_value.present?
228
- filter_defaults[filter_class.to_s] = filter.default_value
227
+ if filter.default.present?
228
+ filter_defaults[filter_class.to_s] = filter.default
229
229
  end
230
230
  end
231
231
 
@@ -252,11 +252,13 @@ module Avo
252
252
 
253
253
  def build_meta
254
254
  {
255
- per_page_steps: Avo.configuration.per_page_steps,
255
+ per_page_steps: [*Avo.configuration.per_page_steps, Avo.configuration.per_page.to_i].sort.uniq,
256
256
  available_view_types: avo_resource.available_view_types,
257
257
  default_view_type: avo_resource.default_view_type || Avo.configuration.default_view_type,
258
+ translation_key: avo_resource.translation_key,
258
259
  authorization: {
259
260
  create: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['create']),
261
+ edit: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['edit']),
260
262
  update: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['update']),
261
263
  show: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['show']),
262
264
  destroy: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['destroy']),
@@ -1,6 +1,8 @@
1
- <script>
1
+ <%= javascript_tag nonce: true do %>
2
2
  var rootPath = '<%= Avo.configuration.root_path %>';
3
3
  var timezone = '<%= Avo.configuration.timezone %>';
4
+ var locale = '<%= Avo.configuration.locale %>';
4
5
  var defaultViewType = '<%= Avo.configuration.default_view_type %>';
5
6
  var license = <%= Avo::App.license.properties.to_json.html_safe %>;
6
- </script>
7
+ var avoResources = <%= Avo::App.get_available_resources(current_user).as_json.html_safe %>;
8
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%= javascript_tag nonce: true do %>
2
+ I18n.defaultLocale = 'en';
3
+ I18n.locale = "<%= Avo.configuration.language_code %>";
4
+ <% end %>
5
+ <%= javascript_include_tag 'translations', skip_pipeline: true %>
@@ -8,8 +8,10 @@
8
8
 
9
9
  <%= javascript_pack_tag 'application' %>
10
10
  <%= stylesheet_pack_tag 'application', media: 'all' %>
11
+ <%= render partial: 'layouts/avo/translations' %>
11
12
  <link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap" rel="stylesheet">
12
13
  </head>
14
+
13
15
  <body class="bg-gray-200">
14
16
 
15
17
  <div id="app" class="flex min-h-screen flex-row h-full">
@@ -19,7 +21,7 @@
19
21
  inline-template
20
22
  >
21
23
  <div>
22
- <application-sidebar :resources='<%= Avo::App.get_resources_navigation(current_user).as_json.html_safe %>' v-if="layout !== 'blank'">
24
+ <application-sidebar v-if="layout !== 'blank'">
23
25
  <template #logo>
24
26
  <%= render_logo %>
25
27
  </template>
@@ -28,8 +30,8 @@
28
30
  </template>
29
31
  </application-sidebar>
30
32
 
31
- <div class="flex-1 h-full overflow-auto">
32
- <div class="relative bg-white p-2 shadow-md h-16 w-full flex items-center z-40" v-if="layout !== 'blank'">
33
+ <div class="flex-1 flex flex-col h-full overflow-auto">
34
+ <div class="relative bg-white p-2 shadow-md h-16 w-full flex flex-shrink-0 items-center z-50" v-if="layout !== 'blank'">
33
35
  <div class="ml-6">
34
36
  <%= render_header %>
35
37
  </div>
@@ -43,7 +45,8 @@
43
45
  <div v-if="layout === 'blank'" class="h-full w-full flex justify-center items-center">
44
46
  <%= yield %>
45
47
  </div>
46
- <div class="content p-8" v-else>
48
+
49
+ <div class="content p-8 flex-1 flex flex-col justify-between items-stretch" v-else>
47
50
  <%= yield %>
48
51
  <%= render_footer %>
49
52
  </div>
@@ -51,6 +54,8 @@
51
54
  </div>
52
55
  </app-layout>
53
56
  </div>
57
+
54
58
  <%= render_scripts %>
59
+
55
60
  </body>
56
61
  </html>
@@ -40,4 +40,5 @@ Gem::Specification.new do |spec|
40
40
  spec.add_dependency 'countries'
41
41
  spec.add_dependency 'pundit'
42
42
  spec.add_dependency 'httparty'
43
+ spec.add_dependency 'i18n-js'
43
44
  end
data/lib/avo.rb CHANGED
@@ -15,6 +15,8 @@ require_relative 'avo/app/licensing/license_manager'
15
15
 
16
16
  module Avo
17
17
  ROOT_PATH = Pathname.new(File.join(__dir__, '..'))
18
+ IN_DEVELOPMENT = ENV['AVO_IN_DEVELOPMENT'] == '1'
19
+ PACKED = !IN_DEVELOPMENT
18
20
 
19
21
  class << self
20
22
  def webpacker
@@ -15,6 +15,7 @@ module Avo
15
15
  # filename: String
16
16
  # }
17
17
  attr_accessor :response
18
+ attr_accessor :no_confirmation
18
19
 
19
20
  @@default = nil
20
21
 
@@ -37,15 +38,16 @@ module Avo
37
38
 
38
39
  def initialize
39
40
  @name ||= name
40
- @message ||= 'Are you sure you want to run this action?'
41
+ @message ||= I18n.t('avo.are_you_sure_you_want_to_run_this_option')
41
42
  @default ||= ''
42
43
  @fields ||= []
43
- @confirm_text = 'Run'
44
- @cancel_text = 'Cancel'
44
+ @confirm_text = I18n.t('avo.run')
45
+ @cancel_text = I18n.t('avo.cancel')
45
46
  @response ||= {}
46
47
  @response[:message_type] ||= :success
47
- @response[:message] ||= 'Action ran successfully!'
48
+ @response[:message] ||= I18n.t('avo.action_ran_successfully')
48
49
  @theme ||= 'success'
50
+ @no_confirmation ||= false
49
51
  end
50
52
 
51
53
  def render_response(model, resource)
@@ -61,6 +63,7 @@ module Avo
61
63
  cancel_text: cancel_text,
62
64
  default: default,
63
65
  action_class: self.class.to_s,
66
+ no_confirmation: no_confirmation,
64
67
  }
65
68
  end
66
69
 
@@ -87,7 +90,7 @@ module Avo
87
90
  end
88
91
 
89
92
  def name
90
- self.class.name.demodulize.underscore.humanize
93
+ self.class.name.demodulize.underscore.humanize(keep_id_suffix: true)
91
94
  end
92
95
 
93
96
  def succeed(text)
@@ -4,7 +4,7 @@ require_relative 'filters/select_filter'
4
4
  require_relative 'filters/boolean_filter'
5
5
  require_relative 'resource'
6
6
  require_relative 'tool'
7
- require_relative 'authorization_service'
7
+ require_relative 'services/authorization_service'
8
8
 
9
9
  module Avo
10
10
  class App
@@ -12,6 +12,7 @@ module Avo
12
12
  root_path: '',
13
13
  resources: [],
14
14
  field_names: {},
15
+ cache_store: nil
15
16
  }
16
17
  @@license = nil
17
18
 
@@ -19,6 +20,13 @@ module Avo
19
20
  def boot
20
21
  @@app[:root_path] = Pathname.new(File.join(__dir__, '..', '..'))
21
22
  init_fields
23
+ I18n.locale = Avo.configuration.language_code
24
+
25
+ if Rails.cache.class == ActiveSupport::Cache::NullStore
26
+ @@app[:cache_store] ||= ActiveSupport::Cache::MemoryStore.new
27
+ else
28
+ @@app[:cache_store] = Rails.cache
29
+ end
22
30
  end
23
31
 
24
32
  def init(current_request = nil)
@@ -34,6 +42,10 @@ module Avo
34
42
  @@license
35
43
  end
36
44
 
45
+ def cache_store
46
+ @@app[:cache_store]
47
+ end
48
+
37
49
  # This method will take all fields available in the Avo::Fields namespace and create a method for them.
38
50
  #
39
51
  # If the field has their `def_method` set up it will follow that convention, if not it will snake_case the name:
@@ -120,6 +132,16 @@ module Avo
120
132
  self.get_resource name.singularize.camelize
121
133
  end
122
134
 
135
+ # This returns the Avo resource by singular snake_cased name
136
+ #
137
+ # get_resource_by_name('User') => Avo::Resources::User
138
+ # get_resource_by_name(User) => Avo::Resources::User
139
+ def get_resource_by_model_name(name)
140
+ get_resources.find do |resource|
141
+ resource.class.name.demodulize == name.to_s
142
+ end
143
+ end
144
+
123
145
  # This returns the Rails model class by singular snake_cased name
124
146
  #
125
147
  # get_model_class_by_name('user') => User
@@ -127,10 +149,16 @@ module Avo
127
149
  name.to_s.camelize.singularize
128
150
  end
129
151
 
130
- def get_resources_navigation(user)
152
+ def get_available_resources(user)
131
153
  App.get_resources
132
154
  .select { |resource| AuthorizationService::authorize user, resource.model, Avo.configuration.authorization_methods.stringify_keys['index'] }
133
- .map { |resource| { label: resource.resource_name_plural.humanize, resource_name: resource.url.pluralize } }
155
+ .map do |resource|
156
+ {
157
+ label: resource.plural_name.humanize(keep_id_suffix: true),
158
+ resource_name: resource.url.pluralize,
159
+ translation_key: resource.translation_key
160
+ }
161
+ end
134
162
  .reject { |i| i.blank? }
135
163
  .to_json
136
164
  .to_s
@@ -7,7 +7,7 @@ module Avo
7
7
  def initialize(name, **args, &block)
8
8
  @defaults = {
9
9
  component: 'belongs-to-field',
10
- placeholder: "Choose #{name.downcase}",
10
+ placeholder: I18n.t('avo.choose_an_option')
11
11
  }
12
12
 
13
13
  @searchable = args[:searchable] == true ? true : false
@@ -22,7 +22,8 @@ module Avo
22
22
  fields[:searchable] = @searchable
23
23
  fields[:is_relation] = true
24
24
  fields[:database_id] = foreign_key model
25
- target_resource = App.get_resources.find { |r| r.class == "Avo::Resources::#{name}".safe_constantize }
25
+
26
+ target_resource = get_target_resource model
26
27
 
27
28
  relation_model = model.public_send(@relation_method)
28
29
 
@@ -49,7 +50,7 @@ module Avo
49
50
  end
50
51
  end
51
52
 
52
- fields[:resource_name_plural] = target_resource.resource_name_plural
53
+ fields[:plural_name] = target_resource.plural_name
53
54
 
54
55
  fields
55
56
  end
@@ -61,6 +62,16 @@ module Avo
61
62
  model.class.reflections[@relation_method].foreign_key
62
63
  end
63
64
  end
65
+
66
+ def get_target_resource(model)
67
+ if model._reflections[id.to_s].klass.present?
68
+ App.get_resource_by_model_name model._reflections[id.to_s].klass.to_s
69
+ elsif model._reflections[id.to_s].options[:class_name].present?
70
+ App.get_resource_by_model_name model._reflections[id.to_s].options[:class_name]
71
+ else
72
+ App.get_resource_by_name class_name.to_s
73
+ end
74
+ end
64
75
  end
65
76
  end
66
77
  end
@@ -12,12 +12,14 @@ module Avo
12
12
 
13
13
  @language = args[:language].present? ? args[:language].to_s : 'javascript'
14
14
  @theme = args[:theme].present? ? args[:theme].to_s : 'material-darker'
15
+ @height = args[:height].present? ? args[:height].to_s : 'auto'
15
16
  end
16
17
 
17
18
  def hydrate_field(fields, model, resource, view)
18
19
  {
19
20
  language: @language,
20
21
  theme: @theme,
22
+ height: @height,
21
23
  }
22
24
  end
23
25
  end
@@ -5,7 +5,7 @@ module Avo
5
5
  @defaults = {
6
6
  sortable: true,
7
7
  component: 'country-field',
8
- placeholder: 'Choose a country',
8
+ placeholder: I18n.t('avo.choose_a_country'),
9
9
  }
10
10
 
11
11
  super(name, **args, &block)