Files
Files live in the page folder, stay private until you ask for their URL, and are managed through PageFiles.
Where files live
A file belongs to a page and sits in its folder, beside the content:
data/pages/blog/my-post/
index.json
data.json
cover.jpg
cover.jpg.index.json
cover.jpg.index.json holds the file's metadata: alt, mime and created. A file's identity is its page's UUID plus its filename; there is no separate file UUID.
Private until you ask for the URL
A file in a page folder is not served just because it exists. The first call to url() (or src() for an image variant) records that URL in the registry (data/storage/registry.db), and only a recorded URL is served. A request for a private file 404s even though it is on disk.
Each URL is exposed on its own: src('w=200') exposes that variant, not the original at /{page-slug}/cover.jpg; call url() for that. The record is not content, so rendering a page never writes to the page folder or to History.
The registry is a derived cache. If registry.db is deleted, files are private again until their pages next render. Make private in a file's admin menu withdraws its URLs, and so does deleting it.
Originals are streamed by PHP. Resized variants are generated on first request into site/public/, which is managed by the CMS: everything in it except index.php is generated and safe to delete. phlat files:rebuild sweeps out whatever is stale.
In templates
Page::file() and Page::image() resolve a filename against the page's own folder:
{var $pdf = $page->file('brochure.pdf')}
<a href="{$pdf->url()}">Download ({$pdf->size|bytes})</a>
<img src="{$page->image('cover.jpg')->src('w=800')}" alt="">
image() throws for files that aren't resizable images, SVG and ICO included; use file() for those. Transforms are in Images.
$page->files() returns every file on the page as PageFile objects:
{foreach $page->files() as $file}
<a href="{$file->url()}">{$file->basename}</a>
{/foreach}
A PageFile has url(), isPublic(), filename, mime, size, extension and type (image, video, audio, document, archive, code or file). url() adds a cache-busting query string; pass false to leave it off.
File and image fields
A file or image field stores a bare filename and resolves it against its own page. {$page->cover} outputs the public URL, and $page->cover->decode() returns the PageFile or PageImage. Uploads are handled by the admin editor.
Uploading
$file = $page->upload($_FILES['file']);
Page::upload() rejects failed uploads and extensions not in media.allowed, cleans the filename and stores the file in the page folder. The default list is jpg, jpeg, png, gif, webp, svg, pdf, mp4, webm, mp3 and zip.
PageFiles
$app->pageFiles() is the layer underneath, for code that doesn't start from a page:
| Method | Does |
|---|---|
store($sourcePath, $name, $pageUuid, $options) |
Copy a local file into a page |
upload($tmpPath, $name, $pageUuid, $options) |
Store an uploaded file |
get($pageUuid, $filename) |
The PageFile, or null if it has no metadata |
update($pageUuid, $filename, $changes) |
Change metadata, such as alt |
delete($pageUuid, $filename) |
Remove the file, its metadata and its variants |
forPage($pageUuid) |
Every file on a page |
isPublic($pageUuid, $filename) |
Whether the file, or a variant of it, has been exposed |
makePrivate($pageUuid, $filename) |
Withdraw its URLs and wipe its mirrors and cached variants |
rebuild() |
Sweep stale variants out of site/public/ |
Storing or deleting a file clears its cached variants and public mirror. Store and update emit file.update:after; delete emits file.delete:after. See Event Hooks.
Image variants
Resized images are generated into site/public/ on first request and regenerated when the source changes. The web server serves them as static files after that. The admin's thumbnails are the only images kept elsewhere: data/cache/files/, made again when missing. See Images.
Caching
Public files are sent with files.cache as Cache-Control (default public, max-age=3600). A file larger than files.cache_max_bytes (default 5 MB) is sent no-store.