Skip to content

Webform 'Rename files' strips non-Latin characters instead of transliterating them #261

Description

@victoralexandrov

_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

  1. Enable all three options at admin/config/media/file-system:
    • Transliterate file names during upload
    • Transliterate the displayed file name
    • Lowercase transliterated file names
  2. Create a webform with a text field (e.g. author_name) and a File component.
  3. On the File component, set Rename files to something like:
    [submission:values:author:author_name]-work1-[submission:values:work1:work1_title]
  4. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions