rails_admin 0.7.0 → 0.8.1

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.

Potentially problematic release.


This version of rails_admin might be problematic. Click here for more details.

Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +13 -2
  3. data/README.md +2 -2
  4. data/app/assets/javascripts/rails_admin/ra.filter-box.js +53 -39
  5. data/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js +50 -32
  6. data/app/assets/javascripts/rails_admin/ra.i18n.coffee +5 -3
  7. data/app/assets/javascripts/rails_admin/ra.widgets.coffee +21 -6
  8. data/app/assets/javascripts/rails_admin/rails_admin.js +2 -3
  9. data/app/assets/stylesheets/rails_admin/base/theming.scss +6 -7
  10. data/app/assets/stylesheets/rails_admin/custom/mixins.scss +3 -4
  11. data/app/assets/stylesheets/rails_admin/custom/theming.scss +4 -4
  12. data/app/assets/stylesheets/rails_admin/custom/variables.scss +4 -4
  13. data/app/assets/stylesheets/rails_admin/rails_admin.scss.erb +3 -3
  14. data/app/assets/stylesheets/rails_admin/themes/default/mixins.scss +2 -2
  15. data/app/assets/stylesheets/rails_admin/themes/default/theming.scss +3 -3
  16. data/app/assets/stylesheets/rails_admin/themes/default/variables.scss +3 -3
  17. data/app/controllers/rails_admin/application_controller.rb +0 -2
  18. data/app/controllers/rails_admin/main_controller.rb +6 -5
  19. data/app/helpers/rails_admin/application_helper.rb +3 -3
  20. data/app/helpers/rails_admin/main_helper.rb +45 -0
  21. data/app/views/layouts/rails_admin/application.html.haml +2 -2
  22. data/app/views/layouts/rails_admin/pjax.html.haml +2 -2
  23. data/app/views/rails_admin/main/_form_datetime.html.haml +4 -1
  24. data/app/views/rails_admin/main/_form_filtering_multiselect.html.haml +1 -0
  25. data/app/views/rails_admin/main/_submit_buttons.html.haml +1 -1
  26. data/app/views/rails_admin/main/export.html.haml +13 -13
  27. data/app/views/rails_admin/main/index.html.haml +20 -64
  28. data/config/locales/rails_admin.en.yml +0 -1
  29. data/lib/generators/rails_admin/templates/initializer.erb +3 -0
  30. data/lib/rails_admin.rb +2 -1
  31. data/lib/rails_admin/abstract_model.rb +14 -16
  32. data/lib/rails_admin/adapters/active_record.rb +17 -8
  33. data/lib/rails_admin/adapters/active_record/association.rb +5 -0
  34. data/lib/rails_admin/adapters/mongoid.rb +7 -12
  35. data/lib/rails_admin/adapters/mongoid/association.rb +5 -0
  36. data/lib/rails_admin/config/actions/export.rb +1 -1
  37. data/lib/rails_admin/config/fields.rb +1 -0
  38. data/lib/rails_admin/config/fields/association.rb +5 -0
  39. data/lib/rails_admin/config/fields/base.rb +4 -0
  40. data/lib/rails_admin/config/fields/factories/paperclip.rb +1 -1
  41. data/lib/rails_admin/config/fields/factories/refile.rb +25 -0
  42. data/lib/rails_admin/config/fields/types/active_record_enum.rb +5 -5
  43. data/lib/rails_admin/config/fields/types/all.rb +1 -0
  44. data/lib/rails_admin/config/fields/types/bson_object_id.rb +16 -2
  45. data/lib/rails_admin/config/fields/types/date.rb +19 -8
  46. data/lib/rails_admin/config/fields/types/datetime.rb +33 -117
  47. data/lib/rails_admin/config/fields/types/json.rb +5 -2
  48. data/lib/rails_admin/config/fields/types/refile.rb +27 -0
  49. data/lib/rails_admin/config/fields/types/serialized.rb +5 -2
  50. data/lib/rails_admin/config/fields/types/time.rb +6 -18
  51. data/lib/rails_admin/config/has_fields.rb +2 -2
  52. data/lib/rails_admin/config/lazy_model.rb +2 -2
  53. data/lib/rails_admin/config/proxyable/proxy.rb +2 -4
  54. data/lib/rails_admin/extensions/pundit.rb +3 -0
  55. data/lib/rails_admin/extensions/pundit/authorization_adapter.rb +63 -0
  56. data/lib/rails_admin/support/datetime.rb +99 -0
  57. data/lib/rails_admin/support/hash_helper.rb +28 -0
  58. data/lib/rails_admin/support/i18n.rb +41 -0
  59. data/lib/rails_admin/version.rb +2 -2
  60. data/vendor/assets/javascripts/rails_admin/bootstrap-datetimepicker.js +2444 -0
  61. data/vendor/assets/javascripts/rails_admin/moment-with-locales.js +9977 -0
  62. data/vendor/assets/stylesheets/rails_admin/_bootstrap-datetimepicker.scss +343 -0
  63. data/vendor/assets/stylesheets/rails_admin/bootstrap-datetimepicker-build.scss +16 -0
  64. metadata +14 -9
  65. data/app/assets/javascripts/rails_admin/jquery.ui.timepicker.js +0 -1437
  66. data/app/assets/javascripts/rails_admin/ra.datetimepicker.js +0 -83
  67. data/app/assets/stylesheets/rails_admin/jquery.ui.timepicker.scss +0 -68
  68. data/app/assets/stylesheets/rails_admin/ra.calendar-additions.scss +0 -45
  69. data/lib/rails_admin/i18n_support.rb +0 -39
  70. data/lib/rails_admin/support/core_extensions.rb +0 -30
