ab_admin 0.7.0 → 0.8.0

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/admin/flags/de.png +0 -0
  3. data/app/assets/images/admin/flags/en.png +0 -0
  4. data/app/assets/images/admin/flags/es.png +0 -0
  5. data/app/assets/images/admin/flags/fr.png +0 -0
  6. data/app/assets/images/admin/flags/it.png +0 -0
  7. data/app/assets/images/admin/flags/ja.png +0 -0
  8. data/app/assets/images/admin/flags/pl.png +0 -0
  9. data/app/assets/images/admin/flags/ru.png +0 -0
  10. data/app/assets/images/admin/flags/{b_de.png → toremove/b_de.png} +0 -0
  11. data/app/assets/images/admin/flags/{b_en.png → toremove/b_en.png} +0 -0
  12. data/app/assets/images/admin/flags/{b_es.png → toremove/b_es.png} +0 -0
  13. data/app/assets/images/admin/flags/{b_fr.png → toremove/b_fr.png} +0 -0
  14. data/app/assets/images/admin/flags/{b_it.png → toremove/b_it.png} +0 -0
  15. data/app/assets/images/admin/flags/{b_ru.png → toremove/b_ru.png} +0 -0
  16. data/app/assets/images/admin/flags/{b_uk.png → toremove/b_uk.png} +0 -0
  17. data/app/assets/images/admin/flags/{flag_de.gif → toremove/flag_de.gif} +0 -0
  18. data/app/assets/images/admin/flags/{flag_de_nonact.gif → toremove/flag_de_nonact.gif} +0 -0
  19. data/app/assets/images/admin/flags/{flag_en.gif → toremove/flag_en.gif} +0 -0
  20. data/app/assets/images/admin/flags/{flag_en_nonact.gif → toremove/flag_en_nonact.gif} +0 -0
  21. data/app/assets/images/admin/flags/{flag_es.gif → toremove/flag_es.gif} +0 -0
  22. data/app/assets/images/admin/flags/{flag_es_nonact.gif → toremove/flag_es_nonact.gif} +0 -0
  23. data/app/assets/images/admin/flags/{flag_fr.gif → toremove/flag_fr.gif} +0 -0
  24. data/app/assets/images/admin/flags/{flag_fr_nonact.gif → toremove/flag_fr_nonact.gif} +0 -0
  25. data/app/assets/images/admin/flags/{flag_it.gif → toremove/flag_it.gif} +0 -0
  26. data/app/assets/images/admin/flags/{flag_it_nonact.gif → toremove/flag_it_nonact.gif} +0 -0
  27. data/app/assets/images/admin/flags/{flag_ru.gif → toremove/flag_ru.gif} +0 -0
  28. data/app/assets/images/admin/flags/{flag_ru_nonact.gif → toremove/flag_ru_nonact.gif} +0 -0
  29. data/app/assets/images/admin/flags/{flag_uk.gif → toremove/flag_uk.gif} +0 -0
  30. data/app/assets/images/admin/flags/{flag_uk_nonact.gif → toremove/flag_uk_nonact.gif} +0 -0
  31. data/app/assets/images/admin/flags/uk.png +0 -0
  32. data/app/assets/javascripts/ab_admin/components/in_place_edit.js.coffee +9 -1
  33. data/app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee +1 -2
  34. data/app/assets/javascripts/ab_admin/core/init.js.coffee +5 -3
  35. data/app/assets/javascripts/ab_admin/core/ui_utils.js.coffee +17 -0
  36. data/app/assets/javascripts/ab_admin/inputs/datetime_input.js.coffee +8 -3
  37. data/app/assets/stylesheets/ab_admin/bootstrap_and_overrides.scss +37 -8
  38. data/app/assets/stylesheets/ab_admin/components/_form.scss +31 -2
  39. data/app/assets/stylesheets/ab_admin/components/_locale_tabs.scss +13 -48
  40. data/app/assets/stylesheets/ab_admin/components/_navigation.scss +5 -0
  41. data/app/assets/stylesheets/ab_admin/main.scss +1 -1
  42. data/app/controllers/admin/base_controller.rb +14 -15
  43. data/app/controllers/admin/locators_controller.rb +18 -0
  44. data/app/controllers/admin/manager_controller.rb +6 -8
  45. data/app/controllers/admin/settings_controller.rb +1 -1
  46. data/app/controllers/admin/static_pages_controller.rb +1 -3
  47. data/app/controllers/admin/structures_controller.rb +2 -7
  48. data/app/controllers/admin/users_controller.rb +1 -1
  49. data/app/views/ab_admin/devise/sessions/new.html.slim +3 -3
  50. data/app/views/admin/base/_search_layout.html.slim +3 -1
  51. data/app/views/admin/base/_table.html.slim +1 -1
  52. data/app/views/admin/base/update.js.erb +3 -1
  53. data/app/views/admin/locators/show.html.slim +22 -1
  54. data/app/views/admin/manager/_chart.html.slim +4 -0
  55. data/app/views/admin/manager/_table.html.slim +16 -13
  56. data/app/views/admin/shared/_action_items.html.slim +12 -0
  57. data/app/views/admin/shared/_content_actions.html.slim +15 -3
  58. data/app/views/admin/shared/_locale_tabs.html.slim +5 -3
  59. data/app/views/admin/shared/_main_menu.html.slim +20 -0
  60. data/app/views/admin/shared/_save_buttons.html.slim +2 -1
  61. data/app/views/admin/users/_table.html.slim +6 -0
  62. data/app/views/layouts/admin/_navigation.html.slim +2 -24
  63. data/config/locales/de.yml +0 -8
  64. data/config/locales/en.yml +3 -11
  65. data/config/locales/it.yml +9 -17
  66. data/config/locales/ru.yml +0 -8
  67. data/config/locales/uk.yml +0 -8
  68. data/config/routes.rb +2 -1
  69. data/lib/ab_admin.rb +6 -1
  70. data/lib/ab_admin/abstract_resource.rb +1 -1
  71. data/lib/ab_admin/carrierwave/base_uploader.rb +1 -1
  72. data/lib/ab_admin/concerns/utilities.rb +2 -2
  73. data/lib/ab_admin/config/base.rb +7 -5
  74. data/lib/ab_admin/core_ext/hash.rb +19 -1
  75. data/lib/ab_admin/core_ext/other.rb +0 -48
  76. data/lib/ab_admin/hooks/will_paginate_id_prefetch.rb +1 -1
  77. data/lib/ab_admin/i18n_tools/model_translator.rb +4 -4
  78. data/lib/ab_admin/models/asset.rb +0 -4
  79. data/lib/ab_admin/models/locator.rb +37 -4
  80. data/lib/ab_admin/models/user.rb +2 -2
  81. data/lib/ab_admin/utils.rb +1 -1
  82. data/lib/ab_admin/utils/eval_helpers.rb +1 -5
  83. data/lib/ab_admin/utils/logger.rb +11 -2
  84. data/lib/ab_admin/utils/mysql.rb +2 -2
  85. data/lib/ab_admin/version.rb +1 -1
  86. data/lib/ab_admin/views/admin_helpers.rb +38 -21
  87. data/lib/ab_admin/views/admin_navigation_helpers.rb +6 -6
  88. data/lib/ab_admin/views/form_builder.rb +4 -1
  89. data/lib/ab_admin/views/helpers.rb +13 -34
  90. data/lib/ab_admin/views/inputs/date_time_picker_input.rb +5 -4
  91. data/lib/ab_admin/views/manager_helpers.rb +21 -3
  92. data/lib/generators/ab_admin/glob/glob_generator.rb +3 -6
  93. data/lib/generators/ab_admin/install/templates/models/asset.rb +1 -1
  94. data/lib/generators/ab_admin/install/templates/models/structure_type.rb +4 -0
  95. data/lib/generators/ab_admin/install/templates/spec/factories/sequences.rb +2 -2
  96. data/lib/generators/ab_admin/install/templates/spec/factories/structure_factory.rb +1 -1
  97. data/lib/generators/ab_admin/install/templates/spec/factories/user_factory.rb +1 -1
  98. data/lib/generators/ab_admin/install/templates/spec/spec_helper.rb +1 -1
  99. data/lib/generators/ab_admin/install/templates/spec/support/controller_macros.rb +4 -4
  100. data/lib/generators/template.rb +2 -2
  101. metadata +51 -39
