neofiles 1.0.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.
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