radiant-paperclipped-extension 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitmodules +3 -0
- data/HELP_admin.markdown +69 -0
- data/LICENSE +21 -0
- data/README.md +137 -0
- data/Rakefile +110 -0
- data/VERSION +1 -0
- data/app/controllers/admin/assets_controller.rb +124 -0
- data/app/helpers/admin/assets_helper.rb +5 -0
- data/app/models/asset.rb +279 -0
- data/app/models/asset_page_tags.rb +101 -0
- data/app/models/asset_tags.rb +272 -0
- data/app/models/old_page_attachment.rb +26 -0
- data/app/models/page_attachment.rb +8 -0
- data/app/views/admin/assets/_asset.html.haml +19 -0
- data/app/views/admin/assets/_asset_table.html.haml +49 -0
- data/app/views/admin/assets/_assets_bucket.html.haml +8 -0
- data/app/views/admin/assets/_assets_container.html.haml +72 -0
- data/app/views/admin/assets/_bucket.html.haml +11 -0
- data/app/views/admin/assets/_bucket_asset.html.haml +9 -0
- data/app/views/admin/assets/_errors.html.haml +3 -0
- data/app/views/admin/assets/_form.html.haml +20 -0
- data/app/views/admin/assets/_page_assets.html.haml +12 -0
- data/app/views/admin/assets/_search_results.html.haml +17 -0
- data/app/views/admin/assets/_show_bucket_link.html.haml +4 -0
- data/app/views/admin/assets/_upload_to_page.html.haml +16 -0
- data/app/views/admin/assets/edit.html.haml +53 -0
- data/app/views/admin/assets/index.html.haml +43 -0
- data/app/views/admin/assets/new.html.haml +27 -0
- data/app/views/admin/assets/remove.html.haml +21 -0
- data/app/views/admin/bucket/_iframe.html.haml +1 -0
- data/config/locales/en.yml +60 -0
- data/config/locales/nl.yml +60 -0
- data/config/locales/pl.yml +60 -0
- data/config/routes.rb +20 -0
- data/db/migrate/001_create_assets.rb +12 -0
- data/db/migrate/002_create_paperclip_attributes.rb +13 -0
- data/db/migrate/003_create_user_observer.rb +13 -0
- data/db/migrate/004_create_page_attachments.rb +19 -0
- data/db/migrate/005_rename_users.rb +13 -0
- data/db/migrate/006_add_default_configs.rb +29 -0
- data/db/migrate/007_add_default_content_types.rb +29 -0
- data/db/migrate/20090316132151_disable_file_types.rb +20 -0
- data/lib/assets_admin_ui.rb +38 -0
- data/lib/mime_type_ext.rb +7 -0
- data/lib/tasks/assets_extension_tasks.rake +123 -0
- data/lib/tasks/paperclip_tasks.rake +79 -0
- data/lib/url_additions.rb +10 -0
- data/paperclipped_extension.rb +56 -0
- data/psds/file_type_icons.psd +0 -0
- data/psds/file_type_icons_.psd +0 -0
- data/public/images/assets/_page_assets.html.haml +26 -0
- data/public/images/assets/add-to-bucket.png +0 -0
- data/public/images/assets/add.png +0 -0
- data/public/images/assets/audio_icon.png +0 -0
- data/public/images/assets/audio_thumbnail.png +0 -0
- data/public/images/assets/delete.png +0 -0
- data/public/images/assets/doc_icon.png +0 -0
- data/public/images/assets/doc_thumbnail.png +0 -0
- data/public/images/assets/edit.png +0 -0
- data/public/images/assets/movie_icon.png +0 -0
- data/public/images/assets/movie_thumbnail.png +0 -0
- data/public/images/assets/new-asset.png +0 -0
- data/public/images/assets/page_edit.png +0 -0
- data/public/images/assets/pdf_icon.png +0 -0
- data/public/images/assets/pdf_thumbnail.png +0 -0
- data/public/images/assets/reorder_assets.png +0 -0
- data/public/javascripts/admin/assets.js +173 -0
- data/public/stylesheets/admin/assets.css +163 -0
- data/spec/controllers/admin/assets_controller_spec.rb +10 -0
- data/spec/models/asset_spec.rb +68 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +27 -0
- data/vendor/plugins/acts_as_list/README +23 -0
- data/vendor/plugins/acts_as_list/init.rb +3 -0
- data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
- data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
- data/vendor/plugins/paperclip/LICENSE +26 -0
- data/vendor/plugins/paperclip/README.rdoc +179 -0
- data/vendor/plugins/paperclip/Rakefile +76 -0
- data/vendor/plugins/paperclip/cucumber/paperclip_steps.rb +6 -0
- data/vendor/plugins/paperclip/generators/paperclip/USAGE +5 -0
- data/vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb +27 -0
- data/vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
- data/vendor/plugins/paperclip/init.rb +1 -0
- data/vendor/plugins/paperclip/lib/generators/paperclip/USAGE +8 -0
- data/vendor/plugins/paperclip/lib/generators/paperclip/paperclip_generator.rb +31 -0
- data/vendor/plugins/paperclip/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
- data/vendor/plugins/paperclip/lib/paperclip.rb +397 -0
- data/vendor/plugins/paperclip/lib/paperclip/attachment.rb +326 -0
- data/vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb +61 -0
- data/vendor/plugins/paperclip/lib/paperclip/geometry.rb +115 -0
- data/vendor/plugins/paperclip/lib/paperclip/interpolations.rb +108 -0
- data/vendor/plugins/paperclip/lib/paperclip/iostream.rb +59 -0
- data/vendor/plugins/paperclip/lib/paperclip/matchers.rb +33 -0
- data/vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
- data/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +74 -0
- data/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
- data/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
- data/vendor/plugins/paperclip/lib/paperclip/processor.rb +49 -0
- data/vendor/plugins/paperclip/lib/paperclip/railtie.rb +24 -0
- data/vendor/plugins/paperclip/lib/paperclip/storage.rb +247 -0
- data/vendor/plugins/paperclip/lib/paperclip/style.rb +90 -0
- data/vendor/plugins/paperclip/lib/paperclip/thumbnail.rb +78 -0
- data/vendor/plugins/paperclip/lib/paperclip/upfile.rb +52 -0
- data/vendor/plugins/paperclip/lib/paperclip/version.rb +3 -0
- data/vendor/plugins/paperclip/lib/tasks/paperclip.rake +79 -0
- data/vendor/plugins/paperclip/paperclip.gemspec +34 -0
- data/vendor/plugins/paperclip/rails/init.rb +2 -0
- data/vendor/plugins/paperclip/shoulda_macros/paperclip.rb +119 -0
- data/vendor/plugins/paperclip/test/.gitignore +1 -0
- data/vendor/plugins/paperclip/test/attachment_test.rb +758 -0
- data/vendor/plugins/paperclip/test/database.yml +4 -0
- data/vendor/plugins/paperclip/test/fixtures/12k.png +0 -0
- data/vendor/plugins/paperclip/test/fixtures/50x50.png +0 -0
- data/vendor/plugins/paperclip/test/fixtures/5k.png +0 -0
- data/vendor/plugins/paperclip/test/fixtures/bad.png +1 -0
- data/vendor/plugins/paperclip/test/fixtures/s3.yml +8 -0
- data/vendor/plugins/paperclip/test/fixtures/text.txt +0 -0
- data/vendor/plugins/paperclip/test/fixtures/twopage.pdf +0 -0
- data/vendor/plugins/paperclip/test/geometry_test.rb +177 -0
- data/vendor/plugins/paperclip/test/helper.rb +148 -0
- data/vendor/plugins/paperclip/test/integration_test.rb +483 -0
- data/vendor/plugins/paperclip/test/interpolations_test.rb +124 -0
- data/vendor/plugins/paperclip/test/iostream_test.rb +78 -0
- data/vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb +24 -0
- data/vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb +37 -0
- data/vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
- data/vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
- data/vendor/plugins/paperclip/test/paperclip_test.rb +317 -0
- data/vendor/plugins/paperclip/test/processor_test.rb +10 -0
- data/vendor/plugins/paperclip/test/storage_test.rb +343 -0
- data/vendor/plugins/paperclip/test/style_test.rb +141 -0
- data/vendor/plugins/paperclip/test/thumbnail_test.rb +227 -0
- data/vendor/plugins/paperclip/test/upfile_test.rb +36 -0
- data/vendor/plugins/responds_to_parent/MIT-LICENSE +20 -0
- data/vendor/plugins/responds_to_parent/README +42 -0
- data/vendor/plugins/responds_to_parent/Rakefile +22 -0
- data/vendor/plugins/responds_to_parent/init.rb +2 -0
- data/vendor/plugins/responds_to_parent/lib/parent_selector_assertion.rb +144 -0
- data/vendor/plugins/responds_to_parent/lib/responds_to_parent.rb +46 -0
- data/vendor/plugins/responds_to_parent/test/assert_select_parent_test.rb +318 -0
- data/vendor/plugins/responds_to_parent/test/responds_to_parent_test.rb +115 -0
- metadata +226 -0
data/app/models/asset.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
require 'mime_type_ext'
|
2
|
+
|
3
|
+
class Asset < ActiveRecord::Base
|
4
|
+
@@known_types = []
|
5
|
+
cattr_accessor :known_types
|
6
|
+
|
7
|
+
# type declaration machinery is consolidated here so that other extensions can add more types
|
8
|
+
# for example: Asset.register_type(:gps, %w{application/gpx+xml application/tcx+xml})
|
9
|
+
# the main Asset register_type() calls are in the class definition below after validation
|
10
|
+
|
11
|
+
def self.register_type(type, mimes)
|
12
|
+
constname = type.to_s.upcase.to_sym
|
13
|
+
Mime.send(:remove_const, constname) if Mime.const_defined?(constname)
|
14
|
+
Mime::Type.register mimes.shift, type, mimes # Mime::Type.register 'image/png', :image, %w[image/x-png image/jpeg image/pjpeg image/jpg image/gif]
|
15
|
+
|
16
|
+
self.class.send :define_method, "#{type}?".intern do |content_type|
|
17
|
+
Mime::Type.lookup_by_extension(type.to_s) == content_type.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
define_method "#{type}?".intern do
|
21
|
+
self.class.send "#{type}?".intern, asset_content_type
|
22
|
+
end
|
23
|
+
|
24
|
+
self.class.send :define_method, "#{type}_condition".intern do
|
25
|
+
types = Mime::Type.lookup_by_extension(type.to_s).all_types
|
26
|
+
send(:sanitize_sql, ['asset_content_type IN (?)', types])
|
27
|
+
end
|
28
|
+
|
29
|
+
self.class.send :define_method, "not_#{type}_condition".intern do
|
30
|
+
types = Mime::Type.lookup_by_extension(type.to_s).all_types
|
31
|
+
send(:sanitize_sql, ['NOT asset_content_type IN (?)', types])
|
32
|
+
end
|
33
|
+
|
34
|
+
named_scope type.to_s.pluralize.intern, :conditions => self.send("#{type}_condition".intern) do
|
35
|
+
def paged (options={})
|
36
|
+
paginate({:per_page => 20, :page => 1}.merge(options))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
named_scope "not_#{type.to_s.pluralize}".intern, :conditions => self.send("not_#{type}_condition".intern) do
|
41
|
+
def paged (options={})
|
42
|
+
paginate({:per_page => 20, :page => 1}.merge(options))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
known_types.push(type)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.known_type?(type)
|
50
|
+
known_types.include?(type)
|
51
|
+
end
|
52
|
+
|
53
|
+
# 'other' here means 'document', really: anything that is not image, audio or video.
|
54
|
+
|
55
|
+
def other?
|
56
|
+
self.class.other?(asset_content_type)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.other?(content_type)
|
60
|
+
!self.mime_types_not_considered_other.include? content_type.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
# the lambda delays interpolation, allowing extensions to affect the other_condition
|
64
|
+
named_scope :others, lambda {{:conditions => self.other_condition}}
|
65
|
+
known_types.push(:other)
|
66
|
+
|
67
|
+
named_scope :newest_first, { :order => 'created_at DESC'}
|
68
|
+
|
69
|
+
def self.other_condition
|
70
|
+
send(:sanitize_sql, ['asset_content_type NOT IN (?)', self.mime_types_not_considered_other])
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.mime_types_not_considered_other
|
74
|
+
Mime::IMAGE.all_types + Mime::AUDIO.all_types + Mime::MOVIE.all_types
|
75
|
+
end
|
76
|
+
|
77
|
+
class << self
|
78
|
+
def search(search, filter, page)
|
79
|
+
unless search.blank?
|
80
|
+
|
81
|
+
search_cond_sql = []
|
82
|
+
search_cond_sql << 'LOWER(asset_file_name) LIKE (:term)'
|
83
|
+
search_cond_sql << 'LOWER(title) LIKE (:term)'
|
84
|
+
search_cond_sql << 'LOWER(caption) LIKE (:term)'
|
85
|
+
|
86
|
+
cond_sql = search_cond_sql.join(" or ")
|
87
|
+
|
88
|
+
@conditions = [cond_sql, {:term => "%#{search.downcase}%" }]
|
89
|
+
else
|
90
|
+
@conditions = []
|
91
|
+
end
|
92
|
+
|
93
|
+
options = { :conditions => @conditions,
|
94
|
+
:order => 'created_at DESC',
|
95
|
+
:page => page,
|
96
|
+
:per_page => 10 }
|
97
|
+
|
98
|
+
@file_types = filter.blank? ? [] : filter.keys
|
99
|
+
if not @file_types.empty?
|
100
|
+
options[:total_entries] = count_by_conditions
|
101
|
+
Asset.paginate_by_content_types(@file_types, :all, options )
|
102
|
+
else
|
103
|
+
Asset.paginate(:all, options)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def find_all_by_content_types(types, *args)
|
108
|
+
with_content_types(types) { find *args }
|
109
|
+
end
|
110
|
+
|
111
|
+
def with_content_types(types, &block)
|
112
|
+
with_scope(:find => { :conditions => types_to_conditions(types).join(' OR ') }, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
def count_by_conditions
|
116
|
+
type_conditions = @file_types.blank? ? nil : Asset.types_to_conditions(@file_types.dup).join(" OR ")
|
117
|
+
@count_by_conditions ||= @conditions.empty? ? Asset.count(:all, :conditions => type_conditions) : Asset.count(:all, :conditions => @conditions)
|
118
|
+
end
|
119
|
+
|
120
|
+
def types_to_conditions(types)
|
121
|
+
types.collect! { |t| '(' + send("#{t}_condition") + ')' }
|
122
|
+
end
|
123
|
+
|
124
|
+
def thumbnail_sizes
|
125
|
+
if Radiant::Config.table_exists? && Radiant::Config["assets.additional_thumbnails"]
|
126
|
+
thumbnails = additional_thumbnails
|
127
|
+
else
|
128
|
+
thumbnails = {}
|
129
|
+
end
|
130
|
+
thumbnails[:icon] = ['42x42#', :png]
|
131
|
+
thumbnails[:thumbnail] = ['100x100>', :png]
|
132
|
+
thumbnails
|
133
|
+
end
|
134
|
+
|
135
|
+
def thumbnail_names
|
136
|
+
thumbnail_sizes.keys
|
137
|
+
end
|
138
|
+
|
139
|
+
# returns a descriptive list suitable for use as options in a select box
|
140
|
+
|
141
|
+
def thumbnail_options
|
142
|
+
asset_sizes = thumbnail_sizes.collect{|k,v|
|
143
|
+
size_id = k
|
144
|
+
size_description = "#{k}: "
|
145
|
+
size_description << (v.is_a?(Array) ? v.join(' as ') : v)
|
146
|
+
[size_description, size_id]
|
147
|
+
}.sort_by{|pair| pair.last.to_s}
|
148
|
+
asset_sizes.unshift ['Original (as uploaded)', 'original']
|
149
|
+
asset_sizes
|
150
|
+
end
|
151
|
+
|
152
|
+
# this is just a pointer that can be alias-chained in other extensions to add to or replace the list of thumbnail mechanisms
|
153
|
+
# its invocation is delayed with a lambda in has_attached_file so that it isn't called when the extension loads, but when an attachment initializes:
|
154
|
+
# that way we can be sure that all the related extensions have loaded and all the alias_chains are in place.
|
155
|
+
|
156
|
+
def thumbnail_definitions
|
157
|
+
thumbnail_sizes
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
def additional_thumbnails
|
162
|
+
Radiant::Config["assets.additional_thumbnails"].gsub(' ','').split(',').collect{|s| s.split('=')}.inject({}) {|ha, (k, v)| ha[k.to_sym] = v; ha}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# order_by 'title'
|
167
|
+
|
168
|
+
has_attached_file :asset,
|
169
|
+
:processors => lambda {|instance| instance.choose_processors }, # this allows us to set processors per file type, and to add more in other extensions
|
170
|
+
:styles => lambda { thumbnail_definitions }, # and this lets extensions add thumbnailers (and also usefully defers the call)
|
171
|
+
:whiny => false,
|
172
|
+
:storage => Radiant::Config["assets.storage"] == "s3" ? :s3 : :filesystem,
|
173
|
+
:s3_credentials => {
|
174
|
+
:access_key_id => Radiant::Config["assets.s3.key"],
|
175
|
+
:secret_access_key => Radiant::Config["assets.s3.secret"]
|
176
|
+
},
|
177
|
+
:bucket => Radiant::Config["assets.s3.bucket"],
|
178
|
+
:url => Radiant::Config["assets.url"] ? Radiant::Config["assets.url"] : "/:class/:id/:basename:no_original_style.:extension",
|
179
|
+
:path => Radiant::Config["assets.path"] ? Radiant::Config["assets.path"] : ":rails_root/public/:class/:id/:basename:no_original_style.:extension"
|
180
|
+
|
181
|
+
has_many :page_attachments, :dependent => :destroy
|
182
|
+
has_many :pages, :through => :page_attachments
|
183
|
+
|
184
|
+
belongs_to :created_by, :class_name => 'User'
|
185
|
+
belongs_to :updated_by, :class_name => 'User'
|
186
|
+
|
187
|
+
validates_attachment_presence :asset
|
188
|
+
validates_attachment_content_type :asset,
|
189
|
+
:content_type => Radiant::Config["assets.content_types"].gsub(' ','').split(',') if Radiant::Config.table_exists? && Radiant::Config["assets.content_types"] && Radiant::Config["assets.skip_filetype_validation"] == nil
|
190
|
+
validates_attachment_size :asset,
|
191
|
+
:less_than => Radiant::Config["assets.max_asset_size"].to_i.megabytes if Radiant::Config.table_exists? && Radiant::Config["assets.max_asset_size"]
|
192
|
+
|
193
|
+
before_save :assign_title
|
194
|
+
|
195
|
+
register_type :image, %w[image/png image/x-png image/jpeg image/pjpeg image/jpg image/gif]
|
196
|
+
register_type :video, %w[video/mpeg video/mp4 video/ogg video/quicktime video/x-ms-wmv video/x-flv]
|
197
|
+
register_type :audio, %w[audio/mpeg audio/mpg audio/ogg application/ogg audio/x-ms-wma audio/vnd.rn-realaudio audio/x-wav]
|
198
|
+
register_type :swf, %w[application/x-shockwave-flash]
|
199
|
+
register_type :pdf, %w[application/pdf]
|
200
|
+
|
201
|
+
# alias for backwards-compatibility: movie can be video or swf
|
202
|
+
register_type :movie, Mime::SWF.all_types + Mime::VIDEO.all_types
|
203
|
+
|
204
|
+
def thumbnail(size='original')
|
205
|
+
return asset.url if size == 'original'
|
206
|
+
case
|
207
|
+
when self.pdf? : "/images/assets/pdf_#{size.to_s}.png"
|
208
|
+
when self.movie? : "/images/assets/movie_#{size.to_s}.png"
|
209
|
+
when self.video? : "/images/assets/movie_#{size.to_s}.png"
|
210
|
+
when self.swf? : "/images/assets/movie_#{size.to_s}.png" #TODO: special icon for swf-files
|
211
|
+
when self.audio? : "/images/assets/audio_#{size.to_s}.png"
|
212
|
+
when self.other? : "/images/assets/doc_#{size.to_s}.png"
|
213
|
+
else
|
214
|
+
self.asset.url(size.to_sym)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def generate_style(name, args={})
|
219
|
+
size = args[:size]
|
220
|
+
format = args[:format] || :jpg
|
221
|
+
asset = self.asset
|
222
|
+
unless asset.exists?(name.to_sym)
|
223
|
+
self.asset.styles[name.to_sym] = { :geometry => size, :format => format, :whiny => true, :convert_options => "", :processors => [:thumbnail] }
|
224
|
+
self.asset.reprocess!
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# this has been added to support other extensions that want to add processors
|
229
|
+
# eg paperclipped_gps uses gpsbabel in the same way as paperclipped uses imagemagick
|
230
|
+
# I also mean to add a video thumbnailer using ffmpeg
|
231
|
+
|
232
|
+
def choose_processors
|
233
|
+
[:thumbnail]
|
234
|
+
end
|
235
|
+
|
236
|
+
def basename
|
237
|
+
File.basename(asset_file_name, ".*") if asset_file_name
|
238
|
+
end
|
239
|
+
|
240
|
+
def extension
|
241
|
+
asset_file_name.split('.').last.downcase if asset_file_name
|
242
|
+
end
|
243
|
+
|
244
|
+
def dimensions(size='original')
|
245
|
+
@dimensions ||= {}
|
246
|
+
@dimensions[size] ||= supported_by_image_spec? && begin
|
247
|
+
image_spec = ImageSpec.new(filesystem_path(size))
|
248
|
+
[image_spec.width, image_spec.height]
|
249
|
+
rescue
|
250
|
+
[0, 0]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def dimensions_known?
|
255
|
+
defined?(ImageSpec) && supported_by_image_spec?
|
256
|
+
end
|
257
|
+
|
258
|
+
def width(size='original')
|
259
|
+
supported_by_image_spec? && self.dimensions(size)[0]
|
260
|
+
end
|
261
|
+
|
262
|
+
def height(size='original')
|
263
|
+
supported_by_image_spec? && self.dimensions(size)[1]
|
264
|
+
end
|
265
|
+
|
266
|
+
private
|
267
|
+
def filesystem_path(size='original')
|
268
|
+
"#{RAILS_ROOT}/public#{thumbnail(size)}"
|
269
|
+
end
|
270
|
+
|
271
|
+
def supported_by_image_spec?
|
272
|
+
image? || swf?
|
273
|
+
end
|
274
|
+
|
275
|
+
def assign_title
|
276
|
+
self.title = basename if title.blank?
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module AssetPageTags
|
2
|
+
include Radiant::Taggable
|
3
|
+
include AssetTags
|
4
|
+
|
5
|
+
desc %{
|
6
|
+
Cycles through all assets attached to the current page.
|
7
|
+
This tag does not require the title atttribute, nor do any of its children.
|
8
|
+
Use the @limit@ and @offset@ attribute to render a specific number of assets.
|
9
|
+
Use @by@ and @order@ attributes to control the order of assets.
|
10
|
+
Use @extensions@ attribute to specify which assets to be rendered.
|
11
|
+
|
12
|
+
*Usage:*
|
13
|
+
<pre><code><r:assets:each [limit=0] [offset=0] [order="asc|desc"] [by="position|title|..."] [extensions="png|pdf|doc"]>...</r:assets:each></code></pre>
|
14
|
+
}
|
15
|
+
tag 'assets:each' do |tag|
|
16
|
+
options = tag.attr.dup
|
17
|
+
result = []
|
18
|
+
assets = tag.locals.page.assets.find(:all, assets_find_options(tag))
|
19
|
+
tag.locals.assets = assets
|
20
|
+
assets.each do |asset|
|
21
|
+
tag.locals.asset = asset
|
22
|
+
result << tag.expand
|
23
|
+
end
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
desc %{
|
28
|
+
References the first asset attached to the current page.
|
29
|
+
|
30
|
+
*Usage:*
|
31
|
+
<pre><code><r:assets:first>...</r:assets:first></code></pre>
|
32
|
+
}
|
33
|
+
tag 'assets:first' do |tag|
|
34
|
+
attachments = tag.locals.page.page_attachments
|
35
|
+
if first = attachments.first
|
36
|
+
tag.locals.asset = first.asset
|
37
|
+
tag.expand
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc %{
|
42
|
+
Renders the contained elements only if the current asset is the first
|
43
|
+
asset attached to the current page.
|
44
|
+
|
45
|
+
*Usage:*
|
46
|
+
<pre><code><r:if_first>...</r:if_first></code></pre>
|
47
|
+
}
|
48
|
+
tag 'assets:if_first' do |tag|
|
49
|
+
attachments = tag.locals.assets
|
50
|
+
asset = tag.locals.asset
|
51
|
+
if asset == attachments.first
|
52
|
+
tag.expand
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
desc %{
|
58
|
+
Renders the contained elements only if the current asset is not the first
|
59
|
+
asset attached to the current page.
|
60
|
+
|
61
|
+
*Usage:*
|
62
|
+
<pre><code><r:unless_first>...</r:unless_first></code></pre>
|
63
|
+
}
|
64
|
+
tag 'assets:unless_first' do |tag|
|
65
|
+
attachments = tag.locals.assets
|
66
|
+
asset = tag.locals.asset
|
67
|
+
if asset != attachments.first
|
68
|
+
tag.expand
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc %{
|
73
|
+
Renders the contained elements only if the current contextual page has one or
|
74
|
+
more assets. The @min_count@ attribute specifies the minimum number of required
|
75
|
+
assets. You can also filter by extensions with the @extensions@ attribute.
|
76
|
+
|
77
|
+
*Usage:*
|
78
|
+
<pre><code><r:if_assets [min_count="n"] [extensions="pdf|jpg"]>...</r:if_assets></code></pre>
|
79
|
+
}
|
80
|
+
tag 'if_assets' do |tag|
|
81
|
+
count = tag.attr['min_count'] && tag.attr['min_count'].to_i || 1
|
82
|
+
assets = tag.locals.page.assets.count(:conditions => assets_find_options(tag)[:conditions])
|
83
|
+
tag.expand if assets >= count
|
84
|
+
end
|
85
|
+
|
86
|
+
desc %{
|
87
|
+
The opposite of @<r:if_assets/>@.
|
88
|
+
}
|
89
|
+
tag 'unless_assets' do |tag|
|
90
|
+
count = tag.attr['min_count'] && tag.attr['min_count'].to_i || 1
|
91
|
+
assets = tag.locals.page.assets.count(:conditions => assets_find_options(tag)[:conditions])
|
92
|
+
tag.expand unless assets >= count
|
93
|
+
end
|
94
|
+
|
95
|
+
# Resets the page Url and title within the asset tag
|
96
|
+
[:title, :url].each do |method|
|
97
|
+
tag "assets:page:#{method.to_s}" do |tag|
|
98
|
+
tag.locals.page.send(method)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
module AssetTags
|
2
|
+
include Radiant::Taggable
|
3
|
+
|
4
|
+
class TagError < StandardError; end
|
5
|
+
|
6
|
+
desc %{
|
7
|
+
The namespace for referencing images and assets. You may specify the @title@
|
8
|
+
attribute on this tag for all contained tags to refer to that asset.
|
9
|
+
|
10
|
+
*Usage:*
|
11
|
+
<pre><code><r:assets [title="asset_title"]>...</r:assets></code></pre>
|
12
|
+
}
|
13
|
+
tag 'assets' do |tag|
|
14
|
+
tag.locals.asset = Asset.find_by_title(tag.attr['title']) || Asset.find(tag.attr['id']) unless tag.attr.empty?
|
15
|
+
tag.expand
|
16
|
+
end
|
17
|
+
|
18
|
+
desc %{
|
19
|
+
Renders the value for a top padding for the image. Put the image in a
|
20
|
+
container with specified height and using this tag you can vertically
|
21
|
+
align the image within it's container.
|
22
|
+
|
23
|
+
*Usage*:
|
24
|
+
<pre><code><r:assets:top_padding container = "140" [size="icon"]/></code></pre>
|
25
|
+
|
26
|
+
*Working Example*:
|
27
|
+
<pre><code>
|
28
|
+
<ul>
|
29
|
+
<r:assets:each>
|
30
|
+
<li style="height:140px">
|
31
|
+
<img style="padding-top:<r:top_padding size='category' container='140' />px"
|
32
|
+
src="<r:url />" alt="<r:title />" />
|
33
|
+
</li>
|
34
|
+
</r:assets:each>
|
35
|
+
</ul>
|
36
|
+
</code></pre>
|
37
|
+
}
|
38
|
+
tag 'assets:top_padding' do |tag|
|
39
|
+
asset, options = asset_and_options(tag)
|
40
|
+
raise TagError, 'Asset is not an image' unless asset.image?
|
41
|
+
raise TagError, "'container' attribute required" unless options['container']
|
42
|
+
size = options['size'] ? options.delete('size') : 'icon'
|
43
|
+
container = options.delete('container')
|
44
|
+
img_height = asset.height(size)
|
45
|
+
(container.to_i - img_height.to_i)/2
|
46
|
+
end
|
47
|
+
|
48
|
+
['height','width'].each do |dimension|
|
49
|
+
desc %{
|
50
|
+
Renders the #{dimension} of the asset.
|
51
|
+
}
|
52
|
+
tag "assets:#{dimension}" do |tag|
|
53
|
+
asset, options = asset_and_options(tag)
|
54
|
+
unless asset.dimensions_known?
|
55
|
+
raise TagError, "Can't determine #{dimension} for this Asset. It may not be a supported type."
|
56
|
+
end
|
57
|
+
size = options['size'] ? options.delete('size') : 'original'
|
58
|
+
asset.send(dimension, size)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
desc %{
|
63
|
+
Renders the containing elements only if the asset's content type matches
|
64
|
+
the regular expression given in the @matches@ attribute. If the
|
65
|
+
@ignore_case@ attribute is set to false, the match is case sensitive. By
|
66
|
+
default, @ignore_case@ is set to true.
|
67
|
+
|
68
|
+
The @title@ attribute is required on the parent tag unless this tag is used in @assets:each@.
|
69
|
+
|
70
|
+
*Usage:*
|
71
|
+
<pre><code><r:assets:each><r:if_content_type matches="regexp" [ignore_case=true|false"]>...</r:if_content_type></r:assets:each></code></pre>
|
72
|
+
}
|
73
|
+
tag 'assets:if_content_type' do |tag|
|
74
|
+
options = tag.attr.dup
|
75
|
+
# XXX build_regexp_for comes from StandardTags
|
76
|
+
# XXX its cool if I use it, right?
|
77
|
+
regexp = build_regexp_for(tag,options)
|
78
|
+
asset_content_type = tag.locals.asset.asset_content_type
|
79
|
+
tag.expand unless asset_content_type.match(regexp).nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
#TODO: could use better docs for Asset#other? case explaining what types it covers
|
83
|
+
Asset.known_types.each do |known_type|
|
84
|
+
desc %{
|
85
|
+
Renders the contents only of the asset is of the type #{known_type}
|
86
|
+
}
|
87
|
+
tag "assets:if_#{known_type}" do |tag|
|
88
|
+
tag.expand if find_asset(tag, tag.attr.dup).send("#{known_type}?".to_sym)
|
89
|
+
end
|
90
|
+
|
91
|
+
desc %{
|
92
|
+
Renders the contents only of the asset is not of the type #{known_type}
|
93
|
+
}
|
94
|
+
tag "assets:unless_#{known_type}" do |tag|
|
95
|
+
tag.expand unless find_asset(tag, tag.attr.dup).send("#{known_type}?".to_sym)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
[:title, :caption, :asset_file_name, :asset_content_type, :asset_file_size, :id].each do |method|
|
100
|
+
desc %{
|
101
|
+
Renders the @#{method.to_s}@ attribute of the asset
|
102
|
+
}
|
103
|
+
tag "assets:#{method.to_s}" do |tag|
|
104
|
+
asset, options = asset_and_options(tag)
|
105
|
+
asset.send(method) rescue nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
tag 'assets:filename' do |tag|
|
110
|
+
asset, options = asset_and_options(tag)
|
111
|
+
asset.asset_file_name rescue nil
|
112
|
+
end
|
113
|
+
|
114
|
+
desc %{
|
115
|
+
Renders an image tag for the asset.
|
116
|
+
|
117
|
+
Using the optional @size@ attribute, different sizes can be display.
|
118
|
+
“thumbnail” and “icon” sizes are built in, but custom ones can be set
|
119
|
+
using by changing assets.addition_thumbnails in the Radiant::Config
|
120
|
+
settings.
|
121
|
+
|
122
|
+
*Usage:*
|
123
|
+
<pre><code><r:assets:image [title="asset_title"] [size="icon|thumbnail"]></code></pre>
|
124
|
+
}
|
125
|
+
tag 'assets:image' do |tag|
|
126
|
+
asset, options = asset_and_options(tag)
|
127
|
+
raise TagError, 'Asset is not an image' unless asset.image?
|
128
|
+
size = options['size'] ? options.delete('size') : 'original'
|
129
|
+
geometry = options['geometry'] ? options.delete('geometry') : nil
|
130
|
+
#This is very experimental and will generate new sizes on the fly
|
131
|
+
asset.generate_style(size, { :size => geometry }) if geometry
|
132
|
+
|
133
|
+
alt = " alt='#{asset.title}'" unless tag.attr['alt'] rescue nil
|
134
|
+
attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
|
135
|
+
attributes << alt unless alt.nil?
|
136
|
+
url = asset.thumbnail(size)
|
137
|
+
%{<img src="#{url}" #{attributes unless attributes.empty?} />} rescue nil
|
138
|
+
end
|
139
|
+
|
140
|
+
desc %{
|
141
|
+
Embeds a flash-movie in a cross-browser-compatible fashion using only HTML
|
142
|
+
If no width and height attributes are given it will use the intrinsic
|
143
|
+
dimensions of the swf file
|
144
|
+
|
145
|
+
*Usage:*
|
146
|
+
<pre><code><r:assets:flash [title="asset_title"] [width="100"] [height="100"]>Fallback content</flash></code></pre>
|
147
|
+
|
148
|
+
*Example with text fallback:*
|
149
|
+
<pre><code><r:assets:flash title="flash_movie">
|
150
|
+
Sorry, you need to have flash installed, <a href="http://adobe.com/flash">get it here</a>
|
151
|
+
</flash></code></pre>
|
152
|
+
|
153
|
+
*Example with image fallback and explicit dimensions:*
|
154
|
+
<pre><code><r:assets:flash title="flash_movie" width="300" height="200">
|
155
|
+
<r:assets:image title="flash_screenshot" />
|
156
|
+
</flash></code></pre>
|
157
|
+
}
|
158
|
+
tag 'assets:flash' do |tag|
|
159
|
+
asset, options = asset_and_options(tag)
|
160
|
+
raise TagError, 'Must be flash' unless asset.swf?
|
161
|
+
url = asset.thumbnail('original')
|
162
|
+
dimensions = [(tag.attr['width'] || asset.width),(tag.attr['height'] || asset.height)]
|
163
|
+
swf_embed_markup url, dimensions, tag.expand
|
164
|
+
end
|
165
|
+
|
166
|
+
tag 'assets:thumbnail' do |tag|
|
167
|
+
asset, options = asset_and_options(tag)
|
168
|
+
asset.generate_thumbnail('test', ['24x24#',nil])
|
169
|
+
asset.save
|
170
|
+
end
|
171
|
+
|
172
|
+
desc %{
|
173
|
+
Renders the url for the asset. If the asset is an image, the <code>size</code> attribute can be used to
|
174
|
+
generate the url for that size.
|
175
|
+
|
176
|
+
*Usage:*
|
177
|
+
<pre><code><r:url [title="asset_title"] [size="icon|thumbnail"]></code></pre>
|
178
|
+
}
|
179
|
+
tag 'assets:url' do |tag|
|
180
|
+
asset, options = asset_and_options(tag)
|
181
|
+
size = options['size'] ? options.delete('size') : 'original'
|
182
|
+
asset.thumbnail(size) rescue nil
|
183
|
+
end
|
184
|
+
|
185
|
+
desc %{
|
186
|
+
Renders a link to the asset. If the asset is an image, the <code>size</code> attribute can be used to
|
187
|
+
generate a link to that size.
|
188
|
+
|
189
|
+
*Usage:*
|
190
|
+
<pre><code><r:assets:link [title="asset_title"] [size="icon|thumbnail"] /></code></pre>
|
191
|
+
}
|
192
|
+
tag 'assets:link' do |tag|
|
193
|
+
asset, options = asset_and_options(tag)
|
194
|
+
size = options['size'] ? options.delete('size') : 'original'
|
195
|
+
text = options['text'] || asset.title
|
196
|
+
anchor = options['anchor'] ? "##{options.delete('anchor')}" : ''
|
197
|
+
attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
|
198
|
+
attributes = " #{attributes}" unless attributes.empty?
|
199
|
+
text = tag.double? ? tag.expand : text
|
200
|
+
url = asset.thumbnail(size)
|
201
|
+
%{<a href="#{url }#{anchor}"#{attributes}>#{text}</a>} rescue nil
|
202
|
+
end
|
203
|
+
|
204
|
+
desc %{
|
205
|
+
Renders the extension of the asset, as extracted from its filename.
|
206
|
+
|
207
|
+
*Usage*:
|
208
|
+
<pre><code>
|
209
|
+
<ul>
|
210
|
+
<r:assets:each extensions="doc|pdf">
|
211
|
+
<li class="<r:extension/>">
|
212
|
+
<r:link/>
|
213
|
+
</li>
|
214
|
+
</r:assets:each>
|
215
|
+
</ul>
|
216
|
+
</code></pre>
|
217
|
+
}
|
218
|
+
tag 'assets:extension' do |tag|
|
219
|
+
raise TagError, 'must be nested inside an assets or assets:each tag' unless tag.locals.asset
|
220
|
+
asset = tag.locals.asset
|
221
|
+
asset.asset_file_name[/\.(\w+)$/, 1]
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
def asset_and_options(tag)
|
226
|
+
options = tag.attr.dup
|
227
|
+
[find_asset(tag, options), options]
|
228
|
+
end
|
229
|
+
|
230
|
+
def find_asset(tag, options)
|
231
|
+
raise TagError, "'title' attribute required" unless title = options.delete('title') or id = options.delete('id') or tag.locals.asset
|
232
|
+
tag.locals.asset || Asset.find_by_title(title) || Asset.find(id)
|
233
|
+
end
|
234
|
+
|
235
|
+
def assets_find_options(tag)
|
236
|
+
attr = tag.attr.symbolize_keys
|
237
|
+
extensions = attr[:extensions] && attr[:extensions].split('|') || []
|
238
|
+
conditions = unless extensions.blank?
|
239
|
+
[ extensions.map { |ext| "assets.asset_file_name LIKE ?"}.join(' OR '),
|
240
|
+
*extensions.map { |ext| "%.#{ext}" } ]
|
241
|
+
else
|
242
|
+
nil
|
243
|
+
end
|
244
|
+
|
245
|
+
by = attr[:by] || 'page_attachments.position'
|
246
|
+
order = attr[:order] || 'asc'
|
247
|
+
|
248
|
+
options = {
|
249
|
+
:order => "#{by} #{order}",
|
250
|
+
:limit => attr[:limit] || nil,
|
251
|
+
:offset => attr[:offset] || nil,
|
252
|
+
:conditions => conditions
|
253
|
+
}
|
254
|
+
end
|
255
|
+
|
256
|
+
def swf_embed_markup(url, dimensions, fallback_content)
|
257
|
+
width, height = dimensions
|
258
|
+
%{<!--[if !IE]> -->
|
259
|
+
<object type="application/x-shockwave-flash" data="#{url}" width="#{width}" height="#{height}">
|
260
|
+
<!-- <![endif]-->
|
261
|
+
<!--[if IE]>
|
262
|
+
<object width="#{width}" height="#{height}"
|
263
|
+
classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
|
264
|
+
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0">
|
265
|
+
<param name="movie" value="#{url}" />
|
266
|
+
<!-->
|
267
|
+
#{fallback_content}
|
268
|
+
</object>
|
269
|
+
<!-- <![endif]-->}
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|