_webform_transliterate() silently drops non-Latin (e.g. Cyrillic) characters when "Rename files" is used, because transliteration.inc is not guaranteed to be loaded
Summary
When a File component's "Rename files" option (extra][rename]) includes a token whose submitted value contains non-Latin characters (e.g. Cyrillic), those characters are silently deleted from the resulting filename instead of being transliterated — even though the site's core Transliteration settings (admin/config/media/file-system) are fully enabled and work correctly for normal file uploads elsewhere on the site.
Steps to reproduce
- Enable all three options at
admin/config/media/file-system:
- Transliterate file names during upload
- Transliterate the displayed file name
- Lowercase transliterated file names
- Create a webform with a text field (e.g.
author_name) and a File component.
- On the File component, set Rename files to something like:
[submission:values:author:author_name]-work1-[submission:values:work1:work1_title]
- Submit the webform with:
author_name containing Cyrillic text, e.g. Христина Иванова
work1_title containing plain ASCII text, e.g. Time for a Change
- a file uploaded for the File component
Expected result
The Cyrillic value should be transliterated to Latin characters, consistent with the site's Transliteration settings and with Drupal 7 Webform's prior behavior on the same site content before migration — producing something like:
Hristina-Ivanova-work1-Time-for-a-Change.jpg
Actual result
The Cyrillic portion is dropped entirely rather than transliterated, and the ASCII portion passes through completely unprocessed (case and spaces preserved), producing a filename like:
-work1-Time for a Change.jpg
(Note the leading space — the sole remnant of the space that separated the two words of the Cyrillic name.)
Root cause
In webform.module:
/**
* Transliterate common non-English characters to 7-bit ASCII.
*/
function _webform_transliterate($name) {
// If transliteration is available, use it to convert names to ASCII.
return function_exists('transliteration_get')
? transliteration_get($name, '')
: str_replace(array('€', 'ƒ', 'Š', /* ... Latin-1 accented characters only ... */),
array('E', 'f', 'S', /* ... */),
$name);
}
This function assumes transliteration_get() (defined in Backdrop core's core/includes/transliteration.inc) is already loaded, and only calls it if function_exists('transliteration_get') is true. But nothing in _webform_transliterate() (or its caller, webform_file_process_rename() in components/file.inc) actually loads that include file.
In contrast, Backdrop core's own upload handling in system.module explicitly loads it before use:
include_once BACKDROP_ROOT . '/core/includes/transliteration.inc';
$_FILES['files']['name'][$field] = transliteration_clean_filename($filename, $langcode);
That core code path only runs during the initial AJAX file upload request for a managed_file/file field. The webform "Rename files" logic, however, runs later — during the final webform submission request, in webform_file_process_rename() — which is a separate request that never triggers system.module's upload-time include. As a result, transliteration.inc has often not been loaded by the time _webform_transliterate() runs, function_exists('transliteration_get') returns false, and the code falls back to its own bundled table.
That fallback table only maps a small set of accented Latin-1 characters (é, ñ, ü, etc.) and has no Cyrillic entries at all. Untransliterated Cyrillic characters pass through the fallback unchanged, and are then stripped by the very next line in webform_file_process_rename():
$new_file_name = preg_replace('/[^a-zA-Z0-9_\- ]/', '', $new_file_name);
which removes anything outside [a-zA-Z0-9_- ] — deleting all the (untransliterated) Cyrillic bytes while leaving any literal ASCII spaces between them, which produces exactly the observed leading-space artifact.
Suggested fix
Have _webform_transliterate() guarantee the include is loaded before checking for the function, the same way system.module does:
function _webform_transliterate($name) {
include_once BACKDROP_ROOT . '/core/includes/transliteration.inc';
return function_exists('transliteration_get')
? transliteration_get($name, '')
: str_replace(array('€', 'ƒ', 'Š', /* ... */), array('E', 'f', 'S', /* ... */), $name);
}
This is a minimal, low-risk one-line addition and removes the dependency on load order / which other code has already run earlier in the request.
Environment
- Backdrop CMS 1.34.3 (core Transliteration merged into core as of 1.3.0)
- Webform module 1.x-4.26.5 (backdrop-contrib/webform)
- Site migrated from a Drupal 7 installation where the equivalent Rename-files + transliteration combination worked correctly
_webform_transliterate()silently drops non-Latin (e.g. Cyrillic) characters when "Rename files" is used, becausetransliteration.incis not guaranteed to be loadedSummary
When a File component's "Rename files" option (
extra][rename]) includes a token whose submitted value contains non-Latin characters (e.g. Cyrillic), those characters are silently deleted from the resulting filename instead of being transliterated — even though the site's core Transliteration settings (admin/config/media/file-system) are fully enabled and work correctly for normal file uploads elsewhere on the site.Steps to reproduce
admin/config/media/file-system:author_name) and a File component.[submission:values:author:author_name]-work1-[submission:values:work1:work1_title]author_namecontaining Cyrillic text, e.g.Христина Ивановаwork1_titlecontaining plain ASCII text, e.g.Time for a ChangeExpected result
The Cyrillic value should be transliterated to Latin characters, consistent with the site's Transliteration settings and with Drupal 7 Webform's prior behavior on the same site content before migration — producing something like:
Hristina-Ivanova-work1-Time-for-a-Change.jpgActual result
The Cyrillic portion is dropped entirely rather than transliterated, and the ASCII portion passes through completely unprocessed (case and spaces preserved), producing a filename like:
-work1-Time for a Change.jpg(Note the leading space — the sole remnant of the space that separated the two words of the Cyrillic name.)
Root cause
In
webform.module:This function assumes
transliteration_get()(defined in Backdrop core'score/includes/transliteration.inc) is already loaded, and only calls it iffunction_exists('transliteration_get')is true. But nothing in_webform_transliterate()(or its caller,webform_file_process_rename()incomponents/file.inc) actually loads that include file.In contrast, Backdrop core's own upload handling in
system.moduleexplicitly loads it before use:That core code path only runs during the initial AJAX file upload request for a managed_file/file field. The webform "Rename files" logic, however, runs later — during the final webform submission request, in
webform_file_process_rename()— which is a separate request that never triggerssystem.module's upload-time include. As a result,transliteration.inchas often not been loaded by the time_webform_transliterate()runs,function_exists('transliteration_get')returnsfalse, and the code falls back to its own bundled table.That fallback table only maps a small set of accented Latin-1 characters (é, ñ, ü, etc.) and has no Cyrillic entries at all. Untransliterated Cyrillic characters pass through the fallback unchanged, and are then stripped by the very next line in
webform_file_process_rename():which removes anything outside
[a-zA-Z0-9_- ]— deleting all the (untransliterated) Cyrillic bytes while leaving any literal ASCII spaces between them, which produces exactly the observed leading-space artifact.Suggested fix
Have
_webform_transliterate()guarantee the include is loaded before checking for the function, the same waysystem.moduledoes:This is a minimal, low-risk one-line addition and removes the dependency on load order / which other code has already run earlier in the request.
Environment