page_list block with custom action - page not found

Permalink
Hi,

I want to customize the PageList block (with an additional search bar which requires a new action).

In my BASE_URL/application/blocks/page_list/view.php, I have:
<form class="form-wrapper cf" action="<?php echo $view->action('customised') ?>" method="post" enctype="multipart/form-data">

The generated form action URL seems correct: BASE_URL/page/customised/{bID}

In my BASE_URL/application/blocks/page_list/controller.php, I have:
<?php
namespace Application\Block\PageList;
use Concrete\Block\PageList\Controller as PageListBlockController;
class Controller extends PageListBlockController{
public function action_customised($bID = false){
   $this->view();
}
}

The extend is working since I can add other functions which work.

But for an unknown reason, I always get a 404 error (Page not found) when I submit my form with the defined action (BASE_URL/page/customised/{bID}).

Thank you in advance for your help!
Cheers,
marc

marcsublet
 
JohntheFish replied on at Permalink Reply
JohntheFish
As an experiment, you could try calling one of the pre-existing block controller actions and supplying the appropriate parameters. It will obviously fail, but maybe confirm there is a problem with the way your action is being 'discovered' by the core.
marcsublet replied on at Permalink Reply
marcsublet
Hi John,

Thanks for the tip.
I have tried with an action defined in the "page_list" block and I get the same 404 error (= the action is not processed).

To be sure, I have duplicated/edited my custom view and controller to the "content" block. Tada! It's working flawlessly as it has to work.

My problem seems to be related to the "page_list" block especially.
If you have an idea, it's very welcome! I will submit it as a bug but I am curious if someone can duplicate the behavior.

Regards,
marc
bleenders replied on at Permalink Best Answer Reply
bleenders
Just ran in to the same problem you have. The page list won't let you add extra actions. If you call them they will always return a 404 error. If you look in the code of the Page List block controller you see:
public function isValidControllerTask($method, $parameters = [])
{
        if (!$this->enableExternalFiltering) {
            return false;
        }
        return parent::isValidControllerTask($method, $parameters);
}


This indicates that when you don't have "Enable Other Blocks to Filter This Page List." checked you won't be allowed to call any actions on the block controller.
But even when you do have it checked it won't work. If you add "return true;" to the method above you'll see that your actions will work.

The problem lies in the parent "isValidControllerTask" method on the Concrete\Core\Block\BlockController class.
public function isValidControllerTask($method, $parameters = [])
    {
        if (strpos($method, 'action_') !== 0) { // gotta start with action_
            return false;
        }
        if (is_callable([$this, $method])) {
            $r = new \ReflectionMethod(get_class($this), $method);
            if (count($parameters) - $r->getNumberOfParameters() <= 1) {
                // how do we get <= 1? If it's 1, that means that the method has one fewer param. That's ok because
                // certain older blocks don't know that the last param ought to be a $bID. If they're equal it's zero
                // which is best. and if they're greater that's ok too.
                return true;
            }
        }
        return false;

if (is_callable([$this, $method]))

Will always return false because:
get_class($this)

Returns string(59) "Concrete\Block\CorePageTypeComposerControlOutput\Controller"
And on that's not the controller we want to check for the new action we made.

I guess this is a bug.
marcsublet replied on at Permalink Reply
marcsublet
Hi,
I think you get it.
I went around the problem with adding a pagelist to the "search" block instead of adding a search to the "pagelist" one.
I have submitted the problem but I will complete it with your comment.
Cheers,
marc
bleenders replied on at Permalink Reply
bleenders
Did some further digging.
You can solve the problem by overriding the getPassThruActionAndParameters method on the block controller.
I just copied the method from the original PageList controller and added it to my extended version.
public function getPassThruActionAndParameters($parameters)
{
        if ($parameters[0] == 'topic') {
            $method = 'action_filter_by_topic';
            $parameters = array_slice($parameters, 1);
        } elseif ($parameters[0] == 'tag') {
            $method = 'action_filter_by_tag';
            $parameters = array_slice($parameters, 1);
        }elseif ($parameters[0] == 'loadpages') {
            $method = 'action_loadpages';
            $parameters = array_slice($parameters, 1);
        }
        elseif (Core::make('helper/validation/numbers')->integer($parameters[0])) {
            // then we're going to treat this as a year.
            $method = 'action_filter_by_date';


I added the elseif ($parameters[0] == 'loadpages') part.
Replace loadpages with name of your action.

Every request you make the validateRequest method on Concrete\Core\Page\Controller\PageController is triggered.
This will iterate over all the block controllers used on the page to see if it can find the action you called. On each controller the getPassThruActionAndParameters is called.
This method is overridden in the original PageList controller and made in such a way that it will return null if the action called is not topic, tag or an integer. This is the reason why all the custom actions made on the PageList controller will return 404.
So you can fix it by overriding the method yourself and add an exception for your custom action/method.