trusty-cms 7.0.49 → 7.1.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/builds/trusty_cms/ckeditor5.css +459 -81
  4. data/app/assets/builds/trusty_cms/ckeditor5.css.map +3 -3
  5. data/app/assets/builds/trusty_cms/ckeditor5.js +11247 -7722
  6. data/app/assets/builds/trusty_cms/ckeditor5.js.map +4 -4
  7. data/app/assets/stylesheets/admin/assets.scss +5 -0
  8. data/app/controllers/admin/assets_controller.rb +7 -14
  9. data/app/javascript/plugins/asset_tags/asset_tag_builder.js +7 -2
  10. data/app/models/asset.rb +144 -48
  11. data/app/models/asset_type.rb +29 -25
  12. data/app/views/admin/assets/_search_results.html.haml +2 -3
  13. data/app/views/admin/assets/edit.html.haml +2 -2
  14. data/app/views/admin/assets/remove.html.haml +1 -1
  15. data/app/views/admin/configuration/_clipped_edit.html.haml +1 -0
  16. data/app/views/admin/configuration/_clipped_show.html.haml +2 -0
  17. data/config/database.yml +30 -0
  18. data/config/initializers/trusty_cms_config.rb +3 -50
  19. data/config/locales/en.yml +6 -1
  20. data/config/routes.rb +0 -2
  21. data/db/migrate/20110606111250_update_configuration.rb +0 -16
  22. data/lib/trusty_cms/geometry.rb +117 -0
  23. data/lib/trusty_cms/version.rb +1 -1
  24. data/spec/dummy/log/development.log +345 -0
  25. data/spec/dummy/log/test.log +0 -0
  26. data/spec/dummy/tmp/cache/747/A70/TrustyCms%3A%3AConfig +0 -0
  27. data/spec/dummy/tmp/cache/85C/FA0/TrustyCms.cache_mtime +0 -0
  28. data/spec/dummy/tmp/local_secret.txt +1 -0
  29. data/spec/dummy/tmp/trusty_config_cache.txt +0 -0
  30. data/spec/lib/trusty_cms/geometry_spec.rb +28 -0
  31. data/spec/models/asset_spec.rb +235 -12
  32. data/trusty_cms.gemspec +1 -1
  33. data/vendor/extensions/clipped-extension/clipped_extension.rb +4 -9
  34. data/vendor/extensions/clipped-extension/lib/asset_tags.rb +10 -4
  35. data/vendor/extensions/clipped-extension/lib/generators/templates/clipped_config.rb +11 -35
  36. data/vendor/extensions/clipped-extension/lib/tasks/active_storage_tasks.rake +66 -0
  37. data/vendor/extensions/clipped-extension/lib/tasks/clipped_extension_tasks.rake +5 -2
  38. data/vendor/extensions/clipped-extension/lib/trusty_cms_clipped_extension/cloud.rb +32 -27
  39. data/yarn.lock +9 -9
  40. metadata +21 -8
  41. data/lib/trusty_cms/deprecation.rb +0 -15
  42. data/vendor/extensions/clipped-extension/lib/paperclip/frame_grab.rb +0 -73
  43. data/vendor/extensions/clipped-extension/lib/paperclip/geometry_transformation.rb +0 -80
  44. data/vendor/extensions/clipped-extension/lib/tasks/paperclip_tasks.rake +0 -79
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe TrustyCms::Geometry do
4
+ describe '.parse' do
5
+ it 'parses basic geometry strings' do
6
+ geom = described_class.parse('100x200')
7
+ expect(geom.width).to eq(100)
8
+ expect(geom.height).to eq(200)
9
+ expect(geom.modifier).to be_nil
10
+ end
11
+
12
+ it 'parses geometry modifiers' do
13
+ geom = described_class.parse('640x480@')
14
+ expect(geom.width).to eq(640)
15
+ expect(geom.height).to eq(480)
16
+ expect(geom.modifier).to eq('@')
17
+ end
18
+ end
19
+
20
+ describe '#transformed_by' do
21
+ it 'scales to fit preserving aspect ratio' do
22
+ source = described_class.new(400, 200)
23
+ result = source.transformed_by('100x100')
24
+ expect(result.width).to eq(100)
25
+ expect(result.height).to eq(50)
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'rack/test'
3
+ require 'base64'
4
+ require 'stringio'
3
5
 
4
6
  RSpec.describe Asset, type: :model do
5
7
  let(:fixtures_path) { TrustyCms::Engine.root.join('spec', 'fixtures', 'files') }
@@ -9,31 +11,252 @@ RSpec.describe Asset, type: :model do
9
11
  end
10
12
 
11
13
  describe 'validations' do
14
+ before do
15
+ AssetType.new :image, icon: 'image', styles: :standard, extensions: %w[jpg jpeg png gif], mime_types: %w[image/png image/x-png image/jpeg image/pjpeg image/jpg image/gif] unless AssetType.known?(:image)
16
+ AssetType.new :video, icon: 'video', mime_types: %w[video/mp4 video/mpeg video/quicktime video/webm] unless AssetType.known?(:video)
17
+ AssetType.new :document, icon: 'document', mime_types: %w[application/msword application/rtf text/plain text/html] unless AssetType.known?(:document)
18
+ end
19
+
12
20
  it 'is valid when the content type is approved' do
13
- asset = described_class.new(asset: upload_fixture('sample.ics', 'text/calendar'))
21
+ asset = described_class.new(asset: upload_fixture('sample.txt', 'text/plain'))
14
22
 
15
23
  expect(asset).to be_valid
16
24
  end
17
25
 
18
26
  it 'is invalid when the content type is not approved' do
19
- asset = described_class.new(asset: upload_fixture('sample.txt', 'text/plain'))
27
+ asset = described_class.new(asset: upload_fixture('sample.txt', 'application/x-unsupported'))
20
28
 
21
29
  expect(asset).not_to be_valid
22
- expect(asset.errors[:asset]).to include(a_string_matching(/file format/i))
30
+ expect(asset.errors[:asset]).to include(a_string_matching(/file type/i))
23
31
  end
24
32
 
25
- it 'is invalid when the file exceeds the maximum size' do
26
- Tempfile.open(['large', '.png']) do |tempfile|
27
- tempfile.binmode
28
- tempfile.write('0' * (10.megabytes + 1))
29
- tempfile.rewind
33
+ context 'with stubbed size limits (1 MB asset / 2 MB video)' do
34
+ before do
35
+ allow(TrustyCms.config).to receive(:[]).and_call_original
36
+ allow(TrustyCms.config).to receive(:[]).with('assets.max_asset_size').and_return('1')
37
+ allow(TrustyCms.config).to receive(:[]).with('assets.max_video_size').and_return('2')
38
+ end
39
+
40
+ it 'is invalid when a non-video file exceeds the maximum asset size' do
41
+ Tempfile.open(['large', '.png']) do |tempfile|
42
+ tempfile.binmode
43
+ tempfile.write('0' * (1.megabyte + 1))
44
+ tempfile.rewind
45
+
46
+ uploaded = Rack::Test::UploadedFile.new(tempfile.path, 'image/png')
47
+ asset = described_class.new(asset: uploaded)
48
+
49
+ expect(asset).not_to be_valid
50
+ expect(asset.errors[:asset]).to include(a_string_matching(/1 MB/))
51
+ end
52
+ end
53
+
54
+ it 'is invalid when a video file exceeds the maximum video size' do
55
+ Tempfile.open(['large', '.mp4']) do |tempfile|
56
+ tempfile.binmode
57
+ tempfile.write('0' * (2.megabytes + 1))
58
+ tempfile.rewind
30
59
 
31
- uploaded = Rack::Test::UploadedFile.new(tempfile.path, 'image/png')
32
- asset = described_class.new(asset: uploaded)
60
+ uploaded = Rack::Test::UploadedFile.new(tempfile.path, 'video/mp4')
61
+ asset = described_class.new(asset: uploaded)
33
62
 
