ab_admin 0.9.0 → 0.10.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/ab_admin/components/in_place_edit.js.coffee +28 -24
  3. data/app/assets/javascripts/ab_admin/core/batch_actions.js.coffee +1 -1
  4. data/app/assets/javascripts/ab_admin/core/init.js.coffee +1 -0
  5. data/app/assets/javascripts/ab_admin/core/ui_utils.js.coffee +21 -1
  6. data/app/assets/javascripts/ab_admin/core/utils.js.coffee +8 -0
  7. data/app/assets/javascripts/ab_admin/inputs/datetime_input.js.coffee +1 -0
  8. data/app/assets/javascripts/ab_admin/main.js +1 -2
  9. data/app/assets/stylesheets/ab_admin/bootstrap_and_overrides.scss +44 -23
  10. data/app/assets/stylesheets/ab_admin/components/_colored_tabs.scss +1 -1
  11. data/app/assets/stylesheets/ab_admin/components/_form.scss +3 -1
  12. data/app/assets/stylesheets/ab_admin/components/_navigation.scss +7 -2
  13. data/app/assets/stylesheets/ab_admin/components/_table_view.scss +75 -9
  14. data/app/assets/stylesheets/ab_admin/components/_tooltip.scss +1 -0
  15. data/app/assets/stylesheets/ab_admin/main.scss +1 -1
  16. data/app/controllers/admin/assets_controller.rb +1 -1
  17. data/app/controllers/admin/base_controller.rb +87 -107
  18. data/app/controllers/admin/manager_controller.rb +17 -47
  19. data/app/views/ab_admin/devise/sessions/new.html.slim +2 -0
  20. data/app/views/admin/assets/batch_edit.html.slim +2 -1
  21. data/app/views/admin/base/_search_layout.html.slim +1 -1
  22. data/app/views/admin/base/create.js.erb +1 -1
  23. data/app/views/admin/base/edit.js.erb +1 -1
  24. data/app/views/admin/base/new.js.erb +1 -1
  25. data/app/views/admin/base/update.js.erb +3 -3
  26. data/app/views/admin/manager/_show_table.html.slim +1 -1
  27. data/app/views/admin/manager/_stats.html.slim +4 -0
  28. data/app/views/admin/manager/_table.html.slim +6 -3
  29. data/app/views/admin/shared/_content_actions.html.slim +22 -15
  30. data/app/views/admin/shared/_save_buttons.html.slim +10 -1
  31. data/app/views/admin/users/_form.html.slim +2 -2
  32. data/config/locales/en.yml +8 -13
  33. data/config/locales/it.yml +1 -0
  34. data/db/migrate/20130101000001_create_users.rb +1 -4
  35. data/db/migrate/20130101000003_create_assets.rb +1 -1
  36. data/db/migrate/20130101000004_create_headers.rb +5 -5
  37. data/db/migrate/20130101000005_create_static_pages.rb +2 -5
  38. data/db/migrate/20130101000006_create_structures.rb +1 -1
  39. data/db/migrate/20130101000007_base_translations.rb +43 -12
  40. data/db/migrate/20130101000008_create_admin_comments.rb +2 -7
  41. data/db/migrate/20130101000009_create_tracks.rb +4 -8
  42. data/lib/ab_admin/abstract_resource.rb +6 -5
  43. data/lib/ab_admin/carrierwave/base_uploader.rb +87 -74
  44. data/lib/ab_admin/carrierwave/glue.rb +0 -5
  45. data/lib/ab_admin/concerns/admin_addition.rb +19 -1
  46. data/lib/ab_admin/concerns/utilities.rb +1 -1
  47. data/lib/ab_admin/config/base.rb +20 -4
  48. data/lib/ab_admin/core_ext/array.rb +0 -5
  49. data/lib/ab_admin/core_ext/string.rb +1 -6
  50. data/lib/ab_admin/devise.rb +7 -0
  51. data/lib/ab_admin/engine.rb +1 -1
  52. data/lib/ab_admin/hooks/ckeditor_lazy.rb +13 -0
  53. data/lib/ab_admin/menu/base_group.rb +0 -1
  54. data/lib/ab_admin/menu/group.rb +2 -4
  55. data/lib/ab_admin/menu/item.rb +4 -8
  56. data/lib/ab_admin/models/asset.rb +7 -10
  57. data/lib/ab_admin/models/locator.rb +1 -1
  58. data/lib/ab_admin/models/settings.rb +2 -2
  59. data/lib/ab_admin/models/structure.rb +3 -3
  60. data/lib/ab_admin/models/track.rb +15 -3
  61. data/lib/ab_admin/utils/csv_document.rb +4 -4
  62. data/lib/ab_admin/utils/eval_helpers.rb +0 -16
  63. data/lib/ab_admin/utils/logger.rb +12 -2
  64. data/lib/ab_admin/utils/mysql.rb +2 -3
  65. data/lib/ab_admin/utils/xls_document.rb +1 -3
  66. data/lib/ab_admin/utils.rb +0 -5
  67. data/lib/ab_admin/version.rb +1 -1
  68. data/lib/ab_admin/views/admin_helpers.rb +33 -16
  69. data/lib/ab_admin/views/admin_navigation_helpers.rb +12 -9
  70. data/lib/ab_admin/views/inputs/ckeditor_input.rb +1 -5
  71. data/lib/ab_admin/views/manager_helpers.rb +9 -3
  72. data/lib/ab_admin/views/search_form_builder.rb +12 -12
  73. data/lib/ab_admin.rb +13 -2
  74. data/lib/generators/ab_admin/install/templates/models/user.rb +1 -2
  75. data/lib/generators/ab_admin/install/templates/uploaders/attachment_file_uploader.rb +1 -1
  76. data/lib/generators/ab_admin/install/templates/uploaders/avatar_uploader.rb +1 -1
  77. data/lib/generators/ab_admin/install/templates/uploaders/picture_uploader.rb +16 -3
  78. data/lib/generators/ab_admin/resource/resource_generator.rb +0 -4
  79. data/lib/generators/ab_admin/resource/templates/controller.erb +0 -7
  80. data/lib/tasks/assets.rake +5 -5
  81. metadata +28 -26
