Files

Files live in the page folder, stay private until you ask for their URL, and are managed through PageFiles.

On this page

Where files live

A file belongs to a page and sits in its folder, beside the content:

site/pages/blog/my-post/
  _index.json
  _data.json
  cover.jpg
  _cover.jpg.json

_cover.jpg.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) exposes that URL, and only an exposed 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. Exposing a URL is not content, so rendering a page never writes to the page folder or to History.

Make private in a file's admin menu withdraws its URLs, and so does deleting it.

Originals are streamed by PHP. Resized variants of a page open to anyone 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. A variant of a gated or unpublished page is never written there. It is kept in site/data/cache/files/ and sent through PHP, behind the page's access check. 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.

Image variants

Resized images are generated on first request and regenerated when the source changes. When the page is open to anyone, the variant is written into site/public/ and the web server serves it as a static file after that. Otherwise it is kept in site/data/cache/files/, made again when missing, and served through PHP behind the page's access check. The admin's thumbnails live there too. 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.

When you need more

To react when a file is stored or deleted, see Event Hooks.