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.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +9 -14
  3. data/Gemfile.lock +84 -70
  4. data/README.md +4 -0
  5. data/app/assets/images/admin/add.png +0 -0
  6. data/app/assets/images/admin/archive_icon.png +0 -0
  7. data/app/assets/images/admin/audio_icon.png +0 -0
  8. data/app/assets/images/admin/audio_thumbnail.png +0 -0
  9. data/app/assets/images/admin/c_icon.png +0 -0
  10. data/app/assets/images/admin/copy.png +0 -0
  11. data/app/assets/images/admin/css_icon.png +0 -0
  12. data/app/assets/images/admin/database_icon.png +0 -0
  13. data/app/assets/images/admin/delete.png +0 -0
  14. data/app/assets/images/admin/document_icon.png +0 -0
  15. data/app/assets/images/admin/document_thumbnail.png +0 -0
  16. data/app/assets/images/admin/flash_icon.png +0 -0
  17. data/app/assets/images/admin/flash_thumbnail.png +0 -0
  18. data/app/assets/images/admin/font_icon.png +0 -0
  19. data/app/assets/images/admin/gzip_icon.png +0 -0
  20. data/app/assets/images/admin/html_icon.png +0 -0
  21. data/app/assets/images/admin/image_icon.png +0 -0
  22. data/app/assets/images/admin/image_thumbnail.png +0 -0
  23. data/app/assets/images/admin/java_icon.png +0 -0
  24. data/app/assets/images/admin/page_edit.png +0 -0
  25. data/app/assets/images/admin/pdf_icon.png +0 -0
  26. data/app/assets/images/admin/pdf_thumbnail.png +0 -0
  27. data/app/assets/images/admin/perl_icon.png +0 -0
  28. data/app/assets/images/admin/php_icon.png +0 -0
  29. data/app/assets/images/admin/presentation_icon.png +0 -0
  30. data/app/assets/images/admin/python_icon.png +0 -0
  31. data/app/assets/images/admin/reorder_assets.png +0 -0
  32. data/app/assets/images/admin/ruby_icon.png +0 -0
  33. data/app/assets/images/admin/script_icon.png +0 -0
  34. data/app/assets/images/admin/spinner.gif +0 -0
  35. data/app/assets/images/admin/spreadsheet_icon.png +0 -0
  36. data/app/assets/images/admin/tar_icon.png +0 -0
  37. data/app/assets/images/admin/unknown_icon.png +0 -0
  38. data/app/assets/images/admin/unknown_thumbnail.png +0 -0
  39. data/app/assets/images/admin/video_icon.png +0 -0
  40. data/app/assets/images/admin/video_thumbnail.png +0 -0
  41. data/app/assets/images/admin/xml_icon.png +0 -0
  42. data/app/assets/images/admin/zip_icon.png +0 -0
  43. data/app/assets/javascripts/admin/assets.js +268 -0
  44. data/app/assets/javascripts/admin/assets_admin.js +16 -0
  45. data/app/assets/javascripts/admin/jquery.fileupload.js +1457 -0
  46. data/app/assets/javascripts/admin/jquery.iframe-transport.js +214 -0
  47. data/app/assets/javascripts/admin/jquery.ui.widget.js +558 -0
  48. data/app/assets/stylesheets/admin/assets.scss +283 -0
  49. data/app/controllers/admin/assets_controller.rb +88 -0
  50. data/app/controllers/admin/page_attachments_controller.rb +23 -0
  51. data/app/helpers/admin/assets_helper.rb +16 -0
  52. data/app/helpers/admin/page_attachments_helper.rb +2 -0
  53. data/app/models/asset.rb +251 -0
  54. data/app/models/asset_type.rb +239 -0
  55. data/app/models/old_page_attachment.rb +26 -0
  56. data/app/models/page_attachment.rb +20 -0
  57. data/app/views/admin/assets/_asset.html.haml +12 -0
  58. data/app/views/admin/assets/_asset_table.html.haml +30 -0
  59. data/app/views/admin/assets/_errors.html.haml +3 -0
  60. data/app/views/admin/assets/_form.html.haml +18 -0
  61. data/app/views/admin/assets/_page_assets.html.haml +12 -0
  62. data/app/views/admin/assets/_search.html.haml +17 -0
  63. data/app/views/admin/assets/_search_results.html.haml +17 -0
  64. data/app/views/admin/assets/edit.html.haml +40 -0
  65. data/app/views/admin/assets/index.html.haml +15 -0
  66. data/app/views/admin/assets/new.html.haml +24 -0
  67. data/app/views/admin/assets/refresh.html.haml +14 -0
  68. data/app/views/admin/assets/remove.html.haml +16 -0
  69. data/app/views/admin/configuration/_clipped_edit.html.haml +8 -0
  70. data/app/views/admin/configuration/_clipped_show.html.haml +12 -0
  71. data/app/views/admin/page_attachments/_attachment.html.haml +25 -0
  72. data/app/views/admin/pages/_asset_popups.html.haml +37 -0
  73. data/app/views/admin/pages/_assets.html.haml +13 -0
  74. data/app/views/admin/removed/_assets_bucket.html.haml +8 -0
  75. data/app/views/admin/removed/_assets_container.html.haml +54 -0
  76. data/app/views/admin/removed/_bucket.html.haml +11 -0
  77. data/app/views/admin/removed/_bucket_asset.html.haml +9 -0
  78. data/app/views/admin/removed/_show_bucket_link.html.haml +4 -0
  79. data/app/views/admin/removed/_upload_to_page.html.haml +12 -0
  80. data/app/views/admin/removed/bucket/_iframe.html.haml +1 -0
  81. data/config/application.rb +0 -37
  82. data/config/initializers/assets.rb +1 -0
  83. data/config/initializers/interpolation.rb +6 -0
  84. data/config/initializers/kraken.rb +7 -0
  85. data/config/initializers/trusty_cms_config.rb +67 -0
  86. data/config/locales/en.yml +101 -0
  87. data/config/routes.rb +14 -3
  88. data/coverage/index.html +26 -26
  89. data/db/migrate/028_create_assets.rb +12 -0
  90. data/db/migrate/029_create_paperclip_attributes.rb +13 -0
  91. data/db/migrate/030_create_user_observer.rb +13 -0
  92. data/db/migrate/031_create_page_attachments.rb +19 -0
  93. data/db/migrate/032_rename_users.rb +13 -0
  94. data/db/migrate/20110513205050_asset_uuid.rb +11 -0
  95. data/db/migrate/20110606111250_update_configuration.rb +34 -0
  96. data/db/migrate/20110609101438_dimensions.rb +13 -0
  97. data/lib/trusty_cms.rb +1 -1
  98. data/spec/dummy/db/schema.rb +22 -0
  99. data/spec/dummy/log/test.log +95620 -0
  100. data/spec/dummy/tmp/cache/747/A70/TrustyCms%3A%3AConfig +0 -0
  101. data/spec/dummy/tmp/cache/85C/FA0/TrustyCms.cache_mtime +0 -0
  102. data/spec/dummy/tmp/capybara/capybara-20180312152412265881245.html +262 -0
  103. data/spec/dummy/tmp/capybara/capybara-201803121526139973682798.html +149 -0
  104. data/spec/factories/layout.rb +1 -1
  105. data/spec/factories/page.rb +3 -3
  106. data/spec/factories/page_part.rb +1 -1
  107. data/spec/factories/user.rb +1 -1
  108. data/spec/models/layout_spec.rb +6 -6
  109. data/spec/spec_helper.rb +4 -4
  110. data/trusty_cms.gemspec +27 -22
  111. data/vendor/extensions/clipped-extension/clipped_extension.rb +40 -0
  112. data/vendor/extensions/clipped-extension/lib/asset_tags.rb +346 -0
  113. data/vendor/extensions/clipped-extension/lib/clipped/engine.rb +11 -0
  114. data/vendor/extensions/clipped-extension/lib/clipped_admin_ui.rb +28 -0
  115. data/vendor/extensions/clipped-extension/lib/generators/templates/clipped_config.rb +53 -0
  116. data/vendor/extensions/clipped-extension/lib/page_asset_associations.rb +13 -0
  117. data/vendor/extensions/clipped-extension/lib/paperclip/frame_grab.rb +73 -0
  118. data/vendor/extensions/clipped-extension/lib/paperclip/geometry_transformation.rb +80 -0
  119. data/vendor/extensions/clipped-extension/lib/tasks/clipped_extension_tasks.rake +124 -0
  120. data/vendor/extensions/clipped-extension/lib/tasks/paperclip_tasks.rake +79 -0
  121. data/vendor/extensions/clipped-extension/lib/trusty-clipped-extension.rb +2 -0
  122. data/vendor/extensions/clipped-extension/lib/trusty_cms_clipped_extension/cloud.rb +41 -0
  123. metadata +219 -67
  124. data/config/initializers/ckeditor.rb +0 -3
  125. data/config/initializers/haml.rb +0 -2
  126. data/config/initializers/rails_patch.rb +0 -2
  127. data/config/initializers/string_extensions.rb +0 -1
  128. 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,11 @@
1
+ require 'acts_as_list'
2
+ require 'uuidtools'
3
+ require 'trusty_cms_clipped_extension/cloud'
4
+ require 'paperclip'
5
+ require 'will_paginate/array'
6
+ module Clipped
7
+ class Engine < Rails::Engine
8
+ paths["app/helpers"] = []
9
+
10
+ end
11
+ end
@@ -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