The file manager's rename prompt was empty: Kernel#j, not escape_javascript

The admin file manager builds its rename and delete buttons with inline
handlers, and interpolated the file name through `j`:

    onclick: "var n=prompt('New name:','#{j entry[:name]}');..."

In a view `j` is `escape_javascript`. Inside an Arbre block it is not: Arbre
resolves unknown methods through `method_missing`, and `j` is not unknown — it
is `Kernel#j`, which prints its argument as JSON to stdout and returns nil. So
every page load wrote the file names to the server log, and the browser got

    prompt('New name:','')

An admin pressing rename saw an empty prompt, and the delete confirmation asked
"Delete ''?". `escape_javascript(...)` spelled out is what those three
interpolations use now.

The page has no test, which is why nothing caught it. It has one now
(spec/requests/admin_files_spec.rb), and it asserts the file name is in both
handlers — with the icons, the folder creation, the failed folder creation and
the delete-returns-to-parent path, because those are the behaviours the
refactoring below could break silently.

Also in the page: the twenty-branch extension-to-emoji `case` moved out of the
view into `WarpEngine::FileIcon`, and the five page actions share one
`redirect_to_files` instead of repeating
`admin_files_path(dir:, picker:, field:)` six times.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 09:02:09 +02:00
co-authored by Claude Opus 5
parent e9594a663d
commit 878d73ce9b
2 changed files with 58 additions and 46 deletions
+26
View File
@@ -0,0 +1,26 @@
module WarpEngine
module FileIcon
BY_EXTENSION = {
%w[.png .jpg .jpeg .gif .bmp .webp .svg] => "🖼️",
%w[.mp3 .ogg .wav .flac .aac] => "🔊",
%w[.mp4 .avi .mkv .webm .mov] => "🎬",
%w[.zip .gz .tar .rar .7z .bz2] => "📦",
%w[.pdf] => "📕",
%w[.doc .docx .odt .txt .md .rtf] => "📄",
%w[.xls .xlsx .csv .ods] => "📊",
%w[.html .htm .css .js .ts .json .xml] => "📝",
%w[.rb .py .lua .c .cpp .h .rs .go] => "💻",
%w[.tic .rom .bin .prg .crt .d64 .t64 .love] => "🎮",
%w[.exe .dmg .appimage .msi .wasm] => "⚙️"
}.freeze
DEFAULT = "📄".freeze
DIRECTORY = "📁".freeze
def self.for(name)
ext = File.extname(name.to_s).downcase
BY_EXTENSION.each { |extensions, icon| return icon if extensions.include?(ext) }
DEFAULT
end
end
end