@@ -27,7 +27,6 @@ en:
27
27
  next: "Next »"
28
28
  truncate: "…"
29
29
  misc:
30
- filter_date_format: "mm/dd/yy" # a combination of 'dd', 'mm' and 'yy' with any delimiter. No other interpolation will be done!
31
30
  search: "Search"
32
31
  filter: "Filter"
33
32
  refresh: "Refresh"
@@ -11,6 +11,9 @@ RailsAdmin.config do |config|
11
11
  ## == Cancan ==
12
12
  # config.authorize_with :cancan
13
13
 
14
+ ## == Pundit ==
15
+ # config.authorize_with :pundit
16
+
14
17
  ## == PaperTrail ==
15
18
  # config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0
16
19
 
@@ -4,10 +4,11 @@ require 'rails_admin/config'
4
4
  require 'rails_admin/extension'
5
5
  require 'rails_admin/extensions/cancan'
6
6
  require 'rails_admin/extensions/cancancan'
7
+ require 'rails_admin/extensions/pundit'
7
8
  require 'rails_admin/extensions/paper_trail'
8
9
  require 'rails_admin/extensions/history'
9
10
  require 'rails_admin/support/csv_converter'
10
- require 'rails_admin/support/core_extensions'
11
+ require 'rails_admin/support/hash_helper'
11
12
 
12
13
  module RailsAdmin
13
14
  # Setup RailsAdmin
@@ -1,3 +1,5 @@
1
+ require 'rails_admin/support/datetime'
2
+
1
3
  module RailsAdmin
2
4
  class AbstractModel
3
5
  cattr_accessor :all
@@ -109,6 +111,10 @@ module RailsAdmin
109
111
  extend Adapters::Mongoid
110
112
  end
111
113
 
114
+ def parse_field_value(field, value)
115
+ value.is_a?(Array) ? value.map { |v| field.parse_value(v) } : field.parse_value(value)
116
+ end
117
+
112
118
  class StatementBuilder
113
119
  def initialize(column, type, value, operator)
114
120
  @column = column
@@ -119,7 +125,6 @@ module RailsAdmin
119
125
 
120
126
  def to_statement
121
127
  return if [@operator, @value].any? { |v| v == '_discard' }
122
-
123
128
  unary_operators[@operator] || unary_operators[@value] ||
124
129
  build_statement_for_type_generic
125
130
  end
@@ -166,13 +171,16 @@ module RailsAdmin
166
171
  end
167
172
 
168
173
  def build_statement_for_date
