We have already received this request three times from customers in the higher education sector, and I think this information could be quite useful for some of them.
TYPO3: Editors with individual user_upload folders
Perhaps you're familiar with this client requirement? Editors should be able to add videos using the "Add media by URL" button. But the files shouldn't be located in fileadmin/user_upload/, but rather in a custom folder? We'll show you how to do that.
Show the "Add media by URL" button for your file field
First, a few potential pitfalls if the “Add media by URL” button isn't visible to editors:
- Make sure the editor has access to fileadmin/user_upload/ (or another storage location – e.g., 3:/user_upload/).
- In the TCA, you should set “fileByUrlAllowed” to true (if it isn't already).
'assets' => [
'label' => 'File',
'config' => [
'type' => 'file',
'maxitems' => 1,
'allowed' => 'mp4, webm, youtube, vimeo, ard',
'appearance' => [
'fileUploadAllowed' => true,
'fileByUrlAllowed' => true,
],
... Set individual user_upload folders for each user
However, if you have a large number of backend users on your system, you might want to set these folders automatically.
Using an EventListener, you can override the default behavior and assign each user their own folder. In this example, the backend user with the username “alexander.kellner” automatically receives the folder 1:/user__alexander_kellner/ as their default upload folder.
<?php
declare(strict_types=1);
namespace In2code\In2template\EventListener\Backend\Resource;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
use TYPO3\CMS\Core\Resource\Event\AfterDefaultUploadFolderWasResolvedEvent;
use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
use TYPO3\CMS\Core\Resource\ResourceFactory;
/**
* Add individual user_upload folder for editors like "fileadmin/user__alexander_kellner"
*/
#[AsEventListener(
identifier: 'in2template/defaultuploadfolder',
)]
final readonly class DefaultUploadFolderResolver
{
private const int STORAGE_USERUPLOAD = 1;
public const string FOLDER_PREFIX = 'user__';
public function __construct(private ResourceFactory $resourceFactory, private Context $context)
{
}
public function __invoke(AfterDefaultUploadFolderWasResolvedEvent $event): void
{
$storage = $this->resourceFactory->getStorageObject(self::STORAGE_USERUPLOAD);
$folderName = self::FOLDER_PREFIX . $storage->sanitizeFileName($this->getUsername());
try {
$folder = $this->resourceFactory->getFolderObjectFromCombinedIdentifier(
self::STORAGE_USERUPLOAD . ':/' . $folderName . '/'
);
} catch (FolderDoesNotExistException) {
if ($storage->hasFolder($folderName) === false) {
$folder = $storage->createFolder($folderName);
} else {
$folder = $storage->getFolder($folderName);
}
}
$event->setUploadFolder($folder);
}
/**
* @return string
* @SuppressWarnings(PHPMD.Superglobals)
*/
private function getUsername(): string
{
try {
$username = $this->context->getPropertyFromAspect('backend.user', 'username');
} catch (AspectNotFoundException $e) {
$username = $GLOBALS['BE_USER']->user['username'] ?? 'default';
}
return str_replace('.', '_', $username);
}
}
Automatically hide folders for editors from other users
If your client wants to restrict editors to their own upload folders for data protection reasons, you can achieve this with a FAL filter.
Register this in the ext_localconf.php file of your site package:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['defaultFilterCallbacks'][] = [
GeneralUtility::makeInstance(UserFolderFilter::class),
'filterUserFolders',
]; The filter file for this is:
<?php
declare(strict_types=1);
namespace In2code\In2template\Backend\Resource\Filter;
use In2code\In2template\EventListener\Backend\Resource\DefaultUploadFolderResolver;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
/**
* Filter to restrict visibility of user folders to only the current users folder for non-admins.
*/
final readonly class UserFolderFilter
{
public function __construct(private Context $context)
{
}
public function filterUserFolders(string $itemName): int|bool
{
if ($this->isAdministrator() === false && $this->isUserFolder($itemName)) {
if ($itemName !== DefaultUploadFolderResolver::FOLDER_PREFIX . $this->getUsername()) {
return -1;
}
}
return true;
}
private function isUserFolder(string $folderName): bool
{
return str_starts_with($folderName, DefaultUploadFolderResolver::FOLDER_PREFIX);
}
private function isAdministrator(): bool
{
return $this->context->getPropertyFromAspect('backend.user', 'isAdmin');
}
/**
* @SuppressWarnings(PHPMD.Superglobals)
*/
private function getUsername(): string
{
try {
$username = $this->context->getPropertyFromAspect('backend.user', 'username');
} catch (AspectNotFoundException $e) {
$username = $GLOBALS['BE_USER']->user['username'] ?? 'default';
}
return str_replace('.', '_', $username);
}
}




