neofiles 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +417 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/images/neofiles/loading.gif +0 -0
  6. data/app/assets/images/neofiles/swf-thumb-100x100.png +0 -0
  7. data/app/assets/images/neofiles/watermark.png +0 -0
  8. data/app/assets/javascripts/neofiles/index.js.coffee +3 -0
  9. data/app/assets/javascripts/neofiles/jquery.fileupload.js +1128 -0
  10. data/app/assets/javascripts/neofiles/jquery.iframe-transport.js +172 -0
  11. data/app/assets/javascripts/neofiles/jquery.neofiles.js.coffee +191 -0
  12. data/app/assets/stylesheets/neofiles/index.css.scss +3 -0
  13. data/app/assets/stylesheets/neofiles/neofiles.css.scss +149 -0
  14. data/app/controllers/concerns/neofiles/not_found.rb +21 -0
  15. data/app/controllers/neofiles/admin_controller.rb +228 -0
  16. data/app/controllers/neofiles/admin_test_controller.rb +5 -0
  17. data/app/controllers/neofiles/files_controller.rb +28 -0
  18. data/app/controllers/neofiles/images_controller.rb +130 -0
  19. data/app/helpers/neofiles/neofiles_helper.rb +188 -0
  20. data/app/models/neofiles/file.rb +319 -0
  21. data/app/models/neofiles/file_chunk.rb +18 -0
  22. data/app/models/neofiles/image.rb +119 -0
  23. data/app/models/neofiles/swf.rb +45 -0
  24. data/app/views/neofiles/admin/_file_compact.html.haml +85 -0
  25. data/app/views/neofiles/admin/file_compact.html.haml +1 -0
  26. data/app/views/neofiles/admin_test/file_compact.erb +7 -0
  27. data/config/locales/ru.yml +21 -0
  28. data/config/routes.rb +1 -0
  29. data/lib/neofiles.rb +94 -0
  30. data/lib/neofiles/engine.rb +33 -0
  31. data/lib/neofiles/version.rb +3 -0
  32. metadata +131 -0