169
- range_filter(*get_filtering_duration)
174
+ start_date, end_date = get_filtering_duration
175
+ start_date = (start_date.to_date rescue nil) if start_date
176
+ end_date = (end_date.to_date rescue nil) if end_date
177
+ range_filter(start_date, end_date)
170
178
  end
171
179
 
172
180
  def build_statement_for_datetime_or_timestamp
173
181
  start_date, end_date = get_filtering_duration
174
- start_date = start_date.to_time.beginning_of_day if start_date
175
- end_date = end_date.to_time.end_of_day if end_date
182
+ start_date = start_date.to_time.try(:beginning_of_day) if start_date
183
+ end_date = end_date.to_time.try(:end_of_day) if end_date
176
184
  range_filter(start_date, end_date)
177
185
  end
178
186
 
@@ -219,7 +227,7 @@ module RailsAdmin
219
227
  end
220
228
 
221
229
  def between
222
- [convert_to_date(@value[1]), convert_to_date(@value[2])]
230
+ [@value[1], @value[2]]
223
231
  end
224
232
 
225
233
  def default
@@ -228,18 +236,8 @@ module RailsAdmin
228
236
 
229
237
  private
230
238
 
231
- def date_format
232
- I18n.t('admin.misc.filter_date_format',
233
- default: I18n.t('admin.misc.filter_date_format', locale: :en)).gsub('dd', '%d').gsub('mm', '%m').gsub('yy', '%Y')
234
- end
235
-
236
- def convert_to_date(value)
237
- value.present? && Date.strptime(value, date_format)
238
- end
239
-
240
239
  def default_date
241
- default_date_value = Array.wrap(@value).first
242
- convert_to_date(default_date_value) rescue false
240
+ Array.wrap(@value).first
243
241
  end
244
242
  end
245
243
  end
@@ -40,7 +40,7 @@ module RailsAdmin
40
40
  end
41
41
 
42
42
  def count(options = {}, scope = nil)
43
- all(options.merge(limit: false, page: false), scope).count
43
+ all(options.merge(limit: false, page: false), scope).count(:all)
44
44
  end
45
45
 
46
46
  def destroy(objects)
@@ -94,6 +94,11 @@ module RailsAdmin
94
94
 
95
95
  def add(field, value, operator)
96
96
  field.searchable_columns.flatten.each do |column_infos|
97
+ if value.is_a?(Array)
98
+ value = value.map { |v| field.parse_value(v) }
99
+ else
100
+ value = field.parse_value(value)
101
+ end
97
102
  statement, value1, value2 = StatementBuilder.new(column_infos[:column], column_infos[:type], value, operator).to_statement
98
103
  @statements << statement if statement.present?
99
104
  @values << value1 unless value1.nil?
@@ -113,7 +118,7 @@ module RailsAdmin
113
118
  def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))
114
119
  wb = WhereBuilder.new(scope)
115
120
  fields.each do |field|
116
- wb.add(field, query, field.search_operator)
121
+ wb.add(field, field.parse_value(query), field.search_operator)
117
122
  end
118
123
  # OR all query statements
119
124
  wb.build
@@ -125,7 +130,10 @@ module RailsAdmin
125
130
  filters.each_pair do |field_name, filters_dump|
126
131
  filters_dump.each do |_, filter_dump|
127
132
  wb = WhereBuilder.new(scope)
128
- wb.add(fields.detect { |f| f.name.to_s == field_name }, filter_dump[:v], (filter_dump[:o] || 'default'))
133
+ field = fields.detect { |f| f.name.to_s == field_name }
134
+ value = parse_field_value(field, filter_dump[:v])
135
+
136
+ wb.add(field, value, (filter_dump[:o] || 'default'))
129
137
  # AND current filter statements to other filter statements
130
138
  scope = wb.build
131
139
  end
@@ -203,7 +211,12 @@ module RailsAdmin
203
211
  return
204
212
  end
205
213
  end
206
- ["(LOWER(#{@column}) #{like_operator} ?)", @value]
214
+
215
+ if ar_adapter == 'postgresql'
216
+ ["(#{@column} ILIKE ?)", @value]
217
+ else
218
+ ["(LOWER(#{@column}) LIKE ?)", @value]
219
+ end
207
220
  end
208
221
 
209
222
  def build_statement_for_enum
@@ -214,10 +227,6 @@ module RailsAdmin
214
227
  def ar_adapter
215
228
  ::ActiveRecord::Base.connection.adapter_name.downcase
216
229
  end
217
-
218
- def like_operator
219
- ar_adapter == 'postgresql' ? 'ILIKE' : 'LIKE'
220
- end
221
230
  end
222
231
  end
223
232
  end
@@ -37,6 +37,11 @@ module RailsAdmin
37
37
  association.foreign_key.to_sym
38
38
  end
39
39
 
40
+ def foreign_key_nullable?
41
+ return true if foreign_key.nil? || type != :has_many
42
+ (column = klass.columns_hash[foreign_key.to_s]).nil? || column.null
43
+ end
44
+
40
45
  def foreign_type
41
46
  options[:foreign_type].try(:to_sym) || :"#{name}_type" if options[:polymorphic]
42
47
  end
@@ -8,7 +8,6 @@ module RailsAdmin
8
8
  module Adapters
9
9
  module Mongoid
10
10
  DISABLED_COLUMN_TYPES = ['Range', 'Moped::BSON::Binary', 'BSON::Binary', 'Mongoid::Geospatial::Point']
11
- ObjectId = defined?(Moped::BSON) ? Moped::BSON::ObjectId : BSON::ObjectId # rubocop:disable ConstantName
12
11
 
13
12
  def new(params = {})
14
13
  AbstractObject.new(model.new(params))
@@ -67,9 +66,7 @@ module RailsAdmin
67
66
 
68
67
  def properties
69
68
  fields = model.fields.reject { |_name, field| DISABLED_COLUMN_TYPES.include?(field.type.to_s) }
70
- fields.collect do |_name, field|
71
- Property.new(field, model)
72
- end
69
+ fields.collect { |_name, field| Property.new(field, model) }
73
70
  end
74
71
 
75
72
  def table_name
@@ -102,6 +99,7 @@ module RailsAdmin
102
99
  conditions_per_collection = {}
103
100
  field.searchable_columns.each do |column_infos|
104
101
  collection_name, column_name = parse_collection_name(column_infos[:column])
102
+ value = parse_field_value(field, value)
105
103
  statement = build_statement(column_name, column_infos[:type], value, operator)
106
104
  next unless statement
107
105
  conditions_per_collection[collection_name] ||= []
@@ -114,7 +112,8 @@ module RailsAdmin
114
112
  statements = []
115
113
 
116
114
  fields.each do |field|
117
- conditions_per_collection = make_field_conditions(field, query, field.search_operator)
115
+ value = parse_field_value(field, query)
116
+ conditions_per_collection = make_field_conditions(field, value, field.search_operator)
118
117
  statements.concat make_condition_for_current_collection(field, conditions_per_collection)
119
118
  end
120
119
 
@@ -134,7 +133,8 @@ module RailsAdmin
134
133
  filters_dump.each do |_, filter_dump|
135
134
  field = fields.detect { |f| f.name.to_s == field_name }
136
135
  next unless field
137
- conditions_per_collection = make_field_conditions(field, filter_dump[:v], (filter_dump[:o] || 'default'))
136
+ value = parse_field_value(field, filter_dump[:v])
137
+ conditions_per_collection = make_field_conditions(field, value, (filter_dump[:o] || 'default'))
138
138
  field_statements = make_condition_for_current_collection(field, conditions_per_collection)
139
139
  if field_statements.many?
140
140
  statements << {'$or' => field_statements}
@@ -265,8 +265,7 @@ module RailsAdmin
265
265
  end
266
266
 
267
267
  def build_statement_for_belongs_to_association_or_bson_object_id
268
- object_id = (object_id_from_string(@value) rescue nil)
269
- {@column => object_id} if object_id
268
+ {@column => @value} if @value
270
269
  end
271
270
 
272
271
  def range_filter(min, max)
@@ -278,10 +277,6 @@ module RailsAdmin
278
277
  {@column => {'$lte' => max}}
279
278
  end
280
279
  end
