Blueprint pages list still shows drafts, ignores status: listed

Hi there!

I am working on a portfolio site for a client. We have a template called ‘project’ that acts as a detail page for a given project that they create dynamically, adding as many of these pages as they need.

They may want to add some of these as ‘feature projects’ so I made a fields section in the home page blueprint to allow them to pick from any of their listed ‘projects’ with the following:

featuredProjects:
  type: pages
  label: Featured Projects
  status: listed
  options: query
  query: site.find('work').children.published.filterBy('intendedTemplate', 'project')

Let’s say the choose ‘Project A’, ‘Project B’ and ‘Project C’ to all be feature projects, but then later on they change ‘Project B’ back to draft mode, this still appears in the list of featured projects on the home page in the panel, and the query even still shows the draft (Project B) as an option for them to choose in the dialogue box for adding / removing select feature projects.

How can I make this so they can’t chose a project that is in draft mode, and is there a way to automatically remove this from the list of featured projects? I figured having status: listed and query: site.find('work').children.published.filterBy('intendedTemplate', 'project') parts would have solved this.

Any suggestions? Thanks!

If featuredProjects is a pages field, then it doesn’t have a status property. Since you already filter by status (you might want to filter by listed instead of published if you only want listed pages, btw), an additional filter is not necessary.

This could be achieved with a page.changeStatus:after hook in which you clean up the field content, but the easier way would be to just exclude draft (and unlisted ) pages from the list in your template:

$featureProjects = $page->featuredProjects()->toPages()->listed();

You have already done that, so I wonder why this doesn’t work as expected. In my test, only published pages can be selected (with drafts that were selected before they became drafts again still being selected, of course, because they are stored in the content).

Could you please post your template code and the complete blueprint?

Hi @texnixe!

You’re right - I am in the fields part of my blueprint, not sections, so I removed the status: listed and modified my query like so:

featuredProjects:
  type: pages
  label: Featured Projects
  options: query
  query: site.find('work').children.listed.filterBy('intendedTemplate', 'project')

In my query above, I changed children.published to children.listed which fixed the dialogue box for selecting a page now. Nice! :slight_smile:

However, trying the hook didn’t do anything in my config.php file, so the list of feature projects still shows all three projects, even when’Project B’ is updated to draft:

<?php
return [
  ...
  'hooks' => [
    'page.changeStatus:after' => function ($page) {
      $featureProjects = $page->featuredProjects()->toPages()->listed();
    }
  ],
  ...
];

I also tried passing in $newPage and $oldPage to the hook function, but nothing changed:

page.changeStatus:after' => function ($newPage, $oldPage) {
  $featureProjects = $newPage->featuredProjects()->toPages()->listed();
}

and…

page.changeStatus:after' => function ($newPage, $oldPage) {
  $featureProjects = $oldPage->featuredProjects()->toPages()->listed();
}

Any other ideas or code examples needed from me? Thanks as always!

Well, if you go down the hook route, you would have to actually update the page ($page->udpdate()) with the modified data, i.e. remove possible drafts from the pages array and update the page with the new array, not just return the filtered data, because this won’t do anything.

That’s why filtering the pages in the template is easier, even though it means that the outdated data remains in the content file.

Thank you for the heads up @pixelijn!

Agreed, it would easier in theory to filter the pages in the template, but that is misleading for the client who is using the panel. Even after clearing my cache and refreshing the page, the list still shows the draft pages so this won’t work.

If you or @texnixe can help me understand how to use the $page->update() idea you briefly mentioned, that’d help a lot! I’m not a php developer, so I’m a bit lost as to where I do this and what I’m supposed to write here.

Thanks!

@pixelijn and @texnixe,

I’m looking at this documentation: https://getkirby.com/docs/reference/objects/page/update

But to my note above, this looks foreign to me as I’m not that savvy with php / kirby still. So how do I have featuredProjects show listed projects only and remove any drafts after a page is changed? Does this $page->update() go in the hook function like above, after a given page status is updated?