@@ -29,6 +29,13 @@ module AbAdmin
29
29
  super
30
30
  end
31
31
  end
32
+
33
+ protected
34
+
35
+ def sign_in_params
36
+ devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt]) if devise_mapping.try(:two_factor_authenticatable?)
37
+ devise_parameter_sanitizer.sanitize(:sign_in)
38
+ end
32
39
  end
33
40
 
34
41
  class PasswordsController < ::Devise::PasswordsController
@@ -2,7 +2,7 @@ module AbAdmin
2
2
  class Engine < ::Rails::Engine
3
3
  engine_name 'ab_admin'
4
4
 
5
- initializer 'ab_admin.assets_precompile', :group => :all do |app|
5
+ initializer 'ab_admin.assets_precompile', group: :all do |app|
6
6
  app.config.assets.precompile += AbAdmin.assets
7
7
  end
8
8
 
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'ckeditor'
3
+
4
+ Ckeditor::Utils.module_eval do
5
+ class << self
6
+ def js_replace(dom_id, options = nil)
7
+ options.present? ? "lazyInitCkeditor('#{dom_id}', #{ActiveSupport::JSON.encode(options)});" : "lazyInitCkEditor('#{dom_id}');"
8
+ end
9
+ end
10
+ end
11
+ rescue LoadError => e
12
+ raise unless e.message.include?('ckeditor')
13
+ end
@@ -2,7 +2,6 @@ module AbAdmin
2
2
  module Menu
3
3
  class BaseGroup
4
4
  include ::Rails.application.routes.url_helpers
5
- include ::AbAdmin::Utils::EvalHelpers
6
5
 
7
6
  def link(title, path, options={})
8
7
  @menu_tree << Item.new(title, path, options)
@@ -10,9 +10,7 @@ module AbAdmin
10
10
  end
11
11
 
12
12
  def render(template)
13
- return if @options[:if] && !call_method_or_proc_on(template, @options[:if])
14
- return if @options[:unless] && call_method_or_proc_on(template, @options[:unless])
15
-
13
+ return unless template.option_conditions_met?(@options)
16
14
  wrapper_class = "dropdown-wrap-#{@raw_title}" if @raw_title.is_a?(Symbol)