@@ -0,0 +1,21 @@
1
+ # Return 404 NOT FOUND response when requested file is not found in the database.
2
+ #
3
+ # This concern is to be included in serving controllers (Files/ImagesController).
4
+ #
5
+ module Neofiles::NotFound
6
+ extend ActiveSupport::Concern
7
+
8
+ include ActionController::Rescue
9
+
10
+ included do
11
+ rescue_from Mongoid::Errors::DocumentNotFound, with: :error_404
12
+
13
+ def error_404
14
+ self.response_body = I18n.t('neofiles.404_not_found')
15
+ self.content_type = 'text/plain; charset=utf-8'
16
+ self.status = 404
17
+ end
18
+
19
+ private :error_404
20
+ end
21
+ end
@@ -0,0 +1,228 @@
1
+ # The main controller, doing all persistence related activities. It extends ApplicationController to derive
2
+ # any application-specific business logic, like before/after filters, auth & auth and stuff.
3
+ #
4
+ # To setup routes to this controller use Neofiles.routes_proc, @see lib/neofiles.rb
5
+ #
6
+ # As the main principle behind whole Neofiles thing is AJAX file manipulations, actions of this controller
7
+ # mainly form backend for AJAX calls.
8
+ #
9
+ class Neofiles::AdminController < ApplicationController
10
+
11
+ # TODO: remove this! it should be controlled on application side
12
+ skip_before_filter :verify_authenticity_token
13
+
14
+ # Build AJAX edit/upload form for a single file in compact way: small file thumbnail + misc buttons, like "delete",
15
+ # "change options" etc.
16
+ #
17
+ # It is expected that someday there will be "full" view (hence the prefix "compact" here), with metadata shown
18
+ # and all kinds of tools exposed.
19
+ #
20
+ # If param[:id] is present, the form displayed is for editing a file, while empty or non existent ID displays
21
+ # an upload form.
22
+ #
23
+ # The parameter fake_request allows to build form when needed, when there is no actual request available
24
+ # (@see #file_save).
25
+ #
26
+ # Main parameters:
27
+ #
28
+ # request[:input_name] - input with this name will be present in HTML and populated with ID of persisted file
29
+ # request[:widget_id] - DOM identifier for this file widget instance
30
+ # request[:clean_remove] - after deleting this file, no substituting upload form should be shown (default '0')
31
+ # request[:append_create] - after persisting new file, action should return form for the file + an upload form
32
+ # (default '0')
33
+ # request[:disabled] - only show file, not allow anything to be edited (default '0')
34
+ # request[:multiple] - allow uploading of multiple files at once (default '0')
35
+ # request[:with_desc] - show short file description (default '0')
36
+ #
37
+ # Parameters clear_remove & append_create are used to organize Albums — technically a collection of single files.
38
+ #
39
+ def file_compact(fake_request = nil)
40
+ request = fake_request || self.request
41
+
42
+ begin
43
+ @file = Neofiles::File.find request[:id] if request[:id].present?
44
+ rescue Mongoid::Errors::DocumentNotFound
45
+ @file = nil
46
+ end
47
+
48
+ @error = I18n.t('neofiles.file_not_found') if request[:id].present? and @file.blank?
49
+
50
+ @input_name = request[:input_name].to_s
51
+ @widget_id = request[:widget_id].presence
52
+ @clean_remove = request[:clean_remove].present? && request[:clean_remove] != '0'
53
+ @append_create = request[:append_create].present? && request[:append_create] != '0'
54
+ @disabled = request[:disabled].present? && request[:disabled] != '0'
55
+ @multiple = request[:multiple].present? && request[:multiple] != '0'
56
+ @with_desc = request[:with_desc].present? && request[:with_desc] != '0'
57
+ @error ||= ''
58
+
59
+ if fake_request
60
+ return render_to_string action: :file_compact, layout: false
61
+ else
62
+ render layout: false
63
+ end
64
+ end
65
+
66
+ # Persist new file(s) to database and return view forms for all of them (@see #file_compact) as one big HTML.
67
+ #
68
+ # Raises exception if something went wrong.
69
+ #
70
+ # This method uses append_create parameter originally passed to #file_compact (stored by JavaScript and sent again
71
+ # via AJAX call).
72
+ #
73
+ def file_save
74
+ data = request[:neofiles]
75
+ raise ArgumentError.new I18n.t('neofiles.data_not_passed') unless data.is_a? Hash
76
+
77
+ files = data[:file]
78
+ files = [files] unless files.is_a? Array
79
+ old_file = data[:id].present? ? Neofiles::File.find(data[:id]) : nil
80
+
81
+ file_objects = []
82
+ errors = []
83
+ last_exception = nil
84
+ files.each_with_index do |uploaded_file, i|
85
+ errors.push("#{I18n.t('neofiles.file_not_passed')} (#{i + 1})") and next unless uploaded_file.respond_to? :read
86
+
87
+ file_class = Neofiles::File.class_by_file_object(uploaded_file)
88
+ file = file_class.new do |f|
89
+ f.description = data[:description].presence || old_file.try(:description)
90
+ f.file = uploaded_file
91
+ end
92
+
93
+ begin
94
+ Rails.application.config.neofiles.before_save.try!(:call, file)
95
+ file.save!
96
+ rescue Exception => ex
97
+ last_exception = ex
98
+ notify_airbrake(ex) if defined? notify_airbrake
99
+ next
100
+ end
101
+
102
+ file_objects << file
103
+ end
104
+
105
+ result = []
106
+ file_objects.each_with_index do |file, i|
107
+ result << file_compact(data.merge(id: file.id, widget_id: "#{data[:widget_id]}_ap_#{i}", append_create: i == file_objects.count - 1 && !old_file && data[:append_create] == '1' ? '1' : '0'))
108
+ end
109
+
110
+ if result.empty?
111
+ raise ArgumentError.new(last_exception || (errors.empty? ? I18n.t('neofiles.file_not_passed') : errors.join("\n")))
112
+ end
113
+
114
+ render text: result.join, layout: false
115
+ end
116
+
117
+ # As we don't actually delete anything, this method only marks file as deleted.
118
+ #
119
+ # This method uses clean_remove parameter originally passed to #file_compact (stored by JavaScript and sent again
120
+ # via AJAX call).
121
+ #
122
+ def file_remove
123
+ file, data = find_file_and_data
124
+
125
+ file.is_deleted = true
126
+ file.save!
127
+
128
+ return render text: '' if data[:clean_remove].present? && data[:clean_remove] != '0'
129
+
130
+ redirect_to neofiles_file_compact_path(data.merge(id: nil))
131
+ end
132
+
133
+ # As Neofiles treats files as immutables, this method updates only auxiliary fields: description, no_wm etc.
134
+ #
135
+ # Returns nothing.
136
+ #
137
+ def file_update
138
+ file, data = find_file_and_data
139
+ file.update data.slice(:description, :no_wm)
140
+ render text: '', layout: false
141
+ end
142
+
143
+ # Neofiles knows how to play with Redactor.js and this method persists files uploaded via this WYSIWYG editor.
144
+ #
145
+ # Redactor.js may know which owner object is edited so we can store owner_type/id for later use.
146
+ #
147
+ # Returns JSON list of persisted files.
148
+ #
149
+ def redactor_upload
150
+ owner_type, owner_id, file = prepare_owner_type(request[:owner_type]), request[:owner_id], request[:file]
151
+ raise ArgumentError.new I18n.t('neofiles.data_not_passed') if owner_type.blank? || owner_id.blank?
152
+ raise ArgumentError.new I18n.t('neofiles.file_not_passed') unless file.present? && file.respond_to?(:read)
153
+
154
+ file_class = Neofiles::File.class_by_file_object(file)
155
+
156
+ file = file_class.new do |f|
157
+ f.owner_type = owner_type
158
+ f.owner_id = owner_id
159
+ f.description = request[:description].presence
160
+
161
+ f.no_wm = true if f.respond_to?(:no_wm)
162
+ f.file = file
163
+ end
164
+
165
+ Rails.application.config.neofiles.before_save.try!(:call, file)
166
+ file.save!
167
+
168
+ # returns JSON {filelink: '/neofiles/serve-file/#{file.id}'}
169
+ render json: {filelink: neofiles_file_path(file), filename: file.filename}
170
+ end
171
+
172
+ # Returns JSON of files assigned to specific owner to show them in Redactor.js tab "previously uploaded files".
173
+ #
174
+ def redactor_list
175
+ type, owner_type, owner_id = request[:type], prepare_owner_type(request[:owner_type]), request[:owner_id]
176
+
177
+ type ||= 'file'
178
+
179
+ begin
180
+ file_class = "Neofiles::#{type.classify}".constantize
181
+ rescue
182
+ raise ArgumentError.new I18n.t('neofiles.unknown_file_type', type: type)
183
+ end
184
+
185
+ result = []
186
+ files = file_class.where(owner_type: owner_type, owner_id: owner_id)
187
+ files.each do |f|
188
+ if f.is_a?(Neofiles::Image)
189
+ result << {
190
+ thumb: neofiles_image_path(f, format: '100x100'),
191
+ image: neofiles_file_path(f),
192
+ title: f.description.to_s,
193
+ #folder: '',
194
+ }
195
+ else
196
+ result << {
197
+ filelink: neofiles_file_path(f),
198
+ title: f.description.to_s,
199
+ #folder: '',
200
+ }
201
+ end
202
+ end
203
+
204
+ # returns JSON [{filelink: '/neofiles/serve-file/#{file.id}', title: '...', thumb: '/neo.../100x100'}, {...}, ...]
205
+ render json: result
206
+
207
+ rescue
208
+ render json: []
209
+ end
210
+
211
+
212
+
213
+ private
214
+
215
+ # Fetch common data from request.
216
+ def find_file_and_data
217
+ data = request[:neofiles]
218
+ raise ArgumentError.new I18n.t('neofiles.data_not_passed') if data.blank? || !(data.is_a? Hash)
219
+ raise ArgumentError.new I18n.t('neofiles.id_not_passed') unless data[:id].present?
220
+
221
+ [Neofiles::File.find(data[:id]), data]
222
+ end
223
+
224
+ # TODO: owner_type must be stored properly as in Mongoid polymorphic relation
225
+ def prepare_owner_type(type)
226
+ type.to_s.gsub(':', '/')
227
+ end
228
+ end
@@ -0,0 +1,5 @@
1
+ class Neofiles::AdminTestController < ApplicationController
2
+ def file_compact
3
+ @file_id = request[:id]
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ # Controller that serves files from the database via single action #show.
2
+ #
3
+ # If the file requested is an image redirect to Neofiles::ImagesController is made.
4
+ #
5
+ class Neofiles::FilesController < ActionController::Metal
6
+
7
+ include ActionController::DataStreaming
8
+ include ActionController::Redirecting
9
+ include Rails.application.routes.url_helpers
10
+ include Neofiles::NotFound
11
+
12
+ def show
13
+ file = Neofiles::File.find params[:id]
14
+
15
+ if file.is_a? Neofiles::Image
16
+ redirect_to neofiles_image_path(params) and return
17
+ end
18
+
19
+ send_file_headers!({
20
+ filename: CGI::escape(file.filename),
21
+ type: file.content_type,
22
+ disposition: 'inline',
23
+ })
24
+
25
+ self.status = 200
26
+ self.response_body = file.data
27
+ end
28
+ end
@@ -0,0 +1,130 @@
1
+ # Special controller for serving images from the database via single action #show.
2
+ #
3
+ class Neofiles::ImagesController < ActionController::Metal
4
+
5
+ class NotAdminException < Exception; end
6
+
7
+ include ActionController::DataStreaming
8
+ include ActionController::RackDelegation
9
+ include Neofiles::NotFound
10
+
11
+ CROP_MAX_WIDTH = Rails.application.config.neofiles.image_max_crop_width
12
+ CROP_MAX_HEIGHT = Rails.application.config.neofiles.image_max_crop_height
13
+
14
+ # Request parameters:
15
+ #
16
+ # format - resize image to no more than that size, example: '100x200'
17
+ # crop - if '1' and params[:format] is present, then cut image sides if its aspect ratio differs from
18
+ # params[:format] (otherwise image aspect ration will be preserved)
19
+ # quality - output JPEG quality, integer from 1 till 100 (forces JPEG output, otherwise image type is preserved)
20
+ # nowm - force returned image to not contain watermark - user must be admin or 403 Forbidden response is returned
21
+ # @see #admin_or_die
22
+ #
23
+ # Maximum allowed format dimensions are set via Rails.application.config.neofiles.image_max_crop_width/height.
24
+ #
25
+ # Watermark is added automatically from /assets/images/neofiles/watermark.png or via proc
26
+ # Rails.application.config.neofiles.watermarker if present.
27
+ #
28
+ def show
29
+
30
+ # get image
31
+ image_file = Neofiles::Image.find params[:id]
32
+
33
+ # prepare headers
34
+ data = image_file.data
35
+ options = {
36
+ filename: CGI::escape(image_file.filename),
37
+ type: image_file.content_type || 'image/jpeg',
38
+ disposition: 'inline',
39
+ }
40
+
41
+ # is resizing needed?
42
+ watermark_image, watermark_width, watermark_height = data, image_file.width, image_file.height
43
+ if params[:format].present?
44
+
45
+ width, height = params[:format].split('x').map(&:to_i)
46
+ watermark_width, watermark_height = width, height
47
+ raise Mongoid::Errors::DocumentNotFound unless width.between?(1, CROP_MAX_WIDTH) and height.between?(1, CROP_MAX_HEIGHT)
48
+
49
+ quality = [[Neofiles::quality_requested(params), 100].min, 1].max if Neofiles::quality_requested?(params)
50
+ setting_quality = quality && options[:type] == 'image/jpeg'
51
+
52
+ image = MiniMagick::Image.read(data)
53
+
54
+ if Neofiles.crop_requested? params
55
+ # construct ImageMagick call:
56
+ # 1) resize to WxH, allow the result to be bigger on one side
57
+ # 2) allign resized to center
58
+ # 3) cut the extending parts
59
+ # 4) set quality if requested
60
+ image.combine_options do |c|
61
+ c.resize "#{width}x#{height}^"
62
+ c.gravity 'center'
63
+ c.extent "#{width}x#{height}"
64
+ c.quality "#{quality}" if setting_quality
65
+ end
66
+ else
67
+ # no cropping so just resize to fit in WxH, one side can be smaller than requested
68
+ if image_file.width > width || image_file.height > height
69
+ image.combine_options do |c|
70
+ c.resize "#{width}x#{height}"
71
+ c.quality "#{quality}" if setting_quality
72
+ end
73
+ else
74
+ setting_quality = false
75
+ watermark_width, watermark_height = image_file.width, image_file.height
76
+ end
77
+ end
78
+
79
+ # quality requested, but we didn't have a chance to set it before -> forcibly resave as JPEG
80
+ if quality && !setting_quality
81
+ image.format 'jpeg'
82
+ image.quality quality.to_s
83
+ end
84
+
85
+ # get image bytes and stuff
86
+ data = image.to_blob
87
+ watermark_image = image
88
+ options[:type] = image.mime_type
89
+ end
90
+
91
+ watermark_image = MiniMagick::Image.read watermark_image unless watermark_image.is_a? MiniMagick::Image
92
+
93
+ # set watermark
94
+ data = Rails.application.config.neofiles.watermarker.(
95
+ watermark_image,
96
+ no_watermark: nowm?(image_file),
97
+ watermark_width: watermark_width,
98
+ watermark_height: watermark_height
99
+ )
100
+
101
+ # stream image headers & bytes
102
+ send_file_headers! options
103
+ headers['Content-Length'] = data.length.to_s
104
+ self.status = 200
105
+ self.response_body = data
106
+
107
+ rescue NotAdminException
108
+ self.response_body = I18n.t 'neofiles.403_access_denied'
109
+ self.content_type = 'text/plain; charset=utf-8'
110
+ self.status = 403
111
+ end
112
+
113
+
114
+
115
+ private
116
+
117
+ # Are we serving without watermark? If yes and user is not admin raise special exception.
118
+ def nowm?(image_file)
119
+ image_file.no_wm? || (params[:nowm] == true && admin_or_die)
120
+ end
121
+
122
+ # Assert the user logged in is admin. @see Neofiles.is_admin?
123
+ def admin_or_die
124
+ if Neofiles.is_admin? self
125
+ true
126
+ else
127
+ raise NotAdminException
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,188 @@
1
+ module Neofiles::NeofilesHelper
2
+
3
+ # Returns HTML IMG tag.
4
+ #
5
+ # image_file - ID, Neofiles::Image of Hash
6
+ # width, height - if both are passed, image will be no more that that size
7
+ # resize_options - crop: '1'/'0' (change or preserve aspect ration, @see Neofiles::ImagesController#show)
8
+ # html_attrs - hash of additional HTML attrs like ALT, TITLE etc.
9
+ #
10
+ def neofiles_img_tag(image_file, width = nil, height = nil, resize_options = {}, html_attrs = {})
11
+
12
+ resize_options ||= {} # в gem_neo_adv мы передаем nil
13
+
14
+ unless image_file.blank?
15
+ resize_options = resize_options.merge(format: [width.to_i, height.to_i].join('x')) if width.to_i > 0 && height.to_i > 0
16
+ size_attrs = resize_options.key?(:size_attrs) ? resize_options[:size_attrs] : true
17
+ image_or_id = image_file.is_a?(Hash) ? image_file[:id] : image_file
18
+
19
+ html_attrs.try :symbolize_keys!
20
+ html_attrs[:src] = neofiles_image_url image_or_id, resize_options
21
+
22
+ html_attrs[:width], html_attrs[:height] = dimensions_after_resize(image_file, width.to_i, height.to_i, resize_options) if size_attrs
23
+ end
24
+
25
+ tag :img, html_attrs
26
+ end
27
+
28
+ # Same as neofiles_img_tag but returned IMG is wrapped into A tag (HTML link) pointing to the file original.
29
+ #
30
+ # link_attrs - HTML attrs for A
31
+ # img_attrs - HTML attrs for IMG
32
+ #
33
+ # Other params are equivalent to neofiles_img_tag.
34
+ #
35
+ def neofiles_img_link(image_file, width = nil, height = nil, resize_options = {}, link_attrs = {}, img_attrs = {})
36
+ link_attrs[:href] = neofiles_image_url image_file unless link_attrs[:href]
37
+ neofiles_link(image_file, neofiles_img_tag(image_file, width, height, resize_options, img_attrs), link_attrs)
38
+ end
39
+
40
+ # Returns HTML A tag with link to the passed file.
41
+ #
42
+ # file - ID or Neofiles::File
43
+ # tag_content - what the link content will be (default: file description of filename)
44
+ # html_attrs - additional HTML attrs like TITLE, TARGET etc.
45
+ #
46
+ def neofiles_link(file, tag_content = nil, html_attrs = {})
47
+ html_attrs[:href] = neofiles_file_url file unless html_attrs[:href]
48
+ content_tag(:a, tag_content.presence || file.description.presence || file.filename, html_attrs)
49
+ end
50
+
51
+ # Returns HTML OBJECT tag to embed SWF (Flash) files.
52
+ #
53
+ # For fully crossbrowser experience include swfobject.js on page, where SWF embedding is performed.
54
+ #
55
+ # id - DOM ID of the object
56
+ # url - path/url to SWF file
57
+ # width - resulting object's area width
58
+ # height - resulting object's area height
59
+ # bgcolor - if passed, the object's area will be colored in this CSS color
60
+ # click_tag - clickTAG is a common name for Flash variable used to tell a movie clip to redirect viewer to a certain
61
+ # URL after clicking (used in banners)
62
+ # alt - alternative HTML, in case if Flash is not available
63
+ #
64
+ def swf_embed(id, url, width, height, bgcolor, click_tag, alt = '')
65
+ url = h(url)
66
+ click_tag = h(click_tag)
67
+
68
+ result = <<HTML
69
+ <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="#{width}" height="#{height}" id="#{id}">
70
+ <param name="movie" value="#{url}" />
71
+ <param name="bgcolor" value="#{bgcolor}" />
72
+ <param name="wmode" value="opaque" />
73
+ <param name="allowfullscreen" value="false" />
74
+ <param name="allowscriptaccess" value="never" />
75
+ <param name="quality" value="autohigh" />
76
+ <param name="flashvars" value="clickTAG=#{click_tag}" />
77
+
78
+ <!--[if !IE]>-->
79
+ <object type="application/x-shockwave-flash" data="#{url}" width="#{width}" height="#{height}">
80
+ <param name="bgcolor" value="#{bgcolor}" />
81
+ <param name="wmode" value="opaque" />
82
+ <param name="allowfullscreen" value="false" />
83
+ <param name="allowscriptaccess" value="never" />
84
+ <param name="quality" value="autohigh" />
85
+ <param name="flashvars" value="clickTAG=#{click_tag}" />
86
+ <!--<![endif]-->
87
+
88
+ #{alt}
89
+
90
+ <!--[if !IE]>-->
91
+ </object>
92
+ <!--<![endif]-->
93
+ </object>
94
+ <script type="text/javascript">
95
+ try { swfobject.registerObject("#{id}", "9.0.0"); } catch(e) {}
96
+ </script>
97
+ HTML
98
+ result.html_safe
99
+ end
100
+
101
+ # Returns CDN (Content Delivery Network) prefix - mainly a domain - if available.
102
+ #
103
+ # Array of CDNs is set via Rails.application.config.neofiles.cdns. If many exist, we choose one by taking remainder
104
+ # of dividing unix epoch creation time of the object, for which prefix is requested, by number of CDNs.
105
+ #
106
+ # If no CDN available, will take current domain via Rails helper root_url.
107
+ #
108
+ # First argument is considered Neofiles::File, ID or Hash. Other arguments are ignored.
109
+ #
110
+ # Returned prefix is of form 'http://doma.in/url/prefix'.
111
+ #
112
+ def neofiles_cdn_prefix(*args)
113
+ cdns = Rails.application.config.neofiles.cdns || []
114
+ cdns << root_url unless cdns.any?
115
+
116
+ if cdns.count > 1
117
+ some_file = args.first
118
+ if some_file.is_a? Neofiles::File
119
+ gen_time = some_file.id.generation_time.sec
120
+ elsif some_file.is_a? Hash
121
+ tmp = some_file[:id] || some_file['id'] || some_file[:_id] || some_file['_id'] || ""
122
+ gen_time = BSON::ObjectId.legal?(tmp) ? BSON::ObjectId.from_string(tmp).generation_time.sec : Time.now.strftime('%U').to_i
123
+ elsif some_file.is_a? String
124
+ gen_time = BSON::ObjectId.legal?(some_file) ? BSON::ObjectId.from_string(some_file).generation_time.sec : Time.now.strftime('%U').to_i
125
+ else
126
+ gen_time = Time.now.strftime('%U').to_i
127
+ end
128
+
129
+ cdn = cdns[gen_time % cdns.count]
130
+ else
131
+ cdn = cdns.first
132
+ end
133
+
134
+ cdn.sub! /\/\z/, ''
135
+ cdn = 'http://' + cdn unless cdn =~ /\Ahttp[s]?:\/\//
136
+ cdn
137
+ end
138
+
139
+ # Override file URL generation to include CDN prefix.
140
+ def neofiles_file_url(*args)
141
+ neofiles_cdn_prefix(*args) + neofiles_file_path(*args)
142
+ end
143
+
144
+ # Override image URL generation to include CDN prefix.
145
+ def neofiles_image_url(*args)
146
+ neofiles_cdn_prefix(*args) + neofiles_image_path(*args)
147
+ end
148
+
149
+
150
+
151
+ private
152
+
153
+ # Calculate dimensions of an image after resize applied, according to rules in Neofiles::ImagesController#show.
154
+ #
155
+ # 1) if not cropping (resize_options[:crop] != '1'), but destination width and height are set, calculate resized
156
+ # dimensions via external method (uses ImageMagick)
157
+ # 2) if cropping (resize_options[:crop] == '1'), resized dimensions are equal to the requested with & height
158
+ # 3) if not cropping and with & height not set, resized dimensions are equal to the input file width & height
159
+ # 4) if some variables are not available (like input file or height, if file ID is passed), returns nil
160
+ #
161
+ # image_file - Neofiles::Image, ID or Hash
162
+ # width, height - resulting width & height
163
+ # resize_options - {crop: '1'/'0'}
164
+ #
165
+ def dimensions_after_resize(image_file, width, height, resize_options)
166
+ we_need_resizing = width > 0 && height > 0
167
+
168
+ if image_file.is_a? Neofiles::Image
169
+ image_file_width = image_file.width
170
+ image_file_height = image_file.height
171
+ else
172
+ image_file_width = image_file[:width]
173
+ image_file_height = image_file[:height]
174
+ end
175
+
176
+ if (image_file_width.present? && image_file_height.present?) && (image_file_width > 0 && image_file_height > 0)
177
+ if we_need_resizing
178
+ ::Neofiles.resized_image_dimensions(image_file, width, height, resize_options)
179
+ else
180
+ [image_file_width, image_file_height]
181
+ end
182
+ elsif we_need_resizing and resize_options[:crop].present? and resize_options[:crop].to_s != '0'
183
+ [width, height]
184
+ else
185
+ [nil, nil]
186
+ end
187
+ end
188
+ end