Kirby Status - Published, Unpublished and Private

Require Kirby 2.3

With this plugin a page can have 3 different statuses:

  • Published - Public to everyone, like without the plugin.
  • Unpublished - Not public to anyone, like if the page was deleted.
  • Private - Public to logged in users.

It may seem like the field is the big thing here, but it took me longer to get the helper functions right. More reading…

Powerful Kirby 2.3

I noticed some things on the way, that the Page Methods can be combined with the Pages Methods which feels really natural and powerful.

An example:

  • I use $pages->published() method to only get published pages.
  • Inside it I use the $page->getStatus() method ($item in the loop).
pages::$methods['published'] = function($pages) {
  foreach( $pages->data as $key => $item ) {
    if( $item->getStatus() != 'published' ) unset( $pages->$key );
    }
    return $pages;
};

Thanks for those features!

6 Likes

Instead of using a pages method for that, consider using a collection filter. Filters are simpler for that because you don’t have to manually unset items, only return true or false for each item.

Also your current solution will modify the parent collection, not only the returned one, so you would first need to clone $this. That’s all easier with collection filters.

1 Like

Instead of using a pages method for that, consider using a collection filter. Filters are simpler for that because you don’t have to manually unset items, only return true or false for each item.

I can not use a collection filter here because $item->getStatus() is not a field. It’s a page method.

Also your current solution will modify the parent collection, not only the returned one, so you would first need to clone $this.

I tried it in my header.php template:

<?php
foreach( $pages->unpublished() as $item ) {
  echo $item->title() . '-' . $item->getStatus() . '<br>';
}

echo '<br><br>';

foreach( $pages as $item ) {
  echo $item->title() . '-' . $item->getStatus() . '<br>';
}
?>

If that was true the last foreach would output the same result as the first one, right?

// First one
Projects title-unpublished
New page-unpublished
NotExists-unpublished
Röd-unpublished
Unpublished-unpublished

// Second one
About-private
Projects title-unpublished
Contact-published
Error-published
Error-published
Home-published
New page-unpublished
NotExists-unpublished
Private-private
Röd-unpublished
rwre-private
test-private
Unpublished-unpublished

It does not seem to change the $pages object. My function does what it’s ment. I see no problem with it.

Hm, that shouldn’t prevent you from using a filter. If it does, that’s a bug. Try this:

pages::$methods['published'] = function($pages) {
  return $pages->filterBy('getStatus', 'published');
};

You are right. The $pages object gets cloned before calling the pages method so you don’t have to. However I don’t know if that is actually intended. @distantnative, what do you think?

Hm, that shouldn’t prevent you from using a filter.

I thought that collection filters was made to filter field values, but instead they are made to filter page methods, which in some cases can be a field value.

I tested it and it worked. On just 3 rows I now use “Pages Methods”, “Collection Filter” and “Page Methods”.

Absolutly brilliant!

However I don’t know if that is actually intended. @distantnative, what do you think?

The most correct would probably be to not “reset” the $pages object, but on the other hand, to prevent weird errors for us humans it might be better to leave it like it is.

You can ask yourself this:

  • What would probably make the most harm in web development?
  • Are there scenarios of where you don’t want to “reset” the $pages object like this?

I don’t know the answer, but maybe these questions can help you find it?

They work for real Page methods, custom page methods, methods in models and for fields. And in case of other types of collections, it’s just the same for the methods of their children.

Yes. If you don’t need a cloned collection, this can be a performance issue because every clone uses memory, which can be quite much for large collections. So we should only clone if required.
But I see and share your point that cloning is always safer because users can’t overwrite collections by accident.

1 Like

New version

0.2

  • Removed private argument in the $page method getStatus().
  • Added docs about how to switch collections depending on if user is logged in or not.
  • Added new $pages method privatePublished().
  • Using collection filters, which may be more future proof.
  • New option prefix. Instead of status. it’s now plugin.status..

I think, I actually did not implement the pages methods. They were there for quite some time, just not documented. And I remember that Bastian was a little surprise when I found them as well. Only added page, file and files methods, I believe.

But to be honest, I feel like cloning is rather consistent with methods like visible() and so on that return a cloned and modified version of the pages collection instead of modifying the existing one.

Ah, right. I remember.

Hm, but the visible method currently clones the collection itself, while the pages methods get a cloned collection without being able to request a non-cloned one. It would definitely be more flexible to pass a non-cloned one, but, as stated previously, cloning is probably easier to understand. So I don’t really know what is better here.

Now I know why I feel that reset/clone the $pages object is more safe by a user perspective. It’s about scope.

When I’m in a pages method, I feel like I’m inside that scope and that the only thing that is coming out is what I return.

Like with this case:

pages::$methods['published'] = function($pages) {
  foreach( $pages->data as $key => $item ) {
    if( $item->getStatus() != 'published' ) unset( $pages->$key );
    }
    return $pages;
};

I never had in my mind of the possibility that I could change the template $pages object with it, because I’m outside the template, inside a pages method scope.

My vote goes to keep it like it is now, even if it’s not as flexible and takes up more memory. How much memory are we talking about here?

1 Like

Very good point. So let’s keep it as it is.

I haven’t tested it yet, but I believe that it shouldn’t actually be much after thinking about it.

Since the collection is made up of Page objects (which don’t get cloned but just referenced as far as I can tell), the only things that will be duplicated are the references.
This means that the only problem would be that the Page objects are kept around even if they have been deleted, but since PHP’s objects don’t live beyond the current request, they can be cleaned up regularly, so I don’t see a problem here either.

1 Like

You Rock :sunglasses:

Kirby Status 0.3

Require Kirby 2.3 beta 2

Minor release.

Changelog

  • Moved field css from inline to a css file.
  • Added package.json.
  • Sass is now used for css.
  • Updated docs.