ab_admin 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/Gemfile +1 -0
  2. data/Guardfile +1 -1
  3. data/app/assets/images/admin/flags/b_uk.png +0 -0
  4. data/app/assets/images/admin/preloader.gif +0 -0
  5. data/app/assets/javascripts/ab_admin/application.js +1 -28
  6. data/app/assets/javascripts/ab_admin/components/admin_assets.js.coffee +29 -6
  7. data/app/assets/javascripts/ab_admin/components/gmaps.js.coffee +120 -91
  8. data/app/assets/javascripts/ab_admin/components/google_translate.js.coffee +3 -2
  9. data/app/assets/javascripts/ab_admin/core/batch_actions.js.coffee +4 -7
  10. data/app/assets/javascripts/ab_admin/core/init.js.coffee +8 -10
  11. data/app/assets/javascripts/ab_admin/core/ui_utils.js.coffee +53 -24
  12. data/app/assets/javascripts/ab_admin/core/utils.js.coffee +7 -0
  13. data/app/assets/javascripts/ab_admin/inputs/datetime_input.js.coffee +1 -2
  14. data/app/assets/javascripts/ab_admin/main.js +28 -0
  15. data/app/assets/stylesheets/ab_admin/application.css.scss +1 -10
  16. data/app/assets/stylesheets/ab_admin/bootstrap_and_overrides.css.scss +4 -0
  17. data/app/assets/stylesheets/ab_admin/components/_base.css.scss +4 -1
  18. data/app/assets/stylesheets/ab_admin/components/_form.css.scss +10 -13
  19. data/app/assets/stylesheets/ab_admin/components/_geo_input.css.scss +5 -9
  20. data/app/assets/stylesheets/ab_admin/fileupload.css.scss +68 -102
  21. data/app/assets/stylesheets/ab_admin/main.css.scss +8 -0
  22. data/app/controllers/admin/base_controller.rb +18 -14
  23. data/app/controllers/admin/manager_controller.rb +51 -6
  24. data/app/controllers/admin/users_controller.rb +23 -0
  25. data/app/views/admin/fileupload/_asset.html.slim +8 -0
  26. data/app/views/admin/fileupload/_container.html.slim +10 -0
  27. data/app/views/admin/fileupload/_file.html.slim +5 -0
  28. data/app/views/admin/fileupload/_ftmpl.html.slim +6 -0
  29. data/app/views/admin/fileupload/_tmpl.html.slim +9 -0
  30. data/app/views/admin/users/_form.html.slim +1 -0
  31. data/config/locales/en.yml +5 -60
  32. data/config/locales/ru.yml +14 -63
  33. data/config/routes.rb +7 -4
  34. data/features/dsl/custom_actions.feature +62 -0
  35. data/features/dsl/parent_resource.feature +18 -0
  36. data/features/step_definitions/configuration_steps.rb +6 -0
  37. data/features/step_definitions/dsl/parent_resource_steps.rb +8 -0
  38. data/features/step_definitions/settings_steps.rb +1 -1
  39. data/features/step_definitions/web_steps/transforms_steps.rb +3 -0
  40. data/features/support/paths.rb +3 -0
  41. data/lib/ab_admin.rb +5 -0
  42. data/lib/ab_admin/abstract_resource.rb +28 -4
  43. data/lib/ab_admin/carrierwave/base_uploader.rb +22 -22
  44. data/lib/ab_admin/concerns/admin_addition.rb +2 -2
  45. data/lib/ab_admin/concerns/utilities.rb +4 -4
  46. data/lib/ab_admin/config/base.rb +18 -0
  47. data/lib/ab_admin/controllers/head_options.rb +1 -1
  48. data/lib/ab_admin/core_ext/array.rb +4 -0
  49. data/lib/ab_admin/core_ext/string.rb +9 -10
  50. data/lib/ab_admin/hooks/quiet_scope_page.rb +16 -16
  51. data/lib/ab_admin/hooks/simple_form_hooks.rb +13 -13
  52. data/lib/ab_admin/i18n_tools/model_translator.rb +67 -0
  53. data/lib/ab_admin/models/asset.rb +32 -1
  54. data/lib/ab_admin/models/attachment_file.rb +1 -1
  55. data/lib/ab_admin/models/locator.rb +7 -0
  56. data/lib/ab_admin/models/settings.rb +7 -0
  57. data/lib/ab_admin/models/structure.rb +4 -0
  58. data/lib/ab_admin/utils.rb +2 -2
  59. data/lib/ab_admin/version.rb +1 -1
  60. data/lib/ab_admin/views/admin_helpers.rb +3 -3
  61. data/lib/ab_admin/views/admin_navigation_helpers.rb +1 -1
  62. data/lib/ab_admin/views/form_builder.rb +42 -39
  63. data/lib/ab_admin/views/helpers.rb +5 -0
  64. data/lib/ab_admin/views/manager_helpers.rb +10 -0
  65. data/lib/ab_admin/views/search_form_builder.rb +12 -5
  66. data/lib/generators/ab_admin/install/install_generator.rb +1 -1
  67. data/lib/generators/ab_admin/install/templates/config/admin_menu.rb +2 -2
  68. data/lib/generators/ab_admin/install/templates/helpers/admin/structures_helper.rb +1 -1
  69. data/lib/generators/ab_admin/install/templates/models/attachment_file.rb +2 -0
  70. data/lib/generators/ab_admin/install/templates/models/locator.rb +0 -1
  71. data/lib/generators/ab_admin/install/templates/models/settings.rb +0 -1
  72. data/lib/generators/ab_admin/install/templates/models/static_page.rb +1 -0
  73. data/lib/generators/ab_admin/install/templates/{scripts → script}/unicorn.sh +0 -0
  74. data/lib/generators/ab_admin/install/templates/uploaders/avatar_uploader.rb +4 -0
  75. data/lib/generators/ab_admin/resource/templates/_search_form.haml.erb +1 -1
  76. data/lib/generators/ab_admin/resource/templates/_search_form.slim.erb +1 -1
  77. data/lib/generators/ab_admin/resource/templates/_table.haml.erb +1 -2
  78. data/lib/generators/ab_admin/resource/templates/_table.slim.erb +1 -2
  79. data/lib/generators/template.rb +1 -0
  80. data/lib/tasks/i18n.rake +7 -0
  81. data/spec/dummy/app/models/ab_admin/ab_admin_collection.rb +35 -0
  82. data/spec/dummy/app/models/ab_admin/ab_admin_product.rb +2 -0
  83. data/spec/dummy/app/models/admin_menu.rb +4 -2
  84. data/spec/dummy/app/models/collection.rb +3 -1
  85. data/spec/generators/install_generator_spec.rb +1 -1
  86. data/spec/generators/resource_generator_spec.rb +1 -1
  87. data/spec/models/avatar_spec.rb +29 -2
  88. metadata +25 -12
  89. data/app/views/admin/fileupload/_asset.html.erb +0 -5
  90. data/app/views/admin/fileupload/_container.html.erb +0 -28
  91. data/app/views/admin/fileupload/_fcontainer.html.erb +0 -25
  92. data/app/views/admin/fileupload/_file.html.erb +0 -5
  93. data/app/views/admin/fileupload/_ftmpl.html.erb +0 -7
  94. data/app/views/admin/fileupload/_tmpl.html.erb +0 -9
  95. data/lib/ab_admin/utils/csv_builder.rb +0 -52
