Automatically update page URL when title is edited?

Hey guys,

I’m wondering if something is possible with a hook/plugin or similar; I have a situation with a client project in which they seem to change the page titles often…it is important that the title of the page and slug match for my design. I know there is the option to ‘create url from title’, however I do not want to leave to them to do this, instead I’m wondering is there a way to automatically update the URL to match the page title, when the title is changed?

I have seen a similar request on this form here but it is not exactly the same, nor is it resolved.

Thanks,

Nick

Yes, you can use a panel.page.update hook as described in the link you posted above. You will run into the same issue though, that you will need to redirect the user to the new location in the panel.

In what way do the title and the slug have to match for the design? What about SEO considerations? From a SEO perspective, changing URLs all the time is not such a great idea?

Thanks. I’m actually using kirby just to serve up a JSON api, which I’m consuming on the frontend with react. The site itself functions much more like a web app, so SEO isn’t my priority here (for that I’m considering rendering react server side instead).

I’m trying to register my hook in config and do something like this:

kirby()->hook('panel.page.update', function($page) {
  $title = $page->title();
  $page->update(array(
    'URL-Key' => str::slug($title),
  ));
  panel()->redirect($page, 'edit');
});

But I’m having no luck? If the redirect became messy or unfeasible, I would probably just send a warning alert on the panel (to remind my client), but even then I would need the hook to listen to the title field specifically (not the whole page).

Any thoughts?

Well, first of all, if you want to change the slug of the default language, you have to move the page, not update it. Are you in a multi-language environment? The Url-Key can only be used for the non-default languages, if you want to update those, you have to pass the language code to the update method: https://getkirby.com/docs/cheatsheet/page/update

Hmm ok. I am not in a multi language environment. I am managing to change the URL with the code below, however once it is saved, instead of redirecting the user to the page which has been moved (e.g. panel/pages/home/some-project/edit, it takes them one level back to panel/pages/home/edit ?

kirby()->hook('panel.page.update', function($page) {
  try {
    $url = str::slug($page->title());
    $page->move($url);
    panel()->redirect('pages/' . $page->id() . '/edit');
  } catch(Exception $e) {
    echo $e->getMessage();
  }
  return;
});

This feels a little sketchy UX wise, so I’m considering just sending an alert when the title field is edited, only problem is I’m not there is a hook available for specific fields? I see you have responded to a similar question here however using the hidden field to check the value against didn’t work for me:

kirby()->hook('panel.page.update', function($page) {
  $title = $page->title();
  if ($page->content()->hasField('hidden')) {
    $titleHistory = $page->hidden();
  } else {
    $titleHistory = "";
  }
  if ($title !== $titleHistory) {
    $page->update(array(
      'hidden' => $title,
    ));
    panel()->alert("WARNING! If you have changed the title, do not forget to update the URL! Go to Change URL > 'Create from title'");
    panel()->redirect('pages/' . $page->id() . '/edit');
  }
});

Thanks for your time btw, much appreciated.

1 Like

The update hook actually allows you to compare the old state with the new state of the page

kirby()->hook('panel.page.update', function($page, $oldpage) {
  $newTitle = $page->title();
  $oldTitle = $oldpage->title();
  if($newTitle !== $oldTitle) {
    // blub blub
  }
}

So you would be able to directly compare the old title and the new title without using an extra field.

Will have to look into why the redirect doesn’t work as expected later.

1 Like

Thanks.

I didn’t know that was possible, really helpful!

It’s now pretty easy to compare:

kirby()->hook('panel.page.update', function($page, $oldpage) {
  $oldTitle = $oldpage->title()->value();
  $newTitle = $page->title()->value();
  if ($oldTitle !== $newTitle) {
    panel()->alert("WARNING! If you have changed the title, do not forget to update the URL! Go to Change URL > 'Create from title'");
    panel()->redirect('pages/' . $page->id() . '/edit');
  }
});

Now I’m able to only move the page if the title (and therefore url) is changed:

kirby()->hook('panel.page.update', function($page, $oldpage) {
  $oldTitle = $oldpage->title()->value();
  $newTitle = $page->title()->value();
  if ($oldTitle !== $newTitle) {
    try {
      $url = str::slug($page->title());
      $page->move($url);
      panel()->redirect('pages/' . $page->id() . '/edit');
    } catch(Exception $e) {
      echo $e->getMessage();
    }
    return;
  }
});

But this still sometimes throws an error/does the strange redirect thing which I’m not confident using in production :frowning:

Thanks

1 Like

This should work:

kirby()->hook('panel.page.update', function($page, $oldpage) {
  $oldTitle = $oldpage->title()->value();
  $newTitle = $page->title()->value();
  if ($oldTitle !== $newTitle) {
      $url = str::slug($newTitle);
      if($move = $page->move($url)) {
        panel()->redirect($page, 'edit');
      }
  }
});

Note the syntax of the panel redirect. Also, I removed the try-catch construct, because a hook doesn’t throw any errors, and the return statement, because a hook doesn’t return anything, either. Instead, the hook only redirects if $page->move() returns true.

2 Likes

Hmm this works, but still occasionally throws an error ‘Page Not Found’ and redirects me back to one level up to my pages panel (but it will still change the URL, just the redirect does not work). I’m not sure what the cause of this is…I thought it could be something to do with how the URLs are stringified, but I have tried multiple variations of throwing in special characters etc. and cannot find a common reason as to why this happens :confused:

Something like this can happen if the page is invisible and you enter a title like “123 Title”, because pages may not start with a number followed by text (while you may have titles that are just a number). Is that maybe your issue as well?

Or maybe I should say: Titles may have a number if you make sure that the UID has not, because Kirby sees prepended numbers as visibility flags. For example, a title like “1984 - A vision that has long become reality” will translate to a UID like this 1984-a-vision-that-has-long-become-reality, with 1984 not regarded as part of the title but just an indicator that makes the page visible.

1 Like