281
-
282
- def object_id_from_string(str)
283
- ObjectId.from_string(str)
284
- end
285
280
  end
286
281
  end
287
282
  end
@@ -48,6 +48,11 @@ module RailsAdmin
48
48
  association.foreign_key.to_sym rescue nil
49
49
  end
50
50
 
51
+ def foreign_key_nullable?
52
+ return if foreign_key.nil?
53
+ true
54
+ end
55
+
51
56
  def foreign_type
52
57
  return unless polymorphic? && [:referenced_in, :belongs_to].include?(macro)
53
58
  association.inverse_type.try(:to_sym) || :"#{name}_type"
@@ -16,7 +16,7 @@ module RailsAdmin
16
16
  proc do
17
17
  if format = params[:json] && :json || params[:csv] && :csv || params[:xml] && :xml
18
18
  request.format = format
19
- @schema = params[:schema].symbolize if params[:schema] # to_json and to_xml expect symbols for keys AND values.
19
+ @schema = HashHelper.symbolize(params[:schema]) if params[:schema] # to_json and to_xml expect symbols for keys AND values.
20
20
  @objects = list_entries(@model_config, :export)
21
21
  index
22
22
  else
@@ -82,4 +82,5 @@ require 'rails_admin/config/fields/factories/devise'
82
82
  require 'rails_admin/config/fields/factories/paperclip'
83
83
  require 'rails_admin/config/fields/factories/dragonfly'
84
84
  require 'rails_admin/config/fields/factories/carrierwave'
85
+ require 'rails_admin/config/fields/factories/refile'
85
86
  require 'rails_admin/config/fields/factories/association'
@@ -57,6 +57,11 @@ module RailsAdmin
57
57
  @associated_collection_cache_all ||= (associated_model_config.abstract_model.count < 100)
58
58
  end
59
59
 
60
+ # determines whether association's elements can be removed
61
+ register_instance_option :removable? do
62
+ association.foreign_key_nullable?
63
+ end
64
+
60
65
  # Reader for the association's child model's configuration
61
66
  def associated_model_config
62
67
  @associated_model_config ||= RailsAdmin.config(association.klass)
@@ -301,6 +301,10 @@ module RailsAdmin
301
301
  (translated.is_a?(Hash) ? translated.to_a.first[1] : translated).html_safe
302
302
  end
303
303
 
304
+ def parse_value(value)
305
+ value
306
+ end
307
+
304
308
  def parse_input(_params)
305
309
  # overriden
306
310
  end
@@ -5,7 +5,7 @@ require 'rails_admin/config/fields/types/file_upload'
5
5
  RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
6
6
  extensions = [:file_name, :content_type, :file_size, :updated_at, :fingerprint]
7
7
  model = parent.abstract_model.model
8
- if (properties.name.to_s =~ /^(.+)_file_name$/) && defined?(::Paperclip) && model.attachment_definitions && model.attachment_definitions.key?(attachment_name = Regexp.last_match[1].to_sym)
8
+ if (properties.name.to_s =~ /^(.+)_file_name$/) && defined?(::Paperclip) && model.try(:attachment_definitions) && model.attachment_definitions.key?(attachment_name = Regexp.last_match[1].to_sym)
9
9
  field = RailsAdmin::Config::Fields::Types.load(:paperclip).new(parent, attachment_name, properties)
10
10
  children_fields = []
11
11
  extensions.each do |ext|
@@ -0,0 +1,25 @@
1
+ require 'rails_admin/config/fields'
2
+ require 'rails_admin/config/fields/types'
3
+ require 'rails_admin/config/fields/types/file_upload'
4
+
5
+ RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
6
+ extensions = [:id, :filename, :size, :content_type]
7
+ model = parent.abstract_model.model
8
+ if (properties.name.to_s =~ /^(.+)_id$/) && defined?(::Refile) && model.ancestors.map(&:to_s).include?("Refile::Attachment(#{attachment_name = Regexp.last_match[1].to_sym})")
9
+ field = RailsAdmin::Config::Fields::Types.load(:refile).new(parent, attachment_name, properties)
10
+ children_fields = []
11
+ extensions.each do |ext|
12
+ children_column_name = "#{attachment_name}_#{ext}".to_sym
13
+ next unless child_properties = parent.abstract_model.properties.detect { |p| p.name.to_s == children_column_name.to_s }
14
+ children_field = fields.detect { |f| f.name == children_column_name } || RailsAdmin::Config::Fields.default_factory.call(parent, child_properties, fields)
15
+ children_field.hide
16
+ children_field.filterable(false)
17
+ children_fields << children_field.name
18
+ end
19
+ field.children_fields(children_fields)
20
+ fields << field
21
+ true
22
+ else
23
+ false
24
+ end
25
+ end
@@ -27,12 +27,12 @@ module RailsAdmin
27
27
  false