17
15
  <<-HTML.html_safe
18
16
  <li class="dropdown #{wrapper_class}">
@@ -26,7 +24,7 @@ module AbAdmin
26
24
 
27
25
  def title(template)
28
26
  return @title unless @options[:badge]
29
- badge = call_method_or_proc_on(template, @options[:badge])
27
+ badge = @options[:badge].is_a?(Symbol) ? template.public_send(@options[:badge]) : template.instance_exec(&@options[:badge])
30
28
  return @title if !badge || badge == 0
31
29
  "#{@title}&nbsp;<span class='badge badge-#{@options[:badge_type] || 'important'}'>#{badge}</span>".html_safe
32
30
  end
@@ -1,8 +1,6 @@
1
1
  module AbAdmin
2
2
  module Menu
3
3
  class Item
4
- include ::AbAdmin::Utils::EvalHelpers
5
-
6
4
  def initialize(title, url, options)
7
5
  @title = title.is_a?(Symbol) ? I18n.t(title, scope: [:admin, :navigation]) : title
8
6
  @url = url
@@ -10,23 +8,21 @@ module AbAdmin
10
8
  end
11
9
 
12
10
  def render(template)
13
- return if @options[:if] && !call_method_or_proc_on(template, @options[:if])
14
- return if @options[:unless] && call_method_or_proc_on(template, @options[:unless])
11
+ return unless template.option_conditions_met?(@options)
15
12
 
16
- item_url = @url.is_a?(String) ? @url : call_method_or_proc_on(template, @url)
13
+ item_url = @url.is_a?(String) ? @url : template.instance_exec(&@url)
17
14
  active = template.request.path.split('/')[2] == item_url.split('/')[2]
18
15
 
19
16
  <<-HTML.html_safe
20
- <li class="#{'active' if active}">#{template.link_to title(template), item_url, @options.except(:if, :unless)}</li>
17
+ <li class="#{'active' if active}">#{template.link_to title(template), item_url, @options.except(:if, :unless, :badge)}</li>
21
18
  HTML
22
19
  end
23
20
 
24
21
  private
25
22
 
26
23
  def title(template)
27
- ActiveSupport::Deprecation.warn('Menu item :badge_counter option is deprecated, use :badge instead') if @options[:badge_counter]
28
24
  return @title unless @options[:badge]
29
- badge = call_method_or_proc_on(template, @options[:badge])
25
+ badge = @options[:badge].is_a?(Symbol) ? template.public_send(@options[:badge]) : template.instance_exec(&@options[:badge])
30
26
  return @title if !badge || badge == 0
31
27
  "#{@title}&nbsp;<span class='badge badge-#{@options[:badge_type] || 'important'}'>#{badge}</span>".html_safe
32
28
  end
@@ -30,7 +30,7 @@ module AbAdmin
30
30
 
31
31
  def ext_list
32
32
  return unless uploaders[:data]
33
- uploaders[:data].new.extension_whitelist
33
+ uploaders[:data].new.extension_allowlist
34
34
  end
35
35
 
36
36
  def clean!
@@ -76,14 +76,12 @@ module AbAdmin
76
76
  AbAdmin.image_types.include?(self.data_content_type)
77
77
  end
78
78
 
79
- def base_filename
80
- base = File.basename(data_file_name, '.*')
81
- base == data_secure_token ? File.basename(original_name, '.*') : base
79
+ def human_filename
80
+ data.human_part
82
81
  end
83
82
 
84
- def base_filename=(value)
85
- return false if value.blank? || File.basename(data_file_name, '.*') == value
86
- self.original_name = value + File.extname(data_file_name)
83
+ def human_filename=(value)
84
+ return if (human_filename.blank? && value.blank?) || human_filename == value
87
85
  rename!(value)
88
86
  end
89
87
 
@@ -116,14 +114,13 @@ module AbAdmin
116
114
 
117
115
  def rename!(name=nil)
118
116
  normalized_name = name ? data.normalize_filename(name) : rand(9999)