34
- expect(asset).not_to be_valid
35
- expect(asset.errors[:asset]).to include(a_string_matching(/10 MB/))
63
+ expect(asset).not_to be_valid
64
+ expect(asset.errors[:asset]).to include(a_string_matching(/2 MB/))
65
+ end
36
66
  end
67
+
68
+ it 'allows a video file that exceeds the asset size limit but is within the video size limit' do
69
+ Tempfile.open(['video', '.mp4']) do |tempfile|
70
+ tempfile.binmode
71
+ tempfile.write('0' * (1.megabyte + 1.kilobyte))
72
+ tempfile.rewind
73
+
74
+ uploaded = Rack::Test::UploadedFile.new(tempfile.path, 'video/mp4')
75
+ asset = described_class.new(asset: uploaded)
76
+
77
+ expect(asset).to be_valid
78
+ end
79
+ end
80
+
81
+ it 'allows a video file within the maximum video size' do
82
+ Tempfile.open(['video', '.mp4']) do |tempfile|
83
+ tempfile.binmode
84
+ tempfile.write('0' * 1.kilobyte)
85
+ tempfile.rewind
86
+
87
+ uploaded = Rack::Test::UploadedFile.new(tempfile.path, 'video/mp4')
88
+ asset = described_class.new(asset: uploaded)
89
+
90
+ expect(asset).to be_valid
91
+ end
92
+ end
93
+ end
94
+ end
95
+ describe 'active storage metadata' do
96
+ before do
97
+ AssetType.new :image, :styles => :standard, :extensions => %w[png], :mime_types => %w[image/png] unless AssetType.known?(:image)
98
+ end
99
+
100
+ let(:png_data) do
101
+ Base64.decode64('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==')
102
+ end
103
+
104
+ def png_io
105
+ io = StringIO.new(png_data)
106
+ io.set_encoding(Encoding::BINARY)
107
+ io.rewind
108
+ io
109
+ end
110
+
111
+ it 'exposes filename, content type, and size from the attachment' do
112
+ asset = described_class.new(caption: '')
113
+ asset.asset.attach(io: png_io, filename: 'pixel.png', content_type: 'image/png')
114
+ asset.save!
115
+
116
+ expect(asset.filename).to eq('pixel.png')
117
+ expect(asset.content_type).to eq('image/png')
118
+ expect(asset.byte_size).to eq(png_data.bytesize)
119
+ expect(asset.original_extension).to eq('png')
120
+ end
121
+
122
+ it 'reports styles and extensions from active storage styles' do
123
+ asset = described_class.new(caption: '')
124
+ asset.asset.attach(io: png_io, filename: 'pixel.png', content_type: 'image/png')
125
+ asset.save!
126
+
127
+ expect(asset.style?('thumbnail')).to be(true)
128
+ expect(asset.extension('thumbnail').to_s).to eq('png')
129
+ end
130
+ end
131
+
132
+ describe 'thumbnails' do
133
+ before do
134
+ AssetType.new :image, :styles => :standard, :extensions => %w[png], :mime_types => %w[image/png] unless AssetType.known?(:image)
135
+ end
136
+
137
+ let(:png_data) do
138
+ Base64.decode64('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==')
139
+ end
140
+
141
+ def png_io
142
+ io = StringIO.new(png_data)
143
+ io.set_encoding(Encoding::BINARY)
144
+ io.rewind
145
+ io
146
+ end
147
+
148
+ it 'returns a variant url for known styles' do
149
+ asset = described_class.new(caption: '')
150
+ asset.asset.attach(io: png_io, filename: 'pixel.png', content_type: 'image/png')
151
+ asset.save!
152
+
153
+ variant = instance_double(ActiveStorage::Variant, processed: instance_double(ActiveStorage::Variant, url: '/rails/active_storage/variant/test'))
154
+ allow(asset).to receive(:asset_variant).with('thumbnail').and_return(variant)
155
+
156
+ expect(asset.thumbnail('thumbnail')).to eq('/rails/active_storage/variant/test')
157
+ end
158
+
159
+ it 'returns the direct asset url for pdfs without variant processing' do
160
+ asset = described_class.new(caption: '')
161
+ allow(asset).to receive_message_chain(:asset, :attached?).and_return(true)
162
+ allow(asset).to receive(:content_type).and_return('application/pdf')
163
+ allow(asset).to receive_message_chain(:asset, :url).and_return('https://s3.amazonaws.com/bucket/myprefix/system/assets/20260501/doc-abc123.pdf')
164
+ allow(asset).to receive(:rewrite_cloud_url) { |url| url }
165
+
166
+ expect(asset).not_to receive(:asset_variant)
167
+ expect(asset.thumbnail('normal')).to eq('https://s3.amazonaws.com/bucket/myprefix/system/assets/20260501/doc-abc123.pdf')
168
+ end
169
+
170
+ it 'returns the asset type icon when nothing is attached' do
171
+ asset = described_class.new(caption: '')
172
+ allow(asset).to receive_message_chain(:asset, :attached?).and_return(false)
173
+ allow(asset).to receive(:asset_variant).and_return(nil)
174
+ allow(asset).to receive_message_chain(:asset_type, :icon).with('normal').and_return('/assets/admin/image_icon.png')
175
+
176
+ expect(asset.thumbnail('normal')).to eq('/assets/admin/image_icon.png')
177
+ end
178
+ end
179
+
180
+ describe '#render_original' do
181
+ it 'returns true for any style when the asset key starts with the configured prefix' do
182
+ asset = described_class.new
183
+ allow(TrustyCms::Config).to receive(:[]).with('assets.storage.prefix').and_return('myprefix')
184
+ allow(asset).to receive_message_chain(:asset, :attached?).and_return(true)
185
+ allow(asset).to receive_message_chain(:asset, :key).and_return('myprefix/system/assets/20260501/image-abc123.jpg')
186
+
187
+ expect(asset.render_original('normal')).to be(true)
188
+ expect(asset.render_original('thumbnail')).to be(true)
189
+ expect(asset.render_original('original')).to be(true)
190
+ end
191
+
192
+ it 'returns true when no prefix is configured but the key contains a path separator' do
193
+ asset = described_class.new
194
+ allow(TrustyCms::Config).to receive(:[]).with('assets.storage.prefix').and_return(nil)
195
+ allow(asset).to receive_message_chain(:asset, :attached?).and_return(true)
196
+ allow(asset).to receive_message_chain(:asset, :key).and_return('system/assets/20260501/image-abc123.jpg')
197
+
198
+ expect(asset.render_original('normal')).to be(true)
199
+ end
200
+
201
+ it 'returns false when the key does not start with the configured prefix' do
202
+ asset = described_class.new
203
+ allow(TrustyCms::Config).to receive(:[]).with('assets.storage.prefix').and_return('myprefix')
204
+ allow(asset).to receive_message_chain(:asset, :attached?).and_return(true)
205
+ allow(asset).to receive_message_chain(:asset, :key).and_return('randomlegacykey')
206
+
207
+ expect(asset.render_original('normal')).to be(false)
208
+ end
209
+
210
+ it 'returns false when no asset is attached' do
211
+ asset = described_class.new
212
+ allow(asset).to receive_message_chain(:asset, :attached?).and_return(false)
213
+
214
+ expect(asset.render_original('normal')).to be(false)
215
+ end
216
+ end
217
+
218
+ describe '#public_url' do
219
+ let(:original_url) { 'https://s3.amazonaws.com/bucket/myprefix/system/assets/20260501/image-abc123.jpg' }
220
+ let(:variant_url) { 'https://s3.amazonaws.com/bucket/variants/abc/xyz.jpg' }
221
+
222
+ it 'returns the original url for new-style assets regardless of style' do
223
+ asset = described_class.new
224
+ allow(asset).to receive(:render_original).and_return(true)
225
+ allow(asset).to receive_message_chain(:asset, :url).and_return(original_url)
226
+ allow(asset).to receive(:rewrite_cloud_url) { |url| url }
227
+
228
+ expect(asset.public_url('normal')).to eq(original_url)
229
+ expect(asset.public_url('thumbnail')).to eq(original_url)
230
+ end
231
+
232
+ it 'returns the original url when style_name is original' do
233
+ asset = described_class.new
234
+ allow(asset).to receive(:render_original).and_return(false)
235
+ allow(asset).to receive_message_chain(:asset, :url).and_return(original_url)
236
+ allow(asset).to receive(:rewrite_cloud_url) { |url| url }
237
+
238
+ expect(asset.public_url('original')).to eq(original_url)
239
+ end
240
+
241
+ it 'returns a variant url for old-style assets' do
242
+ asset = described_class.new
243
+ allow(asset).to receive(:render_original).and_return(false)
244
+ processed = instance_double(ActiveStorage::Variant, url: variant_url)
245
+ variant = instance_double(ActiveStorage::Variant, processed: processed)
246
+ allow(asset).to receive(:asset_variant).and_return(variant)
247
+ allow(asset).to receive(:rewrite_cloud_url) { |url| url }
248
+
249
+ expect(asset.public_url('normal')).to eq(variant_url)
250
+ end
251
+
252
+ it 'falls back to the asset url when no variant exists for an old-style asset' do
253
+ asset = described_class.new
254
+ allow(asset).to receive(:render_original).and_return(false)
255
+ allow(asset).to receive(:asset_variant).and_return(nil)
256
+ allow(asset).to receive_message_chain(:asset, :url).and_return('https://s3.amazonaws.com/bucket/randomlegacykey')
257
+ allow(asset).to receive(:rewrite_cloud_url) { |url| url }
258
+
259
+ expect(asset.public_url('normal')).to eq('https://s3.amazonaws.com/bucket/randomlegacykey')
37
260
  end
38
261
  end
39
262
  end
data/trusty_cms.gemspec CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  require File.expand_path(__FILE__ + '/../lib/trusty_cms/version.rb')
5
5
  Gem::Specification.new do |s|
6
- s.required_ruby_version = '>= 3.0.5'
6
+ s.required_ruby_version = '>= 3.3.4'
7
7
  s.name = 'trusty-cms'
8
8
  s.version = TrustyCms::VERSION
9
9
  s.platform = Gem::Platform::RUBY
@@ -9,11 +9,10 @@ class ClippedExtension < TrustyCms::Extension
9
9
  TrustyCms::AdminUI.send :include, ClippedAdminUI unless defined? admin.asset # defines shards for extension of the asset-admin interface
10
10
  Admin::PagesController.send :helper, Admin::AssetsHelper # currently only provides a description of asset sizes
11
11
  Page.send :include, AssetTags # radius tags for selecting sets of assets and presenting each one
12
- AssetType.new :image, :icon => 'image', :default_radius_tag => 'image', :processors => [:thumbnail], :styles => :standard, :extensions => %w[jpg jpeg png gif], :mime_types => %w[image/png image/x-png image/jpeg image/pjpeg image/jpg image/gif]
13
- AssetType.new :video, :icon => 'video', :processors => [:frame_grab], :styles => :standard, :mime_types => %w[application/x-mp4 video/mpeg video/quicktime video/x-la-asf video/x-ms-asf video/x-msvideo video/x-sgi-movie video/x-flv flv-application/octet-stream video/3gpp video/3gpp2 video/3gpp-tt video/BMPEG video/BT656 video/CelB video/DV video/H261 video/H263 video/H263-1998 video/H263-2000 video/H264 video/JPEG video/MJ2 video/MP1S video/MP2P video/MP2T video/mp4 video/MP4V-ES video/MPV video/mpeg4 video/mpeg4-generic video/nv video/parityfec video/pointer video/raw video/rtx video/ogg video/webm]
14
- AssetType.new :audio, :icon => 'audio', :mime_types => %w[audio/mpeg audio/mpg audio/ogg application/ogg audio/x-ms-wma audio/vnd.rn-realaudio audio/x-wav]
15
- AssetType.new :pdf, :icon => 'pdf', :processors => [:thumbnail], :extensions => %w{pdf}, :mime_types => %w[application/pdf application/x-pdf], :styles => :standard
16
- AssetType.new :document, :icon => 'document', :mime_types => %w[application/msword application/rtf application/vnd.ms-excel application/vnd.ms-powerpoint application/vnd.ms-project application/vnd.ms-works text/plain text/html]
12
+ AssetType.new :image, :icon => 'image', :default_radius_tag => 'image', :styles => :standard, :extensions => %w[jpg jpeg png gif webp avif], :mime_types => %w[image/png image/x-png image/jpeg image/pjpeg image/jpg image/gif image/webp image/avif]
13
+ AssetType.new :video, :icon => 'video', :styles => :standard, :mime_types => %w[video/mp4 video/webm video/quicktime video/mpeg video/ogg video/mp2t application/x-mp4]
14
+ AssetType.new :audio, :icon => 'audio', :mime_types => %w[audio/mpeg audio/mpg audio/ogg audio/mp4 audio/wav audio/x-wav application/ogg audio/flac]
15
+ AssetType.new :pdf, :icon => 'pdf', :extensions => %w{pdf}, :mime_types => %w[application/pdf application/x-pdf], :styles => :standard
17
16
  AssetType.new :other, :icon => 'unknown'
