Kirby 5 is here! Learn more
Skip to content

Preview your changes

Kirby 5 introduces a part of one of the most requested features on our feedback platform. Editors can now make changes and get a full, rendered preview right from the Panel before they save them. They can share the URL for the preview of the unpublished changes with other editors and people in their team to get feedback.

Changes view

The brand new changes view lets you switch between a comparison mode or a full-width preview of the changed version and the latest published version.

The new preview view for pages and the site is located at /panel/pages/(:any)/changes/(changesorlatestorcompare) and /panel/site/changes/(changesorlatestorcompare).

It can be accessed with the new preview button for the site and pages.

Get more details about the changes for the current page, file or user, by accessing the new info dropdown next to the form buttons:

This dropdown will also show more information when the content is locked because another editor is currently working on it.

Changes dialog

All changes by everyone in the team - not just the current editor - are now listed in the Changes dialog (accessible via the “Changes” menu item in the sidebar) and grouped by type (page, user, file). Now everyone in the team can directly use the dialog to jump to changes by other team members and preview them directly on the fly, even while someone else is still actively working on them.

Languages dropdown

We’ve also improved the language dropdown to highlight changes in different translations.

Outlook

With all of those features built on top of our new content version architecture, Kirby 5 starts a path to full revision support in the future. We have already laid the foundations and are looking forward to extending the feature set over time.

New Versions PHP API

All the features above are powered by our brand new PHP Versions API. While a lot of it is internal, we also offer quite a few new methods and classes that you can use in your projects.

Accessing Versions of a model

// get the latest saved content of a page
$page->version('latest');

// get all unsaved changes
$page->version('changes');

Those methods return a new Kirby\Content\Version object with quite some handy additional methods:

Check if a there are changes

$page->version('changes')->exists();

// check if changes exist in a particular language
$page->version('changes')->exists('de');
$page->version('changes')->exists('current');
$page->version('changes')->exists('default');

Getting versioned content

$page->version('latest')->content();
$page->version('changes')->content();

// getting content in a particular language
$page->version('changes')->content('de');
$page->version('changes')->content('current');
$page->version('changes')->content('default');

This will return regular Kirby\Content\Content objects that help to access fields. So for example, if you want to receive the latest or changed text of a page, you can do this:

$page->version('latest')->content()->get('text');
$page->version('changes')->content()->get('text');

Get a modification timestamp of a version

$page->version('changes')->modified();

// for a particular language
$page->version('changes')->modified('de');
$page->version('changes')->modified('current');
$page->version('changes')->modified('default');

Check if a version is locked

$page->version('changes')->isLocked();

// receive lock info
$page->version('changes')->lock();

// by language
$page->version('changes')->isLocked('de');
$page->version('changes')->lock('de');

Check if there are validation errors in a version

$page->version('changes')->isValid();

// receive all validation errors
$page->version('changes')->errors();

// by language
$page->version('changes')->isValid('de');
$page->version('changes')->errors('de');

Check if versions are identical

$latest  = $page->version('latest');
$changes = $page->version('changes');

if ($latest->isIdentical($changes) === true) {
  // there are no changes
}

Update Changes

Be careful with this method. It's rather low-level and for more complex fields, you need to use our new Form class and the Form::toStoredValues() method to provide valid storeable data.

$page->version('changes')->update([
  'text' => 'Changed text'
]);

// by language
$page->version('changes')->update([
  'text' => 'Changed text'
], 'de');

Publish Changes

$page->version('changes')->publish();

// by language
$page->version('changes')->publish('de');

Delete Changes

$page->version('changes')->delete();

// by language
$page->version('changes')->delete('de');

Get the preview URL for the changes

$page->version('changes')->url();

New Panel JS Content API

For Panel plugins, we provide an entirely new content API, which makes it super easy to fetch versions of the content, see if there are any diffs and more.

Get a content version

this.$panel.content.version('latest');
this.$panel.content.version('changes');

Check if there any diffs between latest and changes

this.$panel.content.hasDiff();
this.$panel.content.diff();

Discard content changes

await this.$panel.content.discard();

Check if the content is locked

this.$panel.content.isLocked();

// get lock info
this.$panel.content.lock();

Publish changes

await this.$panel.content.publish();

// you can pass new values optionally
await this.$panel.content.publish({
  text: 'Changed text'
});

Update changes

await this.$panel.content.update({
  text: 'Changed text'
});

Throttle updates

This is particularly useful if you bind this to an input event that gets fired often. All our fields update changes lazily to not hammer the server with requests.

this.$panel.content.updateLazy({
  text: 'Changed text'
});

This method does not provide a response. You can listen to the global content.save event if you need to trigger code after changes have been saved.

this.$panel.events.on("content.save", ({ values }) => {
  // do something here whenever content has been saved
});