@@ -4,10 +4,11 @@ module AbAdmin
4
4
 
5
5
  include Singleton
6
6
 
7
- ACTIONS = [:index, :show, :new, :edit, :create, :update, :destroy, :preview, :batch, :rebuild] unless self.const_defined?(:ACTIONS)
7
+ ACTIONS = [:index, :show, :new, :edit, :create, :update, :destroy, :preview, :batch, :rebuild, :custom_action] unless self.const_defined?(:ACTIONS)
8
8
 
9
9
  attr_accessor :table, :search, :export, :form, :show, :preview_path, :actions, :settings, :custom_settings,
10
- :batch_action_list, :action_items, :disabled_action_items, :resource_action_items, :tree_node_renderer
10
+ :batch_action_list, :action_items, :disabled_action_items, :resource_action_items, :tree_node_renderer,
11
+ :parent_resources, :custom_actions
11
12
 
12
13
  def initialize
13
14
  @actions = ACTIONS
@@ -16,6 +17,8 @@ module AbAdmin
16
17
  @disabled_action_items = []
17
18
  @default_action_items_for = {}
18
19
  @action_items_for = {}
20
+ @parent_resources = []
21
+ @custom_actions = []
19
22
  @model = self.class.name.sub('AbAdmin', '').safe_constantize