18
17
 
19
18
  admin.asset ||= TrustyCms::AdminUI.load_default_asset_regions # loads the shards defined in AssetsAdminUI
@@ -23,10 +22,6 @@ class ClippedExtension < TrustyCms::Extension
23
22
  admin.configuration.show.add :trusty_config, 'admin/configuration/clipped_show', :after => 'defaults'
24
23
  admin.configuration.edit.add :form, 'admin/configuration/clipped_edit', :after => 'edit_defaults'
25
24
 
26
- if TrustyCms::Config.table_exists? && TrustyCms::config["paperclip.command_path"] # This is needed for testing if you are using mod_rails
27
- Paperclip.options[:command_path] = TrustyCms::config["paperclip.command_path"]
28
- end
29
-
30
25
  tab "Assets", :after => "Content" do
31
26
  add_item "All", "/admin/assets"
32
27
  end
@@ -181,17 +181,24 @@ module AssetTags
181
181
  options = tag.attr.dup
182
182
  # XXX build_regexp_for comes from StandardTags
183
183
  regexp = build_regexp_for(tag, options)
184
- asset_content_type = tag.locals.asset.asset_content_type
184
+ asset_content_type = tag.locals.asset.content_type
185
185
  tag.expand unless asset_content_type.match(regexp).nil?