Again, if we have the three example projects I mentioned above, once I change Project B from listed to draft, when I go back to the home page of the panel, I should only see Project A and Project C in the featuredProjects list. This is working for the query selector box using children.listed in my blueprint file, so it seems so close, so easy, haha.

I really appreciate your help. Thanks!

I’ll get back to you later today.

Awesome, thank you @pixelijn!

This should work:

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

            if ( $newPage->status() === 'draft' ) {
                $homePage         = site()->find('home');
                $featuredProjects = $homePage->featuredprojects()->yaml();
                foreach ( $featuredProjects as $key => $value ) {
                    if ( $value === $newPage->id() ) {
                        unset( $featuredProjects[$key]);
                    }
                }
                $homePage->update( [
                    'featuredProjects' => Data::encode( $featuredProjects, 'yaml'),
                  ]);
        
                    
             
             }
        }
      ],

In any case, in your template, if you fetch the page with $page->featuredProjects()->toPages(), drafts will be excluded without you having to do anything else.

1 Like

Wow @pixelijn! Thank you so much for this code snippet - I just tested it out and it’s working just as expected! :smiley:

And thanks for the example for the template too!

Hi @pixelijn!

Everything was working the other day, then of course I go to show the client and it wasn’t working now. My code hasn’t changed, but is there a racing condition or issue in the page changeStatus hook?

I tried changing featuredprojects to featuredProjects, no luck though. It’s like it doesn’t know about the home page now? Very weird…

Have you changed the homepage in config?

When exactly does this happen? Only with the last page in the list that you want to change to draft? Or no matter if you want to make the page a draft or publish?

Hi @texnixe!

I didn’t change the home page or anything else in the config. This happens when I change the first page in the list to Draft instead of Published.

Thing I’m noticing though, it seems to work behind the scenes, so if I click Cancel or Change in the dialogue box, neither matter, because the panel reflects that I have made this a Draft now. If I refresh things seem to work as expected, but the error isn’t nice to look at, client was confused, as was I.

And I just tested changing another page in the list (2nd item, not just the first) and it still gives the error…

Hm, I’m not sure how to reproduce this because the code works in my project with Kirby 3.4.4, although these hook stuff is always a bit tricky, I think.

Can you see errors in the browser’s console maybe?

Ahh, it does show this error in the console:
POST http://127.0.0.1:8000/api/pages/animation/status 500 (Internal Server Error)

(In this case, I had a page named Animation that I turned to Draft mode…)

I’m using the Kirby Vue Starterkit that uses the api and php to JSON stuff.

So it seems that the API is trying to fetch a page that is no longer listed, but again, as soon as I refresh the panel everything works and the api doesn’t show this error anymore.

Hm,can’t say anything on that front. PHP errors probably interfere with the Panel JS in this case. So stuff works but throws errors. Make sure that on the PHP side of stuff you always make sure that you have objects before you call any methods.

Thanks @texnixe! Yeah, I just copied and pasted the answer from above, it’s weird that it worked and now it isn’t. I mean it is working, but the error is back, hah. Code hasn’t changed for two days since I marked this thing solved and now it’s breaking again.

If @pixelijn has any insight, beyond appreciated, otherwise I’ll tell the client to just ignore the error when it occurs and refresh the page. Dirty solution, but whatever works I guess…

Does the error disappear when you remove the hook?

I guess you don’t need the hook to work on all pages, so try to limit it to certain templates:

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

            if ( $newPage->intendedTemplate === 'blah' && $newPage->status() === 'draft' ) {
                $homePage         = site()->find('home');
                $featuredProjects = $homePage->featuredprojects()->yaml();
                foreach ( $featuredProjects as $key => $value ) {
                    if ( $value === $newPage->id() ) {
                        unset( $featuredProjects[$key]);
                    }
                }
                $homePage = $homePage->update( [
                    'featuredProjects' => Data::encode( $featuredProjects, 'yaml'),
                ]);           
             
             }
        }
      ],

Thanks Sonja! Correct, the error disappears when I remove the hook. I’ll try this out after breakfast, I think I know what’s happening now… slightly different then the above example, but this made me realize something that should do the trick… I’ll let you know what I end up doing :slight_smile: