$page->save() in plugin does not do anything, no errors either

In my Simple Events plugin, I would like to set the unpublish date of a new Event page programmatically. To this end, I am getting a list of all pages with the Event type on a cache rebuild, checking each one’s unpublish date, and if it’s not set, I would like to set it and then save that in the page’s frontmatter. Most of that is working well so far. Here is my code, annotated:

if ($this->checkUnpublishedDates) {
  // set unpublish datetime to header.start/header.end plus time set in options.
  $pages = $this->grav['pages'];
  $events = $pages->all()->ofType('event');

  foreach($events as $e) {
    if (!$e->unpublishDate()) {
      $time = " 0:00";
      if (!empty($this->grav['config']->plugins['simple-events']['unpublish_time'])) {
        $time = " ".$this->grav['config']->plugins['simple-events']['unpublish_time'];
      $datetime = $e->header()->start.$time;
      // dump($e->unpublishDate()) at this point gives me the new timestamp correctly
      $e->save(); // no errors, but nothing gets saved

From what I gather from the Grav API and other plugins, this should actually work. So why doesn’t it actually save anything? :thinking:

I’m never sure it’s useful to suggest things when I don’t know the answer but nothing else has been offered …

I looked up the source for the Page->save() method seems it doesn’t handle anything unexpected at all. I wonder if the if ($file) predicate is returning true. Seems like the method will do (almost) nothing if it’s false and the docs say “Save page if there’s a file assigned to it.” (my emphasis). So I think you should put some debugging in there and see if that predicate is true, and then probably look at finding out why not.

Hope that helps you track it down.

Hello hughbris, and thank you for your reply!

Yes, I did stumble over that little if clause as well, but wasn’t sure what to do about it. I did not quite realise I could easily check for this by putting dump($e->file()); in my code though… :laughing: I have now tried this, and while I actually get an output for that, it is the wrong file. So that probably means there’s no file assigned to pages when I get them as a collection.

Now… does anybody know how to assign the respective files to the pages of a collection, perhaps? :smile:

An update: I was just confused. I do get an associated file from dump($e->file());, and it is the correct file. So this should not be the issue. Does anybody else maybe have an idea? Or maybe a way to check what the save method is doing? Your thoughts are very welcome!

@Netzhexe, I’ve installed your github repo and your event page does indeed not get saved.

Which is not surprising because $this->checkUnpublishedDates is false which prevents the code from being executed…

I guess that’s not the problem in your working copy, because your dump($e->file()) is being executed.

So, I set $this->checkUnpublishedDates to true in my clone and your event page does get saved, but I don’t think the result is what you intended.


  • What tells you the page did not save?
  • What did you expect to see in the save page?
  • What are you trying to achieve?

What is actually saved?

The page header changes from start: 1970-01-01 into a Unix timestamp: start: 0.
Using the header anyName: 1970-01-01, this too become converted into a Unix timestamp: anyName: 0.

All dates in a header are probably internally converted into Unix timestamps and Page::save() saves these updated values. Which means your start will get overridden on saving…

I presume you don’t want this to happen. To preserve the header you should do:


If your intention is to change/add values to the header of the page, you should update/add these to $header_old.

Once again I’m finding some time to work on this, and I actually made a sort of breakthrough! But there seems to be some misunderstanding as to what I’m trying to accomplish. So:

I would like to automatically set the unpublish_date in the event’s header from its start date. This way, past events will fall out of page collections automatically and I don’t have to do any extra date checking in the templates (which doesn’t go well with a limit set on a page collection).

Weirdly enough, what you are seeing here @pamtbaau about all the header dates being set to 0? I don’t get that at all. :face_with_raised_eyebrow: Whenever I put a timestamp anywhere, I get a Crikey error just trying to display the page…

But here’s a funny thing, I got it to work, in a way – I just think it’s probably very likely to break if I leave it like that. Here’s the code:

$header_old = $e->header();
// check if header.start is timestamp (int) or time string
$start = $header_old->start;
if (is_int($start)) {
  $start = date("d-m-Y", $start);
} else {
  $tmp = strtotime($start);
  $start = date("d-m-Y", $tmp);

$time = " 0:00";
if (!empty($this->grav['config']->plugins['simple-events']['unpublish_time'])) {
  $time = " ".$this->grav['config']->plugins['simple-events']['unpublish_time'];

$datetime = $start.$time;
$e->modifyHeader('unpublish_date', $datetime);

To recap: I’m getting the start date from the event page’s header, formatting it as a string, then I tack on the hours and minutes because unpublish_date is a datetime.

Now if I use this variable $datetime (a string) to set the unpublish_date with the page’s built-in method, and then dump($e->unpublishDate());, the output is a timestamp. If I save the page at this point, nothing appears to be saved or changed at all in the markdown file.

However, if I set the unpublish_date via the modifyHeader method… the correct datetime string gets saved, and all is well. :question::question::question:

What is remarkable about this is that the string HAS to be in “d-m-y” format – “Y-m-d”, like my event start strings, does not work. I noticed that Grav would save any unpublish_date that I added to an event via Admin in this format (plus hours and minutes). I’m pretty sure though that this may well be different depending on people’s setups, or Grav versions, or possibly language settings, …

I have found the source code for the unpublishDate method in system/src/Grav/Common/Page/Page.php, and it definitely turns whatever it is handed into a timestamp. I haven’t been able to find the bit where the Admin plugin saves it as a datetime string though.

So I guess my question now is: What is the correct way to turn the unpublish_date in the header into the string format needed? Or how do I find out what that format is? (And out of curiosity: why does the timestamp set with the unpublishDate method not get saved?!)

Once again I am very grateful for any and all ideas!

@Netzhexe, I guess this thread is getting too specific to be of interest of others, so I’ll give an answer on your github repo.

I reckon it still depends on the outcome – if it turns out there is a specific, foolproof method for setting dates in the frontmatter programmatically, this could well be of interest to others. But as of now, you are right. Once this matter is resolved, I’ll post the results here. :slight_smile: