EditorUploadTrait
File manager for TinyMCE (admin `rich-editor.js`, full editor) over its own folder `uploads/editorUploads/`: listing, subfolders, upload, rename, delete.
Deliberately separate from assets: images inserted into page or article text have no database
record and no variants; it is a plain folder that receives whatever the editor picks.
Management of the whole uploads/ (including other modules) stays in the admin under
System, Files (FileSystemPresenter), which uses the
same Api layer.
The editor root is EDITOR_UPLOAD_FOLDER. The client works with paths relative to it and the prefix is added only here (editorPath()), so the editor cannot reach outside its own folder even with a hand-made request.
All reads and writes go through FileSystemManager (Api layer); admin never touches the file system directly and gets path traversal protection and name sanitization for free.
Signals (handle*) are chosen on purpose: Nette 3.2+ gives them
#[Requires(sameOrigin: true)] automatically, so the endpoints have CSRF protection without
an own token. They live on the admin BasePresenter side because the editor can be on any
admin page and the link {link editorUpload!} must point to the presenter currently running
(a signal of another presenter cannot be linked).
Table of Contents
Constants
- BLOCKED_EXTENSIONS : mixed = ['php', 'php3', 'php4', 'php5', 'php7', 'php8',...
- Extensions that must not be uploaded into the web-served folder; otherwise an editor upload would mean running foreign code on the server. It is no content whitelist (the editor uploads what is needed), only a safeguard against executable files.
- EDITOR_UPLOAD_FOLDER : mixed = 'editorUploads'
- Editor root (relative to the `uploads/` root). Changing it to `''` would open the whole `uploads/` tree to the manager; the editor user could then reach (and delete) files of other modules such as products and avatars.
- IMAGE_EXTENSIONS : mixed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', '...
- Extensions the file manager shows as a preview instead of a document icon.
Properties
- $editorUploadManager : FileSystemManager
- NOTE: own property name, not `$fileSystemManager`.
Methods
- handleEditorCreateFolder() : void
- Creates a subfolder in `folder` (path relative to the editor root).
- handleEditorDelete() : void
- Deletes a file or (recursively) a folder. An empty path would point to the editor root, which cannot be deleted.
- handleEditorFiles() : void
- Sends the folder listing for the editor file manager. The `folder` parameter is a path relative to the editor root (empty means the root).
- handleEditorRename() : void
- Renames a file or folder (`path` relative to the editor root).
- handleEditorUpload() : void
- Accepts a file from the editor (field `file`, multipart POST; optional `folder` = subfolder of the editor root) and sends JSON `{"location": "/www/uploads/editorUploads/..."}`, the format TinyMCE expects from `images_upload_handler`.
- editorBreadcrumb() : array<int, array{label: string, path: string}>
- Builds the breadcrumb navigation: cumulative segment paths.
- editorParentFolder() : string|null
- Returns the parent folder (null in the editor root).
- editorPath() : string|null
- Converts a client path (relative to the editor root) to an Api path (relative to the `uploads/` root).
- editorUploadName() : string|null
- Builds a safe and unique file name. Sanitization copies the Api layer rules (webalize of the base and of the extension), so the name returned in the URL is exactly the name the file is stored under. The random suffix prevents collisions: `writeFile()` overwrites the same name without asking, so two different `obrazek.jpg` files would overwrite each other.
- editorUploadUrl() : string
- Returns the public URL of a file. `$path` is relative to the `uploads/` root (so it includes `editorUploads/`), the same shape as other uploads in the system.
- ensureEditorRoot() : void
- Creates the editor root folder; when it already exists the Api returns false and nothing happens.
- storeEditorUpload() : bool
- Stores a file; the target folder may not exist (first upload into the root), so after a failure the root is created once and the write is retried. Deeper subfolders are created by the editor user through `editorCreateFolder`.
Constants
BLOCKED_EXTENSIONS
Extensions that must not be uploaded into the web-served folder; otherwise an editor upload would mean running foreign code on the server. It is no content whitelist (the editor uploads what is needed), only a safeguard against executable files.
private
mixed
BLOCKED_EXTENSIONS
= ['php', 'php3', 'php4', 'php5', 'php7', 'php8', 'phps', 'phtml', 'phar', 'cgi', 'pl', 'py', 'sh', 'bash', 'htaccess', 'htpasswd']
EDITOR_UPLOAD_FOLDER
Editor root (relative to the `uploads/` root). Changing it to `''` would open the whole `uploads/` tree to the manager; the editor user could then reach (and delete) files of other modules such as products and avatars.
private
mixed
EDITOR_UPLOAD_FOLDER
= 'editorUploads'
IMAGE_EXTENSIONS
Extensions the file manager shows as a preview instead of a document icon.
private
mixed
IMAGE_EXTENSIONS
= ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'svg', 'bmp']
Properties
$editorUploadManager
NOTE: own property name, not `$fileSystemManager`.
public
FileSystemManager
$editorUploadManager
FileSystemPresenter takes the same manager as
private readonly in its constructor, and PHP cannot redeclare a property of the same
name between public and private.
Attributes
- #[Inject]
Methods
handleEditorCreateFolder()
Creates a subfolder in `folder` (path relative to the editor root).
public
handleEditorCreateFolder([string $folder = '' ][, string $name = '' ]) : void
Parameters
- $folder : string = ''
- $name : string = ''
Attributes
- #[Requires]
- $methods: 'POST'
handleEditorDelete()
Deletes a file or (recursively) a folder. An empty path would point to the editor root, which cannot be deleted.
public
handleEditorDelete([string $path = '' ]) : void
Parameters
- $path : string = ''
Attributes
- #[Requires]
- $methods: 'POST'
handleEditorFiles()
Sends the folder listing for the editor file manager. The `folder` parameter is a path relative to the editor root (empty means the root).
public
handleEditorFiles([string $folder = '' ]) : void
Sends JSON {folder, parent, breadcrumb[], folders[], files[]}; paths in the response are
again relative to the editor root, and a public URL is given for files only. The server
path never leaves the server.
A non-existent folder gives an empty listing, not an error (until something is uploaded the root does not exist physically and the manager just shows the empty state).
Parameters
- $folder : string = ''
handleEditorRename()
Renames a file or folder (`path` relative to the editor root).
public
handleEditorRename([string $path = '' ][, string $name = '' ]) : void
Parameters
- $path : string = ''
- $name : string = ''
Attributes
- #[Requires]
- $methods: 'POST'
handleEditorUpload()
Accepts a file from the editor (field `file`, multipart POST; optional `folder` = subfolder of the editor root) and sends JSON `{"location": "/www/uploads/editorUploads/..."}`, the format TinyMCE expects from `images_upload_handler`.
public
handleEditorUpload() : void
Attributes
- #[Requires]
- $methods: 'POST'
editorBreadcrumb()
Builds the breadcrumb navigation: cumulative segment paths.
private
editorBreadcrumb(string $relative) : array<int, array{label: string, path: string}>
Parameters
- $relative : string
Return values
array<int, array{label: string, path: string}>editorParentFolder()
Returns the parent folder (null in the editor root).
private
editorParentFolder(string $relative) : string|null
Parameters
- $relative : string
Return values
string|nulleditorPath()
Converts a client path (relative to the editor root) to an Api path (relative to the `uploads/` root).
private
editorPath(string $relative) : string|null
This is the only place where the prefix is added, so the segment check lives here too. The
Api rejects .. as well, but without this check a ../ from the request would not escape
uploads/ yet would escape editorUploads/ into neighboring folders of other modules.
Parameters
- $relative : string
Return values
string|null —null when the path contains a forbidden segment
editorUploadName()
Builds a safe and unique file name. Sanitization copies the Api layer rules (webalize of the base and of the extension), so the name returned in the URL is exactly the name the file is stored under. The random suffix prevents collisions: `writeFile()` overwrites the same name without asking, so two different `obrazek.jpg` files would overwrite each other.
private
editorUploadName(string $untrustedName) : string|null
Parameters
- $untrustedName : string
Return values
string|null —null for a forbidden extension or an unusable name
editorUploadUrl()
Returns the public URL of a file. `$path` is relative to the `uploads/` root (so it includes `editorUploads/`), the same shape as other uploads in the system.
private
editorUploadUrl(string $path) : string
Parameters
- $path : string
Return values
stringensureEditorRoot()
Creates the editor root folder; when it already exists the Api returns false and nothing happens.
private
ensureEditorRoot() : void
storeEditorUpload()
Stores a file; the target folder may not exist (first upload into the root), so after a failure the root is created once and the write is retried. Deeper subfolders are created by the editor user through `editorCreateFolder`.
private
storeEditorUpload(string $folder, string $name, string $base64) : bool
Parameters
- $folder : string
- $name : string
- $base64 : string