119
- return false if normalized_name.blank?
120
- data.rename_via_move "#{normalized_name}#{File.extname(data_file_name)}"
117
+ return if normalized_name.blank?
118
+ data.rename_via_move normalized_name
121
119
  end
122
120
 
123
121
  def refresh_assetable
124
122
  return unless assetable.try(:persisted?)
125
123
  assetable.touch
126
- assetable.tire.update_index if assetable.respond_to?(:tire)
127
124
  true
128
125
  end
129
126
 
@@ -127,7 +127,7 @@ module AbAdmin
127
127
  next if locale == I18n.default_locale
128
128
  clean_locale_hash = main_file.data.deep_clear_values
129
129
  path = File.join(main_file.dir, main_file.filename.sub(locale_replace_regexp, locale.to_s))
130
- if File.exists?(path)
130
+ if File.exist?(path)
131
131
  file = self.class.prepare_data(path)
132
132
  self.class.save(path, {locale.to_s => clean_locale_hash.deep_add(file.data)})
133
133
  else
@@ -38,7 +38,7 @@ module AbAdmin
38
38
  end
39
39
 
40
40
  def read_data
41
- paths = base_paths.dup.push(editable_path).compact.find_all { |path| File.exists?(path) }
41
+ paths = base_paths.dup.push(editable_path).compact.find_all { |path| File.exist?(path) }
42
42
  hash = paths.map{|path| YAML.safe_load(File.read(path)) }.inject(&:deep_merge).deep_symbolize_keys
43
43
  SettingsStruct.new(hash)
44
44
  end
@@ -53,7 +53,7 @@ module AbAdmin
53
53
  end
54
54
 
55
55
  def editable_path
56
- editable_paths.detect { |path| File.exists?(path) }
56
+ editable_paths.detect { |path| File.exist?(path) }
57
57
  end
58
58
  end
59
59
 
@@ -15,12 +15,12 @@ module AbAdmin
15
15
  validates_numericality_of :structure_type_id, only_integer: true
16
16
 
17
17
  has_one :static_page, dependent: :destroy
18
- has_many :visible_children, -> { where(is_visible: true) }, class_name: name, foreign_key: 'parent_id'
18
+ has_many :visible_children, -> { where(is_visible: true) }, class_name: name, foreign_key: :parent_id
19
19
 
20
20
  scope :visible, lambda { where(is_visible: true) }
21
- scope :with_type, lambda { |structure_type| where(structure_type_id: structure_type.id) }
21
+ scope :with_type, lambda { |type| where(structure_type_id: (type.is_a?(Symbol) ? StructureType.public_send(type) : type.id)) }
22
22
  scope :with_depth, lambda { |level| where(depth: level.to_i) }
23
- scope :with_position, lambda { |position_type| where(position_type_id: position_type.id).order('lft DESC') }
23
+ scope :with_position, lambda { |type| where(position_type_id: (type.is_a?(Symbol) ? StructureType.public_send(type) : type.id)).order(lft: :desc) }
24
24
  end
25
25
 
26
26
  def redirect?
@@ -44,9 +44,9 @@ module AbAdmin
44
44
  lookups << ['actions', key]
45
45
  end
46
46
  lookups.map!{|l| l.join('.').to_sym }
47
- lookups << key
47
+ lookups << parts.last.humanize
48
48
 
49
- I18n.t(lookups.shift, (parameters.merge(params) || {}).merge(scope: :admin, default: lookups))
49
+ I18n.t(lookups.shift, **(parameters.merge(params) || {}).merge(scope: :admin, default: lookups))
50
50
  end
51
51
 
52
52
  def trackable_changed_attrs
@@ -63,7 +63,19 @@ module AbAdmin
63
63
 
64
64
  def make_trackable
65
65
  self.name ||= trackable.han.first(250)
66
- self.trackable_changes = trackable.saved_changes.except(:updated_at)
66
+ self.trackable_changes = format_trackable_changes(trackable.saved_changes.except(:updated_at))
67
+ end
68
+
69
+ def format_trackable_changes(value)
70
+ value.to_h.deep_transform_values do |v|
71
+ if v.is_a?(BigDecimal)
72
+ v.to_f
73
+ elsif v.is_a?(ActiveSupport::TimeWithZone)
74
+ v.to_s
75
+ else
76
+ v
77
+ end
78
+ end
67
79
  end
