Simple changelog hook for Kirby

As already requested by Discord here is our small solution for a very simple and easy changelog for pages. Thanks to @texnixe for help with the function a few months ago. The hook can be inserted into the config or into a plugin.

    'hooks' => [
      'page.update:after' => function($newPage) {
        $user = $this->user()->username();
        function addToStructure($page, $field, $data = array()) {
          $fieldData = $newPage->$field()->yaml();
          $fieldData[] = $data;
          $fieldData = Data::encode($fieldData, 'yaml');
          try {
            $newPage->update(array($field => $fieldData));
            return true;
          } catch(Exception $e) {
            return $e->getMessage();
          }
        }
        addToStructure($page, 'changelog',[
          'changelog_date' => date('d.m.Y'),
          'changelog_time' => date('G:i:s'),
          'changelog_user' => $user
        ]);
      }
    ]

This can then be used as a blueprint.

    changelog:
      type: structure
      label: Changelog
      limit: 1000
      disabled: true
      fields:
        changelog_date:
          label: Date
          type: text
        changelog_time:
          label: Time
          type: text
        changelog_user:
          label: User
          type: text

Edited by @texnixe to fix code example.

4 Likes

Thank you, Oliver!

I am glad that I could help and could contribute a small part :slight_smile:

1 Like

I’m pretty insterested by your solution, looking for that type of changelog but get an error message saying

Exception: Error
Call to a member function changelog() on null

Does anyone have a similar issue with this code ?

The hook variable page is wrong, should be $newPage

1 Like

Has this Hook always been like that with new and old? Because in the old instance of me the code still works.

No, that was a change that happened in 3.4. Variables are since named, but can be used in any order.

Hello
Having the same error

Call to a member function changelog() on null

only if the page is in draft mode. Does page.update:after not work for drafts?

@Oziris Even if the hook didn’t work, the error message says that you are calling changelog() on something that is not a page. Could you please post the code (with context), where you are calling this method?

Yes, I’ve set this code in my config.php . something that is not a page?!

'hooks' => [
        'page.update:after' => function($newPage) {

            $user = $this->user()->username();

            function addToStructure($newPage, $field, $data = array()){
                $fieldData = page($newPage)->$field()->yaml();
                $fieldData[] = $data;
                $fieldData = yaml::encode($fieldData);
                try {
                    page($newPage)->update(array($field => $fieldData));
                    return true;
                } catch(Exception $e) {
                    return $e->getMessage();
                }
            }

            addToStructure($newPage, 'changelog',[
                'changelog_date' => date('d.m.Y'),
                'changelog_time' => date('G:i:s'),
                'changelog_user' => $user
            ]);

        }
    ],

There is no call to a changelog method in that hook though, so I wonder where the error message comes from?

Sorry, I overlooked the call to $field. But $newPage should already be a page object, without passing it as an argument to the page helper. So if you remove that in both instances and only use $newPage, it should work. I don’t know why that helper is used there.

function addToStructure($newPage, $field, $data = []){
    $fieldData   = $newPage->$field()->yaml();
    $fieldData[] = $data;
    $fieldData   = Data::encode($fieldData, 'yaml');
    try {
        $newPage->update(
            [
                $field => $fieldData,
            ]
        );
        return true;
    } catch(Exception $e) {
        return $e->getMessage();
    }
}
1 Like

Perfect ! Many thanks ! :wave: :wave: :wave:

Thanks for sharing this code guys!

Unfortunately i wasn’t able to integrate it into my usecase. The code is as follows:

<?php

function addToStructure($page, $field, $data = []) {
    $fieldData   = $page->$field()->yaml();
    $fieldData[] = $data;
    $fieldData   = Data::encode($fieldData, 'yaml');

    try {
        $page->update(
            [
                $field => $fieldData,
            ]
        );
        return true;
    } catch (Exception $e) {
        return $e->getMessage();
    }
}

Kirby::plugin('digireporter/changelog', [
    'hooks' => [
        'page.update:after' => function ($newPage, $oldPage) {
            addToStructure($newPage, 'changelog', [
                'changelog_datetime' => date('d.m.Y G:i:s'),
                'changelog_user' => kirby()->user()->id(),
                'changelog_name' => kirby()->user()->name(),
                'changelog_change' => 'Artikelinhalt wurde geändert'
            ]);
        }
    ]
]);

The problem is, the following:

image

When removing $fieldData = Data::encode($fieldData, 'yaml'); everything works fine but, i am not sure about the consequences :slight_smile:

The code shows the index.php of my small plugin.

Also when enhancing with more hooks (e.g. changeTitle:after) updated:after is called as the changelog field resides in the same page. Is there anyway around this except moving changelog onto a separate subpage?

Update: removing $fieldData = Data::encode($fieldData, 'yaml'); didn’t help after all. It just disappears sometimes.

Any idea why this could happen @texnixe @Oli1 ? As to my understanding the page.update:after is called just once and not in a circle (as the hook changes the changelog which is a field of the page itself). Can you confirm?

What sort of field are you using for the user id, a users field? If so, you need to Data::encode that field value as well

in fact it’s all text and the whole changelog field is disabled:

            changelog:
              type: structure
              label: Änderungshistorie
              limit: 250
              sortBy: changelog_datetime desc
              disabled: true
              columns:
                changelog_datetime:
                  width: 1/4
                changelog_name:
                  width: 1/4
                changelog_change:
                  width: 2/4
              fields:
                changelog_datetime:
                  label: Datum
                  type: text
                changelog_name:
                  label: Benutzername
                  type: text
                changelog_change:
                  label: Änderung
                  type: text

i might have said: the error occurs while trying to save the page (and thus create a new changelog entry).

There seems to be a discrepancy between your blueprint (3 fields) and the array you pass, which has 4 fields.

yes, true, but this is intentionally as i just want to show the users human readable name in the changelog but store the user additionally in case a user is going to change his name we still now who “really” did the change.

Do you think this might cause the problem?