186
186
  end
187
187
 
188
+ attribute_mappings = {
189
+ asset_file_name: :filename,
190
+ asset_content_type: :content_type,
191
+ asset_file_size: :byte_size,
192
+ }
193
+
188
194
  [:title, :caption, :asset_file_name, :extension, :asset_content_type, :asset_file_size, :id].each do |method|
189
195
  desc %{
190
196
  Renders the @#{method.to_s}@ attribute of the asset
191
197
  }
192
198
  tag "asset:#{method.to_s}" do |tag|
193
199
  asset, options = asset_and_options(tag)
194
- asset.send(method) rescue nil
200
+ mapped = attribute_mappings.fetch(method, method)
201
+ asset.send(mapped) rescue nil
195
202
  end
196
203
  end
197
204
 
@@ -201,7 +208,7 @@ module AssetTags
201
208
 
202
209
  tag 'asset:filename' do |tag|
203
210
  asset, options = asset_and_options(tag)
204
- asset.asset_file_name rescue nil
211
+ asset.filename rescue nil
205
212
  end
206
213
 
207
214
  desc %{
@@ -308,4 +315,3 @@ module AssetTags
308
315
  }
309
316
  end
310
317
  end
311
-
@@ -3,11 +3,8 @@ TrustyCms.config do |config|
3
3
  # Uncomment and change the settings below to customize the Clipped extension
4
4
 
5
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
6
+ # config["assets.max_asset_size"] = 10 # megabytes
7
+ # config["assets.max_video_size"] = 50 # megabytes
11
8
  # config["assets.display_size"] = "normal"
12
9
  # config["assets.insertion_size"] = "normal"
13
10
  # config["assets.create_image_thumbnails?"] = true
