biovision-courses 0.0.180221
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +26 -0
- data/app/assets/config/biovision_courses_manifest.js +2 -0
- data/app/assets/javascripts/biovision/courses/application.js +13 -0
- data/app/assets/stylesheets/biovision/courses/application.css +15 -0
- data/app/controllers/admin/course_categories_controller.rb +28 -0
- data/app/controllers/admin/course_tags_controller.rb +28 -0
- data/app/controllers/admin/courses_controller.rb +29 -0
- data/app/controllers/biovision/courses/application_controller.rb +7 -0
- data/app/controllers/course_categories_controller.rb +70 -0
- data/app/controllers/course_tags_controller.rb +66 -0
- data/app/controllers/courses_controller.rb +94 -0
- data/app/helpers/biovision/courses/application_helper.rb +6 -0
- data/app/helpers/biovision_courses_helper.rb +80 -0
- data/app/jobs/biovision/courses/application_job.rb +6 -0
- data/app/mailers/biovision/courses/application_mailer.rb +8 -0
- data/app/models/application_record.rb +3 -0
- data/app/models/biovision/courses/application_record.rb +7 -0
- data/app/models/course.rb +109 -0
- data/app/models/course_category.rb +137 -0
- data/app/models/course_course_tag.rb +13 -0
- data/app/models/course_tag.rb +49 -0
- data/app/uploaders/course_image_uploader.rb +46 -0
- data/app/views/admin/course_categories/_nav_item.html.erb +2 -0
- data/app/views/admin/course_categories/entity/_in_list.html.erb +43 -0
- data/app/views/admin/course_categories/index.html.erb +17 -0
- data/app/views/admin/course_categories/show.html.erb +85 -0
- data/app/views/admin/course_tags/_nav_item.html.erb +2 -0
- data/app/views/admin/course_tags/entity/_in_list.html.erb +21 -0
- data/app/views/admin/course_tags/index.html.erb +19 -0
- data/app/views/admin/course_tags/show.html.erb +39 -0
- data/app/views/admin/courses/_nav_item.html.erb +2 -0
- data/app/views/admin/courses/entity/_in_list.html.erb +40 -0
- data/app/views/admin/courses/index.html.erb +17 -0
- data/app/views/admin/courses/show.html.erb +117 -0
- data/app/views/admin/index/dashboard/_biovision_courses.html.erb +12 -0
- data/app/views/course_categories/_form.html.erb +96 -0
- data/app/views/course_categories/edit.html.erb +22 -0
- data/app/views/course_categories/edit.js.erb +1 -0
- data/app/views/course_categories/new.html.erb +15 -0
- data/app/views/course_categories/new.js.erb +1 -0
- data/app/views/course_tags/_form.html.erb +72 -0
- data/app/views/course_tags/edit.html.erb +17 -0
- data/app/views/course_tags/edit.js.erb +1 -0
- data/app/views/course_tags/new.html.erb +15 -0
- data/app/views/course_tags/new.js.erb +1 -0
- data/app/views/courses/_form.html.erb +244 -0
- data/app/views/courses/edit.html.erb +17 -0
- data/app/views/courses/edit.js.erb +1 -0
- data/app/views/courses/form/_wysiwyg.html.erb +14 -0
- data/app/views/courses/new.html.erb +15 -0
- data/app/views/courses/new.js.erb +1 -0
- data/app/views/layouts/biovision/courses/application.html.erb +14 -0
- data/config/locales/courses-ru.yml +138 -0
- data/config/routes.rb +26 -0
- data/db/migrate/20180218120000_prepare_course_privileges.rb +19 -0
- data/db/migrate/20180218120005_create_course_categories.rb +28 -0
- data/db/migrate/20180218120010_create_course_tags.rb +20 -0
- data/db/migrate/20180218120015_create_courses.rb +42 -0
- data/db/migrate/20180218120020_create_course_course_tags.rb +17 -0
- data/lib/biovision/courses.rb +7 -0
- data/lib/biovision/courses/engine.rb +18 -0
- data/lib/biovision/courses/version.rb +5 -0
- data/lib/tasks/biovision/courses_tasks.rake +4 -0
- metadata +207 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module BiovisionCoursesHelper
|
2
|
+
# @param [CourseTag] entity
|
3
|
+
# @param [String] text
|
4
|
+
def admin_course_tag_link(entity, text = entity.name)
|
5
|
+
link_to(text, admin_course_tag_path(entity.id))
|
6
|
+
end
|
7
|
+
|
8
|
+
# @param [CourseCategory] entity
|
9
|
+
# @param [String] text
|
10
|
+
def admin_course_category_link(entity, text = entity.name)
|
11
|
+
link_to(text, admin_course_category_path(entity.id))
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [Course] entity
|
15
|
+
# @param [String] text
|
16
|
+
def admin_course_link(entity, text = entity.title)
|
17
|
+
link_to(text, admin_course_path(entity.id))
|
18
|
+
end
|
19
|
+
|
20
|
+
def course_categories_for_select
|
21
|
+
options = []
|
22
|
+
CourseCategory.for_tree.each do |category|
|
23
|
+
options << [category.name, category.id]
|
24
|
+
if category.child_categories.any?
|
25
|
+
CourseCategory.for_tree(category.id).each do |subcategory|
|
26
|
+
options << ["-#{subcategory.name}", subcategory.id]
|
27
|
+
if subcategory.child_categories.any?
|
28
|
+
CourseCategory.for_tree(subcategory.id).each do |deep_category|
|
29
|
+
options << ["--#{deep_category.name}", deep_category.id]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
options
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [Course] entity
|
39
|
+
# @param [String] text
|
40
|
+
# @param [Hash] options
|
41
|
+
def course_link(entity, text = entity.title, options = {})
|
42
|
+
link_to(text, course_path(entity.id), options)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Course image preview for displaying in "administrative" lists
|
46
|
+
#
|
47
|
+
# @param [Course] entity
|
48
|
+
def course_image_preview(entity)
|
49
|
+
return '' if entity.image.blank?
|
50
|
+
|
51
|
+
versions = "#{entity.image.preview_2x.url} 2x"
|
52
|
+
image_tag(entity.image.preview.url, alt: entity.title, srcset: versions)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Small course image for displaying in lists of courses and feeds
|
56
|
+
#
|
57
|
+
# @param [Course] entity
|
58
|
+
# @param [Hash] add_options
|
59
|
+
def course_image_small(entity, add_options = {})
|
60
|
+
return '' if entity.image.blank?
|
61
|
+
|
62
|
+
alt_text = entity.image_alt_text.to_s
|
63
|
+
versions = "#{entity.image.medium.url} 2x"
|
64
|
+
options = { alt: alt_text, srcset: versions }.merge(add_options)
|
65
|
+
image_tag(entity.image.small.url, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Medium course image for displaying on course page
|
69
|
+
#
|
70
|
+
# @param [Course] entity
|
71
|
+
# @param [Hash] add_options
|
72
|
+
def course_image_medium(entity, add_options = {})
|
73
|
+
return '' if entity.image.blank?
|
74
|
+
|
75
|
+
alt_text = entity.image_alt_text.to_s
|
76
|
+
versions = "#{entity.image.big.url} 2x"
|
77
|
+
options = { alt: alt_text, srcset: versions }.merge(add_options)
|
78
|
+
image_tag(entity.image.medium.url, options)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
class Course < ApplicationRecord
|
2
|
+
include HasOwner
|
3
|
+
include Toggleable
|
4
|
+
|
5
|
+
PER_PAGE = 24
|
6
|
+
|
7
|
+
DESCRIPTION_LIMIT = 50000
|
8
|
+
DURATION_LIMIT = 50
|
9
|
+
LEAD_LIMIT = 1000
|
10
|
+
META_LIMIT = 255
|
11
|
+
PRIORITY_RANGE = (1..999)
|
12
|
+
SLUG_LIMIT = 250
|
13
|
+
SLUG_PATTERN = /\A[a-z][-0-9a-z]*[0-9a-z]\z/i
|
14
|
+
SLUG_PATTERN_HTML = '^[a-zA-Z][-0-9a-zA-Z]*[0-9a-zA-Z]$'
|
15
|
+
TITLE_LIMIT = 250
|
16
|
+
|
17
|
+
toggleable :visible, :highlight, :online
|
18
|
+
|
19
|
+
mount_uploader :image, CourseImageUploader
|
20
|
+
|
21
|
+
belongs_to :course_category, counter_cache: true
|
22
|
+
belongs_to :user, optional: true
|
23
|
+
belongs_to :agent, optional: true
|
24
|
+
has_many :course_course_tags, dependent: :destroy
|
25
|
+
has_many :course_tags, through: :course_course_tags
|
26
|
+
|
27
|
+
after_initialize :set_next_priority
|
28
|
+
before_validation { self.slug = Canonizer.transliterate(title.to_s) if slug.blank? }
|
29
|
+
before_validation { self.slug = slug.to_s.downcase }
|
30
|
+
before_validation :normalize_priority
|
31
|
+
|
32
|
+
validates_presence_of :title, :slug
|
33
|
+
validates_uniqueness_of :title
|
34
|
+
validates_uniqueness_of :slug
|
35
|
+
validates_length_of :title, maximum: TITLE_LIMIT
|
36
|
+
validates_length_of :subtitle, maximum: TITLE_LIMIT
|
37
|
+
validates_length_of :slug, maximum: SLUG_LIMIT
|
38
|
+
validates_format_of :slug, with: SLUG_PATTERN
|
39
|
+
validates_length_of :image_alt_text, maximum: META_LIMIT
|
40
|
+
validates_length_of :meta_title, maximum: META_LIMIT
|
41
|
+
validates_length_of :meta_keywords, maximum: META_LIMIT
|
42
|
+
validates_length_of :meta_description, maximum: META_LIMIT
|
43
|
+
validates_length_of :lead, maximum: LEAD_LIMIT
|
44
|
+
validates_length_of :description, maximum: DESCRIPTION_LIMIT
|
45
|
+
validates_length_of :duration, maximum: DURATION_LIMIT
|
46
|
+
validates_numericality_of :price, greater_than_or_equal_to: 0, allow_blank: true
|
47
|
+
validates_numericality_of :special_price, greater_than_or_equal_to: 0, allow_blank: true
|
48
|
+
|
49
|
+
scope :ordered_by_priority, -> { order 'priority asc, title asc' }
|
50
|
+
scope :visible, -> { where(visible: true, deleted: false) }
|
51
|
+
scope :for_tree, ->(course_category_id = nil) { siblings(course_category_id).ordered_by_priority }
|
52
|
+
scope :siblings, ->(course_category_id) { where(course_category_id: course_category_id) }
|
53
|
+
scope :list_for_visitors, -> { visible.ordered_by_priority }
|
54
|
+
|
55
|
+
# @param [Integer] page
|
56
|
+
def self.page_for_administration(page = 1)
|
57
|
+
ordered_by_priority.page(page).per(PER_PAGE)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [Integer] page
|
61
|
+
def self.page_for_visitors(page = 1)
|
62
|
+
list_for_visitors.page(page).per(PER_PAGE)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.entity_parameters
|
66
|
+
texts = %i(title subtitle slug lead description duration)
|
67
|
+
decoration = %i(price special_price special_price_end image image_alt_text)
|
68
|
+
meta_texts = %i(meta_title meta_keywords meta_description metadata)
|
69
|
+
meta_data = %i(course_category_id priority visible highlight online)
|
70
|
+
|
71
|
+
texts + decoration + meta_texts + meta_data
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param [User] user
|
75
|
+
def editable_by?(user)
|
76
|
+
owned_by?(user) || UserPrivilege.user_has_privilege?(user, :chief_course_manager)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param [User] user
|
80
|
+
def lockable_by?(user)
|
81
|
+
UserPrivilege.user_has_privilege?(user, :chief_course_manager)
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param [Integer] delta
|
85
|
+
def change_priority(delta)
|
86
|
+
new_priority = priority + delta
|
87
|
+
criteria = { course_category_id: course_category_id, priority: new_priority }
|
88
|
+
adjacent = self.class.find_by(criteria)
|
89
|
+
if adjacent.is_a?(self.class) && (adjacent.id != id)
|
90
|
+
adjacent.update!(priority: priority)
|
91
|
+
end
|
92
|
+
self.update(priority: new_priority)
|
93
|
+
|
94
|
+
self.class.for_tree(course_category_id).map { |e| [e.id, e.priority] }.to_h
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def set_next_priority
|
100
|
+
if id.nil? && priority == 1
|
101
|
+
self.priority = self.class.siblings(course_category_id).maximum(:priority).to_i + 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def normalize_priority
|
106
|
+
self.priority = PRIORITY_RANGE.first if priority < PRIORITY_RANGE.first
|
107
|
+
self.priority = PRIORITY_RANGE.last if priority > PRIORITY_RANGE.last
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
class CourseCategory < ApplicationRecord
|
2
|
+
include Toggleable
|
3
|
+
|
4
|
+
PRIORITY_RANGE = (1..500)
|
5
|
+
NAME_LIMIT = 50
|
6
|
+
SLUG_LIMIT = 50
|
7
|
+
SLUG_PATTERN = /\A[a-z][-0-9a-z]*[0-9a-z]\z/i
|
8
|
+
SLUG_PATTERN_HTML = '^[a-zA-Z][-0-9a-zA-Z]*[0-9a-zA-Z]$'
|
9
|
+
|
10
|
+
toggleable :visible
|
11
|
+
|
12
|
+
mount_uploader :image, CourseImageUploader
|
13
|
+
|
14
|
+
belongs_to :parent, class_name: CourseCategory.to_s, optional: true, touch: true
|
15
|
+
has_many :child_categories, class_name: CourseCategory.to_s, foreign_key: :parent_id, dependent: :destroy
|
16
|
+
has_many :courses, dependent: :destroy
|
17
|
+
|
18
|
+
after_initialize :set_next_priority
|
19
|
+
before_validation { self.slug = Canonizer.transliterate(name.to_s) if slug.blank? }
|
20
|
+
before_validation { self.slug = slug.to_s.downcase }
|
21
|
+
before_validation :generate_long_slug
|
22
|
+
before_validation :normalize_priority
|
23
|
+
before_save { self.children_cache.uniq! }
|
24
|
+
after_create :cache_parents!
|
25
|
+
after_save { parent.cache_children! unless parent.nil? }
|
26
|
+
|
27
|
+
validates_presence_of :name, :slug
|
28
|
+
validates_uniqueness_of :name, scope: [:parent_id]
|
29
|
+
validates_uniqueness_of :slug, scope: [:parent_id]
|
30
|
+
validates_length_of :name, maximum: NAME_LIMIT
|
31
|
+
validates_length_of :slug, maximum: SLUG_LIMIT
|
32
|
+
validates_format_of :slug, with: SLUG_PATTERN
|
33
|
+
|
34
|
+
scope :ordered_by_priority, -> { order 'priority asc, name asc' }
|
35
|
+
scope :visible, -> { where(visible: true, deleted: false) }
|
36
|
+
scope :for_tree, ->(parent_id = nil) { siblings(parent_id).ordered_by_priority }
|
37
|
+
scope :siblings, ->(parent_id) { where(parent_id: parent_id) }
|
38
|
+
|
39
|
+
def self.entity_parameters
|
40
|
+
%i(image name slug priority visible)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.creation_parameters
|
44
|
+
entity_parameters + %i(parent_id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def full_title
|
48
|
+
(parents.map { |parent| parent.name } + [name]).join ' / '
|
49
|
+
end
|
50
|
+
|
51
|
+
def depth
|
52
|
+
parent_ids.count
|
53
|
+
end
|
54
|
+
|
55
|
+
def parent_ids
|
56
|
+
parents_cache.split(',').compact
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Integer>]
|
60
|
+
def branch_ids
|
61
|
+
parents_cache.split(',').map(&:to_i).reject { |i| i < 1 }.uniq + [id]
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array<Integer>]
|
65
|
+
def subbranch_ids
|
66
|
+
[id] + children_cache
|
67
|
+
end
|
68
|
+
|
69
|
+
def image_alt_text
|
70
|
+
name
|
71
|
+
end
|
72
|
+
|
73
|
+
def title
|
74
|
+
name
|
75
|
+
end
|
76
|
+
|
77
|
+
def parents
|
78
|
+
return [] if parents_cache.blank?
|
79
|
+
CourseCategory.where(id: parent_ids).order('id asc')
|
80
|
+
end
|
81
|
+
|
82
|
+
def cache_parents!
|
83
|
+
return if parent.nil?
|
84
|
+
self.parents_cache = "#{parent.parents_cache},#{parent_id}".gsub(/\A,/, '')
|
85
|
+
save!
|
86
|
+
end
|
87
|
+
|
88
|
+
def cache_children!
|
89
|
+
child_categories.order('id asc').each do |child|
|
90
|
+
self.children_cache += [child.id] + child.children_cache
|
91
|
+
end
|
92
|
+
save!
|
93
|
+
end
|
94
|
+
|
95
|
+
def can_be_deleted?
|
96
|
+
child_categories.count < 1
|
97
|
+
end
|
98
|
+
|
99
|
+
# @param [Course] course
|
100
|
+
def course?(course)
|
101
|
+
course.course_category == self
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param [Integer] delta
|
105
|
+
def change_priority(delta)
|
106
|
+
new_priority = priority + delta
|
107
|
+
criteria = { parent_id: parent_id, priority: new_priority }
|
108
|
+
adjacent = self.class.find_by(criteria)
|
109
|
+
if adjacent.is_a?(self.class) && (adjacent.id != id)
|
110
|
+
adjacent.update!(priority: priority)
|
111
|
+
end
|
112
|
+
self.update(priority: new_priority)
|
113
|
+
|
114
|
+
self.class.for_tree(parent_id).map { |e| [e.id, e.priority] }.to_h
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def set_next_priority
|
120
|
+
if id.nil? && priority == 1
|
121
|
+
self.priority = self.class.siblings(parent_id).maximum(:priority).to_i + 1
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def normalize_priority
|
126
|
+
self.priority = PRIORITY_RANGE.first if priority < PRIORITY_RANGE.first
|
127
|
+
self.priority = PRIORITY_RANGE.last if priority > PRIORITY_RANGE.last
|
128
|
+
end
|
129
|
+
|
130
|
+
def generate_long_slug
|
131
|
+
if parent.nil?
|
132
|
+
self.long_slug = slug
|
133
|
+
else
|
134
|
+
self.long_slug = "#{parent.long_slug}_#{slug}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CourseCourseTag < ApplicationRecord
|
2
|
+
after_create :update_course_tags_cache
|
3
|
+
after_destroy :update_course_tags_cache
|
4
|
+
|
5
|
+
belongs_to :course
|
6
|
+
belongs_to :course_tag, counter_cache: :courses_count
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def update_course_tags_cache
|
11
|
+
course.update tags_cache: course.course_tags.ordered_by_name.map(&:name)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class CourseTag < ApplicationRecord
|
2
|
+
include RequiredUniqueName
|
3
|
+
include RequiredUniqueSlug
|
4
|
+
include Toggleable
|
5
|
+
|
6
|
+
PER_PAGE = 20
|
7
|
+
NAME_LIMIT = 50
|
8
|
+
SLUG_LIMIT = 50
|
9
|
+
SLUG_PATTERN = /\A[a-z][-0-9a-z]*[0-9a-z]\z/i
|
10
|
+
SLUG_PATTERN_HTML = '^[a-zA-Z][-0-9a-zA-Z]*[0-9a-zA-Z]$'
|
11
|
+
|
12
|
+
toggleable :visible
|
13
|
+
|
14
|
+
mount_uploader :image, CourseImageUploader
|
15
|
+
|
16
|
+
has_many :course_course_tags, dependent: :destroy
|
17
|
+
has_many :courses, through: :course_course_tags
|
18
|
+
|
19
|
+
before_validation { self.slug = Canonizer.transliterate(name.to_s) if slug.blank? }
|
20
|
+
before_validation { self.slug = slug.to_s.downcase }
|
21
|
+
validates_length_of :name, maximum: NAME_LIMIT
|
22
|
+
validates_length_of :slug, maximum: SLUG_LIMIT
|
23
|
+
validates_format_of :slug, with: SLUG_PATTERN
|
24
|
+
|
25
|
+
scope :visible, -> { where(visible: true) }
|
26
|
+
scope :list_for_visitors, -> { visible.ordered_by_name }
|
27
|
+
|
28
|
+
# @param [Integer] page
|
29
|
+
def self.page_for_administration(page = 1)
|
30
|
+
ordered_by_name.page(page).per(PER_PAGE)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [Integer] page
|
34
|
+
def self.page_for_visitors(page = 1)
|
35
|
+
list_for_visitors.page(page).per(PER_PAGE)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.entity_parameters
|
39
|
+
%i(visible image name slug)
|
40
|
+
end
|
41
|
+
|
42
|
+
def image_alt_text
|
43
|
+
name
|
44
|
+
end
|
45
|
+
|
46
|
+
def title
|
47
|
+
name
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class CourseImageUploader < CarrierWave::Uploader::Base
|
2
|
+
include CarrierWave::MiniMagick
|
3
|
+
include CarrierWave::BombShelter
|
4
|
+
|
5
|
+
storage :file
|
6
|
+
|
7
|
+
def max_pixel_dimensions
|
8
|
+
[3840, 3840]
|
9
|
+
end
|
10
|
+
|
11
|
+
def store_dir
|
12
|
+
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id/10000.floor}/#{model.id/100.floor}/#{model.id}"
|
13
|
+
end
|
14
|
+
|
15
|
+
process :auto_orient
|
16
|
+
|
17
|
+
def auto_orient
|
18
|
+
manipulate! do |image|
|
19
|
+
image.tap(&:auto_orient)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version :big do
|
24
|
+
resize_to_fit(1280, 1280)
|
25
|
+
end
|
26
|
+
|
27
|
+
version :medium, from_version: :big do
|
28
|
+
resize_to_fit(640, 640)
|
29
|
+
end
|
30
|
+
|
31
|
+
version :small, from_version: :medium do
|
32
|
+
resize_to_fit(320, 320)
|
33
|
+
end
|
34
|
+
|
35
|
+
version :preview_2x, from_version: :small do
|
36
|
+
resize_to_fit(160, 160)
|
37
|
+
end
|
38
|
+
|
39
|
+
version :preview, from_version: :preview_2x do
|
40
|
+
resize_to_fit(80, 80)
|
41
|
+
end
|
42
|
+
|
43
|
+
def extension_white_list
|
44
|
+
%w(jpg jpeg png)
|
45
|
+
end
|
46
|
+
end
|