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