@@ -19,35 +16,14 @@ TrustyCms.config do |config|
19
16
  # config["assets.thumbnails.video"] = "normal:size=640x640>,format=jpg|small:size=320x320>,format=jpg"
20
17
  # config["assets.thumbnails.pdf"] = "normal:size=640x640>,format=jpg|small:size=320x320>,format=jpg"
21
18
 
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"
19
+ # ActiveStorage configuration is handled in config/storage.yml and
20
+ # config.active_storage.service (per environment). For S3 or other
21
+ # cloud providers, configure the appropriate ActiveStorage service.
22
+ #
23
+ # Optional CDN host override for ActiveStorage URLs:
24
+ # config["assets.cdn.host"] = "https://assets.example.com"
25
+ #
26
+ # Or provide a dynamic host in an initializer:
27
+ # TrustyCmsClippedExtension::Cloud.host_provider = -> { AssetBucket.current&.asset_host }
52
28
 
53
29
  end
@@ -0,0 +1,66 @@
1
+ def obtain_class
2
+ class_name = ENV['CLASS'] || ENV['class']
3
+ raise "Must specify CLASS" unless class_name
4
+ Object.const_get(class_name)
5
+ end
6
+
7
+ def obtain_attachments(klass)
8
+ name = ENV['ATTACHMENT'] || ENV['attachment']
9
+ attachment_names = if klass.respond_to?(:reflect_on_all_attachments)
10
+ klass.reflect_on_all_attachments.map(&:name)
11
+ else
12
+ []
13
+ end
14
+ raise "Class #{klass.name} has no ActiveStorage attachments" if attachment_names.empty?
15
+ if name.present? && attachment_names.include?(name.to_sym)
16
+ [name.to_sym]
17
+ else
18
+ attachment_names
19
+ end
20
+ end
21
+
22
+ def for_all_attachments
23
+ klass = obtain_class
24
+ names = obtain_attachments(klass)
25
+
26
+ klass.find_each do |instance|
27
+ names.each do |name|
28
+ attachment = instance.public_send(name)
29
+ result = if attachment.attached?
30
+ yield(instance, name, attachment)
31
+ else
32
+ true
33
+ end
34
+ print result ? "." : "x"; $stdout.flush
35
+ end
36
+ end
37
+ puts " Done."
38
+ end
39
+
40
+ namespace :active_storage do
41
+ desc "Refreshes both metadata and variants."
42
+ task :refresh => ["active_storage:refresh:metadata", "active_storage:refresh:variants"]
43
+
44
+ namespace :refresh do
45
+ desc "Regenerates variants for a given CLASS (and optional ATTACHMENT)."
46
+ task :variants => :environment do
47
+ for_all_attachments do |instance, name, attachment|
48
+ if instance.respond_to?(:refresh_variants!) && name == :asset
49
+ instance.refresh_variants!
50
+ else
51
+ attachment.preview if attachment.previewable?
52
+ end
53
+ true
54
+ end
55
+ end
56
+
57
+ desc "Re-analyzes metadata for a given CLASS (and optional ATTACHMENT)."
58
+ task :metadata => :environment do
59
+ for_all_attachments do |instance, name, attachment|
60
+ attachment.analyze unless attachment.analyzed?
61
+ instance.save(validate: false)
62
+ true
63
+ end
64
+ end
65
+ end
66
+ end
@@ -52,8 +52,11 @@ namespace :trusty do
52
52
  asset_path = File.join(Rails.root, "assets")
53
53
  mkdir_p asset_path
54
54
  Asset.find(:all).each do |asset|
55
- puts "Exporting #{asset.asset_file_name}"
56
- cp asset.asset.path, File.join(asset_path, asset.asset_file_name)
55
+ next unless asset.asset.attached?
56
+ puts "Exporting #{asset.filename}"
57
+ asset.asset.open do |file|
58
+ cp file.path, File.join(asset_path, asset.filename)
59
+ end
57
60
  end
58
61
  puts "Done."
59
62
  end
@@ -1,41 +1,46 @@
1
+ require 'uri'
2
+
1
3
  module TrustyCmsClippedExtension
2
4
 
3
5
  module Cloud