20
23
  add_admin_addition_to_model
21
24
  end
@@ -57,7 +60,7 @@ module AbAdmin
57
60
  ACTIONS - Array(options[:except]).map(&:to_sym)
58
61
  else
59
62
  actions_to_keep
60
- end
63
+ end << :custom_action
61
64
  end.map(&:to_sym)
62
65
  end
63
66
 
@@ -93,6 +96,21 @@ module AbAdmin
93
96
  def tree(&block)
94
97
  instance.tree_node_renderer = block
95
98
  end
99
+
100
+ def belongs_to(*args)
101
+ options = args.extract_options!
102
+ args.each do |name|
103
+ instance.parent_resources << OpenStruct.new(:name => name, :options => options)
104
+ end
105
+ end
106
+
107
+ def member_action(name, options={}, &block)
108
+ instance.custom_actions << AbAdmin::Config::CustomAction.new(name, options, &block)
109
+ end
110
+
111
+ def collection_action(name, options={}, &block)
112
+ instance.custom_actions << AbAdmin::Config::CustomAction.new(name, options.merge(:collection => true), &block)
113
+ end
96
114
  end
97
115
 
98
116
  def export
@@ -118,7 +136,13 @@ module AbAdmin
118
136
  end
119
137
 
120
138
  def resource_action_items
121
- @resource_action_items ||= [:edit, :show, :destroy, :preview]
139
+ @resource_action_items ||= [:edit, :show, :destroy, :preview] & @actions
140
+ end
141
+
142
+ def custom_action_for(name, context)
143
+ custom_action = @custom_actions.detect { |a| a.name == name.to_sym }
144
+ raise "No allowed custom action found #{name}" if !custom_action || !custom_action.for_context?(context)
145
+ custom_action
122
146
  end
123
147
 
124
148
  end
@@ -10,24 +10,24 @@ module AbAdmin
10
10
  include ::CarrierWave::MiniMagick
11
11
  include ::CarrierWave::MimeTypes
12
12
  include AbAdmin::Utils::EvalHelpers
13
-
13
+
14
14
  storage :file
15
-
15
+
16
16
  process :set_content_type
17
-
17
+
18
18
  with_options :if => :image? do |img|
19
19
  img.process :strip
20
20
  img.process :cropper => lambda { |model| model.cropper_geometry }
21
21
  img.process :rotate => lambda { |model| model.rotate_degrees }
22
22
  end
23
-
23
+
24
24
  process :set_model_info
25
-
26
- # default store assets path
25
+
26
+ # default store assets path
27
27
  def store_dir
28
28
  "uploads/#{model.class.to_s.underscore}/#{model.id}"
29
29
  end
30
-
30
+
31
31
  # Strips out all embedded information from the image
32
32
  # process :strip
33
33
  #
@@ -38,13 +38,13 @@ module AbAdmin
38
38
  img
39
39
  end
40
40
  end
41
-
41
+
42
42
  # Reduces the quality of the image to the percentage given
43
43
  # process :quality => 85
44
44
  #
45
45
  def quality(percentage)
46
46
  percentage = normalize_param(percentage)
47
-
47
+
48
48
  unless percentage.blank?
49
49
  manipulate! do |img|
50
50
  img.quality(percentage.to_s)
@@ -53,13 +53,13 @@ module AbAdmin
53
53
  end
54
54
  end
55
55
  end
56
-
56
+
57
57
  # Rotate image by degress
58
58
  # process :rotate => "-90"
59
59
  #
60
60
  def rotate(degrees = nil)
61
61
  degrees = normalize_param(degrees)
62
-
62
+
63
63
  unless degrees.blank?
64
64
  manipulate! do |img|
65
65
  img.rotate(degrees.to_s)
@@ -68,7 +68,7 @@ module AbAdmin
68
68
  end
69
69
  end
70
70
  end
71
-
71
+
72
72
  # Crop image by specific coordinates
73
73
  # http://www.imagemagick.org/script/command-line-processing.php?ImageMagick=6ddk6c680muj4eu2vr54vdveb7#geometry
74
74
  # process :cropper => [size, offset]
@@ -76,7 +76,7 @@ module AbAdmin
76
76
  #
77
77
  def cropper(*geometry)
78
78
  geometry = normalize_param(geometry[0]) if geometry.size == 1
79
-
79
+
80
80
  if geometry && geometry.size == 4
81
81
  manipulate! do |img|
82
82
  img.crop '%ix%i+%i+%i' % geometry
@@ -86,10 +86,10 @@ module AbAdmin
86
86
  end
87
87
  end
88
88
 
89
- def watermark(path_to_file)
89
+ def watermark(path_to_file, gravity='SouthEast')
90
90
  manipulate! do |img|
91
91
  logo = ::MiniMagick::Image.open(path_to_file)
92
- img.composite(logo) { |c| c.gravity 'SouthEast' }
92
+ img.composite(logo) { |c| c.gravity gravity }
93
93
  end
94
94
  end
95
95
 
@@ -97,31 +97,31 @@ module AbAdmin
97
97
  image_name = [model.class.to_s.underscore, version_name].compact.join('_')
98
98
  "/assets/defaults/#{image_name}.png"
99
99
  end
100
-
100
+
101
101
  def image?(new_file = nil)
102
102
  (file || new_file).content_type.include? 'image'
103
103
  end
104
-
104
+
105
105
  def dimensions
106
106
  [magick[:width], magick[:height]]
107
107
  end
108
-
108
+
109
109
  def magick
110
110
  #@magick ||= ::MiniMagick::Image.new(current_path)
111
111
  ::MiniMagick::Image.new(current_path)
112
112
  end
113
-
113
+
114
114
  protected
115
-
115
+
116
116
  def set_model_info
117
117
  model.data_content_type = file.content_type
118
118
  model.data_file_size = file.size
119
-
119
+
120
120
  if image? && model.has_dimensions?
121
121
  model.width, model.height = dimensions
122
122
  end
123
123
  end
124
-
124
+
125
125
  def normalize_param(value)
126
126
  if value.is_a?(Proc) || value.is_a?(Method)
127
127
  evaluate_method(model, value, file)
@@ -5,8 +5,8 @@ module AbAdmin
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- scope :admin, scoped
9
- scope :base, scoped
8
+ scope(:admin, scoped) unless respond_to?(:admin)
9
+ scope(:base, scoped) unless respond_to?(:base)
10
10
  scope :ids, lambda { |ids| where("#{quoted_table_name}.id IN (?)", AbAdmin.val_to_array(ids).push(0)) }
11
11
 
12
12
  class_attribute :batch_actions, :instance_writer => false
@@ -101,9 +101,9 @@ module AbAdmin
101
101
  end
102
102
  end
103
103
 
104
- def generate_guid(column=:guid)
104
+ def generate_token(column=:guid)
105
105
  loop do
106
- token = Devise.friendly_token
106
+ token = ::Devise.friendly_token
107
107
  break token unless to_adapter.find_first({column => token})
108
108
  end
109
109
  end
@@ -113,9 +113,9 @@ module AbAdmin
113
113
  "#{self.class.model_name.singular}_#{id}"
114
114
  end
115
115
 
116
- def generate_token(column)
116
+ def generate_token(column=:guid)
117
117
  begin
118
- self[column] = Devise.friendly_token
118
+ self[column] = ::Devise.friendly_token
119
119
  end while self.class.exists?(column => self[column])
120
120
  end
121
121
  end
@@ -126,5 +126,23 @@ module AbAdmin
126
126
  normalize_display_options!
127
127
  end
128
128
  end
