ab_admin 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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