28
28
  end
29
29
 
30
+ def parse_value(value)
31
+ value.present? ? enum.invert[value.to_i] : nil
32
+ end
33
+
30
34
  def parse_input(params)
31
- if params[name].present?
32
- params[name] = enum.invert[params[name].to_i]
33
- elsif params[name]
34
- params[name] = nil
35
- end
35
+ params[name] = parse_value(params[name]) if params[name]
36
36
  end
37
37
  end
38
38
  end
@@ -10,6 +10,7 @@ require 'rails_admin/config/fields/types/enum'
10
10
  require 'rails_admin/config/fields/types/file_upload'
11
11
  require 'rails_admin/config/fields/types/paperclip'
12
12
  require 'rails_admin/config/fields/types/carrierwave'
13
+ require 'rails_admin/config/fields/types/refile'
13
14
  require 'rails_admin/config/fields/types/float'
14
15
  require 'rails_admin/config/fields/types/has_and_belongs_to_many_association'
15
16
  require 'rails_admin/config/fields/types/has_many_association'
@@ -8,6 +8,14 @@ module RailsAdmin
8
8
  # Register field type for the type loader
9
9
  RailsAdmin::Config::Fields::Types.register(self)
10
10
 
11
+ OBJECT_ID ||= begin
12
+ if defined?(Moped::BSON)
13
+ Moped::BSON::ObjectId
14
+ elsif defined?(BSON::ObjectId)
15
+ BSON::ObjectId
16
+ end
17
+ end
18
+
11
19
  register_instance_option :label do
12
20
  label = ((@label ||= {})[::I18n.locale] ||= abstract_model.model.human_attribute_name name)
13
21
  label = 'Id' if label == ''
@@ -26,13 +34,19 @@ module RailsAdmin
26
34
  serial?
27
35
  end
28
36
 
29
- def parse_input(params)
30
- params[name] = (params[name].blank? ? nil : abstract_model.object_id_from_string(params[name])) if params[name].is_a?(::String)
37
+ def parse_value(value)
38
+ value.present? ? OBJECT_ID.from_string(value) : nil
39
+ rescue BSON::ObjectId::Invalid
40
+ nil
31
41
  rescue => e
32
42
  unless ['BSON::InvalidObjectId', 'Moped::Errors::InvalidObjectId'].include? e.class.to_s
33
43
  raise e
34
44
  end
35
45
  end
46
+
47
+ def parse_input(params)
48
+ params[name] = parse_value(params[name]) if params[name].is_a?(::String)
49
+ end
36
50
  end
37
51
  end
38
52
  end
@@ -5,17 +5,28 @@ module RailsAdmin
5
5
  module Fields
6
6
  module Types
7
7
  class Date < RailsAdmin::Config::Fields::Types::Datetime
8
- # Register field type for the type loader
9
8
  RailsAdmin::Config::Fields::Types.register(self)
10
9
 
11
- @format = :long
12
- @i18n_scope = [:date, :formats]
13
- @js_plugin_options = {
14
- 'showTime' => false,
15
- }
10
+ register_instance_option :date_format do
11
+ :long
12
+ end
13
+
14
+ register_instance_option :i18n_scope do
15
+ [:date, :formats]
16
+ end
17
+
18
+ register_instance_option :datepicker_options do
19
+ {
20
+ showTodayButton: true,
21
+ format: parser.to_momentjs,
22
+ }
23
+ end
16
24
 
