Simple Website Framework

Versions, Trash and Backups

Wednesday October 21st, 2026

Written by Scary le Poo

Three things protect your content, and they nest: every save keeps the version before it; every delete goes to a trash rather than the bin; and the whole site can be zipped, by hand, on a schedule, or by itself once a week. None of it needs a database or anything beyond PHP. All of it lives in one folder that the web server refuses to serve.

These features belong to the editor, so they are available when $editorEnabled is true. The command-line backup works regardless.


Where it all goes

Everything is stored under $backupDir, which is backups/ in the site root unless you say otherwise:

backups/
  .htaccess                          denies all web access
  index.html                         empty, so a listing shows nothing
  versions/<page>/<timestamp>.html   earlier versions of each page
  trash/<page>/<timestamp>.html      pages you deleted (with their versions)
  trash-files/<path>.<timestamp>       files deleted or replaced on the Files screen
  archives/swf-<site>-<kind>-<timestamp>-<how>.zip   site backups

The folder is created the first time it is needed, with the two protection files already in place, and they are put back on every run if something removes them. The site's own .htaccess also answers 404 for anything under backups/, so there are two locks. The only way to get a file out is the editor's own download link, logged in.

If your server is not Apache (or LiteSpeed, which honours .htaccess), those locks do nothing. Set $backupDir to an absolute path outside the web root instead:

$backupDir = '/home/yourname/site-backups';

That is the safest arrangement on any server, and the Backups screen will tell you it is in use. The folder must be writable by the user PHP runs as.


Page versions

When you save a page in the editor, the file as it was before the save is copied into versions/ first. Saving with no changes keeps nothing. The newest $backupKeepVersions copies (20 by default) are kept per page; older ones are dropped as new ones arrive.

History on the edit screen lists them, newest first. Each entry can show what changed between that version and the page as it is now, line by line, with unchanged stretches folded. View opens the old file as plain text. Restore puts it back as the live page; the page you replaced is kept in the history first, so a restore is itself undoable.

If the backup folder cannot be written, the editor refuses to save at all rather than save without keeping the previous version, and tells you which folder to fix.

Drafts also feed the history: publishing a draft over a live page keeps the old live page as a version, and discarding a draft keeps the draft as one, so nothing typed into the editor is ever simply lost. Saving a draft itself does not create versions; the draft file is the working copy.


Deleting pages

Delete on the edit screen moves the page to trash/ together with its version history, and removes any folder in pages/ that is left empty. Nothing is destroyed. Three pages cannot be deleted because the theme needs them, home, 404 and navigation, and neither can the footer columns.

Deleted pages are listed on the Backups screen. Restore puts one back, history included, and opens it for editing. It will refuse if a new page with the same address has been created since. Delete permanently removes it from the trash for good.


Site backups

There are two kinds, and the difference is the same split the upgrade package uses:

  • Content — everything that is yours and nothing that is the framework's: pages/, images/, your theme folders, config/config.php, robots.txt, .htaccess, and any other folder you have added to the site root. This is the one to keep. It is small, and it restores onto any version of the framework: unzip it over a working install and replace files when asked.
  • Full — the complete site, framework included. Bigger, and the framework part can always be downloaded again, but it is one file that is the whole site. Unzip it into an empty web root to recreate the site exactly.

Neither includes the backup folder itself, cache files, or the editor's own state files. Both include config/config.php, which holds your editor password hash and, if set, the two-factor secret, so treat a downloaded backup with the same care as the site.

The file name says which site it is, what it is and how it was made: swf-example.com-content-20261021-031500-manual.zip was a content backup of example.com made by hand; -auto means the weekly one, -cron a scheduled run. The site part is the domain from $WebsiteURL, so backups from several sites can sit in one folder and still be told apart. Each zip also contains a short BACKUP.txt saying the same.

By hand

Open Backups from the editor toolbar and click Make content backup or Make full backup. The zip is built, kept in archives/, and downloaded. Backups you make this way are never removed automatically; delete them from the same screen when you no longer want them.

Once a week, by itself

Every time a page is saved or deleted, the editor checks whether there is a content backup newer than $backupAutoDays days (7 by default). If there is not, it makes one before reporting the save, and says so in the status line. You never have to remember. Nothing happens if nobody edits the site, which is fine, because then nothing has changed since the last one. Set $backupAutoDays = 0 to switch this off.

On a schedule

For backups on a timetable regardless of editing, run the command-line script from your host's cron:

php /path/to/your/site/plugins/editor/backup-cli.php content

A nightly content backup at 3:15 is this crontab line:

15 3 * * * php /path/to/your/site/plugins/editor/backup-cli.php content

Most hosting control panels have a page for cron jobs that takes the same command. Use full instead of content for a full-site backup, list to see what is stored, version to print the framework version, and check-update to ask GitHub whether a newer framework version exists (see Update Notices). The script only runs from the command line; requested over the web it answers 404.

Rotation

Automatic and scheduled backups are rotated so they cannot fill the disk: the newest $backupKeepArchives of each kind (7 by default) are kept. Backups made by hand are never rotated. With the defaults and a nightly cron, that is a rolling week of content backups.

If the zip extension is missing from your PHP, the Backups screen says so. Page versions and the trash still work without it; only the zips need it.


Restoring a whole site

Deliberately, this is not a button. A page you restore from inside the editor is one file in pages/; a whole-site restore means replacing code, and the editor should not be able to overwrite its own code from an upload. So it works the same way as an upgrade:

  1. Download the backup you want from the Backups screen, or copy it off the server.
  2. For a content backup: make sure a working framework install is in place (a fresh swf-install zip is fine), then unzip the backup over it, replacing files.
  3. For a full backup: unzip it into the web root, replacing files, or into an empty one.
  4. Delete any cached-*.html files in the site root.

Keep a copy of your backups somewhere that is not the server. A backup that only exists on the machine it is backing up is a convenience, not a safeguard.


Settings

All optional; an older config.php without them gets these defaults.

SettingDefaultWhat it does
$backupDir'backups'Where everything is stored. Relative to the site root, or an absolute path (recommended outside the web root on non-Apache servers).
$backupKeepVersions20Earlier versions kept per page.
$backupKeepArchives7Automatic and scheduled site backups kept, per kind. Manual ones are never rotated.
$backupAutoDays7On save, make a content backup if the newest is older than this. 0 switches the weekly backup off.