68
80
  end
69
81
  end
@@ -3,8 +3,6 @@ require 'csv'
3
3
  module AbAdmin
4
4
  module Utils
5
5
  class CsvDocument
6
- include AbAdmin::Utils::EvalHelpers
7
-
8
6
  def initialize(source, options = {})
9
7
  @source = source
10
8
  @options = options
@@ -33,7 +31,10 @@ module AbAdmin
33
31
 
34
32
  I18n.with_locale options[:locale] do
35
33
  each_record do |item|
36
- csv << column_data.map { |column| AbAdmin.pretty_data call_method_or_proc_on(item, column, context: context) }
34
+ csv << column_data.map do |column|
35
+ value = column.is_a?(Symbol) ? item.public_send(column) : context.instance_exec(item, &column)
36
+ AbAdmin.pretty_data value
37
+ end
37
38
  end
38
39
  end
39
40
  end
@@ -62,7 +63,6 @@ module AbAdmin
62
63
  @source.class
63
64
  end
64
65
  end
65
-
66
66
  end
67
67
  end
68
68
  end
@@ -83,22 +83,6 @@ module AbAdmin
83
83
  raise ArgumentError, 'Methods must be a symbol denoting the method to call, a block to be invoked, or a string to be evaluated'
84
84
  end
85
85
  end
86
-
87
- def call_method_or_proc_on(obj, symbol_or_proc, options = {})
88
- options.reverse_merge!(exec: true)
89
- case symbol_or_proc
90
- when String
91
- symbol_or_proc
92
- when Symbol
93
- obj.public_send(symbol_or_proc.to_sym, *options[:attrs])
94
- when Proc
95
- if options[:context]
96
- options[:context].instance_exec(obj, &symbol_or_proc)
97
- else
98
- options[:exec] ? obj.instance_exec(&symbol_or_proc) : symbol_or_proc.call(obj)
99
- end
100
- end
101
- end
102
86
  end
103
87
  end
104
88
  end
@@ -8,11 +8,21 @@ module AbAdmin
8
8
  error("#{e.class} #{message}\n#{backtrace}\n\n")
9
9
  end
10
10
 
11
+ def puts(msg)
12
+ debug msg
13
+ end
14
+
11
15
  def reopen
12
16
  @logdev = LogDevice.new(@logdev.filename)
13
17
  end
14
18
  end
15
19
 
20
+ class BasicFormatter
21
+ def call(severity, time, _, msg)
22
+ "#{msg}\n"
23
+ end
24
+ end
25
+
16
26
  class Formatter
17
27
  FORMAT = "[%s] %5s %s\n".freeze
18
28
  DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%3N'.freeze
@@ -22,9 +32,9 @@ module AbAdmin
22
32
  end
23
33
  end
24
34
 
25
- def self.for_file(filename)
35
+ def self.for_file(filename, formatter: nil)
26
36
  logger = ExtendedLogger.new(Rails.root.join('log', filename))
27
- logger.formatter = Formatter.new
37
+ logger.formatter = formatter || Formatter.new
28
38
  logger
29
39
  end
30
40
  end
@@ -14,14 +14,13 @@ module AbAdmin
14
14
 
15
15
  # remove duplicate records by columns
16
16
  def remove_duplicates(*cols, deleted_id_order: '<')
17
- conds = cols.map { |col| "#{table_name}.#{col} IS NOT NULL AND #{table_name}.#{col} = t.#{col}" }.join(' AND ')
17
+ condition_sql = 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 #{deleted_id_order} 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 #{condition_sql}
20
20
  SQL
21
21
  connection.execute(query)
22
22
  end
23
23
 
24
-
25
24
  # Disables key updates for model table
26
25
  def disable_keys
27
26
  connection.execute("ALTER TABLE #{quoted_table_name} DISABLE KEYS")
@@ -12,8 +12,6 @@ module AbAdmin
12
12
  end
13
13
 
14
14
  class XlsDocument
15
- include AbAdmin::Utils::EvalHelpers
16
-
17
15
  def initialize(source, options = {})
18
16
  @source = source
19
17
  @options = options
