trusty-cms 3.1.11 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +9 -14
- data/Gemfile.lock +84 -70
- data/README.md +4 -0
- data/app/assets/images/admin/add.png +0 -0
- data/app/assets/images/admin/archive_icon.png +0 -0
- data/app/assets/images/admin/audio_icon.png +0 -0
- data/app/assets/images/admin/audio_thumbnail.png +0 -0
- data/app/assets/images/admin/c_icon.png +0 -0
- data/app/assets/images/admin/copy.png +0 -0
- data/app/assets/images/admin/css_icon.png +0 -0
- data/app/assets/images/admin/database_icon.png +0 -0
- data/app/assets/images/admin/delete.png +0 -0
- data/app/assets/images/admin/document_icon.png +0 -0
- data/app/assets/images/admin/document_thumbnail.png +0 -0
- data/app/assets/images/admin/flash_icon.png +0 -0
- data/app/assets/images/admin/flash_thumbnail.png +0 -0
- data/app/assets/images/admin/font_icon.png +0 -0
- data/app/assets/images/admin/gzip_icon.png +0 -0
- data/app/assets/images/admin/html_icon.png +0 -0
- data/app/assets/images/admin/image_icon.png +0 -0
- data/app/assets/images/admin/image_thumbnail.png +0 -0
- data/app/assets/images/admin/java_icon.png +0 -0
- data/app/assets/images/admin/page_edit.png +0 -0
- data/app/assets/images/admin/pdf_icon.png +0 -0
- data/app/assets/images/admin/pdf_thumbnail.png +0 -0
- data/app/assets/images/admin/perl_icon.png +0 -0
- data/app/assets/images/admin/php_icon.png +0 -0
- data/app/assets/images/admin/presentation_icon.png +0 -0
- data/app/assets/images/admin/python_icon.png +0 -0
- data/app/assets/images/admin/reorder_assets.png +0 -0
- data/app/assets/images/admin/ruby_icon.png +0 -0
- data/app/assets/images/admin/script_icon.png +0 -0
- data/app/assets/images/admin/spinner.gif +0 -0
- data/app/assets/images/admin/spreadsheet_icon.png +0 -0
- data/app/assets/images/admin/tar_icon.png +0 -0
- data/app/assets/images/admin/unknown_icon.png +0 -0
- data/app/assets/images/admin/unknown_thumbnail.png +0 -0
- data/app/assets/images/admin/video_icon.png +0 -0
- data/app/assets/images/admin/video_thumbnail.png +0 -0
- data/app/assets/images/admin/xml_icon.png +0 -0
- data/app/assets/images/admin/zip_icon.png +0 -0
- data/app/assets/javascripts/admin/assets.js +268 -0
- data/app/assets/javascripts/admin/assets_admin.js +16 -0
- data/app/assets/javascripts/admin/jquery.fileupload.js +1457 -0
- data/app/assets/javascripts/admin/jquery.iframe-transport.js +214 -0
- data/app/assets/javascripts/admin/jquery.ui.widget.js +558 -0
- data/app/assets/stylesheets/admin/assets.scss +283 -0
- data/app/controllers/admin/assets_controller.rb +88 -0
- data/app/controllers/admin/page_attachments_controller.rb +23 -0
- data/app/helpers/admin/assets_helper.rb +16 -0
- data/app/helpers/admin/page_attachments_helper.rb +2 -0
- data/app/models/asset.rb +251 -0
- data/app/models/asset_type.rb +239 -0
- data/app/models/old_page_attachment.rb +26 -0
- data/app/models/page_attachment.rb +20 -0
- data/app/views/admin/assets/_asset.html.haml +12 -0
- data/app/views/admin/assets/_asset_table.html.haml +30 -0
- data/app/views/admin/assets/_errors.html.haml +3 -0
- data/app/views/admin/assets/_form.html.haml +18 -0
- data/app/views/admin/assets/_page_assets.html.haml +12 -0
- data/app/views/admin/assets/_search.html.haml +17 -0
- data/app/views/admin/assets/_search_results.html.haml +17 -0
- data/app/views/admin/assets/edit.html.haml +40 -0
- data/app/views/admin/assets/index.html.haml +15 -0
- data/app/views/admin/assets/new.html.haml +24 -0
- data/app/views/admin/assets/refresh.html.haml +14 -0
- data/app/views/admin/assets/remove.html.haml +16 -0
- data/app/views/admin/configuration/_clipped_edit.html.haml +8 -0
- data/app/views/admin/configuration/_clipped_show.html.haml +12 -0
- data/app/views/admin/page_attachments/_attachment.html.haml +25 -0
- data/app/views/admin/pages/_asset_popups.html.haml +37 -0
- data/app/views/admin/pages/_assets.html.haml +13 -0
- data/app/views/admin/removed/_assets_bucket.html.haml +8 -0
- data/app/views/admin/removed/_assets_container.html.haml +54 -0
- data/app/views/admin/removed/_bucket.html.haml +11 -0
- data/app/views/admin/removed/_bucket_asset.html.haml +9 -0
- data/app/views/admin/removed/_show_bucket_link.html.haml +4 -0
- data/app/views/admin/removed/_upload_to_page.html.haml +12 -0
- data/app/views/admin/removed/bucket/_iframe.html.haml +1 -0
- data/config/application.rb +0 -37
- data/config/initializers/assets.rb +1 -0
- data/config/initializers/interpolation.rb +6 -0
- data/config/initializers/kraken.rb +7 -0
- data/config/initializers/trusty_cms_config.rb +67 -0
- data/config/locales/en.yml +101 -0
- data/config/routes.rb +14 -3
- data/coverage/index.html +26 -26
- data/db/migrate/028_create_assets.rb +12 -0
- data/db/migrate/029_create_paperclip_attributes.rb +13 -0
- data/db/migrate/030_create_user_observer.rb +13 -0
- data/db/migrate/031_create_page_attachments.rb +19 -0
- data/db/migrate/032_rename_users.rb +13 -0
- data/db/migrate/20110513205050_asset_uuid.rb +11 -0
- data/db/migrate/20110606111250_update_configuration.rb +34 -0
- data/db/migrate/20110609101438_dimensions.rb +13 -0
- data/lib/trusty_cms.rb +1 -1
- data/spec/dummy/db/schema.rb +22 -0
- data/spec/dummy/log/test.log +95620 -0
- data/spec/dummy/tmp/cache/747/A70/TrustyCms%3A%3AConfig +0 -0
- data/spec/dummy/tmp/cache/85C/FA0/TrustyCms.cache_mtime +0 -0
- data/spec/dummy/tmp/capybara/capybara-20180312152412265881245.html +262 -0
- data/spec/dummy/tmp/capybara/capybara-201803121526139973682798.html +149 -0
- data/spec/factories/layout.rb +1 -1
- data/spec/factories/page.rb +3 -3
- data/spec/factories/page_part.rb +1 -1
- data/spec/factories/user.rb +1 -1
- data/spec/models/layout_spec.rb +6 -6
- data/spec/spec_helper.rb +4 -4
- data/trusty_cms.gemspec +27 -22
- data/vendor/extensions/clipped-extension/clipped_extension.rb +40 -0
- data/vendor/extensions/clipped-extension/lib/asset_tags.rb +346 -0
- data/vendor/extensions/clipped-extension/lib/clipped/engine.rb +11 -0
- data/vendor/extensions/clipped-extension/lib/clipped_admin_ui.rb +28 -0
- data/vendor/extensions/clipped-extension/lib/generators/templates/clipped_config.rb +53 -0
- data/vendor/extensions/clipped-extension/lib/page_asset_associations.rb +13 -0
- data/vendor/extensions/clipped-extension/lib/paperclip/frame_grab.rb +73 -0
- data/vendor/extensions/clipped-extension/lib/paperclip/geometry_transformation.rb +80 -0
- data/vendor/extensions/clipped-extension/lib/tasks/clipped_extension_tasks.rake +124 -0
- data/vendor/extensions/clipped-extension/lib/tasks/paperclip_tasks.rake +79 -0
- data/vendor/extensions/clipped-extension/lib/trusty-clipped-extension.rb +2 -0
- data/vendor/extensions/clipped-extension/lib/trusty_cms_clipped_extension/cloud.rb +41 -0
- metadata +219 -67
- data/config/initializers/ckeditor.rb +0 -3
- data/config/initializers/haml.rb +0 -2
- data/config/initializers/rails_patch.rb +0 -2
- data/config/initializers/string_extensions.rb +0 -1
- data/config/initializers/symbol_extensions.rb +0 -1
@@ -0,0 +1,346 @@
|
|
1
|
+
module AssetTags
|
2
|
+
include TrustyCms::Taggable
|
3
|
+
include ActionView::Helpers::TagHelper
|
4
|
+
include ActionView::Helpers::AssetTagHelper
|
5
|
+
|
6
|
+
class TagError < StandardError; end
|
7
|
+
|
8
|
+
%w{top_padding width height caption asset_file_name asset_content_type asset_file_size id filename image flash thumbnail url link extension if_content_type page:title page:url}.each do |name|
|
9
|
+
deprecated_tag "assets:#{name}", :substitute => "asset:#{name}", :deadline => '2.0'
|
10
|
+
end
|
11
|
+
|
12
|
+
desc %{
|
13
|
+
The namespace for referencing images and assets.
|
14
|
+
|
15
|
+
*Usage:*
|
16
|
+
<pre><code><r:asset [name="asset name"]>...</r:asset></code></pre>
|
17
|
+
}
|
18
|
+
tag 'asset' do |tag|
|
19
|
+
tag.locals.asset = find_asset(tag, tag.attr) unless tag.attr.empty?
|
20
|
+
tag.expand
|
21
|
+
end
|
22
|
+
|
23
|
+
desc %{
|
24
|
+
Cycles through all assets attached to the current page.
|
25
|
+
This tag does not require the name atttribute, nor do any of its children.
|
26
|
+
Use the @limit@ and @offset@ attribute to render a specific number of assets.
|
27
|
+
Use @by@ and @order@ attributes to control the order of assets.
|
28
|
+
Use @extensions@ attribute to specify which assets to be rendered.
|
29
|
+
|
30
|
+
*Usage:*
|
31
|
+
<pre><code><r:assets:each [limit=0] [offset=0] [order="asc|desc"] [by="position|title|..."] [extensions="png|pdf|doc"]>...</r:assets:each></code></pre>
|
32
|
+
}
|
33
|
+
tag 'assets' do |tag|
|
34
|
+
tag.expand
|
35
|
+
end
|
36
|
+
tag 'assets:each' do |tag|
|
37
|
+
options = tag.attr.dup
|
38
|
+
tag.locals.assets = tag.locals.page.assets.scoped(assets_find_options(tag))
|
39
|
+
tag.render('asset_list', tag.attr.dup, &tag.block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# General purpose paginated asset lister. Very useful dryness.
|
43
|
+
# Tag.locals.assets must be defined but can be empty.
|
44
|
+
|
45
|
+
tag 'asset_list' do |tag|
|
46
|
+
raise TagError, "r:asset_list: no assets to list" unless tag.locals.assets
|
47
|
+
options = tag.attr.symbolize_keys
|
48
|
+
result = ""
|
49
|
+
paging = pagination_find_options(tag)
|
50
|
+
assets = paging ? tag.locals.assets.paginate(paging) : tag.locals.assets.all
|
51
|
+
assets.each do |asset|
|
52
|
+
tag.locals.asset = asset
|
53
|
+
result << tag.expand
|
54
|
+
end
|
55
|
+
if paging && assets.total_pages > 1
|
56
|
+
tag.locals.paginated_list = assets
|
57
|
+
result << tag.render('pagination', tag.attr.dup)
|
58
|
+
end
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
desc %{
|
63
|
+
References the first asset attached to the current page.
|
64
|
+
|
65
|
+
*Usage:*
|
66
|
+
<pre><code><r:assets:first>...</r:assets:first></code></pre>
|
67
|
+
}
|
68
|
+
tag 'assets:first' do |tag|
|
69
|
+
if tag.locals.asset = tag.locals.page.assets.first
|
70
|
+
tag.expand
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
desc %{
|
75
|
+
Renders the contained elements only if the current contextual page has one or
|
76
|
+
more assets. The @min_count@ attribute specifies the minimum number of required
|
77
|
+
assets. You can also filter by extensions with the @extensions@ attribute.
|
78
|
+
|
79
|
+
*Usage:*
|
80
|
+
<pre><code><r:if_assets [min_count="n"]>...</r:if_assets></code></pre>
|
81
|
+
}
|
82
|
+
tag 'if_assets' do |tag|
|
83
|
+
count = tag.attr['min_count'] && tag.attr['min_count'].to_i || 1
|
84
|
+
assets = tag.locals.page.assets.count(:conditions => assets_find_options(tag)[:conditions])
|
85
|
+
tag.expand if assets >= count
|
86
|
+
end
|
87
|
+
|
88
|
+
desc %{
|
89
|
+
The opposite of @<r:if_assets/>@.
|
90
|
+
}
|
91
|
+
tag 'unless_assets' do |tag|
|
92
|
+
count = tag.attr['min_count'] && tag.attr['min_count'].to_i || 1
|
93
|
+
assets = tag.locals.page.assets.count(:conditions => assets_find_options(tag)[:conditions])
|
94
|
+
tag.expand unless assets >= count
|
95
|
+
end
|
96
|
+
|
97
|
+
# Resets the page Url and title within the asset tag
|
98
|
+
[:title, :url].each do |method|
|
99
|
+
tag "asset:page:#{method.to_s}" do |tag|
|
100
|
+
tag.locals.page.send(method)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
desc %{
|
111
|
+
Renders the value for a top padding for a thumbnail. Put the thumbnail in a
|
112
|
+
container with specified height and using this tag you can vertically
|
113
|
+
align the image within its container.
|
114
|
+
|
115
|
+
*Usage*:
|
116
|
+
<pre><code><r:asset:top_padding container = "140" [size="icon"]/></code></pre>
|
117
|
+
|
118
|
+
*Working Example*:
|
119
|
+
<pre><code>
|
120
|
+
<ul>
|
121
|
+
<r:asset:each>
|
122
|
+
<li style="height:140px">
|
123
|
+
<img style="padding-top:<r:top_padding size='category' container='140' />px"
|
124
|
+
src="<r:url />" alt="<r:title />" />
|
125
|
+
</li>
|
126
|
+
</r:asset:each>
|
127
|
+
</ul>
|
128
|
+
</code></pre>
|
129
|
+
}
|
130
|
+
tag 'asset:top_padding' do |tag|
|
131
|
+
asset, options = asset_and_options(tag)
|
132
|
+
raise TagError, "'container' attribute required" unless options['container']
|
133
|
+
size = options['size'] ? options.delete('size') : 'icon'
|
134
|
+
raise TagError, "asset #{tag.locals.asset.title} has no '#{size}' thumbnail" unless tag.locals.asset.has_style?(size)
|
135
|
+
container = options.delete('container')
|
136
|
+
((container.to_i - asset.height(size).to_i)/2).to_s
|
137
|
+
end
|
138
|
+
|
139
|
+
['height','width'].each do |dimension|
|
140
|
+
desc %{
|
141
|
+
Renders the #{dimension} of the asset.
|
142
|
+
}
|
143
|
+
tag "asset:#{dimension}" do |tag|
|
144
|
+
asset, options = asset_and_options(tag)
|
145
|
+
unless asset.dimensions_known?
|
146
|
+
raise TagError, "Can't determine #{dimension} for this Asset. It may not be a supported type."
|
147
|
+
end
|
148
|
+
size = options['size'] ? options.delete('size') : 'original'
|
149
|
+
asset.send(dimension, size)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
desc %{
|
154
|
+
Returns a string representing the orientation of the asset, which must be imageable.
|
155
|
+
(ie, it is an image or it has been processed to produce an image).
|
156
|
+
Can be 'horizontal', 'vertical' or 'square'.
|
157
|
+
}
|
158
|
+
tag 'asset:orientation' do |tag|
|
159
|
+
asset, options = asset_and_options(tag)
|
160
|
+
size = options['size'] ? options.delete('size') : 'original'
|
161
|
+
raise TagError, "asset #{tag.locals.asset.title} has no '#{size}' thumbnail" unless tag.locals.asset.has_style?(size)
|
162
|
+
asset.orientation(size)
|
163
|
+
end
|
164
|
+
|
165
|
+
desc %{
|
166
|
+
Returns the aspect ratio of the asset, which must be an image.
|
167
|
+
}
|
168
|
+
tag 'asset:aspect' do |tag|
|
169
|
+
asset, options = asset_and_options(tag)
|
170
|
+
size = options['size'] ? options.delete('size') : 'original'
|
171
|
+
raise TagError, "asset #{tag.locals.asset.title} has no '#{size}' thumbnail" unless tag.locals.asset.has_style?(size)
|
172
|
+
asset.aspect(size)
|
173
|
+
end
|
174
|
+
|
175
|
+
desc %{
|
176
|
+
Renders the containing elements only if the asset's content type matches
|
177
|
+
the regular expression given in the @matches@ attribute. If the
|
178
|
+
@ignore_case@ attribute is set to false, the match is case sensitive. By
|
179
|
+
default, @ignore_case@ is set to true.
|
180
|
+
|
181
|
+
The @name@ or @id@ attribute is required on the parent tag unless this tag is used in @asset:each@.
|
182
|
+
|
183
|
+
*Usage:*
|
184
|
+
<pre><code><r:asset:each><r:if_content_type matches="regexp" [ignore_case=true|false"]>...</r:if_content_type></r:asset:each></code></pre>
|
185
|
+
}
|
186
|
+
tag 'asset:if_content_type' do |tag|
|
187
|
+
options = tag.attr.dup
|
188
|
+
# XXX build_regexp_for comes from StandardTags
|
189
|
+
regexp = build_regexp_for(tag,options)
|
190
|
+
asset_content_type = tag.locals.asset.asset_content_type
|
191
|
+
tag.expand unless asset_content_type.match(regexp).nil?
|
192
|
+
end
|
193
|
+
|
194
|
+
#TODO: could use better docs for Asset#other? case explaining what types it covers
|
195
|
+
|
196
|
+
[:title, :caption, :asset_file_name, :extension, :asset_content_type, :asset_file_size, :id].each do |method|
|
197
|
+
desc %{
|
198
|
+
Renders the @#{method.to_s}@ attribute of the asset
|
199
|
+
}
|
200
|
+
tag "asset:#{method.to_s}" do |tag|
|
201
|
+
asset, options = asset_and_options(tag)
|
202
|
+
asset.send(method) rescue nil
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
tag 'asset:name' do |tag|
|
207
|
+
tag.render('asset:title', tag.attr.dup)
|
208
|
+
end
|
209
|
+
|
210
|
+
tag 'asset:filename' do |tag|
|
211
|
+
asset, options = asset_and_options(tag)
|
212
|
+
asset.asset_file_name rescue nil
|
213
|
+
end
|
214
|
+
|
215
|
+
desc %{
|
216
|
+
Renders an image tag for the asset.
|
217
|
+
|
218
|
+
Using the optional @size@ attribute, different sizes can be display.
|
219
|
+
"thumbnail" and "icon" sizes are built in, but custom ones can be set
|
220
|
+
by changing `assets.thumbnails.[type]` in the TrustyCms::Config settings.
|
221
|
+
|
222
|
+
*Usage:*
|
223
|
+
<pre><code><r:asset:image [name="asset name" or id="asset id"] [size="icon|thumbnail|whatever"]></code></pre>
|
224
|
+
}
|
225
|
+
tag 'asset:image' do |tag|
|
226
|
+
tag.locals.asset, options = asset_and_options(tag)
|
227
|
+
size = options.delete('size') || 'original'
|
228
|
+
raise TagError, "asset #{tag.locals.asset.title} has no '#{size}' thumbnail" unless tag.locals.asset.has_style?(size)
|
229
|
+
options['alt'] ||= tag.locals.asset.title
|
230
|
+
url = tag.locals.asset.thumbnail(size)
|
231
|
+
ActionController::Base.helpers.image_tag(url, options)
|
232
|
+
end
|
233
|
+
|
234
|
+
desc %{
|
235
|
+
Embeds a flash-movie in a cross-browser-compatible fashion using only HTML
|
236
|
+
If no width and height attributes are given it will use the intrinsic
|
237
|
+
dimensions of the swf file
|
238
|
+
|
239
|
+
*Usage:*
|
240
|
+
<pre><code><r:asset:flash [name="asset name" or id="asset id"] [width="100"] [height="100"]>Fallback content</flash></code></pre>
|
241
|
+
|
242
|
+
*Example with text fallback:*
|
243
|
+
<pre><code><r:asset:flash name="flash_movie">
|
244
|
+
Sorry, you need to have flash installed, <a href="http://adobe.com/flash">get it here</a>
|
245
|
+
</flash></code></pre>
|
246
|
+
|
247
|
+
*Example with image fallback and explicit dimensions:*
|
248
|
+
<pre><code><r:asset:flash name="flash_movie" width="300" height="200">
|
249
|
+
<r:asset:image name="flash_screenshot" />
|
250
|
+
</flash></code></pre>
|
251
|
+
}
|
252
|
+
tag 'asset:flash' do |tag|
|
253
|
+
asset, options = asset_and_options(tag)
|
254
|
+
if tag.locals.asset.flash?
|
255
|
+
url = asset.thumbnail('original')
|
256
|
+
dimensions = [(tag.attr['width'] || asset.width),(tag.attr['height'] || asset.height)]
|
257
|
+
swf_embed_markup url, dimensions, tag.expand
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
desc %{
|
262
|
+
Renders the url for the asset. If the asset is an image, the <code>size</code> attribute can be used to
|
263
|
+
generate the url for that size.
|
264
|
+
|
265
|
+
*Usage:*
|
266
|
+
<pre><code><r:url [name="asset name" or id="asset id"] [size="icon|thumbnail"]></code></pre>
|
267
|
+
}
|
268
|
+
tag 'asset:url' do |tag|
|
269
|
+
asset, options = asset_and_options(tag)
|
270
|
+
size = options['size'] ? options.delete('size') : 'original'
|
271
|
+
asset.thumbnail(size) rescue nil
|
272
|
+
end
|
273
|
+
|
274
|
+
desc %{
|
275
|
+
Renders a link to the asset. If the asset is an image, the <code>size</code> attribute can be used to
|
276
|
+
generate a link to that size.
|
277
|
+
|
278
|
+
*Usage:*
|
279
|
+
<pre><code><r:asset:link [name="asset name" or id="asset id"] [size="icon|thumbnail"] /></code></pre>
|
280
|
+
}
|
281
|
+
tag 'asset:link' do |tag|
|
282
|
+
asset, options = asset_and_options(tag)
|
283
|
+
size = options['size'] ? options.delete('size') : 'original'
|
284
|
+
text = options['text'] || asset.title
|
285
|
+
anchor = options['anchor'] ? "##{options.delete('anchor')}" : ''
|
286
|
+
attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
|
287
|
+
attributes = " #{attributes}" unless attributes.empty?
|
288
|
+
text = tag.double? ? tag.expand : text
|
289
|
+
url = asset.thumbnail(size)
|
290
|
+
%{<a href="#{url }#{anchor}"#{attributes}>#{text}</a>} rescue nil
|
291
|
+
end
|
292
|
+
|
293
|
+
private
|
294
|
+
def asset_and_options(tag)
|
295
|
+
options = tag.attr.dup
|
296
|
+
[find_asset(tag, options), options]
|
297
|
+
end
|
298
|
+
|
299
|
+
def find_asset(tag, options)
|
300
|
+
tag.locals.asset ||= if title = (options.delete('name') || options.delete('title'))
|
301
|
+
Asset.find_by_title(title)
|
302
|
+
elsif id = options.delete('id')
|
303
|
+
Asset.find_by_id(id)
|
304
|
+
end
|
305
|
+
tag.locals.asset || raise(TagError, "Asset not found.")
|
306
|
+
end
|
307
|
+
|
308
|
+
def assets_find_options(tag)
|
309
|
+
attr = tag.attr.symbolize_keys
|
310
|
+
extensions = attr[:extensions] && attr[:extensions].split('|') || []
|
311
|
+
conditions = unless extensions.blank?
|
312
|
+
# this is soon to be removed in favour of asset types
|
313
|
+
[ extensions.map { |ext| "assets.asset_file_name LIKE ?"}.join(' OR '),
|
314
|
+
*extensions.map { |ext| "%.#{ext}" } ]
|
315
|
+
else
|
316
|
+
nil
|
317
|
+
end
|
318
|
+
|
319
|
+
by = attr[:by] || 'page_attachments.position'
|
320
|
+
order = attr[:order] || 'asc'
|
321
|
+
|
322
|
+
options = {
|
323
|
+
:order => "#{by} #{order}",
|
324
|
+
:limit => attr[:limit] || nil,
|
325
|
+
:offset => attr[:offset] || nil,
|
326
|
+
:conditions => conditions
|
327
|
+
}
|
328
|
+
end
|
329
|
+
|
330
|
+
def swf_embed_markup(url, dimensions, fallback_content)
|
331
|
+
width, height = dimensions
|
332
|
+
%{<!--[if !IE]> -->
|
333
|
+
<object type="application/x-shockwave-flash" data="#{url}" width="#{width}" height="#{height}">
|
334
|
+
<!-- <![endif]-->
|
335
|
+
<!--[if IE]>
|
336
|
+
<object width="#{width}" height="#{height}"
|
337
|
+
classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
|
338
|
+
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0">
|
339
|
+
<param name="movie" value="#{url}" />
|
340
|
+
<!-->
|
341
|
+
#{fallback_content}
|
342
|
+
</object>
|
343
|
+
<!-- <![endif]-->}
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ClippedAdminUI
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval {
|
5
|
+
attr_accessor :asset
|
6
|
+
alias_method :assets, :asset
|
7
|
+
|
8
|
+
def load_default_asset_regions
|
9
|
+
OpenStruct.new.tap do |asset|
|
10
|
+
asset.edit = TrustyCms::AdminUI::RegionSet.new do |edit|
|
11
|
+
edit.main.concat %w{edit_header edit_form edit_regenerate}
|
12
|
+
edit.form.concat %w{edit_title edit_metadata}
|
13
|
+
end
|
14
|
+
asset.new = asset.edit
|
15
|
+
asset.index = TrustyCms::AdminUI::RegionSet.new do |index|
|
16
|
+
index.top.concat %w{filters}
|
17
|
+
index.bottom.concat %w{}
|
18
|
+
index.thead.concat %w{thumbnail_header content_type_header actions_header}
|
19
|
+
index.tbody.concat %w{thumbnail_cell title_cell content_type_cell actions_cell}
|
20
|
+
index.paginate
|
21
|
+
end
|
22
|
+
asset.remove = asset.index
|
23
|
+
end
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
TrustyCms.config do |config|
|
2
|
+
|
3
|
+
# Uncomment and change the settings below to customize the Clipped extension
|
4
|
+
|
5
|
+
# The default settings
|
6
|
+
# config["paperclip.url"] = "/system/:attachment/:id/:style/:basename:no_original_style.:extension"
|
7
|
+
# config["paperclip.path"] = ":rails_root/public/system/:attachment/:id/:style/:basename:no_original_style.:extension"
|
8
|
+
# config["paperclip.storage"] = "filesystem"
|
9
|
+
# config["paperclip.skip_filetype_validation"] = true
|
10
|
+
# config["assets.max_asset_size"] = 5 # megabytes
|
11
|
+
# config["assets.display_size"] = "normal"
|
12
|
+
# config["assets.insertion_size"] = "normal"
|
13
|
+
# config["assets.create_image_thumbnails?"] = true
|
14
|
+
# config["assets.create_video_thumbnails?"] = true
|
15
|
+
# config["assets.create_pdf_thumbnails?"] = true
|
16
|
+
# Check http://www.imagemagick.org/script/command-line-processing.php#geometry
|
17
|
+
# for more details on ImageMagick settings for thumbnail generation
|
18
|
+
# config["assets.thumbnails.image"] = "normal:size=640x640>|small:size=320x320>"
|
19
|
+
# config["assets.thumbnails.video"] = "normal:size=640x640>,format=jpg|small:size=320x320>,format=jpg"
|
20
|
+
# config["assets.thumbnails.pdf"] = "normal:size=640x640>,format=jpg|small:size=320x320>,format=jpg"
|
21
|
+
|
22
|
+
# An example of using Amazon S3
|
23
|
+
# add `gem "fog", "~> 1.0"` to your Gemfile and run `bundle install`
|
24
|
+
# config["paperclip.storage"] = "fog"
|
25
|
+
# config["paperclip.path"] = ":attachment/:id/:style/:basename:no_original_style.:extension"
|
26
|
+
# config["paperclip.fog.provider"] = "AWS"
|
27
|
+
# config["paperclip.fog.directory"] = "bucket-name"
|
28
|
+
# config["paperclip.s3.key"] = "S3_KEY"
|
29
|
+
# config["paperclip.s3.secret"] = "S3_SECRET"
|
30
|
+
# optionally use a custom domain name; requires a CNAME DNS record
|
31
|
+
# config["paperclip.fog.host"] = "http://assets.example.com"
|
32
|
+
# optionally set the S3 region of your bucket; defaults to US East
|
33
|
+
# Asia North East => ap-northeast-1
|
34
|
+
# Asia South East => ap-southeast-1
|
35
|
+
# EU West => eu-west-1
|
36
|
+
# US East => us-east-1
|
37
|
+
# US West => us-west-1
|
38
|
+
# config["paperclip.s3.region"] = "us-east-1"
|
39
|
+
|
40
|
+
# An example of using Rackspace Cloud Files
|
41
|
+
# add `gem "fog", "~> 1.0"` to your Gemfile and run `bundle install`
|
42
|
+
# config["paperclip.storage"] = "fog"
|
43
|
+
# config["paperclip.path"] = ":attachment/:id/:style/:basename:no_original_style.:extension"
|
44
|
+
# config["paperclip.fog.provider"] = "Rackspace"
|
45
|
+
# config["paperclip.fog.directory"] = "container-name"
|
46
|
+
# config["paperclip.rackspace.username"] = "RACKSPACE_USERNAME"
|
47
|
+
# config["paperclip.rackspace.api_key"] = "RACKSPACE_API_KEY"
|
48
|
+
# paperclip.fog.host is your Cloud Files CDN URL
|
49
|
+
# config["paperclip.fog.host"] = "http://a.b.c.rackcdn.com"
|
50
|
+
# optionally use a custom domain name; requires a CNAME DNS record
|
51
|
+
# config["paperclip.fog.host"] = "http://assets.example.com"
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PageAssetAssociations
|
2
|
+
|
3
|
+
#TODO: Turn page_attachments into a generic, polymorphic asset-attachment mechanism
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval {
|
7
|
+
has_many :page_attachments, -> { order 'position desc' }
|
8
|
+
has_many :assets,{ :through => :page_attachments}, -> { order 'page_attachments.position ASC' }
|
9
|
+
accepts_nested_attributes_for :page_attachments, :allow_destroy => true
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class FrameGrab < Processor
|
3
|
+
|
4
|
+
attr_accessor :time_offset, :current_geometry, :target_geometry, :whiny, :current_format, :target_format
|
5
|
+
|
6
|
+
def initialize(file, options = {}, attachment = nil)
|
7
|
+
super
|
8
|
+
@file = file
|
9
|
+
@time_offset = options[:time_offset] || '-4'
|
10
|
+
geometry = options[:geometry]
|
11
|
+
unless geometry.blank?
|
12
|
+
@crop = geometry[-1,1] == '#'
|
13
|
+
@target_geometry = Geometry.parse(geometry)
|
14
|
+
@current_geometry = Geometry.parse(video_dimensions(file))
|
15
|
+
end
|
16
|
+
@current_format = File.extname(@file.path)
|
17
|
+
@target_format = options[:format] || 'jpg'
|
18
|
+
@basename = File.basename(@file.path, @current_format)
|
19
|
+
@whiny = options[:whiny].nil? ? true : options[:whiny]
|
20
|
+
end
|
21
|
+
|
22
|
+
def crop?
|
23
|
+
!!@crop
|
24
|
+
end
|
25
|
+
|
26
|
+
def make
|
27
|
+
src = @file
|
28
|
+
dst = Tempfile.new([ @basename, @target_format ].compact.join("."))
|
29
|
+
dst.binmode
|
30
|
+
|
31
|
+
begin
|
32
|
+
# grab frame at offset
|
33
|
+
cmd = %Q[-itsoffset #{time_offset} -i :source -y -vcodec mjpeg -vframes 1 -an -f rawvideo ]
|
34
|
+
|
35
|
+
# if scale-and-crop parameters can be calculated, we pipe to convert for resizing
|
36
|
+
if scale_and_crop = transformation_options
|
37
|
+
cmd << %{pipe: | convert #{scale_and_crop} - #{target_format}:- }
|
38
|
+
|
39
|
+
# otherwise we let ffmpeg resize the to the right size without preserving aspect ratio
|
40
|
+
else
|
41
|
+
cmd << %{-s #{target_geometry} pipe: }
|
42
|
+
end
|
43
|
+
|
44
|
+
# then pipe to composite to overlay video icon
|
45
|
+
cmd << %{| composite -gravity center :icon - :dest }
|
46
|
+
|
47
|
+
Paperclip.run('ffmpeg', cmd, :source => File.expand_path(src.path), :dest => File.expand_path(dst.path), :icon => AssetType.find(:video).icon_path, :swallow_stderr => false)
|
48
|
+
rescue PaperclipCommandLineError => e
|
49
|
+
raise PaperclipError, "There was an error processing the thumbnail for #{@basename}: #{e}" if whiny
|
50
|
+
end
|
51
|
+
|
52
|
+
dst
|
53
|
+
end
|
54
|
+
|
55
|
+
# get video dimensions in nasty hacky way
|
56
|
+
def video_dimensions(file)
|
57
|
+
dim = Paperclip.run('ffmpeg', '-i :source 2>&1', :source => File.expand_path(file.path), :expected_outcodes => [0,1], :swallow_stderr => false)
|
58
|
+
$1 if dim =~ /(\d+x\d+)/
|
59
|
+
end
|
60
|
+
|
61
|
+
# Duplicated from Thumbnail. We can't just subclass because of assumed compatibility with Geometry.from_file
|
62
|
+
def transformation_options
|
63
|
+
if current_geometry
|
64
|
+
scale, crop = current_geometry.transformation_to(target_geometry, crop?)
|
65
|
+
trans = []
|
66
|
+
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
|
67
|
+
trans << "-crop" << %["#{crop}"] << "+repage" if crop
|
68
|
+
trans.join(" ")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Paperclip
|
2
|
+
|
3
|
+
class TransformationError < PaperclipError; end
|
4
|
+
class StyleError < PaperclipError; end
|
5
|
+
|
6
|
+
class Geometry
|
7
|
+
|
8
|
+
# Returns a new Geometry object with the the same dimensions as this but with no modifier.
|
9
|
+
def without_modifier
|
10
|
+
Geometry.new(self.width, self.height)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the dimensions that would result if a thumbnail was created by transforming this geometry into that geometry.
|
14
|
+
# Its purpose is to mimic imagemagick conversions. Used like so:
|
15
|
+
# file_geometry.transformed_by(style_geometry)
|
16
|
+
# it returns the size of the thumbnail image you would get by applying that rule.
|
17
|
+
# This saves us having to go back to the file, which is expensive with S3.
|
18
|
+
# We understand all the Imagemagick geometry arguments described at http://www.imagemagick.org/script/command-line-processing.php#geometry
|
19
|
+
# including both '^' and paperclip's own '#' modifier.
|
20
|
+
#
|
21
|
+
def transformed_by (other)
|
22
|
+
other = Geometry.parse(other) unless other.is_a? Geometry
|
23
|
+
# if the two geometries are similar, or the destination geometry is a fixed size, the resulting dimensions are fixed
|
24
|
+
return other.without_modifier if self =~ other || ['#', '!', '^'].include?(other.modifier)
|
25
|
+
# otherwise, we apply the transformation
|
26
|
+
raise TransformationError, "geometry is not transformable without both width and height" if self.height == 0 or self.width == 0
|
27
|
+
case other.modifier
|
28
|
+
when '>'
|
29
|
+
(other.width < self.width || other.height < self.height) ? scaled_to_fit(other) : self
|
30
|
+
when '<'
|
31
|
+
(other.width > self.width && other.height > self.height) ? scaled_to_fit(other) : self
|
32
|
+
when '%'
|
33
|
+
scaled_by(other)
|
34
|
+
when '@'
|
35
|
+
scaled_by(other.width * 100 / (self.width * self.height))
|
36
|
+
else
|
37
|
+
scaled_to_fit(other)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
alias :* :transformed_by
|
41
|
+
|
42
|
+
# Tests whether two geometries are identical in dimensions and modifier.
|
43
|
+
def == (other)
|
44
|
+
self.to_s == other.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
# Tests whether two geometries have the same dimensions, ignoring modifier.
|
48
|
+
def =~ (other)
|
49
|
+
self.height.to_i == other.height.to_i && self.width.to_i == other.width.to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
# Scales this geometry to fit within that geometry.
|
53
|
+
def scaled_to_fit(other)
|
54
|
+
if (other.width > 0 && other.height == 0)
|
55
|
+
Geometry.new(other.width, self.height * other.width / self.width)
|
56
|
+
elsif (other.width == 0 && other.height > 0)
|
57
|
+
Geometry.new(self.width * other.height / self.height, other.height)
|
58
|
+
else
|
59
|
+
ratio = Geometry.new( other.width / self.width, other.height / self.height )
|
60
|
+
if ratio.square?
|
61
|
+
other.without_modifier
|
62
|
+
elsif ratio.horizontal?
|
63
|
+
Geometry.new(ratio.height * self.width, other.height)
|
64
|
+
else
|
65
|
+
Geometry.new(other.width, ratio.width * self.height)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Scales this geometry by the percentage(s) specified in that geometry.
|
71
|
+
def scaled_by(other)
|
72
|
+
other = Geometry.new("#{other}%") unless other.is_a? Geometry
|
73
|
+
if other.height > 0
|
74
|
+
Geometry.new(self.width * other.width / 100, self.height * other.height / 100)
|
75
|
+
else
|
76
|
+
Geometry.new(self.width * other.width / 100, self.height * other.width / 100)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|