Empty route leeds to 404-Error - SilverStripe 3.5 - php

Like superficial descriped in SilverStripe Docs, I'm trying to set a custom controller for my homepage.
I changed the default homepage link to 'custom-home' and added those two routes.
The second one, with the path in it works and directs me to my controller. The first (empty) one just sends me to an 404-error page.
Couldn't figure out how to fix that. Any suggestions?
routes.yml
Director:
rules:
'': 'MyHome_Controller'
'custom-home': 'MyHome_Controller
_config.php
RootURLController::set_default_homepage_link('custom-home');
MyHome_Controller.php
<?php
class MyHome_Controller extends Page_Controller {
private static $allowed_actions = [];
private static $url_handlers = [];
public function init() {
parent::init();
}
public function Link($action = null) {
return Director::baseURL() . 'custom-home';
}
public function index() {
$data = [
'Title' => 'Hello World',
'ClassName' => __CLASS__,
];
return $this
->customise($data)
->renderWith([__CLASS__, 'Page']);
}
}

I believe the way the empty route (RootURLController) works is that you're telling it the URLSegment of a page in the CMS that should resolve to the root URL. So I think what you need to do is go into the CMS and change the URLSegment of your CustomHomePage to 'custom-home'.

Related

What is the best prestashop-way to include tpl in AdminController?

