neofiles 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +1 -0
- data/README.md +417 -0
- data/Rakefile +40 -0
- data/app/assets/images/neofiles/loading.gif +0 -0
- data/app/assets/images/neofiles/swf-thumb-100x100.png +0 -0
- data/app/assets/images/neofiles/watermark.png +0 -0
- data/app/assets/javascripts/neofiles/index.js.coffee +3 -0
- data/app/assets/javascripts/neofiles/jquery.fileupload.js +1128 -0
- data/app/assets/javascripts/neofiles/jquery.iframe-transport.js +172 -0
- data/app/assets/javascripts/neofiles/jquery.neofiles.js.coffee +191 -0
- data/app/assets/stylesheets/neofiles/index.css.scss +3 -0
- data/app/assets/stylesheets/neofiles/neofiles.css.scss +149 -0
- data/app/controllers/concerns/neofiles/not_found.rb +21 -0
- data/app/controllers/neofiles/admin_controller.rb +228 -0
- data/app/controllers/neofiles/admin_test_controller.rb +5 -0
- data/app/controllers/neofiles/files_controller.rb +28 -0
- data/app/controllers/neofiles/images_controller.rb +130 -0
- data/app/helpers/neofiles/neofiles_helper.rb +188 -0
- data/app/models/neofiles/file.rb +319 -0
- data/app/models/neofiles/file_chunk.rb +18 -0
- data/app/models/neofiles/image.rb +119 -0
- data/app/models/neofiles/swf.rb +45 -0
- data/app/views/neofiles/admin/_file_compact.html.haml +85 -0
- data/app/views/neofiles/admin/file_compact.html.haml +1 -0
- data/app/views/neofiles/admin_test/file_compact.erb +7 -0
- data/config/locales/ru.yml +21 -0
- data/config/routes.rb +1 -0
- data/lib/neofiles.rb +94 -0
- data/lib/neofiles/engine.rb +33 -0
- data/lib/neofiles/version.rb +3 -0
- 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,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
|