trusty-cms 3.1.11 → 3.2.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 +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
|