129
+
130
+ class CustomAction
131
+ include AbAdmin::Config::OptionalDisplay
132
+
133
+ attr_reader :name, :options, :data
134
+
135
+ def initialize(name, options={}, &block)
136
+ raise 'Can not create member action without a block' unless block_given?
137
+ @name = name
138
+ @options = options
139
+ @data = block
140
+ normalize_display_options!
141
+ end
142
+
143
+ def collection?
144
+ options[:collection]
145
+ end
146
+ end
129
147
  end
130
148
  end
@@ -6,7 +6,7 @@ module AbAdmin
6
6
  def head_options(record, options = {})
7
7
  return if record.nil?
8
8
 
9
- options = { :spliter => Utils.title_spliter, :append_title => true }.merge(options)
9
+ options = { :spliter => AbAdmin.title_spliter, :append_title => true }.merge(options)
10
10
 
11
11
  header = options[:header] || (record.respond_to?(:header) ? record.header : nil)
12
12
 
@@ -1,4 +1,8 @@
1
1
  class Array
2
+ def add_or_delete(el)
3
+ include?(el) ? delete(el) : push(el)
4
+ end
5
+
2
6
  def word_count
3
7
  each_with_object({}) do |word, h|
4
8
  h[word] ||= 0
@@ -55,26 +55,25 @@ class String
55
55
  self.tr(keyboard[from], keyboard[to])
56
56
  end
57
57
 