6
+ mattr_accessor :host_provider
4
7
 
5
8
  def self.credentials
6
- case TrustyCms.config["paperclip.fog.provider"]
7
- when "AWS"
8
- {
9
- :provider => "AWS",
10
- :aws_access_key_id => TrustyCms.config["paperclip.s3.key"],
11
- :aws_secret_access_key => TrustyCms.config["paperclip.s3.secret"],
12
- :region => TrustyCms.config["paperclip.s3.region"],
13
- }
14
- when "Google"
15
- {
16
- :provider => "Google",
17
- :rackspace_username => TrustyCms.config["paperclip.google_storage.access_key_id"],
18
- :rackspace_api_key => TrustyCms.config["paperclip.google_storage.secret_access_key"]
19
- }
20
- when "Rackspace"
21
- {
22
- :provider => "Rackspace",
23
- :rackspace_username => TrustyCms.config["paperclip.rackspace.username"],
24
- :rackspace_api_key => TrustyCms.config["paperclip.rackspace.api_key"]
25
- }
26
- end
9
+ service = ActiveStorage::Blob.service
10
+ return {} unless service.respond_to?(:config)
11
+
12
+ service.config
27
13
  end
28
14
 
29
15
  def self.host
30
- return TrustyCms.config["paperclip.fog.host"] if TrustyCms.config["paperclip.fog.host"]
31
- case TrustyCms.config["paperclip.fog.provider"]
32
- when "AWS"
33
- "http://#{TrustyCms.config['paperclip.fog.directory']}.s3.amazonaws.com"
34
- else
35
- nil
16
+ if host_provider
17
+ host_provider.call
18
+ elsif defined?(TrustyCms::Config)
19
+ TrustyCms::Config['assets.cdn.host']
36
20
  end
37
21
  end
38
22
 
23
+ def self.rewrite_url(url)
24
+ return url unless url.present?
25
+ configured_host = host
26
+ return url if configured_host.blank?
27
+
28
+ uri = URI.parse(url)
29
+ return url unless uri.is_a?(URI::HTTP)
30
+
31
+ host_uri = URI.parse(configured_host.to_s.match?(/\Ahttps?:\/\//) ? configured_host.to_s : "https://#{configured_host}")
32
+ uri.scheme = host_uri.scheme if host_uri.scheme
33
+ uri.host = host_uri.host if host_uri.host
34
+ uri.port = host_uri.port if host_uri.port
35
+
36
+ if host_uri.path.present? && host_uri.path != '/'
37
+ uri.path = File.join(host_uri.path, uri.path.sub(%r{\A/}, ''))
38
+ end
39
+
40
+ uri.to_s
41
+ rescue URI::InvalidURIError
42
+ url
43
+ end
39
44
  end
40
45
 
41
46
  end
data/yarn.lock CHANGED
@@ -1735,9 +1735,9 @@ flat-cache@^3.0.4:
1735
1735
  rimraf "^3.0.2"
1736
1736
 
1737
1737
  flatted@^3.2.9:
1738
- version "3.3.3"
1739
- resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358"
1740
- integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
1738
+ version "3.4.2"
1739
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
1740
+ integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
1741
1741
 
1742
1742
  fs.realpath@^1.0.0:
1743
1743
  version "1.0.0"
@@ -2262,9 +2262,9 @@ lodash.truncate@^4.4.2:
2262
2262
  integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
2263
2263
 
2264
2264
  lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21:
2265
- version "4.17.23"
2266
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a"
2267
- integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==
2265
+ version "4.18.1"
2266
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c"
2267
+ integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
2268
2268
 
2269
2269
  log-symbols@^4.1.0:
2270
2270
  version "4.1.0"
@@ -2961,9 +2961,9 @@ picocolors@^1.1.1:
2961
2961
  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
2962
2962
 
2963
2963
  picomatch@^2.3.1:
2964
- version "2.3.1"
2965
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
2966
- integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
2964
+ version "2.3.2"
2965
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601"
2966
+ integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
2967
2967
 
2968
2968
  postcss-html@^0.36.0:
2969
2969
  version "0.36.0"