17
- def parse_input(params)
18
- params[name] = self.class.normalize(params[name], localized_date_format).to_date if params[name].present?
25
+ register_instance_option :html_attributes do
26
+ {
27
+ required: required?,
28
+ size: 18,
29
+ }
19
30
  end
20
31
  end
21
32
  end
@@ -1,148 +1,68 @@
1
1
  require 'rails_admin/config/fields/base'
2
- require 'rails_admin/i18n_support'
2
+ require 'rails_admin/support/datetime'
3
3
 
4
4
  module RailsAdmin
5
5
  module Config
6
6
  module Fields
7
7
  module Types
8
8
  class Datetime < RailsAdmin::Config::Fields::Base
9
- # Register field type for the type loader
10
9
  RailsAdmin::Config::Fields::Types.register(self)
11
10
 
12
- @format = :long
13
- @i18n_scope = [:time, :formats]
14
- @js_plugin_options = {}
15
-
16
- class << self
17
- include RailsAdmin::I18nSupport
18
-
19
- attr_reader :format, :i18n_scope, :js_plugin_options
20
-
21
- def normalize(date_string, format)
22
- unless I18n.locale == 'en'
23
- format.to_s.scan(/%[AaBbp]/) do |match|
24
- case match
25
- when '%A'
26
- english = I18n.t('date.day_names', locale: :en)
27
- day_names.each_with_index { |d, i| date_string = date_string.gsub(/#{d}/, english[i]) }
28
- when '%a'
29
- english = I18n.t('date.abbr_day_names', locale: :en)
30
- abbr_day_names.each_with_index { |d, i| date_string = date_string.gsub(/#{d}/, english[i]) }
31
- when '%B'
32
- english = I18n.t('date.month_names', locale: :en)[1..-1]
33
- month_names.each_with_index { |m, i| date_string = date_string.gsub(/#{m}/, english[i]) }
34
- when '%b'
35
- english = I18n.t('date.abbr_month_names', locale: :en)[1..-1]
36
- abbr_month_names.each_with_index { |m, i| date_string = date_string.gsub(/#{m}/, english[i]) }
37
- when '%p'
38
- date_string = date_string.gsub(/#{I18n.t('date.time.am', default: "am")}/, 'am')
39
- date_string = date_string.gsub(/#{I18n.t('date.time.pm', default: "pm")}/, 'pm')
40
- end
41
- end
42
- end
43
- parse_date_string(date_string)
44
- end
45
-
46
- # Parse normalized date strings using time zone
47
- def parse_date_string(date_string)
48
- ::Time.zone.parse(date_string)
49
- end
11
+ def parser
12
+ @parser ||= RailsAdmin::Support::Datetime.new(strftime_format)
50
13
  end
51
14
 
52
- def formatted_date_value
53
- value = bindings[:object].new_record? && self.value.nil? ? default_value : self.value
54
- value.nil? ? '' : I18n.l(value, format: localized_date_format).strip
15
+ def parse_value(value)
16
+ parser.parse_string(value)
55
17
  end
56
18
 
57
- def formatted_time_value
58
- value.nil? ? '' : I18n.l(value, format: localized_time_format)
59
- end
60
-
61
- # Ruby to javascript formatting options translator
62
- def js_date_format
63
- # Ruby format options as a key and javascript format options
64
- # as a value
65
- translations = {
66
- '%a' => 'D', # The abbreviated weekday name ("Sun")
67
- '%A' => 'DD', # The full weekday name ("Sunday")
68
- '%b' => 'M', # The abbreviated month name ("Jan")
69
- '%B' => 'MM', # The full month name ("January")
70
- '%d' => 'dd', # Day of the month (01..31)
71
- '%D' => 'mm/dd/y', # American date format mm/dd/yy
72
- '%e' => 'd', # Day of the month (1..31)
73
- '%F' => 'yy-mm-dd', # ISO 8601 date format
74
- # "%H" => "??", # Hour of the day, 24-hour clock (00..23)
75
- # "%I" => "??", # Hour of the day, 12-hour clock (01..12)
76
- '%m' => 'mm', # Month of the year (01..12)
77
- '%-m' => 'm', # Month of the year (1..12)
78
- # "%M" => "??", # Minute of the hour (00..59)
79
- # "%p" => "??", # Meridian indicator ("AM" or "PM")
80
- # "%S" => "??", # Second of the minute (00..60)
81
- '%Y' => 'yy', # Year with century
82
- '%y' => 'y', # Year without a century (00..99)
83
- }
84
- localized_date_format.gsub(/%\w/) { |match| translations[match] }
19
+ def parse_input(params)
20
+ params[name] = parse_value(params[name]) if params[name]
85
21
  end
86
22
 
87
- def js_plugin_options
88
- options = {
89
- 'datepicker' => {
90
- 'dateFormat' => js_date_format,
91
- 'dayNames' => self.class.day_names,
92
- 'dayNamesShort' => self.class.abbr_day_names,
93
- 'dayNamesMin' => self.class.abbr_day_names,
94
- 'firstDay' => 1,
95
- 'monthNames' => self.class.month_names,
96
- 'monthNamesShort' => self.class.abbr_month_names,
97
- 'value' => formatted_date_value,
98
- },
99
- 'timepicker' => {
100
- 'amPmText' => meridian_indicator? ? %w(Am Pm) : ['', ''],
101
- 'hourText' => I18n.t('datetime.prompts.hour', default: I18n.t('datetime.prompts.hour', locale: :en)),
102
- 'minuteText' => I18n.t('datetime.prompts.minute', default: I18n.t('datetime.prompts.minute', locale: :en)),
103
- 'showPeriod' => meridian_indicator?,
104
- 'value' => formatted_time_value,
105
- },
106
- }
107
-
108
- options.merge(self.class.js_plugin_options)
23
+ def value
24
+ parent_value = super
25
+ if %w(DateTime Date Time).include?(parent_value.class.name)
26
+ parent_value.in_time_zone
27
+ else
28
+ parent_value
29
+ end
109
30
  end
110
31
 
111
- def localized_format(scope = [:time, :formats])
112
- format = date_format.to_sym
113
- I18n.t(format, scope: scope, default: [
114
- I18n.t(format, scope: scope, locale: :en),
115
- I18n.t(self.class.format, scope: scope, locale: :en),
116
- ]).to_s
32
+ register_instance_option :date_format do
33
+ :long
117
34
  end
118
35
 
119
- def localized_date_format
120
- localized_format([:date, :formats])
36
+ register_instance_option :i18n_scope do
37
+ [:time, :formats]
121
38
  end
122
39
 
123
- def localized_time_format
124
- meridian_indicator? ? '%I:%M %p' : '%H:%M'
40
+ register_instance_option :strftime_format do
41
+ fallback = ::I18n.t(date_format, scope: i18n_scope, locale: :en)
42
+ ::I18n.t(date_format, scope: i18n_scope, default: fallback).to_s
125
43
  end
126
44
 
127
- def meridian_indicator?
128
- strftime_format.include? '%p'
45
+ register_instance_option :datepicker_options do
46
+ {
47
+ showTodayButton: true,
48
+ format: parser.to_momentjs,
49
+ }
129
50
  end
130
51
 
131
- def parse_input(params)
132
- params[name] = self.class.normalize(params[name], "#{localized_date_format} #{localized_time_format}") if params[name].present?
52
+ register_instance_option :html_attributes do
53
+ {
54
+ required: required?,
55
+ size: 22,
56
+ }
133
57
  end
134
58
 
135
59
  register_instance_option :sort_reverse? do
136
60
  true
137
61
  end
138
62
 
139
- register_instance_option :date_format do
140
- self.class.format
141
- end
142
-
143
63
  register_instance_option :formatted_value do
144
- if time = value
145
- I18n.l(time, format: strftime_format)
64
+ if time = (value || default_value)
65
+ ::I18n.l(time, format: strftime_format)
146
66
  else
147
67
  ''.html_safe
148
68
  end
@@ -151,10 +71,6 @@ module RailsAdmin
151
71
  register_instance_option :partial do
152
72
  :form_datetime
153
73
  end
154
-
155
- register_instance_option :strftime_format do
156
- localized_format self.class.i18n_scope
157
- end
158
74
  end
159
75
  end
160
76
  end