58
+ def count_words
59
+ clean_text.scan(/(\p{Alnum}+([-'.]\p{Alnum}+)*)/u).size
60
+ end
61
+
58
62
  def words_count
59
63
  frequencies = Hash.new(0)
60
64
  downcase.scan(/(\w+([-'.]\w+)*)/) { |word, ignore| frequencies[word] += 1 }
61
- return frequencies
65
+ frequencies
62
66
  end
63
67
 
64
68
  def self.randomize(length = 8)
65
69
  Array.new(length) { (rand(122-97) + 97).chr }.join
66
70
  end
67
71
 
68
- end
69
-
70
- if Object.const_defined?(:HTMLEntities)
71
- require 'htmlentities'
72
- class String
73
- def clean_text
74
- coder = HTMLEntities.new
75
- coder.decode(self.no_html)
76
- end
72
+ def clean_text
73
+ coder = HTMLEntities.new
74
+ coder.decode(self.no_html)
77
75
  end
76
+
78
77
  end
79
78
 
80
79
  unless ''.respond_to?(:each)
@@ -1,16 +1,16 @@
1
- module ActiveRecord
2
- module Scoping
3
- module Named
4
- module ClassMethods
5
- def valid_scope_name?(name)
6
- silence_names = [:page, :visible, :un_visible, :admin, :base]
7
- if respond_to?(name, true) && !silence_names.include?(name.to_sym)
8
- logger.warn "Creating scope :#{name}. " \
9
- "Overwriting existing method #{self.name}.#{name}."
10
- end
11
- end
12
- end
13
- end
14
- end
15
- end
16
-
1
+ #module ActiveRecord
2
+ # module Scoping
3
+ # module Named
4
+ # module ClassMethods
5
+ # def valid_scope_name?(name)
6
+ # silence_names = [:page, :visible, :un_visible, :admin, :base]
7
+ # if respond_to?(name, true) && !silence_names.include?(name.to_sym)
8
+ # logger.warn "Creating scope :#{name}. " \
9
+ # "Overwriting existing method #{self.name}.#{name}."
10
+ # end
11
+ # end
12
+ # end
13
+ # end
14
+ # end
15
+ #end
16
+ #
@@ -1,4 +1,4 @@
1
- require 'simple_form'
1
+ #require 'simple_form'
2
2
 
3
3
  #module WrappedButton
4
4
  # def wrapped_button(*args, &block)
@@ -17,15 +17,15 @@ require 'simple_form'
17
17
  # end
18
18
  #end
19
19
  #SimpleForm::FormBuilder.send :include, WrappedButton
20
-
21
- ::SimpleForm::Inputs::CollectionRadioButtonsInput.class_exec do
22
- def item_wrapper_class
23
- "radio #{options.delete(:item_wrapper_class)}"
24
- end
25
- end
26
-
27
- ::SimpleForm::Inputs::CollectionCheckBoxesInput.class_exec do
28
- def item_wrapper_class
29
- "radio #{options.delete(:item_wrapper_class)}"
30
- end
31
- end
20
+ #
21
+ #::SimpleForm::Inputs::CollectionRadioButtonsInput.class_exec do
22
+ # def item_wrapper_class
23
+ # "radio #{options.delete(:item_wrapper_class) || 'inline'}"
24
+ # end
25
+ #end
26
+ #
27
+ #::SimpleForm::Inputs::CollectionCheckBoxesInput.class_exec do
28
+ # def item_wrapper_class
29
+ # "radio #{options.delete(:item_wrapper_class) || 'inline'}"
30
+ # end
31
+ #end
@@ -0,0 +1,67 @@
1
+ module AbAdmin
2
+ module I18nTools
3
+ class ModelTranslator
4
+
5
+ IGNORE_COLUMNS = %w(id reset_password_sent_at remember_created_at current_sign_in_at confirmation_token
6
+ reset_password_token password_salt failed_attempts)
7
+
8
+ def initialize
9
+ @locales = Globalize.available_locales
10
+ @models = AbAdmin.translate_models.map{|m| m.constantize }
11
+ @models_i18n_hash = {}
12
+ end
13
+
14
+ def make_hash
15
+ @locales.each do |locale|
16
+ I18n.with_locale(locale) do
17
+ @models_i18n_hash[locale] = {'activerecord' => {'attributes' => {}, 'models' => {}}}
18
+
19
+ models_hash = @models.each_with_object({}) do |model, h|
20
+ model_i18n = {
21
+ 'zero' => model.model_name.human(:count => 0),
22
+ 'one' => model.model_name.human(:count => 1),
23
+ 'few' => (model.model_name.human(:count => 2) rescue model.model_name.human(:count => 1)),
24
+ 'many' => (model.model_name.human(:count => 9) rescue model.model_name.human(:count => 1)),
25
+ 'other' => (model.model_name.human(:count => 9) rescue model.model_name.human(:count => 1))
26
+ }
27
+ @models_i18n_hash[locale]['activerecord']['models'][model.model_name.i18n_key.to_s]= model_i18n
28
+ attributes = model.columns.map(&:name)
29
+ attributes.concat(model.translated_attribute_names.map(&:to_s)) if model.translates?
30
+ attributes.reject! { |el| IGNORE_COLUMNS.include?(el) }
31
+ h[model.model_name.underscore] = attributes.each_with_object({}) do |attr, o|
32
+ o[attr] = ha(model, attr, locale).presence || attr
33
+ model.reflect_on_all_associations.map(&:name).map(&:to_s).reject { |a| a =~ /^translation/ }.each do |assoc|
34
+ o[assoc] = ha(model, assoc, locale)
35
+ end
36
+ if model.new.respond_to?("#{attr}_#{locale.to_s}".to_sym)
37
+ @locales.each do |locale_1|
38
+ o["#{attr}_#{locale_1.to_s}"] = "#{ha(model, attr, locale)} (#{I18n.t(locale_1, :scope => [:attrs])})"
39
+ end
40
+ end
41
+ end.sort.to_hash
42
+ end
43
+ @models_i18n_hash[locale]['activerecord']['attributes'] = models_hash
44
+ end
45
+ end
46
+ end
47
+
48
+ def ha(model, attr, locale)
49
+ model.human_attribute_name(attr, :locale => locale)
50
+ end
51
+
52
+ def write_yaml
53
+ locale_dir = Rails.root.join('config/locales')
54
+ @locales.each do |locale|
55
+ Locator.save(locale_dir.join("#{locale}.attrs.yml"), {locale.to_s => @models_i18n_hash[locale]})
56
+ end
57
+ end
58
+
59
+ def self.i18n_models!
60
+ model_translator = new
61
+ model_translator.make_hash
62
+ model_translator.write_yaml
63
+ end
64
+
65
+ end
66
+ end
67
+ end