Skip to content

Commit d9112f4

Browse files
authored
Merge pull request #3597 from AlchemyCMS/fix-filename-conversion
fix: Use Ruby/Rails internals to create human readable file names
2 parents bddb4cd + c72a016 commit d9112f4

12 files changed

Lines changed: 66 additions & 41 deletions

File tree

app/controllers/alchemy/admin/pictures_controller.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ def url
6464

6565
def create
6666
@picture = Picture.new(picture_params)
67-
@picture.name = @picture.humanized_name
6867
if @picture.save
6968
render successful_uploader_response(file: @picture)
7069
else

app/models/alchemy/attachment.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class Attachment < BaseRecord
2525
include Alchemy::TouchElements
2626
include Alchemy::RelatableResource
2727

28+
before_save :set_name, if: -> { Alchemy.storage_adapter.set_attachment_name?(self) }
29+
2830
include Alchemy.storage_adapter.attachment_class_methods
2931

3032
stampable stamper_class_name: Alchemy.config.user_class_name
@@ -97,8 +99,6 @@ def ransackable_scopes(_auth_object = nil)
9799
validate :file_type_allowed,
98100
unless: -> { self.class.allowed_filetypes.include?("*") }
99101

100-
before_save :set_name, if: -> { Alchemy.storage_adapter.set_attachment_name?(self) }
101-
102102
scope :with_file_type, ->(file_type) { where(file_mime_type: file_type) }
103103

104104
# Instance methods
@@ -179,7 +179,7 @@ def file_type_allowed
179179
end
180180

181181
def set_name
182-
self.name ||= convert_to_humanized_name(file_name, extension)
182+
self.name ||= Alchemy.storage_adapter.file_basename(self).humanize
183183
end
184184
end
185185
end

app/models/alchemy/picture.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def self.preprocessor_class=(klass)
6262
@_preprocessor_class = klass
6363
end
6464

65+
before_create :set_name, if: :image_file_name
66+
6567
include Alchemy.storage_adapter.picture_class_methods
6668

6769
# We need to define this method here to have it available in the validations below.
@@ -195,14 +197,6 @@ def urlname
195197
end
196198
end
197199

198-
# Returns a humanized, readable name from image filename.
199-
#
200-
def humanized_name
201-
return "" if image_file_name.blank?
202-
203-
convert_to_humanized_name(image_file_name, image_file_extension)
204-
end
205-
206200
# Returns the format the image should be rendered with
207201
#
208202
# Only returns a format differing from original if an +image_output_format+
@@ -287,6 +281,12 @@ def image_file_dimensions
287281

288282
private
289283

284+
# Returns a humanized, readable name from image filename.
285+
#
286+
def set_name
287+
self.name ||= Alchemy.storage_adapter.image_file_basename(self).humanize
288+
end
289+
290290
def image_file_type_allowed
291291
unless image_file_extension&.in?(self.class.allowed_filetypes)
292292
errors.add(:image_file, Alchemy.t("not a valid image"))

app/models/alchemy/storage_adapter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ class UnknownAdapterError < StandardError; end
99
:by_file_format_scope,
1010
:by_file_type_scope,
1111
:not_file_type_scope,
12+
:file_basename,
1213
:file_extension,
1314
:file_formats,
1415
:file_mime_type,
1516
:file_name,
1617
:file_size,
1718
:has_convertible_format?,
19+
:image_file_basename,
1820
:image_file_extension,
1921
:image_file_format,
2022
:image_file_height,

app/models/alchemy/storage_adapter/active_storage.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ def not_file_type_scope(file_type)
9696
Attachment.with_attached_file.joins(:file_blob).where.not(active_storage_blobs: {content_type: file_type})
9797
end
9898

99+
# @param [Alchemy::Attachment]
100+
# @return [String]
101+
def file_basename(attachment)
102+
filename = attachment.file&.filename.to_s
103+
File.basename(filename, File.extname(filename))
104+
end
105+
99106
# @param [Alchemy::Attachment]
100107
# @return [String]
101108
def file_name(attachment)
@@ -126,6 +133,13 @@ def has_convertible_format?(picture)
126133
picture.image_file&.variable?
127134
end
128135

136+
# @param [Alchemy::Picture]
137+
# @return [String]
138+
def image_file_basename(picture)
139+
filename = picture.image_file&.filename.to_s
140+
File.basename(filename, File.extname(filename))
141+
end
142+
129143
# @param [Alchemy::Picture]
130144
# @return [String]
131145
def image_file_name(picture)

app/models/alchemy/storage_adapter/dragonfly.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ def file_name(attachment)
127127
attachment.read_attribute(:file_name)
128128
end
129129

130+
# @param [Alchemy::Attachment]
131+
# @return [String]
132+
def file_basename(attachment)
133+
attachment.file&.basename&.to_s
134+
end
135+
130136
# @param [Alchemy::Attachment]
131137
# @return [Integer]
132138
def file_size(attachment)
@@ -152,6 +158,12 @@ def has_convertible_format?(picture)
152158
image_file_extension(picture).in?(CONVERTIBLE_FILE_FORMATS)
153159
end
154160

161+
# @param [Alchemy::Picture]
162+
# @return [String]
163+
def image_file_basename(picture)
164+
picture.image_file&.basename&.to_s
165+
end
166+
155167
# @param [Alchemy::Picture]
156168
# @return [String]
157169
def image_file_name(picture)

lib/alchemy/name_conversions.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ def convert_to_urlname(name)
1717
.parameterize
1818
end
1919

20-
# Converts a filename and suffix into a human readable name.
21-
#
22-
def convert_to_humanized_name(name, suffix)
23-
name.gsub(/\.#{::Regexp.quote(suffix)}$/i, "").tr("_", " ").strip
24-
end
25-
2620
# Sanitizes a given filename by removing directory traversal attempts and HTML entities.
2721
def sanitized_filename(file_name)
2822
file_name = File.basename(file_name)

spec/controllers/alchemy/admin/pictures_controller_spec.rb

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,14 @@ module Alchemy
141141
describe "#create" do
142142
subject { post :create, params: params }
143143

144-
let(:params) { {picture: {name: ""}} }
145-
let(:picture) { mock_model("Picture", humanized_name: "Cute kittens") }
146-
147144
context "with passing validations" do
148-
before do
149-
expect(Picture).to receive(:new).and_return(picture)
150-
expect(picture).to receive(:name=).and_return("Cute kittens")
151-
expect(picture).to receive(:name).and_return("Cute kittens")
152-
expect(picture).to receive(:save).and_return(true)
145+
let(:params) do
146+
{picture: {image_file: fixture_file_upload("my FileNämü.png")}}
147+
end
148+
149+
it "creates a new picture with human friendly name" do
150+
expect { subject }.to change { Picture.count }.by(1)
151+
expect(Picture.last.name).to eq("My filenämü")
153152
end
154153

155154
it "renders json response with success message" do
@@ -162,6 +161,8 @@ module Alchemy
162161
end
163162

164163
context "with failing validations" do
164+
let(:params) { {picture: {name: ""}} }
165+
165166
it_behaves_like "having a json uploader error message"
166167
end
167168
end
70 Bytes
Loading
73 Bytes
Loading

0 commit comments

Comments
 (0)