rails_admin 0.7.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.

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