I need interacts with a .tpl file in my adminController class, but when I try to do that, this error appears
Fatal error: Call to undefined method RiddlePageController::getCacheId() in /home/USER/public_html/prestashop/modules/RiddleModule/controllers/admin/RiddlePage.php on line 48
This is my admin controller code:
class RiddlePageController extends AdminController {
public function __construct()
{
$this->html = '';
$this->display = 'view';
$this->meta_title = $this->l('metatitle');
$this->module = "RiddleModule";
parent::__construct();
}
public function initContent()
{
$this->postProcess();
$this->show_toolbar = true;
$this->display = 'view';
$this->meta_title = $this->l('Modulo');
parent::initContent();
}
public function initToolBarTitle()
{
$this->toolbar_title = $this->l('Titulo');
}
public function initToolBar()
{
return true;
}
public function renderView() {
$this->context->smarty->assign(
array(
'img1' => "http://www.free-3dmodels.com/image/Flowers-3D-Model-3662994d.png",
'img2' => "http://www.all3dmodel.com/Images/39.jpg"
)
);
// in return have error "getCacheId"
return $this->display(__FILE__, 'content.tpl', $this->getCacheId());
// return "<b>This works fine!!</b>";
}
my tpl file have only {$img1} and {$img2} for testing.
Maybe I do all wrong, and this is not the best way to make in my own admin page.
Your error is because the AdminController class doesn't have the getCacheId method.
To answer to your question you have to made some little fix.
First (extends ModuleAdminController not AdminController):
class AdminRiddlePageController extends ModuleAdminController
{
}
Then if you want to view your custom tpl, place a view.tpl file in:
prestashop/modules/RiddleModule/views/templates/admin/riddlepage/helpers/view/
or
prestashop/modules/RiddleModule/views/templates/admin/riddle_page/helpers/view/ (I don't remember well if the underscore is necessary)
And your renderView method should be like this:
public function renderView()
{
/* Your code */
/* Use this snippet to assign vars to smarty */
$this->tpl_view_vars = array(
'myvar' => 1,
'secondvar' => true
)
return parent::renderView();
}
AdminController class has not an implementation of display method you use to render TPL.
You can use something like this after set module var:
$this->module->display(_PS_MODULE_DIR_.$this->module->name.DIRECTORY_SEPARATOR.$this->module->name.'.php', 'content.tpl')
Good luck.
As #TheDrot told us, the answer are in using $this->context->smarty->fetch(location), but not in renderList, but in the return statement of renderView is OK and prestashop get the tpl file and load correctly the smarty variables. Ex:
public function renderView(){
$this->context->smarty->assign(
array(
'img1' => "http://www.free-3dmodels.com/image/Flowers-3D-Model-3662994d.png",
'img2' => "http://www.all3dmodel.com/Images/39.jpg"
)
);
return $this->context->smarty->fetch(_PS_MODULE_DIR_ . "RiddleModule/controllers/front/prueba.tpl");
}
The file location isn't important to load the TPL file in this case

Calling an action on ModelAsController subclass

I have a custom route in my routes.yml that forwards any unknown request to BaseController
'$URLSegment/$Name/$Action/$ID': 'BaseController'
And from there the the request is routed (among other places) to my main Controller where it is handled by the index() of the controller.
However it will always use the index() so if I go to the URL test1/test2/action/5 it will still be run by index()
This is my BaseController
class BaseController extends ModelAsController {
public function getNestedController() {
$action
$params = $this->getRequest()->params();
$this->loadMain($params['URLSegment'], $params['Name'], $params['Action'], $params['ID']);
}
private function loadMain($first, $name, $action, $id) {
$main = new MainController();
$main->{$action}();
}
}
and this will call the function however the index() function has already run and set the template.
I know I could call the function from index() and return the template like that however I'm fairly sure this would bypass the security features of the allowed actions which I'm keen to maintain.
I've defined $allowed_actions in my MainController and have added:
private static $url_handlers = array(
'$URLSegment/$Name/something/$ID' => 'something'
);
but it still just calls index().
How can I maintain the same action routing of SilverStripe through the custom routed MainController
You need to define private static $allowed_actions and maybe private static $url_handlers also, the latter providing custom routing.
Basically all the things from: https://docs.silverstripe.org/en/3/developer_guides/controllers/routing/
This isn't well documented but if you are wanting to reverse the URL convention of /path/$Action/$Name/$ID to something like /path/$Name/$Action then you put:
private static $url_handlers = array(
'path//$Name/$Action' => 'handleAction'
);
The handleAction is important as it calls the parents (Controller`) handleAction and routes it correctly applying permissions and so forth.

Silverstripe: Not able to access Page fields in controller with custom route

I'm using SilverStripe 3.3.1 and have a custom route set up to handle urls with many parameters. That works.
However, the routing rule causes Page fields and functions to be inaccessible in the Page_Controller and templates. Any ideas how to fix this?
//MyPage class
class MyPage extends Page {
//Not accessible if route to controller specified in config.yml
private static $db = array(
'MyPageVar' => 'Int',
);
//Not accessible if route to controller specified in config.yml
public function getMySpecialVar() {
return $this->MyPageVar;
}
}
//MyPage_Controller class
class MyPage_Controller extends Page_Controller {
private static $allowed_actions = array(
'index',
'detailsearch',
);
private static $url_handlers = array (
'detailsearch/$Key1/$Value1/$Key2/$Value2/$Key3/$Value3/$Key4/$Value4/$Key5/$Value5' => 'detailsearch',
);
/**
* UseMyPageVar()
*
* #return Boolean
*/
public function UseMyPageVar() {
//Empty if route to controller specified in config.yml
Debug::show($this->MyPageVar);
Debug::show($this->Title);
Debug::show($this->Content);
//Error if route to controller specified in config.yml
Debug::show($this->getMySpecialVar());
return true;
}
}
MyPage.ss
<!-- This work as expected if no route is specified. -->
<!-- But all vars are empty if route is specified in config.yml -->
<p>MyVar: $MyPageVar</p>
<p>Title: $Title</p>
<p>Content: $Content</p>
Routing rule in config.yml
Director:
rules:
'mypage': 'MyPage_Controller'
This question is also posted on the Silverstripe forum:
http://www.silverstripe.org/community/forums/general-questions/editpost/413506
It's not pretty, but for now I've solved the problem by using a private var in the Controller class to hold a reference to the page.
//MyPage_Controller class
class MyPage_Controller extends Page_Controller {
private $_page; //reference to page that's lost with custom routing
//ContentController uses route, which has been changed to
// 'MyPage_Controller' by routing rule, to initialize
// page reference. Can't find anything so reference
// not set. (set to -1)
public function init() {
parent::init();
//Initialize using default route overwritten in routing rule
// This will break if URL segment changed in CMS
$route = array_search($this->URLSegment,
Config::inst()->get('Director', 'rules'));
$link = str_replace($this->URLSegment, $route, $this->Link());
$this->_page = $this->Page($link);
}
//Use private var to access page fields
public function MyPageVar() {
Debug::show($this->_page->MyPageVar);
}
//expose $Content to templates
public function Content() {
return $this->_page->Content;
}
//Can't use Title() so expose Page Title as $PageTitle
public function PageTitle() {
return $this->_page->Title;
}
}
Three things spring to mind when I look at your code:
That "mypage" in config.yml should be the name of a public method on MyPage_Controller. As it is, SilverStripe cannot find a matching method called mypage and will default to calling index() instead.
Routes should really go in a separate routes.yml file so you can "namespace" it to be invoked before or after SilverStripe's own core routes. If you don't do this, then it may result in the weird behaviour you're experiencing.
Did you know that you can debug your routes using the ?debug_request=1 URL param? See: https://docs.silverstripe.org/en/3.3/developer_guides/debugging/url_variable_tools#general-testing

SilverStripe 3.1+ Dynamically creating page redirects

I have a page type 'ProductPage', it has tabs that are navigated to like so:
/ProductPageUrlSegment/?tab=video
/ProductPageUrlSegment/?tab=audio
/ProductPageUrlSegment/?tab=photos
I'd like for redirects to be created when each new product page is created so that if you navigated /ProductPageUrlSegment/video it goes to /ProductPageUrlSegment/?tab=video
and the same for all tabs. I'm not clear if I should be using Routing https://docs.silverstripe.org/en/3.3/developer_guides/controllers/routing/ or redirection https://docs.silverstripe.org/en/3.3/developer_guides/controllers/redirection/
I have a link function for another project which goes to the parent page
public function Link() {
return $this->Parent()->Link() . '#' . $this->URLSegment;
}
Mine would be something like:
public function LinkVideo() {
return $this->Link()->'/?=video' . '#' . $this->URLSegment->'/video';
}
I don't have the knowledge to work this out so any guidance appreciated.
This will achieve the above, by handling the URL as an action and then redirecting to the same page, but with the get variable set...
class ProductPage extends Page {
}
class ProductPage_Controller extends Page_Controller {
private static $allowed_actions = array(
'video',
'audio',
'photos',
);
public function video() {
$this->redirect($this->Link().'?tab=video');
}
public function audio() {
$this->redirect($this->Link().'?tab=audio');
}
public function photos() {
$this->redirect($this->Link().'?tab=photos');
}
}

Controller routing not working as expected in silverstripe 3.1

I'm setting up routing to a controller and I keep getting either a 404, or the 'getting started with the silverstripe framework' page.
In routes.yaml I have:
---
Name: nzoaroutes
After: framework/routes#coreroutes
---
Director:
rules:
'view-meetings/$Action/$type': 'ViewMeeting_Controller'
My controller looks like this:
class ViewMeeting_Controller extends Controller {
public static $allowed_actions = array('HospitalMeetings');
public static $url_handlers = array(
'view-meetings/$Action/$ID' => 'HospitalMeetings'
);
public function init() {
parent::init();
if(!Member::currentUser()) {
return $this->httpError(403);
}
}
/* View a list of Hospital meetings of a specified type for this user */
public function HospitalMeetings(SS_HTTPRequest $request) {
print_r($arguments, 1);
}
}
And I've created a template (ViewMeeting.ss) that simply outputs $Content, but when I flush the site cache and visit /view-meetings/HospitalMeetings/6?flush=1
I get the default 'getting started with the Silverstripe framework' page
I know the routing in routes.yaml is working, because if I change the route there and visit the old URL I get a 404, but the request doesn't seem to fire my $Action...
You had 2 different rules in your YAML and controller ($type vs $ID). Also, I don't think you need to define the route in both YAML and the Controller.
Try this, the YAML tell SS to send everything that starts with 'view-meetings' to your Controller, then $url_handlers tell the Controller what to do with the request depending on everything after 'view-meetings' in the URL.
routes.yaml
---
Name: nzoaroutes
After: framework/routes#coreroutes
---
Director:
rules:
'view-meetings': 'ViewMeeting_Controller'
ViewMeeting_Controller.php
class ViewMeeting_Controller extends Controller {
private static $allowed_actions = array('HospitalMeetings');
public static $url_handlers = array(
'$Action/$type' => 'HospitalMeetings'
);
public function init() {
parent::init();
if(!Member::currentUser()) {
return $this->httpError(403);
}
}
public function HospitalMeetings(SS_HTTPRequest $request) {
}
}
The Silverstripe documentation on routing isn't at all clear on this point, but for $Action to be correctly interpreted you should use a double slash before it in the routes.yml file:
view-meetings//$Action/$type
According to the documentation, this sets something called the 'shift point'. Exactly what this means isn't described very well either in the documentation or in the source code which matches URLs against rules.
I'm doing some guessing here, but what if you drop the
public static $url_handlers = array(
'view-meetings/$Action/$ID' => 'HospitalMeetings'
);
part and change the Action method to:
// View a list of Hospital meetings of a specified type for this
public function HospitalMeetings(SS_HTTPRequest $request) {
// Should print 8 if url is /view-meetings/HospitalMeetings/6
print_r($request->param('type');
}

Categories