@@ -58,7 +56,7 @@ module AbAdmin
58
56
  row = index + 1
59
57
 
60
58
  column_data.each_with_index do |column, num|
61
- value = call_method_or_proc_on(item, column, context: context)
59
+ value = column.is_a?(Symbol) ? item.public_send(column) : context.instance_exec(item, &column)
62
60
 
63
61
  case value
64
62
  when Date
@@ -1,10 +1,5 @@
1
1
  module AbAdmin
2
2
  module Utils
3
- autoload :CSVBuilder, 'ab_admin/utils/csv_builder'
4
- autoload :EvalHelpers, 'ab_admin/utils/eval_helpers'
5
- autoload :Logger, 'ab_admin/utils/logger'
6
- autoload :Mysql, 'ab_admin/utils/mysql'
7
-
8
3
  def all_models
9
4
  Kernel.suppress_warnings do
10
5
  Dir.glob(Rails.root.to_s + '/app/models/**/*.rb').reject { |path| path =~ /concerns|shared/ }.each { |file| require file }
@@ -1,3 +1,3 @@
1
1
  module AbAdmin
2
- VERSION = '0.9.0'
2
+ VERSION = '0.10.0'
3
3
  end
@@ -5,9 +5,12 @@ module AbAdmin
5
5
  record = Array(object).last
6
6
  record.fallbacks_for_empty_translations = false if record.respond_to?(:fallbacks_for_empty_translations)
7
7
  options = args.extract_options!
8
+ options[:wrapper] = :bootstrap
8
9
  options[:remote] = true if request.xhr?
9
10
  options[:html] ||= {}
10
- options[:html][:class] ||= 'form-horizontal'
11
+ options[:html][:class] = Array(options[:html][:class])
12
+ options[:html][:class] << 'form-horizontal' if options[:html][:class].empty?
13
+ options[:html][:class] << 'save-error' if Array(object).last.errors.of_kind?(:base, :changed)
11
14
  options[:builder] ||= ::AbAdmin::Views::FormBuilder
12
15
  options[:html]['data-id'] = record.id
13
16
  if controller_name == 'manager' && resource_class == Array(object).last.class
@@ -77,7 +80,7 @@ module AbAdmin
77
80
  value: opts[:value] || item[attr],
78
81
  title: opts[:title] || item[attr]
79
82
  }
80
- link_to html_title, '#', class: "editable #{opts[:class]}", data: data.update(opts[:data] || {})
83
+ link_to html_title, '#', class: "editable #{opts[:class]}", placeholder: opts[:placeholder], data: data.update(opts[:data] || {})
81
84
  end
82
85
 
83
86
  def options_for_ckeditor(options = {})
