ab_admin 0.8.3 → 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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +11 -5
- data/app/assets/javascripts/ab_admin/components/admin_assets.js.coffee +0 -25
- data/app/assets/javascripts/ab_admin/components/google_translate.js.coffee +3 -5
- data/app/assets/javascripts/ab_admin/components/in_place_edit.js.coffee +33 -25
- data/app/assets/javascripts/ab_admin/core/batch_actions.js.coffee +1 -1
- data/app/assets/javascripts/ab_admin/core/columns_hider.js.coffee +24 -23
- data/app/assets/javascripts/ab_admin/core/init.js.coffee +7 -2
- data/app/assets/javascripts/ab_admin/core/search_form.js.coffee +1 -7
- data/app/assets/javascripts/ab_admin/core/ui_utils.js.coffee +23 -7
- data/app/assets/javascripts/ab_admin/core/utils.js.coffee +16 -2
- data/app/assets/javascripts/ab_admin/inputs/datetime_input.js.coffee +1 -0
- data/app/assets/javascripts/ab_admin/main.js +3 -4
- data/app/assets/stylesheets/ab_admin/bootstrap_and_overrides.scss +71 -25
- data/app/assets/stylesheets/ab_admin/components/_base.scss +21 -1
- data/app/assets/stylesheets/ab_admin/components/_colored_tabs.scss +1 -1
- data/app/assets/stylesheets/ab_admin/components/_form.scss +16 -18
- data/app/assets/stylesheets/ab_admin/components/_grid_view.scss +2 -2
- data/app/assets/stylesheets/ab_admin/components/_locale_tabs.scss +11 -23
- data/app/assets/stylesheets/ab_admin/components/_navigation.scss +7 -11
- data/app/assets/stylesheets/ab_admin/components/_table_view.scss +85 -11
- data/app/assets/stylesheets/ab_admin/components/_tooltip.scss +81 -0
- data/app/assets/stylesheets/ab_admin/components/_tree_view.scss +1 -1
- data/app/assets/stylesheets/ab_admin/devise.scss +2 -2
- data/app/assets/stylesheets/ab_admin/fileupload.scss +2 -9
- data/app/assets/stylesheets/ab_admin/main.scss +1 -2
- data/app/controllers/admin/assets_controller.rb +1 -1
- data/app/controllers/admin/base_controller.rb +133 -149
- data/app/controllers/admin/dashboards_controller.rb +2 -2
- data/app/controllers/admin/locators_controller.rb +8 -6
- data/app/controllers/admin/manager_controller.rb +19 -49
- data/app/controllers/admin/static_pages_controller.rb +0 -4
- data/app/controllers/admin/structures_controller.rb +2 -2
- data/app/views/ab_admin/devise/sessions/new.html.slim +2 -0
- data/app/views/admin/assets/batch_edit.html.slim +3 -2
- data/app/views/admin/base/_search_layout.html.slim +7 -6
- data/app/views/admin/base/create.js.erb +6 -3
- data/app/views/admin/base/edit.js.erb +1 -1
- data/app/views/admin/base/index.html.slim +4 -4
- data/app/views/admin/base/new.js.erb +1 -1
- data/app/views/admin/base/update.js.erb +7 -2
- data/app/views/admin/fileupload/_asset_templates.html.slim +1 -2
- data/app/views/admin/fileupload/_image.html.slim +1 -2
- data/app/views/admin/locators/edit.html.slim +7 -6
- data/app/views/admin/manager/_map.html.slim +4 -0
- data/app/views/admin/manager/_show_table.html.slim +1 -1
- data/app/views/admin/manager/_stats.html.slim +4 -0
- data/app/views/admin/manager/_table.html.slim +7 -4
- data/app/views/admin/shared/_content_actions.html.slim +35 -30
- data/app/views/admin/shared/_flash.html.slim +5 -4
- data/app/views/admin/shared/_locale_tabs.html.slim +2 -2
- data/app/views/admin/shared/_main_menu.html.slim +1 -1
- data/app/views/admin/shared/_save_buttons.html.slim +10 -1
- data/app/views/admin/structures/_form.html.slim +1 -1
- data/app/views/admin/users/_form.html.slim +3 -3
- data/app/views/admin/users/_search_form.html.slim +1 -1
- data/app/views/layouts/admin/_footer.html.slim +0 -1
- data/app/views/layouts/admin/_navigation.html.slim +1 -1
- data/app/views/layouts/admin/application.html.slim +2 -2
- data/config/locales/de.yml +1 -2
- data/config/locales/en.yml +9 -15
- data/config/locales/it.yml +1 -0
- data/config/locales/ru.yml +0 -1
- data/config/locales/uk.yml +0 -1
- data/db/migrate/20130101000001_create_users.rb +1 -4
- data/db/migrate/20130101000003_create_assets.rb +1 -1
- data/db/migrate/20130101000004_create_headers.rb +5 -5
- data/db/migrate/20130101000005_create_static_pages.rb +2 -5
- data/db/migrate/20130101000006_create_structures.rb +1 -1
- data/db/migrate/20130101000007_base_translations.rb +43 -12
- data/db/migrate/20130101000008_create_admin_comments.rb +2 -7
- data/db/migrate/20130101000009_create_tracks.rb +4 -8
- data/lib/ab_admin/abstract_resource.rb +6 -5
- data/lib/ab_admin/carrierwave/base_uploader.rb +87 -75
- data/lib/ab_admin/carrierwave/glue.rb +0 -5
- data/lib/ab_admin/concerns/admin_addition.rb +15 -27
- data/lib/ab_admin/concerns/translations_macro.rb +97 -0
- data/lib/ab_admin/concerns/utilities.rb +2 -2
- data/lib/ab_admin/config/base.rb +27 -4
- data/lib/ab_admin/controllers/callbacks.rb +3 -26
- data/lib/ab_admin/core_ext/array.rb +1 -50
- data/lib/ab_admin/core_ext/hash.rb +2 -31
- data/lib/ab_admin/core_ext/other.rb +0 -6
- data/lib/ab_admin/core_ext/string.rb +1 -86
- data/lib/ab_admin/devise.rb +7 -0
- data/lib/ab_admin/engine.rb +2 -1
- data/lib/ab_admin/hooks/ckeditor_lazy.rb +13 -0
- data/lib/ab_admin/hooks/will_paginate_id_prefetch.rb +8 -6
- data/lib/ab_admin/hooks/will_paginate_no_uri.rb +1 -1
- data/lib/ab_admin/i18n_tools/google_translate.rb +3 -1
- data/lib/ab_admin/i18n_tools/model_translator.rb +1 -1
- data/lib/ab_admin/menu/base_group.rb +0 -1
- data/lib/ab_admin/menu/group.rb +2 -4
- data/lib/ab_admin/menu/item.rb +4 -8
- data/lib/ab_admin/models/asset.rb +7 -10
- data/lib/ab_admin/models/header.rb +2 -2
- data/lib/ab_admin/models/locator.rb +29 -3
- data/lib/ab_admin/models/settings.rb +2 -2
- data/lib/ab_admin/models/structure.rb +3 -3
- data/lib/ab_admin/models/track.rb +15 -3
- data/lib/ab_admin/models/user.rb +12 -48
- data/lib/ab_admin/utils/csv_document.rb +8 -6
- data/lib/ab_admin/utils/eval_helpers.rb +0 -13
- data/lib/ab_admin/utils/logger.rb +12 -2
- data/lib/ab_admin/utils/mysql.rb +2 -3
- data/lib/ab_admin/utils/xls_document.rb +18 -18
- data/lib/ab_admin/utils.rb +0 -5
- data/lib/ab_admin/version.rb +1 -1
- data/lib/ab_admin/views/admin_helpers.rb +43 -28
- data/lib/ab_admin/views/admin_navigation_helpers.rb +18 -16
- data/lib/ab_admin/views/form_builder.rb +7 -5
- data/lib/ab_admin/views/helpers.rb +0 -9
- data/lib/ab_admin/views/inputs/ckeditor_input.rb +1 -5
- data/lib/ab_admin/views/manager_helpers.rb +15 -6
- data/lib/ab_admin/views/search_form_builder.rb +13 -13
- data/lib/ab_admin/views/will_paginate_bootstrap_renderer.rb +60 -0
- data/lib/ab_admin.rb +44 -32
- data/lib/generators/ab_admin/glob/glob_generator.rb +4 -5
- data/lib/generators/ab_admin/glob/templates/migration.erb +10 -7
- data/lib/generators/ab_admin/install/templates/config/ab_admin.rb.erb +1 -1
- data/lib/generators/ab_admin/install/templates/models/user.rb +1 -13
- data/lib/generators/ab_admin/install/templates/spec/spec_helper.rb +0 -1
- data/lib/generators/ab_admin/install/templates/spec/support/database_cleaner.rb +8 -11
- data/lib/generators/ab_admin/install/templates/uploaders/attachment_file_uploader.rb +1 -1
- data/lib/generators/ab_admin/install/templates/uploaders/avatar_uploader.rb +1 -1
- data/lib/generators/ab_admin/install/templates/uploaders/picture_uploader.rb +16 -3
- data/lib/generators/ab_admin/model/model_generator.rb +3 -4
- data/lib/generators/ab_admin/model/templates/resource.erb +5 -2
- data/lib/generators/ab_admin/resource/resource_generator.rb +0 -4
- data/lib/generators/ab_admin/resource/templates/controller.erb +2 -9
- data/lib/tasks/assets.rake +5 -5
- metadata +45 -85
- data/app/assets/images/admin/Jcrop.gif +0 -0
- data/app/assets/images/admin/flags/de.png +0 -0
- data/app/assets/images/admin/flags/en.png +0 -0
- data/app/assets/images/admin/flags/es.png +0 -0
- data/app/assets/images/admin/flags/fr.png +0 -0
- data/app/assets/images/admin/flags/it.png +0 -0
- data/app/assets/images/admin/flags/ja.png +0 -0
- data/app/assets/images/admin/flags/pl.png +0 -0
- data/app/assets/images/admin/flags/ru.png +0 -0
- data/app/assets/images/admin/flags/uk.png +0 -0
- data/app/assets/javascripts/ab_admin/components/croppable_image.js.coffee +0 -33
- data/app/assets/stylesheets/ab_admin/components/_columns_hider.scss +0 -5
- data/app/assets/stylesheets/ab_admin/components/_perms.scss +0 -39
- data/app/views/admin/shared/_columns_hider.html.slim +0 -9
- data/lib/ab_admin/hooks/globalize_locale_suffix_accessors.rb +0 -25
- data/lib/ab_admin/hooks/globalize_valid_locale.rb +0 -9
- data/lib/generators/ab_admin/ckeditor_assets/ckeditor_assets_generator.rb +0 -19
- data/lib/generators/template.rb +0 -96
|
@@ -1,15 +1,46 @@
|
|
|
1
|
-
class BaseTranslations < ActiveRecord::Migration
|
|
2
|
-
def
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
class BaseTranslations < ActiveRecord::Migration[5.2]
|
|
2
|
+
def change
|
|
3
|
+
create_table :static_page_translations do |t|
|
|
4
|
+
t.references :static_page, null: false
|
|
5
|
+
t.string :locale, limit: 5, null: false
|
|
6
|
+
t.string :title
|
|
7
|
+
t.text :content
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
add_index :static_page_translations, [:static_page_id, :locale], unique: true, name: 'static_pages_ts_static_page_id_locale'
|
|
12
|
+
|
|
13
|
+
create_table :header_translations do |t|
|
|
14
|
+
t.references :header, null: false
|
|
15
|
+
t.string :locale, limit: 5, null: false
|
|
16
|
+
t.string :title
|
|
17
|
+
t.string :h1
|
|
18
|
+
t.string :keywords
|
|
19
|
+
t.text :description
|
|
20
|
+
t.text :seo_block
|
|
21
|
+
|
|
22
|
+
t.timestamps
|
|
23
|
+
end
|
|
24
|
+
add_index :header_translations, [:header_id, :locale], unique: true, name: 'headers_ts_header_id_locale'
|
|
25
|
+
|
|
26
|
+
create_table :structure_translations do |t|
|
|
27
|
+
t.references :structure, null: false
|
|
28
|
+
t.string :locale, limit: 5, null: false
|
|
29
|
+
t.string :title
|
|
30
|
+
t.string :redirect_url
|
|
31
|
+
|
|
32
|
+
t.timestamps
|
|
33
|
+
end
|
|
34
|
+
add_index :structure_translations, [:structure_id, :locale], unique: true, name: 'structures_ts_structure_id_locale'
|
|
35
|
+
|
|
36
|
+
create_table :asset_translations do |t|
|
|
37
|
+
t.references :asset, null: false
|
|
38
|
+
t.string :locale, limit: 5, null: false
|
|
39
|
+
t.string :name
|
|
40
|
+
t.string :alt
|
|
8
41
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Structure.drop_translation_table!
|
|
13
|
-
Asset.drop_translation_table!
|
|
42
|
+
t.timestamps
|
|
43
|
+
end
|
|
44
|
+
add_index :asset_translations, [:asset_id, :locale], unique: true, name: 'assets_ts_asset_id_locale'
|
|
14
45
|
end
|
|
15
46
|
end
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
class CreateAdminComments < ActiveRecord::Migration
|
|
1
|
+
class CreateAdminComments < ActiveRecord::Migration[5.2]
|
|
2
2
|
def change
|
|
3
3
|
create_table :admin_comments do |t|
|
|
4
4
|
t.references :user
|
|
5
5
|
t.string :user_name
|
|
6
|
-
t.
|
|
7
|
-
t.string :resource_type, limit: 50, null: false
|
|
6
|
+
t.references :resource, polymorphic: true
|
|
8
7
|
t.references :resource_user
|
|
9
8
|
t.text :body
|
|
10
9
|
|
|
11
10
|
t.timestamps
|
|
12
11
|
end
|
|
13
|
-
|
|
14
|
-
add_index :admin_comments, :user_id
|
|
15
|
-
add_index :admin_comments, :resource_user_id
|
|
16
|
-
add_index :admin_comments, [:resource_type, :resource_id]
|
|
17
12
|
end
|
|
18
13
|
end
|
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
class CreateTracks < ActiveRecord::Migration
|
|
1
|
+
class CreateTracks < ActiveRecord::Migration[5.2]
|
|
2
2
|
def change
|
|
3
3
|
create_table :tracks do |t|
|
|
4
4
|
t.string :name
|
|
5
5
|
t.string :key
|
|
6
|
-
t.
|
|
7
|
-
t.
|
|
8
|
-
t.
|
|
6
|
+
t.references :trackable, polymorphic: true
|
|
7
|
+
t.references :user
|
|
8
|
+
t.references :owner
|
|
9
9
|
t.column :trackable_changes, :mediumtext
|
|
10
10
|
t.text :parameters
|
|
11
11
|
|
|
12
12
|
t.timestamps
|
|
13
13
|
end
|
|
14
|
-
|
|
15
|
-
add_index :tracks, [:trackable_type, :trackable_id]
|
|
16
|
-
add_index :tracks, :owner_id
|
|
17
|
-
add_index :tracks, :user_id
|
|
18
14
|
add_index :tracks, :key
|
|
19
15
|
end
|
|
20
16
|
end
|
|
@@ -6,14 +6,15 @@ 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, :chart, :
|
|
10
|
-
:
|
|
9
|
+
attr_accessor :model, :table, :search, :export, :form, :chart, :stats, :map, :modal_form, :show,
|
|
10
|
+
:preview_path, :actions, :custom_settings,
|
|
11
|
+
:batch_actions, :action_items, :disabled_action_items, :resource_action_items, :tree_node_renderer,
|
|
11
12
|
:parent_resources, :custom_actions, :permitted_params, :scopes
|
|
12
13
|
|
|
13
14
|
def initialize
|
|
14
15
|
@actions = ACTIONS
|
|
15
16
|
@custom_settings = {}
|
|
16
|
-
@
|
|
17
|
+
@batch_actions= [AbAdmin::Config::BatchAction.new(:destroy, confirm: I18n.t('admin.delete_confirmation'))]
|
|
17
18
|
@action_items = []
|
|
18
19
|
@disabled_action_items = []
|
|
19
20
|
@default_action_items_for = {}
|
|
@@ -87,9 +88,9 @@ module AbAdmin
|
|
|
87
88
|
|
|
88
89
|
def batch_action(name, options={}, &block)
|
|
89
90
|
if options
|
|
90
|
-
instance.
|
|
91
|
+
instance.batch_actions << AbAdmin::Config::BatchAction.new(name.to_sym, options, &block)
|
|
91
92
|
else
|
|
92
|
-
instance.
|
|
93
|
+
instance.batch_actions.reject!{|a| a.name == name.to_sym }
|
|
93
94
|
end
|
|
94
95
|
end
|
|
95
96
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require 'mime/types'
|
|
2
1
|
require 'mini_magick'
|
|
3
2
|
require 'carrierwave/processing/mini_magick'
|
|
4
3
|
|
|
@@ -8,8 +7,7 @@ module AbAdmin
|
|
|
8
7
|
include ::CarrierWave::MiniMagick
|
|
9
8
|
include AbAdmin::Utils::EvalHelpers
|
|
10
9
|
|
|
11
|
-
class_attribute :
|
|
12
|
-
self.transliterate = true
|
|
10
|
+
class_attribute :human_filenames
|
|
13
11
|
self.human_filenames = true
|
|
14
12
|
|
|
15
13
|
attr_accessor :internal_identifier
|
|
@@ -26,41 +24,48 @@ module AbAdmin
|
|
|
26
24
|
|
|
27
25
|
process :set_model_info
|
|
28
26
|
|
|
29
|
-
def
|
|
30
|
-
"#{
|
|
27
|
+
def filename
|
|
28
|
+
"#{[human_part, secure_token].compact.join('_')}#{extension}"
|
|
31
29
|
end
|
|
32
30
|
|
|
33
|
-
def
|
|
34
|
-
|
|
31
|
+
def full_filename(*)
|
|
32
|
+
return filename unless version_name
|
|
33
|
+
base = "#{version_filename_part}#{version_extension}"
|
|
34
|
+
return base unless human_filenames
|
|
35
|
+
[human_part, base].compact.join('_')
|
|
35
36
|
end
|
|
36
37
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
return secure_token unless version_name
|
|
40
|
-
version_name.to_s.start_with?('retina_') ? "#{version_name.to_s.sub(/^retina_/, '')}@2x" : version_name.to_s
|
|
38
|
+
def human_part
|
|
39
|
+
normalize_filename(model.send("#{mounted_as}_file_name").to_s.strip.remove(/\.\w+$/)).remove(secure_token).chomp('_').presence
|
|
41
40
|
end
|
|
42
41
|
|
|
43
|
-
def
|
|
44
|
-
|
|
42
|
+
def extension
|
|
43
|
+
File.extname(model.original_name).downcase
|
|
45
44
|
end
|
|
46
45
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
system_part = base_filename_part
|
|
50
|
-
human_filename_part = for_file.chomp(ext)
|
|
51
|
-
return "#{system_part || version_name}#{ext}" if human_filename_part == secure_token
|
|
52
|
-
system_part ? "#{human_filename_part}_#{system_part}#{ext}" : "#{human_filename_part}#{ext}"
|
|
46
|
+
def version_extension
|
|
47
|
+
webp? ? '.webp' : extension
|
|
53
48
|
end
|
|
54
49
|
|
|
55
|
-
def
|
|
56
|
-
|
|
50
|
+
def version_filename_part
|
|
51
|
+
return secure_token unless version_name
|
|
52
|
+
strict_version_name = version_name.to_s.remove('retina_').remove('_webp')
|
|
53
|
+
strict_version_name = nil if strict_version_name.to_sym == :default
|
|
54
|
+
"#{strict_version_name}#{'@2x' if retina?}"
|
|
57
55
|
end
|
|
58
56
|
|
|
59
|
-
# use secure token in the filename for non processed image
|
|
60
57
|
def secure_token
|
|
61
58
|
model.data_secure_token ||= AbAdmin.friendly_token(20).downcase
|
|
62
59
|
end
|
|
63
60
|
|
|
61
|
+
def retina?
|
|
62
|
+
version_name.to_s.start_with?('retina_')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def webp?
|
|
66
|
+
version_name.to_s.end_with?('_webp')
|
|
67
|
+
end
|
|
68
|
+
|
|
64
69
|
def store_model_filename(record)
|
|
65
70
|
old_file_name = filename
|
|
66
71
|
new_file_name = model_filename(old_file_name, record)
|
|
@@ -68,69 +73,81 @@ module AbAdmin
|
|
|
68
73
|
rename_via_move(new_file_name)
|
|
69
74
|
end
|
|
70
75
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def filename
|
|
74
|
-
internal_identifier || model.send("#{mounted_as}_file_name") || (store_filename && "#{secure_token}#{File.extname(store_filename).downcase}")
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def write_internal_identifier(internal_identifier)
|
|
78
|
-
self.internal_identifier = internal_identifier
|
|
79
|
-
versions.values.each{|v| v.internal_identifier = internal_identifier }
|
|
76
|
+
def save_original_name(file)
|
|
77
|
+
model.original_name ||= file.original_filename if file.respond_to?(:original_filename)
|
|
80
78
|
end
|
|
81
79
|
|
|
82
|
-
# transliterate original filename
|
|
83
|
-
# allow to build custom filename
|
|
84
80
|
def model_filename(base_filename, record)
|
|
85
81
|
custom_file_name = model.build_filename(base_filename, record)
|
|
86
82
|
return unless custom_file_name
|
|
87
|
-
normalize_filename(custom_file_name)
|
|
83
|
+
normalize_filename(custom_file_name)
|
|
88
84
|
end
|
|
85
|
+
private :model_filename
|
|
89
86
|
|
|
90
87
|
def normalize_filename(raw_filename)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
to_path = File.join(dir, v.full_filename(new_file_name))
|
|
104
|
-
next if from_path == to_path || !File.exists?(from_path)
|
|
105
|
-
moves << [from_path, to_path]
|
|
106
|
-
end
|
|
107
|
-
moves.each { |move| FileUtils.mv(*move) }
|
|
88
|
+
I18n.transliterate(raw_filename.unicode_normalize).parameterize(separator: '_').gsub(/[\-_]+/, '_').downcase
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def rename_via_move(new_filename)
|
|
92
|
+
dir = File.dirname(path)
|
|
93
|
+
old_names = versions.values.unshift(self).map(&:full_filename)
|
|
94
|
+
model.send("#{mounted_as}_file_name=", "#{[new_filename.presence, secure_token].compact.join('_')}#{extension}")
|
|
95
|
+
new_names = versions.values.unshift(self).map(&:full_filename)
|
|
96
|
+
old_names.zip(new_names).each do |old_name, new_name|
|
|
97
|
+
old_path, new_path = File.join(dir, old_name), File.join(dir, new_name)
|
|
98
|
+
next if old_path == new_path || !File.exist?(old_path)
|
|
99
|
+
FileUtils.mv(old_path, new_path)
|
|
108
100
|
end
|
|
109
|
-
|
|
110
|
-
write_internal_identifier new_file_name
|
|
111
|
-
model.send("write_#{mounted_as}_identifier")
|
|
112
|
-
retrieve_from_store!(new_file_name) if human_filenames
|
|
113
|
-
|
|
114
|
-
new_file_name
|
|
101
|
+
retrieve_from_store!(model.send("#{mounted_as}_file_name"))
|
|
115
102
|
end
|
|
116
103
|
|
|
117
|
-
private :write_internal_identifier, :store_filename, :model_filename
|
|
118
|
-
|
|
119
|
-
def rmagick_included?
|
|
120
|
-
self.class.included_modules.map(&:to_s).include?('CarrierWave::RMagick')
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# prevent large number of subdirectories
|
|
124
104
|
def store_dir
|
|
125
105
|
str_id = model.id.to_s.rjust(4, '0')
|
|
126
106
|
[AbAdmin.uploads_dir, model.class.to_s.underscore, str_id[0..2], str_id[3..-1]].join('/')
|
|
127
107
|
end
|
|
128
108
|
|
|
109
|
+
def convert_to_webp(options = {})
|
|
110
|
+
webp_path = "#{File.dirname(path)}/#{full_filename}"
|
|
111
|
+
WebP.encode(path, webp_path, options_for_webp(options))
|
|
112
|
+
@file = ::CarrierWave::SanitizedFile.new(tempfile: webp_path, filename: webp_path, content_type: 'image/webp')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def options_for_webp(options)
|
|
116
|
+
w, h = width, height
|
|
117
|
+
options = options.dup
|
|
118
|
+
ratio = w.to_f / h
|
|
119
|
+
if options[:resize_to_fill]
|
|
120
|
+
res_w, res_h = options[:resize_to_fill]
|
|
121
|
+
res_ratio = res_w.to_f / res_h
|
|
122
|
+
options.update(resize_w: res_w, resize_h: res_h) unless w == res_w && h == res_h
|
|
123
|
+
if ratio > res_ratio
|
|
124
|
+
crop_res_w = h * res_ratio
|
|
125
|
+
crop_res_h = h
|
|
126
|
+
options.update(crop_x: ((w - crop_res_w) / 2).to_i, crop_y: 0, crop_w: crop_res_w.to_i, crop_h: crop_res_h.to_i)
|
|
127
|
+
elsif ratio < res_ratio
|
|
128
|
+
crop_res_w = w
|
|
129
|
+
crop_res_h = w / res_ratio
|
|
130
|
+
options.update(crop_x: 0, crop_y: ((h - crop_res_h) / 2).to_i, crop_w: crop_res_w.to_i, crop_h: crop_res_h.to_i)
|
|
131
|
+
end
|
|
132
|
+
elsif options[:resize_to_fit]
|
|
133
|
+
res_w, res_h = options[:resize_to_fit]
|
|
134
|
+
res_ratio = res_w.to_f / res_h
|
|
135
|
+
if ratio == res_ratio
|
|
136
|
+
options.update(resize_w: res_w, resize_h: res_h) unless w == res_w && h == res_h
|
|
137
|
+
elsif ratio > res_ratio
|
|
138
|
+
options.update(resize_w: res_h)
|
|
139
|
+
elsif ratio < res_ratio
|
|
140
|
+
options.update(resize_h: res_w)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
options.except(:resize_to_fill, :resize_to_fit)
|
|
144
|
+
end
|
|
145
|
+
|
|
129
146
|
# Strips out all embedded information from the image
|
|
130
147
|
# process :strip
|
|
131
148
|
#
|
|
132
149
|
def strip
|
|
133
|
-
|
|
150
|
+
minimagick! do |img|
|
|
134
151
|
img.strip
|
|
135
152
|
img = yield(img) if block_given?
|
|
136
153
|
img
|
|
@@ -144,7 +161,7 @@ module AbAdmin
|
|
|
144
161
|
percentage = normalize_param(percentage)
|
|
145
162
|
|
|
146
163
|
unless percentage.blank?
|
|
147
|
-
|
|
164
|
+
minimagick! do |img|
|
|
148
165
|
img.quality percentage.to_s
|
|
149
166
|
img = yield(img) if block_given?
|
|
150
167
|
img
|
|
@@ -160,7 +177,7 @@ module AbAdmin
|
|
|
160
177
|
|
|
161
178
|
unless degrees.blank?
|
|
162
179
|
manipulate! do |img|
|
|
163
|
-
|
|
180
|
+
self.class.included_modules.map(&:to_s).include?('CarrierWave::RMagick') ? img.rotate!(degrees.to_i) : img.rotate(degrees.to_s)
|
|
164
181
|
img = yield(img) if block_given?
|
|
165
182
|
img
|
|
166
183
|
end
|
|
@@ -176,7 +193,7 @@ module AbAdmin
|
|
|
176
193
|
geometry = normalize_param(geometry[0]) if geometry.size == 1
|
|
177
194
|
|
|
178
195
|
if geometry && geometry.size == 4
|
|
179
|
-
|
|
196
|
+
minimagick! do |img|
|
|
180
197
|
img.crop '%ix%i+%i+%i' % geometry
|
|
181
198
|
img = yield(img) if block_given?
|
|
182
199
|
img
|
|
@@ -185,7 +202,7 @@ module AbAdmin
|
|
|
185
202
|
end
|
|
186
203
|
|
|
187
204
|
def watermark(watermark_path, gravity='SouthEast')
|
|
188
|
-
|
|
205
|
+
minimagick! do |img|
|
|
189
206
|
resolved_path = watermark_path.is_a?(Symbol) ? send(watermark_path) : watermark_path
|
|
190
207
|
watermark_image = ::MiniMagick::Image.open(resolved_path)
|
|
191
208
|
img.composite(watermark_image) { |c| c.gravity gravity }
|
|
@@ -202,12 +219,7 @@ module AbAdmin
|
|
|
202
219
|
end
|
|
203
220
|
|
|
204
221
|
def dimensions
|
|
205
|
-
[
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def magick
|
|
209
|
-
#@magick ||= ::MiniMagick::Image.new(current_path)
|
|
210
|
-
::MiniMagick::Image.new(current_path)
|
|
222
|
+
[width, height]
|
|
211
223
|
end
|
|
212
224
|
|
|
213
225
|
protected
|
|
@@ -11,11 +11,6 @@ module AbAdmin
|
|
|
11
11
|
mount_uploader(:data, uploader, options, &block)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def sunrise_uploader(*args)
|
|
15
|
-
ActiveSupport::Deprecation.warn('`sunrise_uploader` is deprecated, use `ab_admin_uploader` instead')
|
|
16
|
-
ab_admin_uploader(*args)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
14
|
def validates_filesize_of(*attr_names)
|
|
20
15
|
validates_with FileSizeValidator, _merge_attributes(attr_names)
|
|
21
16
|
end
|
|
@@ -4,19 +4,23 @@ module AbAdmin
|
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
6
|
included do
|
|
7
|
+
attr_accessor :last_updated_timestamp
|
|
8
|
+
validate :do_not_overwrite, if: :last_updated_timestamp
|
|
7
9
|
scope(:admin, proc { all }) unless respond_to?(:admin)
|
|
8
10
|
scope(:base, -> { all }) unless respond_to?(:base)
|
|
9
|
-
scope :by_ids, lambda { |ids| where("#{quoted_table_name}.id IN (?)", AbAdmin.val_to_array(ids).push(0)) } unless respond_to?(:by_ids)
|
|
10
11
|
|
|
11
12
|
class_attribute :batch_actions, instance_writer: false
|
|
12
13
|
self.batch_actions = [:destroy]
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
def updated_timestamp(associations: true)
|
|
17
|
+
res = [updated_at]
|
|
18
|
+
res += translations.map(&:updated_at) if self.class.translates?
|
|
19
|
+
if associations
|
|
20
|
+
associations = self.class.nested_attributes_options.keys unless associations.is_a?(Array)
|
|
21
|
+
res += associations.flat_map{|assoc| Array(send(assoc)).map(&:updated_timestamp) }
|
|
19
22
|
end
|
|
23
|
+
res.compact.max
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def for_input_token
|
|
@@ -27,28 +31,6 @@ module AbAdmin
|
|
|
27
31
|
"#{self.class.model_name.human(count: 1)} ##{self.id} #{AbAdmin.safe_display_name(self)}"
|
|
28
32
|
end
|
|
29
33
|
|
|
30
|
-
def translated_any(attr)
|
|
31
|
-
send(attr).presence || translations.detect { |r| r.send(attr).present? }.try!(attr)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def new_changes
|
|
35
|
-
excluded_attrs = [:updated_at]
|
|
36
|
-
excluded_attrs += translated_attribute_names if self.class.translates?
|
|
37
|
-
all_changes = changes.except(*excluded_attrs).map { |k, v| [k, v[1]] }.to_h
|
|
38
|
-
if self.class.translates?
|
|
39
|
-
globalize.dirty.each do |attr, changes|
|
|
40
|
-
changes.each do |change|
|
|
41
|
-
all_changes["#{attr}_#{change[0]}"] = send("#{attr}_#{change[0]}")
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
all_changes
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def admin_comments_count_non_zero
|
|
49
|
-
self[:admin_comments_count].to_i.zero? ? nil : self[:admin_comments_count]
|
|
50
|
-
end
|
|
51
|
-
|
|
52
34
|
def token_data(method, options={})
|
|
53
35
|
assoc = self.class.reflect_on_association(method)
|
|
54
36
|
scope = send(method)
|
|
@@ -93,6 +75,12 @@ module AbAdmin
|
|
|
93
75
|
scope.where(sql, val: send(order_col)).ransack(query[:q]).result(distinct: true).first
|
|
94
76
|
end
|
|
95
77
|
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def do_not_overwrite
|
|
81
|
+
return if new_record? || last_updated_timestamp.blank?
|
|
82
|
+
errors.add(:base, :changed) if updated_timestamp.to_i > last_updated_timestamp.to_i
|
|
83
|
+
end
|
|
96
84
|
end
|
|
97
85
|
end
|
|
98
86
|
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module AbAdmin
|
|
2
|
+
module Concerns
|
|
3
|
+
module TranslationsMacro
|
|
4
|
+
def translates(*attr_names)
|
|
5
|
+
options = attr_names.extract_options!
|
|
6
|
+
setup_translations(options) unless translates?
|
|
7
|
+
add_translated_attributes(attr_names.map(&:to_sym) - translated_attribute_names)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def setup_translations(options)
|
|
11
|
+
include InstanceMethods
|
|
12
|
+
extend ClassMethods
|
|
13
|
+
|
|
14
|
+
class_attribute :translated_attribute_names, :translation_options
|
|
15
|
+
self.translated_attribute_names = []
|
|
16
|
+
self.translation_options = options
|
|
17
|
+
|
|
18
|
+
has_many :translations, class_name: translation_class.name, foreign_key: class_name.foreign_key, dependent: :destroy, autosave: true, inverse_of: :translated_model
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_translated_attributes(attr_names)
|
|
22
|
+
attr_names.each do |attr_name|
|
|
23
|
+
define_translation_accessors(attr_name)
|
|
24
|
+
self.translated_attribute_names << attr_name
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def define_translation_accessors(attr_name)
|
|
29
|
+
define_method("#{attr_name}_translation") { translation_for_locale(I18n.locale).try!(:send, attr_name) }
|
|
30
|
+
define_method("#{attr_name}_translation=") {|v| translation_for_locale(I18n.locale).try!(:send, "#{attr_name}=", v) }
|
|
31
|
+
alias_method attr_name, "#{attr_name}_translation"
|
|
32
|
+
alias_method "#{attr_name}=", "#{attr_name}_translation="
|
|
33
|
+
define_method("#{attr_name}_default"){ translation_for_locale(I18n.default_locale).try!(:send, attr_name) }
|
|
34
|
+
AbAdmin.translated_locales.each do |l|
|
|
35
|
+
define_method("#{attr_name}_#{l}") {translation_for_locale(l).try!(:send, attr_name)}
|
|
36
|
+
define_method("#{attr_name}_#{l}=") {|v| translation_for_locale(l).try!(:send, "#{attr_name}=", v)}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def class_name
|
|
41
|
+
name.split('::').last
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def translates?
|
|
45
|
+
included_modules.include?(InstanceMethods)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module InstanceMethods
|
|
49
|
+
def translation_for_locale(l)
|
|
50
|
+
return unless AbAdmin.translated_locales.include?(l)
|
|
51
|
+
translations.detect{|r| r.locale == l.to_s} || translations.new(locale: l.to_s)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def translated_attributes
|
|
55
|
+
translated_attribute_names.map{|attr| [attr, send(attr)] }.to_h.stringify_keys
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def attributes
|
|
59
|
+
super.merge!(translated_attributes)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
module ClassMethods
|
|
64
|
+
def translation_class
|
|
65
|
+
@translation_class ||= begin
|
|
66
|
+
klass = self.const_defined?(:Translation, false) ? self.const_get(:Translation, false) : self.const_set(:Translation, Class.new(BaseTranslation))
|
|
67
|
+
klass.belongs_to :translated_model, class_name: self.name, foreign_key: class_name.foreign_key, inverse_of: :translations, touch: translation_options.fetch(:touch, false)
|
|
68
|
+
klass
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def translations_table_name
|
|
73
|
+
translation_class.table_name
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def translated?(name)
|
|
77
|
+
translated_attribute_names.include?(name.to_sym)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class BaseTranslation < ::ActiveRecord::Base
|
|
83
|
+
self.abstract_class = true
|
|
84
|
+
|
|
85
|
+
validates :locale, presence: true
|
|
86
|
+
after_initialize :init
|
|
87
|
+
|
|
88
|
+
def init
|
|
89
|
+
self.locale ||= I18n.locale.to_s
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.table_exists?
|
|
93
|
+
table_name.present? && super
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -70,7 +70,7 @@ module AbAdmin
|
|
|
70
70
|
add_cond << assoc.klass.instance_exec(&assoc.scope).to_sql[/WHERE(.*?)(?:(?:ORDER|LIMIT).*)?$/, 1] if assoc.scope
|
|
71
71
|
if assoc.klass.default_scopes.present?
|
|
72
72
|
assoc.klass.default_scopes.each do |scope|
|
|
73
|
-
add_cond << scope.call.to_sql[/WHERE(.*?)(?:(?:ORDER|LIMIT).*)?$/, 1]
|
|
73
|
+
add_cond << scope.scope.call.to_sql[/WHERE(.*?)(?:(?:ORDER|LIMIT).*)?$/, 1]
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
count_klass = assoc_count.klass
|
|
@@ -85,7 +85,7 @@ module AbAdmin
|
|
|
85
85
|
|
|
86
86
|
def all_translated_attribute_names
|
|
87
87
|
if translates?
|
|
88
|
-
|
|
88
|
+
AbAdmin.translated_locales.map do |loc|
|
|
89
89
|
translated_attribute_names.map { |attr| "#{attr}_#{loc}" }
|
|
90
90
|
end.flatten
|
|
91
91
|
else
|
data/lib/ab_admin/config/base.rb
CHANGED
|
@@ -23,12 +23,20 @@ module AbAdmin
|
|
|
23
23
|
next if column_name == :id || options[:skip].try(:include?, column_name)
|
|
24
24
|
builder.field(column_name)
|
|
25
25
|
end
|
|
26
|
+
model.translated_attribute_names.each do |column_name|
|
|
27
|
+
builder.field(column_name, sortable: false)
|
|
28
|
+
end if model.translates?
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
class Table < BaseBuilder
|
|
31
34
|
self.partial_name = 'table'
|
|
35
|
+
|
|
36
|
+
def row_html_class
|
|
37
|
+
return :row_html_class if options[:row_html_class].is_a?(TrueClass)
|
|
38
|
+
options[:row_html_class]
|
|
39
|
+
end
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
class Search < BaseBuilder
|
|
@@ -39,6 +47,14 @@ module AbAdmin
|
|
|
39
47
|
self.partial_name = 'chart'
|
|
40
48
|
end
|
|
41
49
|
|
|
50
|
+
class Stats < BaseBuilder
|
|
51
|
+
self.partial_name = 'stats'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Map < BaseBuilder
|
|
55
|
+
self.partial_name = 'map'
|
|
56
|
+
end
|
|
57
|
+
|
|
42
58
|
class Export < BaseBuilder
|
|
43
59
|
def render_options
|
|
44
60
|
{column_names: fields.map(&:name), column_data: fields.map(&:data),
|
|
@@ -111,7 +127,7 @@ module AbAdmin
|
|
|
111
127
|
end
|
|
112
128
|
|
|
113
129
|
class BatchAction
|
|
114
|
-
attr_reader :name, :options, :data, :
|
|
130
|
+
attr_reader :name, :options, :data, :form
|
|
115
131
|
|
|
116
132
|
def initialize(name, options={}, &block)
|
|
117
133
|
@name = name
|
|
@@ -119,10 +135,13 @@ module AbAdmin
|
|
|
119
135
|
if options.has_key?(:form)
|
|
120
136
|
@form = options[:form].is_a?(String) ? options[:form] : "##{name}_batch_form"
|
|
121
137
|
end
|
|
122
|
-
@title = options[:title] || I18n.t("admin.actions.batch_#{name}.link", default: name.to_s.humanize)
|
|
123
138
|
@data = block_given? ? block : name.to_sym
|
|
124
139
|
end
|
|
125
140
|
|
|
141
|
+
def title
|
|
142
|
+
options[:title] || I18n.t("admin.actions.#{name}.title", default: name.to_s.humanize)
|
|
143
|
+
end
|
|
144
|
+
|
|
126
145
|
def confirm
|
|
127
146
|
options[:confirm]
|
|
128
147
|
end
|
|
@@ -169,8 +188,12 @@ module AbAdmin
|
|
|
169
188
|
@data = block
|
|
170
189
|
end
|
|
171
190
|
|
|
172
|
-
def
|
|
173
|
-
|
|
191
|
+
def param_name
|
|
192
|
+
options[:as] || name
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def apply(context, relation)
|
|
196
|
+
data.is_a?(Proc) ? data.call(context, relation) : relation.public_send(name)
|
|
174
197
|
end
|
|
175
198
|
end
|
|
176
199
|
end
|