[{"data":1,"prerenderedAt":131},["ShallowReactive",2],{"article-\u002Fwriting\u002Ffile-field-paths-8x-10-stable-and-back-under-maintenance-20261001":3},{"id":4,"title":5,"articleType":6,"categories":7,"date":10,"description":11,"extension":12,"meta":13,"paragraphs":14,"path":126,"readingTime":127,"sitemap":128,"stem":129,"__hash__":130},"articleEntries\u002Farticles-data\u002Ffile-field-paths-8x-10-stable-and-back-under-maintenance-20261001.json","File (Field) Paths 8.x-1.0; stable and back under maintenance","Blog post",[8,9],"Drupal","Planet Drupal","2026-10-01T16:00:00+10:00","Drupal drops your uploads in a dated folder and leaves the filename alone. File (Field) Paths builds both from tokens instead. 8.x-1.0 is stable on Drupal 10.3 and 11, and the module is actively maintained again.","json",{},[15,25,49,57,80,92,102,112,121],{"type":16,"layout":17,"regions":18},"section","layout_onecol",{"content":19},[20,23],{"type":21,"html":22},"text_formatted","\u003Cp>File (Field) Paths is stable. \u003Ccode>8.x-1.0\u003C\u002Fcode> is the first stable release since the Drupal 7 one (long may it live), and it does what it has always done: sorts and renames your uploads with token patterns, so you get a filesystem you can read.\u003C\u002Fp>",{"type":21,"html":24},"\u003Cp>If you've been holding off while it sat in RC, you don't need to. It has 215 automated tests and 1287 assertions where \u003Ccode>beta8\u003C\u002Fcode> had 21 tests, a safer default for where uploads wait, and a plan for what comes next. It's under active maintenance again.\u003C\u002Fp>",{"type":16,"title":26,"layout":17,"regions":27},"If you haven't used it",{"content":28},[29,31,33,38,40,42],{"type":21,"html":30},"\u003Cp>Core file fields let you choose a directory, and that setting takes tokens. Drupal ships \u003Ccode>[date:custom:Y]-[date:custom:m]\u003C\u002Fcode> as the default. What it can't take is a token about the entity you're attaching the file to, because at the moment the file is saved those values don't exist yet. The file name isn't touched at all.\u003C\u002Fp>",{"type":21,"html":32},"\u003Cp>It puts the upload somewhere else first, waits for the save, then moves and renames it once the tokens mean something. You get two patterns per field. Here's what I run on an article's image field:\u003C\u002Fp>",{"type":34,"title":35,"code":36,"language":37},"code","The settings on an article's image field","File path    [node:url:path]\nFile name    [file:ffp-name-only-original].[file:ffp-extension-original]","text",{"type":21,"html":39},"\u003Cp>Upload \u003Ccode>FFP Showcase Hero Shot.PNG\u003C\u002Fcode> to this post and it ends up at \u003Ccode>writing\u002Ffile-field-paths-8x-10-stable-and-back-under-maintenance-20261001\u002Fffp-showcase-hero-shot.png\u003C\u002Fcode>. The directory is the post's own URL, the name gets transliterated and cleaned up on the way through, and the original filename stays on the file record. You'll want Pathauto installed for the cleaning, and \u003Cem>Cleanup using Pathauto\u003C\u002Fem> ticked on the file name, or the raw value goes straight in. Building the path out of the entity the file belongs to is the part core has never done.\u003C\u002Fp>",{"type":21,"html":41},"\u003Cp>Changing a pattern doesn't touch anything you've already uploaded. New files follow it, old ones stay put until you ask, either with \u003Cem>Retroactive update\u003C\u002Fem> on the field or \u003Ccode>drush filefield_paths:update\u003C\u002Fcode>. Do that on a copy first. It moves every file at once, and it'll break links to them unless \u003Cem>Create Redirect\u003C\u002Fem> is on, which wants the \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fproject\u002Fredirect\">Redirect\u003C\u002Fa> module.\u003C\u002Fp>",{"type":43,"alt":44,"caption":45,"width":46,"height":47,"src":48},"media","The File (Field) Paths section of an image field's settings form, showing a file path and file name built from node tokens","\u003Cp>A path and a name built from the entity the file is attached to. That is the whole trick.\u003C\u002Fp>",960,1040,"\u002Fimages\u002Fwriting\u002Ffile-field-paths-8x-10-stable-and-back-under-maintenance-20261001\u002Ffilefield-paths-10-field-settings.png",{"type":16,"title":50,"layout":17,"regions":51},"What's new in 8.x-1.0",{"content":52},[53,55],{"type":21,"html":54},"\u003Cp>The image widget no longer shows the wrong thumbnail when two uploads share a name (\u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fi\u002F3277844\">#3277844\u003C\u002Fa>). A new upload no longer creates a redirect every time (\u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fi\u002F3494240\">#3494240\u003C\u002Fa>), and the PHP 8.2 that \u003Ccode>rc2\u003C\u002Fcode> already needed is finally declared (\u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fi\u002F3622262\">#3622262\u003C\u002Fa>). Being stable also means security advisory coverage.\u003C\u002Fp>",{"type":21,"html":56},"\u003Cp>I've been putting all of my modules on \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fu\u002Falexskrypnyk\">Alex Skrypnyk\u003C\u002Fa>'s \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FAlexSkrypnyk\u002Fdrupal_extension_scaffold\">Drupal Extension Scaffold\u003C\u002Fa>, running Drupal's own CI templates on my own GitLab with GitHub beside it. Most of my time on this release went into the tests that pipeline runs: 9 test files at \u003Ccode>rc1\u003C\u002Fcode>, 36 now. That's what tells me the module still works when I come back to it after months on something else.\u003C\u002Fp>",{"type":16,"title":58,"layout":17,"regions":59},"Where uploads wait",{"content":60},[61,63,66,68,72,74],{"type":21,"html":62},"\u003Cp>Because the tokens only resolve once the entity is saved, every upload waits somewhere first. If that staging directory sits under \u003Ccode>public:\u002F\u002F\u003C\u002Fcode>, a file headed for a private field is in the web root until the save finishes. The module's been saying so on the status report since \u003Ccode>beta6\u003C\u002Fcode> in December 2022, as a partial fix for \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fsa-contrib-2022-065\">SA-CONTRIB-2022-065\u003C\u002Fa>:\u003C\u002Fp>",{"type":34,"title":64,"code":65,"language":37},"The status report, when staging points at public:\u002F\u002F","File (Field) Paths temporary path      Insecure!\n\nThis site supports private files but the File (Field) Paths temporary\npath is under public:\u002F\u002F which could lead to private files being\ntemporarily exposed publicly. Change the temporary path to be under\ntemporary:\u002F\u002F or private:\u002F\u002F in order to secure your files.",{"type":21,"html":67},"\u003Cp>That release also added an update hook, so database updates move a \u003Ccode>public:\u002F\u002F\u003C\u002Fcode> staging path onto \u003Ccode>temporary:\u002F\u002F\u003C\u002Fcode>, or \u003Ccode>private:\u002F\u002F\u003C\u002Fcode> if \u003Ccode>temporary:\u002F\u002F\u003C\u002Fcode> isn't writable. If you've run updates any time since 2022 it's already happened:\u003C\u002Fp>",{"type":34,"title":69,"code":70,"language":71,"highlighted":70},"Check where uploads wait","drush config:get filefield_paths.settings temp_location","bash",{"type":21,"html":73},"\u003Cp>Since \u003Ccode>rc1\u003C\u002Fcode> a field can set its own staging location, and the update hook and the status report don't check that override yet. If you've set one, have a look at it. It's on the \u003Ccode>1.1\u003C\u002Fcode> list. \u003Ccode>temporary:\u002F\u002F\u003C\u002Fcode> has been the recommended scheme since 2022, and now that \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fi\u002F3121826\">#3121826\u003C\u002Fa> in \u003Ccode>rc2\u003C\u002Fcode> builds image derivatives on demand, \u003Ccode>private:\u002F\u002F\u003C\u002Fcode> is only for the case it was always for: several web servers with no shared temporary directory.\u003C\u002Fp>",{"type":43,"alt":75,"caption":76,"width":77,"height":78,"src":79},"The File (Field) Paths settings form with Temporary file location set to temporary:\u002F\u002Ffilefield_paths","\u003Cp>The site-wide setting, at Configuration &gt; Media &gt; File system &gt; File (Field) Paths.\u003C\u002Fp>",984,296,"\u002Fimages\u002Fwriting\u002Ffile-field-paths-8x-10-stable-and-back-under-maintenance-20261001\u002Ffilefield-paths-10-temporary-location.png",{"type":16,"title":81,"layout":17,"regions":82},"What's next",{"content":83},[84,86,88,90],{"type":21,"html":85},"\u003Cp>Field Tokens, Custom Formatters and JSON:API Views have each had this treatment this year. This is the fourth, and the one with the longest queue. The code is modern and the tests are in. The queue is next.\u003C\u002Fp>",{"type":21,"html":87},"\u003Cp>The \u003Ca href=\"https:\u002F\u002Fgit.drupalcode.org\u002Fproject\u002Ffilefield_paths\u002F-\u002Fmilestones\">milestones\u003C\u002Fa> carry that plan. \u003Ccode>1.0\u003C\u002Fcode> takes bug fixes only, \u003Ccode>1.1\u003C\u002Fcode> is where the queue catch-up goes, \u003Ccode>1.2\u003C\u002Fcode> is the long tail, and breaking changes wait for \u003Ccode>2.0.0\u003C\u002Fcode>. The oldest open issue, \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fi\u002F1115740\">#1115740\u003C\u002Fa> from April 2011, is on the \u003Ccode>1.1\u003C\u002Fcode> list, with a kernel test on its issue fork that reproduces it. The patch is still to write.\u003C\u002Fp>",{"type":21,"html":89},"\u003Cp>If you want to take something on, \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fi\u002F3069511\">#3069511\u003C\u002Fa>, replacing existing files, is the most-watched issue in the queue and needs a design decision more than it needs code. \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fi\u002F3182718\">#3182718\u003C\u002Fa>, taxonomy hierarchy in paths, has a community patch waiting on review.\u003C\u002Fp>",{"type":21,"html":91},"\u003Cp>If you write code against this one, I've deprecated six procedural functions in \u003Ccode>8.x-1.0\u003C\u002Fcode> and they go in \u003Ccode>2.0.0\u003C\u002Fcode>. Each has a \u003Ccode>#[\\Deprecated]\u003C\u002Fcode> attribute naming what replaced it, so Upgrade Status will tell you if you're calling them.\u003C\u002Fp>",{"type":16,"title":93,"layout":17,"regions":94},"A big thanks to the community",{"content":95},[96,98,100],{"type":21,"html":97},"\u003Cp>Big thanks to \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fu\u002Fvoleger\">Oleh Vehera\u003C\u002Fa>, who kept this module alive while my focus was elsewhere. The Drupal 9 port, then Drupal 10, the move to Drush 12, the hooks lifted into classes and the services behind them are all his. So is the status report warning above. He has commit access.\u003C\u002Fp>",{"type":21,"html":99},"\u003Cp>And to everyone else who kept it moving. rpayanm, dunebaud and Paulino Michelazzo cut releases. Bryan Sharpe, Peter Wolanin, Jeremy Stoller, Chandansha Fakir, Youri van Koppen and solideogloria got \u003Ccode>rc1\u003C\u002Fcode> out. dshields, xamount, Sakshi Sharma and Francesco Maria Battaglia fixed the last things standing between \u003Ccode>rc2\u003C\u002Fcode> and today. Jeffrey Clark, Aidan Lister and Magnus Gunnarsson were doing this back in the Drupal 6 days. If you have ever put a patch on this module, your name is in the changelog.\u003C\u002Fp>",{"type":21,"html":101},"\u003Cp>I'm independent now, working on open source full time, across Drupal and Druxt. There's no client work paying for it, so sponsorship is what makes the time possible. That's what got \u003Ccode>8.x-1.0\u003C\u002Fcode> out, and it's what keeps the rest of them moving.\u003C\u002Fp>",{"type":16,"layout":17,"regions":103},{"content":104},[105,107,110],{"type":21,"html":106},"\u003Cp>Tens of thousands of sites run this one, and several thousand are still on \u003Ccode>7.x-1.x\u003C\u002Fcode>. If any of this is useful to you, \u003Ccode>8.x-1.0\u003C\u002Fcode> is out now:\u003C\u002Fp>",{"type":34,"code":108,"language":71,"highlighted":109},"composer require 'drupal\u002Ffilefield_paths:^1.0'","\u003Cspan class=\"token function\">composer\u003C\u002Fspan> require \u003Cspan class=\"token string\">'drupal\u002Ffilefield_paths:^1.0'\u003C\u002Fspan>",{"type":21,"html":111},"\u003Cp>Composer maps the tag to \u003Ccode>1.0.0\u003C\u002Fcode>, so \u003Ccode>^1.0\u003C\u002Fcode> is the constraint you want. If you're stuck on PHP 8.1, \u003Ccode>1.0.0-rc1\u003C\u002Fcode> is the last release that runs there, but it carries bugs \u003Ccode>rc2\u003C\u002Fcode> fixed and the security team doesn't cover release candidates. Moving to 8.2 is the better answer.\u003C\u002Fp>",{"type":16,"layout":17,"regions":113},{"content":114},[115],{"type":116,"description":117,"url":118,"gitpod":119,"drupalUrl":120},"repository","\u003Cp>Source on GitHub, with the project page, the issue queue and the full changelog on Drupal.org. Patches welcome in the queue, and if your sites lean on this one, sponsoring is what keeps it maintained.\u003C\u002Fp>","https:\u002F\u002Fgithub.com\u002FDecipher\u002Ffilefield_paths",false,"https:\u002F\u002Fwww.drupal.org\u002Fproject\u002Ffilefield_paths",{"type":16,"layout":17,"regions":122},{"content":123},[124],{"type":21,"html":125},"\u003Cp>So what's in your path pattern? Mine is the post's own URL. I'd like to know what people are doing with the entity tokens I never thought to try.\u003C\u002Fp>","\u002Fwriting\u002Ffile-field-paths-8x-10-stable-and-back-under-maintenance-20261001","5 min",{"loc":126},"articles-data\u002Ffile-field-paths-8x-10-stable-and-back-under-maintenance-20261001","lBJkLMpRX7c3Mt2j7IQM2KIGkSbEDbAJ8htkFdy0rrs",1790836388256]