I'm running a Magento Community website (on version 1.5.1) and I'm having an issue with "404" pages.
We have users landing on our site from direct links and also google/bing search results. The pages they go to may not be correct as they may have changed. Magento uses MVC to route requests to the correct controller, but when there isn't a controller Magento displays a static CMS page (i.e. "404" page). The problem is that this page doesn't allow me to write custom PHP code on it so I can't log the URL that caused the 404.
If I can find the right point in the code prior to displaying the CMS 404 page then I can log the URL and use that to do the appropriate URL rewriting.
Can someone help me as to where the code is that finally gives up on any controller and displays the custom CMS "404" page?
Basically this is happening in Mage_Cms_IndexController::noRouteAction(). But you also could just have a look at your webservers log for entries with the 404 return code (404 is set in the same method).
Alex was spot-on with his answer. I thought I'd post the code I wrote to solve the problem based on his answer.
/**
* Render CMS 404 Not found page
*
* #param string $coreRoute
*/
public function noRouteAction($coreRoute = null)
{
$this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
$this->getResponse()->setHeader('Status','404 File not found');
/* JCS */
$path = $this->getRequest()->getPathInfo();
if (!startsWith($path, '/media/')) {
if (!startsWith($path, '/images/')) {
if (!startsWith($path, '/ebay/')) {
if (!startsWith($path, '/app/')) {
Mage::log('JCS:noRouteAction:path:'.$path);
}
}
}
}
$pageId = Mage::getStoreConfig(Mage_Cms_Helper_Page::XML_PATH_NO_ROUTE_PAGE);
if (!Mage::helper('cms/page')->renderPage($this, $pageId)) {
$this->_forward('defaultNoRoute');
}
}
I also added a startsWith function:
/* JCS */
function startsWith($haystack, $needle)
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
Related
I'm wondering how I can render a view, or display a page with my default theme in OctoberCMS, via a route that executes a function in a controller.
If I have the following route:
Route::get('bransje', [
'uses' => 'Ekstremedia\Cityportal\CPController#bransje'
]);
And in my controller CPController ive tried several things, like I used to with Laravel:
public function bransje() {
$stuff = Stuff::with('info');
return View::make('cms::bransje')->with('stuff',$stuff);
}
But I cannot seem to get it to work, and I've tried to search the web, but it's hard to find answers. I have found a workaround, and that is to make a plugin component, then I can include that component and do:
public function onRun()
{
$this->eventen = $this->page['stuff'] = $this->stuff();
}
protected function stuff()
{
return ...
}
Is there any way so I can make pages without using the Cms, and that are wrapped in my default theme? I've tried
return View::make('my-theme-name::page');
and a lot of variants but no luck.
I know I can also do a:
==
public function onRun()
{
}
in the start of my page in the cms, but I'm not sure how to call a function from my plugin controller via there.
You can bypass frontend routing by using routes.php file in your plugin.
Full example in this video turotial.
If this answer can still be useful (Worked for October v434).
I have almost the same scenerio.
What I want to achieve is a type of routing like facebook page and profile.
facebook.com/myprofile is the same url structure as facebook.com/mypage
First I create a page in the CMS for each scenario (say catchpage.htm)
Then a created a catchall route at the buttom of routes.php in my plugin that will also not disturb the internal working of octobercms.
if (!Request::is('combine/*') && !Request::is('backend/*') && !Request::is('backend')) {
// Last fail over for looking up slug from the database
Route::get('{slug}/{slug2?}', function ($slug, $slug2 = null) {
//Pretend this are our routes and we can check them against the database
$routes = ["bola", "sade", "bisi", "ade", "tayo"];
if(in_array($slug, $routes)) {
$cmsController = new Cms\Classes\Controller;
return $cmsController->render("/catchpage", ['slug' => $slug]);
}
// Some fallback to 404
return Response::make(View::make('cms::404'), 404);
});
}
The if Request::is check is a list of all the resource that october uses under the hood, please dont remove the combine as it is the combiner route. Remove it and the style and script will not render. Also the backend is the url to the backend, make sure to supply the backend and the backend/*.
Finally don't forget to return Response::make(View::make('cms::404'), 404); if the resource is useless.
You may put all these in a controller though.
If anyone has a better workaround, please let us know.
I have problem with magento messages. I am building custom module which in theory should be able to restrict access to some parts of the store. I have created an observer which hook into controller_action_predispatch event and checks if current request can be accessed by the user. If the action cannot be accessed the observer redirects user and sets the error info. I want to set the redirect url to the page the customer is coming from in order to avoid clicking through entire shop. I am looking at the HTTP_REFERER and use it if it is set, otherwise I redirect customer to homepage. The problem is that in the later case (homepage redirect) everything works great but when I set url based on the referer I do not see error message in message box.
The code from the observer ($name variable is a string):
Mage::getSingleton('core/session')->addError('Acces to '.$name.' section is denied');
$url = Mage::helper('core/http')->getHttpReferer() ? Mage::helper('core/http')->getHttpReferer() : Mage::getUrl();
Mage::app()->getResponse()->setRedirect($url);
What I found interesting is that if I do any change in the observer file and save it, then the next request which fails and gets redirected to referer url shows the error information but any subsequent loses the messages.
I was thinking that the problem is in the full url and my local instalation (I am using .local domain) but so I tried adding
$url = str_replace(Mage::getBaseUrl(), '/', $url);
but this did not helped.
I also tried redirect using php header() function without any result as well.
All cache is disabled. The workflow which triggers the problem is as follows:
I'm going to any accessible page (for example /customer/account)
Click on cart link (cart for this account is disabled)
Return to /customer/account and the error message is displayed
Click on cart link again
Return to /customer/account but no error message
Any hint on where to look will be appreciated.
//A Success Message
Mage::getSingleton('core/session')->addSuccess("Some success message");
//A Error Message
Mage::getSingleton('core/session')->addError("Some error message");
//A Info Message (See link below)
Mage::getSingleton('core/session')->addNotice("This is just a FYI message...");
//These lines are required to get it to work
session_write_close(); //THIS LINE IS VERY IMPORTANT!
$this->_redirect('module/controller/action');
// or
$url = 'path/to/your/page';
$this->_redirectUrl($url);
This will work in a controller, but if you're trying to redirect after output has already been sent, then you can only do that through javascript:
<script language=”javascript” type=”text/javascript”>
window.location.href=”module/controller/action/getparam1/value1/etc";
</script>
Your messages get lost because you use an unfavorably way for a redirect in controller_action_predispatch. Your solution causes on the one hand the "message lost" and on the other hand, it wastes processing power of your server.
When you take a look at Mage_Core_Controller_Varien_Action::dispatch(), you'll see that your solution doesn't stop the execution of the current action, but it should do that with a redirect. Instead Magento executes the current action to its end, including the rendering of the message you had added before. So no wonder why the message gets lost with the next client request, Magento had it already rendered before, with the server response which includes your redirect.
Further you'll see in Mage_Core_Controller_Varien_Action::dispatch() only one possibility to stop the execution of the current action and skip directly to the redirect, which is in line 428 catch (Mage_Core_Controller_Varien_Exception $e) [...]. So you have to use Mage_Core_Controller_Varien_Exception which is quite unpopular, but the only right solution for your purpose. The only problem is, this class has a bug since it was introduced in Magento 1.3.2. But this can be easily fixed.
Just create your own class which is derived from Mage_Core_Controller_Varien_Exception:
/**
* Controller exception that can fork different actions,
* cause forward or redirect
*/
class Your_Module_Controller_Varien_Exception
extends Mage_Core_Controller_Varien_Exception
{
/**
* Bugfix
*
* #see Mage_Core_Controller_Varien_Exception::prepareRedirect()
*/
public function prepareRedirect($path, $arguments = array())
{
$this->_resultCallback = self::RESULT_REDIRECT;
$this->_resultCallbackParams = array($path, $arguments);
return $this;
}
}
So you can now implement your solution realy clean with that:
/**
* Your observer
*/
class Your_Module_Model_Observer
{
/**
* Called before frontend action dispatch
* (controller_action_predispatch)
*
* #param Varien_Event_Observer $observer
*/
public function onFrontendActionDispatch($observer)
{
// [...]
/* #var $action Mage_Core_Model_Session */
$session = Mage::getSingleton('core/session');
/* #var $helper Mage_Core_Helper_Http */
$helper = Mage::helper('core/http');
// puts your message in the session
$session->addError('Your message');
// prepares the redirect url
$params = array();
$params['_direct'] = $helper->getHttpReferer()
? $helper->getHttpReferer() : Mage::getHomeUrl();
// force the redirect
$exception = new Your_Module_Controller_Varien_Exception();
$exception->prepareRedirect('', $params);
throw $exception;
}
}
this will work , so try it:
$url = 'path/to/your/page';
$this->_redirectUrl($url);
return false;
This means you are not allowing again to execute anything else.
I've configured my magento index.php so it detects the browser language and redirects the user to the corresponding store view. That works fine. But there is a problem with this with url specific for each store view. For example, a have a product with the url:
./product-url-in-english --> English view
./product-url-in-catalan --> Catalan view
If my browser is configured in English and I go to the Catalan url, then I got a 404 error, because that url is only for the Catalan view, but given that I'm detecting the browser language and redirecting to the English view, the Catalan URL won't work.
If I use the paramenters from_store and store, it will work, but I don't know how to reflect that on my index.php file. The code that detects the browser language is the following:
/* Language detection */
function getLanguageCode()
{
$default_language_code = 'es';
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
foreach (explode(",", strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE'])) as $accept) {
if (preg_match("!([a-z-]+)(;q=([0-9.]+))?!", trim($accept), $found)) {
$langs[] = $found[1];
$quality[] = (isset($found[3]) ? (float) $found[3] : 1.0);
}
}
// Order the codes by quality
array_multisort($quality, SORT_NUMERIC, SORT_DESC, $langs);
// get list of stores and use the store code for the key
$stores = Mage::app()->getStores(false, true);
// iterate through languages found in the accept-language header
foreach ($langs as $lang) {
$lang = substr($lang,0,2);
if (isset($stores[$lang]) && $stores[$lang]->getIsActive())
return $lang;
}
}
return $default_language_code;
}
/* Store or website code */
if(isset($_SERVER['MAGE_RUN_CODE']))
$mageRunCode = $_SERVER['MAGE_RUN_CODE'];
else
$mageRunCode = getLanguageCode();
/* Run store or run website */
$mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';
Mage::run($mageRunCode, $mageRunType);
What do I need to change in my index.php file so I can handle url from all the store views and redirect to the specific page without getting a 404 error?
Thanks
I have been searching forever trying to find a way to disable the feed link (ie: www.[websitename].com/?format=feed&type=rss).
Is there a way to disable this link? I have some bad google indexes of these and I want to return 404 not found when google tries to index them again.
Try editing the libraries/joomla/document/feed/feed.php file.
Find __construct method of JFeedDocument class in the file and change it to look like this:
function __construct($options = array()) {
JError::raiseError(404, JText::_('Resource Not Found'));
}
But, in this case, you won't have "feed" support at all.
Just in case someone is out there looking for this, what worked for me was setting 404 Not Found headers in the JDocumentFeed constructor:
function __construct($options = array()) {
if (!headers_sent()) {
header('HTTP/1.0 404 Not Found');
//I guess you could include here a file to show the 404 page
}
exit();
}
The call for JError::raiseError(404, JText::_('Resource Not Found')); gave me a backtrace of the error.
For example in my logs I have many lines that repeat, such as:
ERROR - 2011-07-06 09:19:01 --> 404 Page Not Found --> favicon.ico
Is there any way for me to find out who is calling these errant URLs? The favicon is just an example, there are some URLs that for example show an intent to hack, and others it's just the same error repeated over and over and over again. Basically I'd love to know which IP's to potentially block as well as what sites to contact if they have bad links (or if I should just redirect them on my server with .htaccess).
If you feel the need to do this, just extend the Exceptions class and override the show_404() function:
// v2.x: core/MY_Exceptions.php
// v1.x: libraries/MY_Exceptions.php
class MY_Exceptions extends CI_Exceptions {
/**
* 404 Page Not Found Handler
*
* #access private
* #param string
* #return string
*/
function show_404($page = '', $log_error = TRUE)
{
$heading = "404 Page Not Found";
$message = "The page you requested was not found.";
// By default we log this, but allow a dev to skip it
if ($log_error)
{
// Custom code here, maybe logging some $_SERVER variables
// $_SERVER['HTTP_REFERER'] or $_SERVER['REMOTE_ADDR'] perhaps
// Just add whatever you want to the log message
log_message('error', '404 Page Not Found --> '.$page);
}
echo $this->show_error($heading, $message, 'error_404', 404);
exit;
}
}
If you keep getting stuff like favicon.ico, there's a good chance this is your fault, so you might want to look into that.
Just to clarify:
$_SERVER['HTTP_REFERER'] will give you the URL the page was requested from, so you can see where the request originated, whether it be on your site or elsewhere.
$_SERVER['REMOTE_ADDR'] should give you the IP address the request was made by
http://php.net/manual/en/reserved.variables.server.php
Brief Example:
if ($log_error)
{
$msg = '';
if (isset($_SERVER['HTTP_REFERER']))
{
$msg .= 'Referer was '.$_SERVER['HTTP_REFERER'];
}
else
{
$msg .= 'Referer was not set or empty';
}
if (isset($_SERVER['REMOTE_ADDR']))
{
$msg .= 'IP address was '.$_SERVER['REMOTE_ADDR'];
}
else
{
$msg .= 'Unable to track IP';
}
log_message('error', '404 Page Not Found --> '.$page.' - '.$msg);
}
I believe if you want this, you'd have to log it yourself by creating a custom 404 page and manually logging to the log file. You can access the IP address in PHP via $_SERVER['REMOTE_ADDR'].