ab_admin 0.9.0 → 0.10.0

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