@@ -122,8 +125,9 @@ module AbAdmin
122
125
  %(<span class="badge #{css}">#{text}</span>).html_safe
123
126
  end
124
127
 
125
- def icon(name, white=false)
126
- "<i class='icon-#{name} #{'icon-white' if white}'></i> ".html_safe
128
+ def icon(name, white=false, title: nil)
129
+ title_html = %( title="#{h(title)}") if title
130
+ "<i class='icon-#{name}#{' icon-white' if white}#{' tool' if title}'#{title_html}></i> ".html_safe
127
131
  end
128
132
 
129
133
  def locale_flag(code)
@@ -132,12 +136,16 @@ module AbAdmin
132
136
 
133
137
  def admin_pretty_data(object)
134
138
  case object
135
- when String, Integer, BigDecimal, Float
139
+ when Integer, BigDecimal, Float
136
140
  object
141
+ when String
142
+ object.html_safe? ? object : object.no_html.gsub("\n", '<br/>').html_safe
137
143
  when TrueClass, FalseClass
138
144
  color_bool(object)
139
- when Date, DateTime, Time, ActiveSupport::TimeWithZone
140
- I18n.l(object, format: :long)
145
+ when Date
146
+ I18n.l(object, format: AbAdmin.date_format)
147
+ when DateTime, Time, ActiveSupport::TimeWithZone
148
+ I18n.l(object, format: AbAdmin.datetime_format)
141
149
  when NilClass
142
150
  ''
143
151
  when ActiveRecord::Base
@@ -178,6 +186,11 @@ module AbAdmin
178
186
  content_tag(:div, html, options)
179
187
  end
180
188
 
189
+ def copy_btn(text, btn_text: nil, tooltip: false)
190
+ return if text.blank?
191
+ content_tag(:div, "#{icon('share')} #{btn_text}".html_safe, 'data-clipboard-text' => text, class: ['btn', 'btn-mini', 'js-copy', ('tool' if tooltip)], title: (text.no_html if tooltip))
192
+ end
193
+
181
194
  def ha(attr)
182
195
  resource_class.han(attr)
183
196
  end
@@ -186,15 +199,19 @@ module AbAdmin
186
199
  AbAdmin.site_name.is_a?(String) ? AbAdmin.site_name : AbAdmin.site_name.call
187
200
  end
188
201
 
189
- def call_method_or_proc_on(obj, symbol_or_proc, options = {})
190
- exec = options[:exec].nil? ? true : options[:exec]
191
- case symbol_or_proc
192
- when String
193
- symbol_or_proc
194
- when Symbol
195
- obj.send(symbol_or_proc.to_sym)
196
- when Proc
197
- exec ? instance_exec(obj, &symbol_or_proc) : symbol_or_proc.call(obj)
202
+ def option_conditions_met?(options, object=nil)
203
+ return true unless options
204
+ condition = options[:if] || options[:unless]
205
+ return true unless condition
206
+ options[:if] ? method_or_proc_on(condition, object) : !method_or_proc_on(condition, object)
207
+ end
208
+
209
+ def method_or_proc_on(symbol_or_proc, object=nil)
210
+ return unless symbol_or_proc
211
+ if symbol_or_proc.is_a?(Symbol)
212
+ (object || self).public_send(symbol_or_proc)
213
+ else
214
+ object ? instance_exec(object, &symbol_or_proc) : instance_exec(&symbol_or_proc)
198
215
  end
199
216
  end
200
217
  end
@@ -63,7 +63,7 @@ module AbAdmin
63
63
  end
64
64
 
65
65
  def collection_params
66
- params.slice(:index_view, *button_scopes.map(&:first)).permit!.to_h.symbolize_keys
66
+ params.slice(:index_view, :view_options, *button_scopes.map(&:name)).permit!.to_h.symbolize_keys
67
67
  end
68
68
 
69
69
  def short_action_link(action, item)
@@ -80,9 +80,7 @@ module AbAdmin
80
80
  class: 'btn btn-info', title: t('admin.actions.show.link')
81
81
  when :preview
82
82
  preview_path = preview_resource_path(item)
83
- if preview_path
84
- link_to icon('eye-open', true), preview_path, class: 'btn btn-inverse', title: t('admin.actions.preview.link'), target: '_blank'
85
- end
83
+ link_to(icon('eye-open', true), preview_path, class: 'btn btn-inverse', title: t('admin.actions.preview.link'), target: '_blank') if preview_path
86
84
  when :history
87
85
  item_link_to_can? :history, item, icon('book', true), history_resource_path(item),
88
86
  class: 'btn btn-info', title: t('admin.actions.history.link')
@@ -111,9 +109,8 @@ module AbAdmin
111
109
  when :show
112
110
  link_to_can? :show, t('admin.actions.show.link'), resource_path, class: 'btn btn-info'
113
111
  when :preview
114
- if path = preview_resource_path(resource)
115
- link_to t('admin.actions.preview.link'), path, class: 'btn btn-inverse', title: t('admin.actions.preview.link'), target: '_blank'
116
- end
112
+ preview_path = preview_resource_path(resource)
113
+ link_to(t('admin.actions.preview.link'), preview_path, class: 'btn btn-inverse', title: t('admin.actions.preview.link'), target: '_blank') if preview_path
117
114
  when :history
118
115
  link_to_can? :history, t('admin.actions.history.link'), history_resource_path, class: 'btn btn-info'
119
116
  when AbAdmin::Config::ActionItem
@@ -129,8 +126,14 @@ module AbAdmin
129
126
  end
130
127
  end
131
128
 
129
+ def preview_resource_path(item)
130
+ return unless controller_name == 'manager' && manager.preview_path && option_conditions_met?(manager.preview_path[:options], item)
131
+ manager.preview_path[:value].is_a?(Symbol) ? public_send(manager.preview_path[:value], item) : instance_exec(item, &manager.preview_path[:value])
132
+ end
133
+
132
134
  def link_to_can?(act, *args, &block)
133
- item_link_to_can?(act, get_subject, *args, &block)
135
+ subject = params[:id] ? resource : resource_class
136
+ item_link_to_can?(act, subject, *args, &block)
134
137
  end
135
138
 
136
139
  def item_link_to_can?(act, item, *args, &block)
@@ -207,7 +210,7 @@ module AbAdmin
207
210
  def batch_action_item(item)
208
211
  if settings[:batch]
209
212
  content_tag :td do
210
- check_box_tag 'by_ids[]', item.id, false, id: "batch_action_item_#{item.id}", class: 'batch_check'
213
+ check_box_tag 'q[id_in][]', item.id, false, id: "batch_action_item_#{item.id}", class: 'batch_check'
211
214
  end
212
215
  end
213
216
  end
@@ -3,11 +3,7 @@ module AbAdmin
3
3
  module Inputs
4
4
  class CkeditorInput < ::SimpleForm::Inputs::Base
5
5
  def input(wrapper_options=nil)
6
- unless @builder.template.instance_variable_get(:@ckeditor_loaded)
7
- @builder.template.concat @builder.template.javascript_include_tag(Ckeditor.cdn_url)
8
- @builder.template.instance_variable_set(:@ckeditor_loaded, true)
9
- end
10
- input_html_options.reverse_merge!({width: 800, height: 200})
6
+ input_html_options.reverse_merge!({width: 800, height: 200, data: {cdn_url: Ckeditor.cdn_url}})
11
7
  @builder.cktext_area(attribute_name, input_html_options)
12
8
  end
13
9
  end
@@ -17,6 +17,10 @@ module AbAdmin
17
17
  manager.chart ||= ::AbAdmin::Config::Chart.default_for_model(resource_class)
18
18
  end
19
19
 
20
+ def stats_builder
21
+ manager.stats ||= ::AbAdmin::Config::Stats.default_for_model(resource_class)
22
+ end
23
+
20
24
  def map_builder
21
25
  manager.map ||= ::AbAdmin::Config::Map.default_for_model(resource_class)
22
26
  end
@@ -39,7 +43,7 @@ module AbAdmin
39
43
  elsif field.options[:image]
40
44
  item_image_link(item, assoc: field.name)
41
45
  else
42
- admin_pretty_data call_method_or_proc_on(item, field.data)
46
+ admin_pretty_data method_or_proc_on(field.data, item)
43
47
  end
44
48
  end
45
49
 
@@ -54,10 +58,12 @@ module AbAdmin
54
58
  end
55
59
 
56
60
  INDEX_VIEW_ICONS = {table: 'list', tree: 'move', grid: 'th', chart: 'signal', map: 'map-marker', stats: 'eye-open'}
61
+ INDEX_VIEW_SYMBOLS = {pie_chart: '◔'}
57
62
  def index_view_link(index_view)
58
- options = {class: "btn tool tool-bottom #{('active' if settings[:current_index_view] == index_view)}", title: t("admin.index_view.#{index_view}", default: index_view.to_s)}
63
+ options = {class: ['btn', 'tool', 'tool-bottom', ('active' if settings[:current_index_view] == index_view)], title: t("admin.index_view.#{index_view}", default: index_view.to_s)}
59
64
  url = url_for({index_view: index_view, q: params[:q]}.reject_blank)
60
- title = INDEX_VIEW_ICONS[index_view.to_sym] ? icon(INDEX_VIEW_ICONS[index_view.to_sym]) : index_view
65
+ title = INDEX_VIEW_ICONS[index_view.to_sym] ? icon(INDEX_VIEW_ICONS[index_view.to_sym]) : (INDEX_VIEW_SYMBOLS[index_view.to_sym] || index_view.to_s)
66
+ options[:class] << 'btn-symbol' if INDEX_VIEW_SYMBOLS[index_view.to_sym]
61
67
  link_to title, url, options
62
68
  end
63
69
  end