@@ -246,14 +246,6 @@ ru:
246
246
  updater_id: Обновил
247
247
  remember_me: Запомнить
248
248
  password: Пароль
249
- attrs:
250
- en: анг.
251
- it: итал.
252
- ru: рус.
253
- uk: укр.
254
- de: нем.
255
- fr: фран.
256
- es: исп.
257
249
  flash:
258
250
  admin:
259
251
  actions:
@@ -246,14 +246,6 @@ uk:
246
246
  updater_id: "Обновил"
247
247
  remember_me: "Запомнить"
248
248
  password: "Пароль"
249
- attrs:
250
- en: "анг."
251
- it: "итал."
252
- ru: "рус."
253
- uk: "укр."
254
- de: "нем."
255
- fr: "фран."
256
- es: "исп."
257
249
  flash:
258
250
  admin:
259
251
  actions:
data/config/routes.rb CHANGED
@@ -25,7 +25,8 @@ Rails.application.routes.draw do
25
25
  end
26
26
 
27
27
  resource :locators do
28
- post :prepare, :reload, on: :collection
28
+ post :prepare, :reload, :import, on: :collection
29
+ get :export, on: :collection
29
30
  end
30
31
 
31
32
  resources :admin_comments
data/lib/ab_admin.rb CHANGED
@@ -135,6 +135,8 @@ module AbAdmin
135
135
  mattr_accessor :site_name
136
136
  @@site_name = 'AbAdmin'
137
137
 
138
+ mattr_accessor :root_path
139
+
138
140
  mattr_accessor :body_css_class
139
141
 
140
142
  mattr_accessor :favicon_path
@@ -152,7 +154,7 @@ module AbAdmin
152
154
  @@translate_models = %w(User Asset Structure StaticPage Header AdminComment)
153
155
 
154
156
  mattr_accessor :assets
155
- @@assets = %w(ab_admin/devise.css bootstrap.js ab_admin/loading.gif ab_admin/clear.png)
157
+ @@assets = %w(ab_admin/devise.css bootstrap.js ab_admin/loading.gif ab_admin/clear.png ab_admin/clear.png admin/flags/*.png)
156
158
 
157
159
  mattr_accessor :test_settings
158
160
  @@test_settings = {}
@@ -174,6 +176,9 @@ module AbAdmin
174
176
 
175
177
  mattr_accessor :base_url
176
178
 
179
+ mattr_accessor :default_resource_settings
180
+ @@default_resource_settings = {index_view: 'table', search: true, batch: true, hotkeys: true, list_dblclick: true}
181
+
177
182
  mattr_accessor :default_per_page
178
183
  @@default_per_page = 50
179
184
 
@@ -6,7 +6,7 @@ module AbAdmin
6
6
  ACTIONS = [:index, :show, :new, :edit, :create, :update, :destroy, :preview, :batch, :rebuild, :custom_action, :history]
7
7
  end
8
8
 
9
- attr_accessor :model, :table, :search, :export, :form, :modal_form, :show, :preview_path, :actions, :custom_settings,
9
+ attr_accessor :model, :table, :search, :export, :form, :chart, :modal_form, :show, :preview_path, :actions, :custom_settings,
10
10
  :batch_action_list, :action_items, :disabled_action_items, :resource_action_items, :tree_node_renderer,
11
11
  :parent_resources, :custom_actions, :permitted_params, :scopes
12
12
 
@@ -47,7 +47,7 @@ module AbAdmin
47
47
  end
48
48
 
49
49
  def human_full_filename(for_file=filename)
50
- ext = File.extname(for_file).downcase
50
+ ext = File.extname(for_file)
51
51
  human_filename_part = for_file.chomp(ext)
52
52
  tech_filename_part = "#{base_filename_part}#{ext}"
53
53
  human_filename_part == secure_token ? tech_filename_part : "#{human_filename_part}_#{tech_filename_part}"
@@ -32,8 +32,8 @@ module AbAdmin
32
32
  #ActiveRecord::Base.descendants.find_all { |model| model.descends_from_active_record? }
33
33
  end
34
34
 
35
- def han(attr)
36
- human_attribute_name(attr)
35
+ def han(attr, locale: nil)
36
+ locale ? "#{human_attribute_name(attr)} (#{locale})" : human_attribute_name(attr)
37
37
  end
38
38
 
39
39
  def quote_column(col_name)
@@ -3,8 +3,7 @@ module AbAdmin
3
3
  class BaseBuilder
4
4
  attr_reader :options, :fields
5
5
  attr_accessor :partial
6
- class_attribute :field_defaults, :partial_name, instance_writer: false
7
- self.field_defaults = {}
6
+ class_attribute :partial_name, instance_writer: false
8
7
 
9
8
  def initialize(options={}, &block)
10
9
  @fields = []
@@ -14,7 +13,7 @@ module AbAdmin
14
13
  end
15
14
 
16
15
  def field(name, options={}, &block)
17
- @fields << Field.new(name, options.reverse_merge!(field_defaults), &block)
16
+ @fields << Field.new(name, options, &block)
18
17
  end
19
18
 
20
19
  def self.default_for_model(model, options={})
@@ -30,7 +29,6 @@ module AbAdmin
30
29
  end
31
30
 
32
31
  class Table < BaseBuilder
33
- self.field_defaults = {sortable: true}
34
32
  self.partial_name = 'table'
35
33
  end
36
34
 
@@ -38,6 +36,10 @@ module AbAdmin
38
36
  self.partial_name = 'search_form'
39
37
  end
40
38
 
39
+ class Chart < BaseBuilder
40
+ self.partial_name = 'chart'
41
+ end
42
+
41
43
  class Export < BaseBuilder
42
44
  def render_options
43
45
  {column_names: fields.map(&:name), column_data: fields.map(&:data),
@@ -164,11 +166,11 @@ module AbAdmin
164
166
  def initialize(name, options={}, &block)
165
167
  @name = name
166
168
  @options = options
169
+ @options[:badge] = {} if @options[:badge] && !@options[:badge].is_a?(Hash)
167
170
  @data = block
168
171
  end
169
172
 
170
173
  def apply(relation, params)
171
- return relation unless params[name].present?
172
174
  data.is_a?(Proc) ? data.call(relation, params) : relation.public_send(name)
173
175
  end
174
176
  end
@@ -103,10 +103,15 @@ class Hash
103
103
  new_hash
104
104
  end
105
105
 
106
- def no_blank
106
+ def reject_blank
107
107
  reject { |_, v| v.blank? }
108
108
  end
109
109
 
110
+ def no_blank
111
+ ActiveSupport::Deprecation.warn('`no_blank` is deprecated, use `reject_blank` instead')
112
+ reject_blank
113
+ end
114
+
110
115
  def try_keys(*try_keys)
111
116
  try_keys.each do |k|
112
117
  return self[k] if self.has_key?(k)
@@ -122,4 +127,17 @@ class Hash
122
127
  }
123
128
  end unless Hash.method_defined?(:deep_stringify_keys)
124
129
 
130
+ def flatten_hash!(k = [])
131
+ inject({}) do |h, v|
132
+ new_k = k + [v[0]]
133
+ h.merge!(v[-1].is_a?(Hash) ? v[-1].flatten_hash!(new_k) : {new_k => v[-1]})
134
+ end
135
+ end
136
+
137
+ def flatten_hash(k = [])
138
+ dup.inject({}) do |h, v|
139
+ new_k = k + [v[0]]
140
+ h.merge!(v[-1].is_a?(Hash) ? v[-1].dup.flatten_hash(new_k) : {new_k => v[-1]})
141
+ end
142
+ end
125
143
  end
@@ -4,16 +4,6 @@ class NilClass
4
4
  end
5
5
  end
6
6
 
7
- class Time
8
- def formatted_datetime
9
- I18n.l(self, format: '%d.%m.%Y %H:%M')
10
- end
11
-
12
- def formatted_date
13
- I18n.l(self, format: '%d.%m.%Y')
14
- end
15
- end
16
-
17
7
  class TrueClass
18
8
  def to_i
19
9
  1
@@ -37,41 +27,3 @@ class Date
37
27
  self.to_date == ::Date.tomorrow
38
28
  end
39
29
  end
40
-
41
- #module ActiveSupport
42
- # class OrderedHash < ::Hash
43
- # def to_yaml(opts = {})
44
- # YAML::quick_emit(self, opts) do |out|
45
- # out.map(nil, to_yaml_style) do |map|
46
- # each do |k, v|
47
- # map.add(k, v)
48
- # end
49
- # end
50
- # end
51
- # end
52
- # end
53
- #end
54
-
55
- #class Numeric
56
- # # round a given number to the nearest step
57
- # def round_by(increment)
58
- # (self /increment).round * increment
59
- # end
60
- #end
61
- #
62
- #class Float
63
- # def ceil_to(i=4)
64
- # #(self * 10**i).ceil.to_f / 10**i
65
- # (self * (10.0 ** i)).round(10).ceil * (10.0 ** (-i))
66
- # end
67
- #
68
- # def floor_to(i=4)
69
- # (self * 10**i).floor.to_f / 10**i
70
- # end
71
- #
72
- # def round_to
73
- # return (self+0.5).floor if self > 0.0
74
- # return (self-0.5).ceil if self < 0.0
75
- # 0
76
- # end
77
- #end
@@ -40,7 +40,7 @@ module WillPaginate
40
40
  rel.total_entries = total.to_i unless total.blank?
41
41
 
42
42
  if large
43
- ids = rel.except(:includes).pluck("#{quoted_table_name}.id")
43
+ ids = rel.except(:includes).pluck(Arel.sql("#{quoted_table_name}.id"))
44
44
  new_rel = rel.except(:limit, :offset, :where).where(id: ids)
45
45
  new_rel.paginate_limit = rel.limit_value.to_i
46
46
  new_rel.paginate_offset = rel.offset_value.to_i
@@ -5,7 +5,8 @@ module AbAdmin
5
5
  IGNORE_COLUMNS = %w(id reset_password_sent_at remember_created_at current_sign_in_at confirmation_token
6
6
  reset_password_token password_salt failed_attempts)
7
7
 
8
- def initialize
8
+ def initialize(options: {})
9
+ @options = options
9
10
  @locales = Globalize.available_locales
10
11
  @models = AbAdmin.translate_models.map{|m| m.constantize }
11
12
  @models_i18n_hash = {}
@@ -33,10 +34,9 @@ module AbAdmin
33
34
  model.reflect_on_all_associations.map(&:name).map(&:to_s).reject { |a| a =~ /^translation/ }.each do |assoc|
34
35
  o[assoc] = ha(model, assoc, locale)
35
36
  end
36
- if model.new.respond_to?("#{attr}_#{locale.to_s}".to_sym)
37
+ if @options[:locale_attributes] && model.new.respond_to?("#{attr}_#{locale.to_s}".to_sym)
37
38
  @locales.each do |locale_1|
38
- attr_suffix = I18n.t(locale_1, scope: [:attrs], default: locale_1.to_s.humanize)
39
- o["#{attr}_#{locale_1.to_s}"] = "#{ha(model, attr, locale)} (#{attr_suffix})"
39
+ o["#{attr}_#{locale_1.to_s}"] = "#{ha(model, attr, locale)} (#{locale_1.to_s})"
40
40
  end
41
41
  end
42
42
  end.sort.to_h
@@ -52,10 +52,6 @@ module AbAdmin
52
52
  data.versions[thumb_size] ? url(thumb_size) : url
53
53
  end
54
54
 
55
- def format_created_at
56
- I18n.l(created_at, format: '%d.%m.%Y %H:%M')
57
- end
58
-
59
55
  def human_name
60
56
  original_name.presence || data_file_name
61
57
  end
@@ -1,3 +1,5 @@
1
+ require 'csv'
2
+
1
3
  module AbAdmin
2
4
  module Models
3
5
  module Locator
@@ -23,13 +25,44 @@ module AbAdmin
23
25
  def prepare_data(path)
24
26
  data = YAML.load_file(path)
25
27
  locale = data.keys.first
26
- OpenStruct.new({locale: locale.to_sym, data: data[locale], flat_data: flat_hash(data[locale]),
28
+ OpenStruct.new({locale: locale.to_sym, data: data[locale], flat_data: data[locale].flatten_hash,
27
29
  filename: File.basename(path), path: path, dir: File.dirname(path)})
28
30
  end
29
31
 
30
- def flat_hash(hash, k = [])
31
- return {k => hash} unless hash.is_a?(Hash)
32
- hash.inject({}) { |h, v| h.merge! flat_hash(v[-1], k + [v[0]]) }
32
+ def export_csv(*keys, locales: nil)
33
+ return if keys.blank?
34
+ locales ||= I18n.available_locales
35
+ I18n.backend.available_locales # Force load translations
36
+ filter_keys = keys.map {|k| k.include?('*') ? Regexp.new("\\A#{k.gsub('.', '\.').gsub('*', '.*')}\\z") : k}
37
+ data = filter_keys.each_with_object(Hash.new { |h, k| h[k] = [] }) do |key, res|
38
+ locales.each_with_index do |l, i|
39
+ translations[l].find_all{|k, _| key.is_a?(Regexp) ? k =~ key : k == key }.each{|k, v| res[k][i] = v}
40
+ end
41
+ end
42
+ for_csv = [['DO NOT EDIT THIS COLUMN!', *locales]] + data.map{|k, v| [k, *v] }
43
+ for_csv.map(&:to_csv).join
44
+ end
45
+
46
+ def import_csv(csv, locales: nil)
47
+ return if csv.blank?
48
+ locales ||= I18n.available_locales
49
+ csv_data = CSV.parse(csv)
50
+ csv_data.shift.each_with_index do |l, i|
51
+ next if i.zero? || !locales.include?(l.to_sym)
52
+ path = Rails.root.join('config', 'locales', "#{l}.yml")
53
+ raise "Missing file #{path}" unless File.file?(path)
54
+ data = YAML.load_file(path)
55
+ csv_data.each do |d|
56
+ key_parts = [l.to_s] + d[0].split('.')
57
+ raise "Invalid key #{d[0]}" unless data.dig(*key_parts)
58
+ data.store_multi(d[i], *key_parts)
59
+ end
60
+ save path, data
61
+ end
62
+ end
63
+
64
+ def translations
65
+ @translations ||= I18n.backend.send(:translations).slice(*I18n.available_locales).transform_values{|v| v.flatten_hash.transform_keys{|k| k.join('.') } }
33
66
  end
34
67
  end
35
68
 
@@ -32,8 +32,8 @@ module AbAdmin
32
32
  end
33
33
 
34
34
  def activate!
35
- confirm unless confirmed?
36
- unlock_access! if access_locked?
35
+ confirm if respond_to?(:confirm) && !confirmed?
36
+ unlock_access! if respond_to?(:unlock_access!) && access_locked?
37
37
  end
38
38
 
39
39
  def active?
@@ -9,7 +9,7 @@ module AbAdmin
9
9
  Kernel.suppress_warnings do
10
10
  Dir.glob(Rails.root.to_s + '/app/models/**/*.rb').reject { |path| path =~ /concerns|shared/ }.each { |file| require file }
11
11
  end
12
- ActiveRecord::Base.direct_descendants
12
+ [ActiveRecord::Base, ApplicationRecord].flat_map{|sc| sc.subclasses.find_all { |model| model.connection.table_exists?(model.table_name) } }
13
13
  end
14
14
 
15
15
  def load_files!(base_path = 'lib/utils')
@@ -90,11 +90,7 @@ module AbAdmin
90
90
  when Symbol, String
91
91
  obj.send(symbol_or_proc.to_sym, *options[:attrs])
92
92
  when Proc
93
- if exec
94
- obj.instance_exec(&symbol_or_proc)
95
- else
96
- symbol_or_proc.call(obj)
97
- end
93
+ exec ? obj.instance_exec(&symbol_or_proc) : symbol_or_proc.call(obj)
98
94
  end
99
95
  end
100
96
 
@@ -4,7 +4,7 @@ module AbAdmin
4
4
  class ExtendedLogger < ::Logger
5
5
  def exception(e, options={})
6
6
  message = "#{e.message} #{"DATA:#{options[:data].inspect}" if options && options[:data]}"
7
- backtrace = e.backtrace.map { |l| "#{' ' * 2}#{l}" }.join("\n")
7
+ backtrace = e.backtrace.map { |l| "#{' ' * 2}#{l}" }.join("\n") if e.backtrace
8
8
  error("#{e.class} #{message}\n#{backtrace}\n\n")
9
9
  end
10
10
 
@@ -13,9 +13,18 @@ module AbAdmin
13
13
  end
14
14
  end
15
15
 
16
+ class Formatter
17
+ FORMAT = "[%s] %5s %s\n".freeze
18
+ DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%3N'.freeze
19
+
20
+ def call(severity, time, _, msg)
21
+ FORMAT % [time.strftime(DATETIME_FORMAT), severity, msg]
22
+ end
23
+ end
24
+
16
25
  def self.for_file(filename)
17
26
  logger = ExtendedLogger.new(Rails.root.join('log', filename))
18
- logger.formatter = ::Logger::Formatter.new
27
+ logger.formatter = Formatter.new
19
28
  logger
20
29
  end
21
30
  end
@@ -13,10 +13,10 @@ module AbAdmin
13
13
  end
14
14
 
15
15
  # remove duplicate records by columns
16
- def remove_duplicates(*cols)
16
+ def remove_duplicates(*cols, deleted_id_order: '<')
17
17
  conds = cols.map { |col| "#{table_name}.#{col} IS NOT NULL AND #{table_name}.#{col} = t.#{col}" }.join(' AND ')
18
18
  query = <<-SQL
19
- DELETE FROM #{table_name} USING #{table_name}, #{table_name} AS t WHERE #{table_name}.id < t.id AND #{conds}
19
+ DELETE FROM #{table_name} USING #{table_name}, #{table_name} AS t WHERE #{table_name}.id #{deleted_id_order} t.id AND #{conds}
20
20
  SQL
21
21
  connection.execute(query)
22
22
  end
@@ -1,3 +1,3 @@
1
1
  module AbAdmin
2
- VERSION = '0.7.0'
2
+ VERSION = '0.8.0'
3
3
  end
@@ -21,34 +21,52 @@ module AbAdmin
21
21
  end
22
22
  end
23
23
 
24
+ def editable_bool(item, attr)
25
+ url = "/admin/#{item.class.model_name.plural}/#{item.id}"
26
+ content_tag :div, class: 'auto-submit-checkbox-wrap' do
27
+ check_box_tag("#{item.class.model_name.singular}[#{attr}]", '1', item.send(attr), class: 'js-auto-submit-checkbox', data: {url: url})
28
+ end
29
+ end
30
+
24
31
  def admin_editable(item, attr, options=nil)
25
32
  options = {} unless options.is_a?(Hash)
26
- options[:type] ||= 'select' if options[:source]
27
-
28
- case attr.to_s
29
- when /_at$/
30
- options[:type] ||= 'date'
31
- when /^is_/
32
- options[:type] ||= 'select'
33
- options[:source] ||= {1 => 'yes', 0 => 'no'}
34
- options[:value] ||= item[attr] ? 1 : 0
35
- options[:title] ||= item[attr] ? 'yes' : 'no'
36
- when /description|body|content/
37
- options[:type] ||= 'textarea'
33
+ title = options[:title] || item[attr]
34
+ html_title = admin_pretty_data(title).to_s.html_safe
35
+ return html_title unless can?(:update, item)
36
+ options[:source] = options[:collection].is_a?(Hash) ? options[:collection] : options[:collection].map{|r| [r.id, AbAdmin.display_name(r)] }.to_h if options[:collection]
37
+
38
+ unless options[:type]
39
+ if options[:source]
40
+ options[:type] = 'select'
38
41
  else
39
- options[:type] ||= 'text'
42
+ case attr.to_s
43
+ when /_at$/
44
+ options[:type] ||= 'date'
45
+ options[:title] ||= html_title
46
+ when /^is_/
47
+ options[:type] ||= 'select'
48
+ options[:source] ||= {1 => 'yes', 0 => 'no'}
49
+ options[:value] ||= item[attr] ? 1 : 0
50
+ options[:title] ||= item[attr] ? 'yes' : 'no'
51
+ when /description|body|content/
52
+ options[:type] ||= 'textarea'
53
+ else
54
+ options[:type] ||= 'text'
55
+ end
56
+ end
40
57
  end
41
58
 
42
59
  data = {
43
60
  type: options[:type],
44
61
  source: options[:source].try(:to_json),
45
62
  model: options[:model] || item.class.model_name.singular,
63
+ accept: options[:accept],
46
64
  url: options[:url] || "/admin/#{item.class.model_name.plural}/#{item.id}",
47
65
  name: attr,
48
66
  value: options[:value] || item[attr],
49
67
  title: options[:title] || item[attr]
50
68
  }
51
- link_to admin_pretty_data(data[:title]).html_safe, '#', class: "editable #{options[:class]}", data: data.update(options[:data] || {})
69
+ link_to html_title, '#', class: "editable #{options[:class]}", data: data.update(options[:data] || {})
52
70
  end
53
71
 
54
72
  def options_for_ckeditor(options = {})
@@ -86,8 +104,11 @@ module AbAdmin
86
104
  render 'admin/admin_comments/comments'
87
105
  end
88
106
 
89
- def color_bool(val, success_class='badge-success')
90
- %(<span class="badge #{success_class if val}">#{val ? '+' : '-'}</span>).html_safe
107
+ def color_bool(val, options={})
108
+ options.reverse_merge!(true_css: 'badge-success', false_css: nil, nil_css: nil)
109
+ css = options["#{val.inspect}_css".to_sym]
110
+ text = val.nil? ? '?' : (val ? '+' : '-')
111
+ %(<span class="badge #{css}">#{text}</span>).html_safe
91
112
  end
92
113
 
93
114
  def icon(name, white=false)
@@ -156,11 +177,7 @@ module AbAdmin
156
177
  when Symbol, String
157
178
  obj.send(symbol_or_proc.to_sym)
158
179
  when Proc
159
- if exec
160
- instance_exec(obj, &symbol_or_proc)
161
- else
162
- symbol_or_proc.call(obj)
163
- end
180
+ exec ? instance_exec(obj, &symbol_or_proc) : symbol_or_proc.call(obj)
164
181
  end
165
182
  end
166
183