Revisions support

I have missed that thread.


I agree that it probably would be nicer to have a folder with the revisions / versions than having the files directly into the page folder.

Template name

In your post you don’t have a template name in your filename. What if they change name / template and then go back to a revision / version? I think it will need a name in the filename for it to work?


Totally agree.

Now I would need this feature. I got an idea for a plugin.

This could still be an option:

c::set('revisions', 2);

On save a hook is triggered, which over time creates this:


Maybe it reminds of the thumbs or the cache folder and yes it would be.

Additional hooks would be for deleting, renaming and changing template. That to always keep in sync.


  • The revisions is not a part of the content folder, which means it does not slow down your site.
  • By limit to like 2 or 10 revisions the site will not grow forever.
  • Because the folder structure is the same as in content, it’s easy to see what it is even for the human eye.
  • The dates are a human friendly kind of timestamp.
  • Multi language support.


To really make this useful a field would be required to show the revisions for the current page in the panel. Clicking on a revision could bring it back, after asking the user first.

Only text

My idea is that only the text is used for revisioning. That’s the only thing you edit in the panel. Even if there are images, they are not edited in any way. It also would be much faster to just save a text file than save a whole folder complete with images.

Do you want a plugin like this? Should I build it, or some kind of collaboration? @1n3JgKl9pQ6cUMrW?

I think this goes far beyond my skills :slight_smile:

I am good in creating creative, easy solution plug-ins, like the content-backup plug-in - but altering the core of such a complex system as Kirby is, is not my cup of tea :stuck_out_tongue:

I think your backup plugin is quite advanced.

I’m not thinking to altering the core.

I was thinking hooks:

kirby()->hook('', function($page) {
  // Copy text file to /revisions/some/folder/filename.txt

That would be a really good feature to have. I might be able to help test and perhaps write bits of it.

You probably would need to move versions on the hook, because otherwise if the editor decides to rename a page’s slug… the revisions would be lost. But it might get a bit tricky.

Bastian has written previously that he had thought about versioning a few times, but it would add a lot of complexity so he’s not keen on going along with it. Writing a plugin as a proof of concept would be interesting, at the very least.


I think a plugin would be great. Integrating this into the core would increase complexity by a lot, but a separate plugin can’t be wrong here, so I think experimenting with this in a plugin would be cool. :slight_smile:
I’m curious what you can make.

1 Like

My guess is that moving pages could be the most critical thing in with a plugin like this, because we need to move the revisions as well. We don’t want to risk losing the data at that point.

I have some other things I need to do before I can start with it. If you want to start something right now, feel free to do so and I’ll jump into it later.

@lukasbestle It would probably be helpful if you would drop some pitfalls. You are smart and have probably calculated them already?

Your idea looks great and very doable.

One idea though: Maybe it makes sense to listen for file hooks as well. And then on every hook besides the page move hook, you just copy the whole directory (excluding subdirectories) of the page to the revisions directory so that files are backed up and versioned as well.

Also one potential issue we need to think about: What about subpages? Subpages are stored in their parent’s revision directory, so what should happen if the parent page is changed? The subpages definitely need to be preserved somehow.

All files in a folder

It would probably be simple to just copy the whole directory but if I have 30 images on a page, it needs to copy those 30 images on every save.

  • It could grow fast if every image in that page is 3MB (90MB every save).
  • It would slow down the panel.

About subpages

I’m not sure if there is a relationship between them. I don’t think a page can be moved in the panel. I don’t think it matters if a folder has a subfolder?

Every page has a uri and to think flat, don’t mind what’s parent or children.

To protect against collisions the structure could be like this:


revision folder before the revision is a name protection against collisions. Else the date could theoreticly collide with a subpage.


I’m not sure what should happend on delete. Maybe the user by mistake deleted a page. Maybe not delete the revisions on delete?

If your page has a 50 MB video file, and you copy it for each text revision up to limit=20 revisions, you get 1GB of duplicate files.

I’d restrict the scope to the text part. That’s a limitation, sure, but it helps keeping things manageable.

1 Like

What is making the versioning of files an option which is turned off by default? This way, people who really need it could turn it on if they really want to…

Theoretically, you could have a subpage/child called ‘revision’, than the model would break… maybe just add an underscore before the date? This usually isn’t possible in the panel and should be safe to use. Every other key word could be used by a child otherwise and break the whole thing.

Another interesting point is definitely the user interface. Will you be able to restore single fields as well as the whole page? Or just “klick-and-restore-last-revision”? This change should also be undoable, so you also always need to store the current version as revision… and, a problem I have seen lately, was that hooks currently can’t access old data, so you’d need some kind of compare-fields. Lot’s of things to be aware of… here is @texnixe’s approach which I used (and which works like charm ;)): Listen to certain page changes in hooks

Great idea by the way, would love to test this out :wink:

Or maybe it could be .revisions, since there can be no page with a leading dot at all. Then the subpage issue is solved.

1 Like

I’m not sure about the option, not for a first version at least. It would take more time to develop and I want a first version out quite soon.

If @lukasbestle suggestion with a dot work I think that would be great.

I was thinking just a list of the 10 last revisions and go back to a restore point. But also compare them to the current.

I will try to be extra careful with old data then. Thanks!

1 Like

I agree, for a first version, I would leave files out. Would be interesting to test performance though with heavy file loads.

1 Like

I really like the revision feature with comparison/diff at Draft:

1 Like


Name for it?



I’ve been thinking of how I would do this and came to some conclusions.

  • I will create the most basic revisison plugin possible with the aim to make it solid and well working with not anything extra than what is needed.
  • I hope that someone else than me want to build a field for it. That would be a great collaboration, I think.


A simple field would be just a list of the recent savings and the possibility to restore to one of them.


This will not be of high priority, but it would be nice to have. I also think that some diff tool class would be needed. I guess it’s hard to create a diff tool from nothing.

Diff is not mandatory, but I thought sometimes we may want to revert a single field, not the whole page. Being able to display a previous version and simply copy/paste the content of a field could be useful.

1 Like

I’ve started already.

Head file?

When using visible/order on pages it will look like this in the revisions folder:


All of the folders are in fact the same page but I have change the order. It means three different page folders for only one page. (1-child, 2-child, 3-child)

I could try to recursively delete all except the current one on every save, but I have another idea. Create a head.txt file.

Folder without order number and template name

One folder with two files:


Inside head.txt:

sort: 3
template: default
language: en

Inside content.txt it would just be the content, a copy of default.txt in this case from the content folder.


  • I don’t need to delete or move around folders and files very much. They are always the same.
  • It’s easy to find the template and the sort order from the head file.
  • On next edit it knows from the head file what template and sort order it was before, if needed.


I’ve decided that revision is the best name for it. I’ve googled it.

Feedback on head.txt?

1 Like

To be able to see what is happening I added the field at this early state of the plugin. That means I can deliver a teaser already.

The only thing it does is to save content into the correct folder and create folders if they don’t exist.

I had an idea of a head.txt file but I’ve tested it and it had flaws.

Sort, invisible and visible

If you change sort order on a page it creates a domino effect on many other pages. The revisions are therefor, by choice, not aware of sort, visible and invisible.

That means I don’t need it in a head.txt file.


Because I was only having the language code inside the head.txt file I could not see which revisions was in english and which was in swedish in the directory structure.

Instead it simply .revisions/en/ for english revisions.

That means I don’t need it in a head.txt file.


When I was having a content.txt and a head.txt I needed to have a folder for them to group them.

The only thing left in the head.txt file was the template name. To get rid of the grouping folder I got rid of the template inside the head file as well.

Instead I put it much like Kirby does, like this:


Because the date/time always uses the same number of characters, it’s really simple to get the template name from the filename.

The result for it is this:

  • Easy to see what revisions is in what language.
  • No messing around with sorting, at all.
  • All revision as single files in a directory. Not deeper than it needs to be.

Just some thoughts about why I changed my mind this time.