I have create a observer, before edit button click event are occurred.
using this I could change the value of select box
For an observer to be called, two things should exist, an event and an $observer which listens to that event. To set an observer on an event simply google, you will find out how.
If you wanna debug your observer,there are two important methods you can watch to understand what's going on. The first one is Mage_Core_Model_App#dispatchEvent at app/code/core/Mage/Core/Model/App.php:1271
public function dispatchEvent($eventName, $args)
{
$eventName = strtolower($eventName);
foreach ($this->_events as $area=>$events) {
// (...)
This is where all events has a stop on their way. During development, you can inspect the $eventName by setting a breakpoint here (my favorite), logging the value to a file, or even get very dirty and simply echo to see under the hood.
public function dispatchEvent($eventName, $args)
echo "BlaBlaBla"; // just used to find the printed lines in view source code of browser
print_r($eventName);
$eventName = strtolower($eventName);
foreach ($this->_events as $area=>$events) {
// (...)
Remember, you are editing core files, and this is just to explore and find the event name, get rid of these lines afterwards.
The second method which actually does the job is Mage_Core_Model_App#_callObserverMethod at app/code/core/Mage/Core/Model/App.php:1338
/**
* #param object $object
* #param string $method
* #param Varien_Event_Observer $observer
* #return Mage_Core_Model_App
* #throws Mage_Core_Exception
*/
protected function _callObserverMethod($object, $method, $observer)
{
if (method_exists($object, $method)) {
$object->$method($observer);
} elseif (Mage::getIsDeveloperMode()) {
Mage::throwException('Method "'.$method.'" is not defined in "'.get_class($object).'"');
}
return $this;
}
$object->$method($observer) will call the observer method on an object, just like before you can set a breakpoint, log to a file or even echo to see what's going on under the hood.
First find the event you are interested in, then try to add bind the event to the observer then, if not working, you can use the second method to debug.
And, yay.... Magento is never simple.
Related
I'm working on a media asset management system. I want the user to be able to fill out a form with file width, height, extension and colorspace, then transform the image and serve it back as a download.
I can get that to work by responding to the Post-Request with the URL of the newly created file.
What I want is for that file to be deleted after download or after some time.
(Or, preferably, a way to use laravels download() Response, which I apparently can't use inside an Axios/Ajax post request).
Thanks for your help :)
There are two ways you can do this.
Let's assume you have a file in storage/app/download.zip:
1. Because Laravel uses Symfony's HttpFoundation internally, you can use the deleteFileAfterSend method:
public function download()
{
return response()
->download(storage_path('app/download.zip'))
->deleteFileAfterSend(true);
}
2. Create a Terminable Middleware that deletes the file after the download response was prepared.
class StorageDownload
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
\Storage::delete('download.zip');
}
}
You'll need to register the middlware and assign it to your route for it to work.
As for triggering the download using JavaScript, something as trivial as setting the window location will work:
axios
.post('files/export/' + file.id, formData)
.then(function() {
window.location = 'files/download/' + file.id
});
Don't worry, this will not navigate away from your current page, it will just trigger the download.
for me , i like to handle this case using cron
you can read details here
add any column to check it's been downloaded or not (e.g status with value 0 or 1)
and you can create accessor on your model to count the date diff
public function getRangeDateAttribute(){
$today = Carbon::now()->startOfDay();
$downloadDate = $this->created_at;
$datediff = $today->diffInDays($downloadDate);
return $datediff
}
then sample code inside task scheduling :
if($media->status == 1 || $media->rangeDate > 7 ){ //checking the status & range date passed specific days you wanted
do whatever you wanted here
}
How to add a custom action to an existing Controller in Shopware?
Examples (url structure):
/account/bonus
/account/custom
/account/...
Usually it's easier and cleaner to create a new controller for that purpose, but in some cases it's necessary.
You should not replace the "account" controller.
You can define you own action for existing controller with following:
public static function getSubscribedEvents()
{
return [
'Enlight_Controller_Action_Frontend_Account_MyBonus' => 'onAccountMyBonus',
];
}
and then
public function onAccountMyBonus(\Enlight_Event_EventArgs $args)
{
$args->setProcessed(true);
.....
your code here
}
Spoiler: Replace the controller
There is no cleaner way than to replace the whole controller and extend it's functionality, so it's nearly as clean as Shopware's hooks.
Guide
Add a new Subscriber to your Plugin
class AccountSubscriber implements SubscriberInterface
{
/**
* #return array
*/
public static function getSubscribedEvents()
{
return array(
'Enlight_Controller_Dispatcher_ControllerPath_Frontend_Account' => 'getAccountController'
);
}
/**
* #return string
*/
public function getAccountController()
{
return $this->getPath() . '/Controllers/Frontend/AccountExtended.php';
}
/**
* #return string
*/
public function getPath()
{
$plugin = Shopware()->Container()->get('kernel')->getPlugins()['AcmeYourPlugin'];
return $plugin->getPath();
}
}
Downsides
Unfortunately some controller have private methods which impact the logic. Like the Account Controller. So it's not always possible to simply extend the controller.
In the end, try to add a new controller with a new route.
It's easier, and cleaner.
There is a cleaner way than replacing the whole Controller.
It is also not recommended to replace a whole controller due to the lack of update compatibility.
In the worst case something like that could kill the whole website.
A while ago I created a thread in the shopware forum (german) discussing the exact same issue. I wanted to extend an existing finishAction() in the checkout Controller.
public function onPostDispatchCheckout(\Enlight_Controller_ActionEventArgs $args)
{
/** #var \Enlight_Controller_Action $controller */
$controller = $args->getSubject();
/** #var \Enlight_Controller_Request_Request $request */
$request = $controller->Request();
if ($request->getActionName() !== 'finish') {
return;
}
// do your stuff here
}
So even though it is not the exact same issue you have, the procedure is quite the same.
First off you subscribe to the controller (in my case the PostDispatchCheckout Controller) afterwards you edit the controller in your Bootstrap.php
To make sure, that it just alters a specific action you have to use the if-construction so your code gets just triggered on the wished action [in my case the finishAction()].
I hope this helps. What wonders me though is why you have to add a new action to an already existing controller. I can think of no situation where something like that is more practicable than creating a complete new custom controller.
Kind regards,
Max
Okay so I have an observer that is observing the controller_action_postdispatch_customer_account_createpost action. My issue is that in the method I try to do the following:
public function customerSaveAfter($observer)
{
/** #var Mage_Customer_Model_Customer $customer */
$customer = $observer->getEvent()->getCustomer();
}
No matter what I do $customer is NULL. There is another extension that is called right before this and it uses that method exactly the same way and comes up with a customer. Please help.
The customer object is blank because the controller_action_postdispatch_customer_account_createpost event is a controller action event, and has nothing to do with the customer object. That event is issued in the following code
#File: app/code/core/Mage/Core/Controller/Varien/Action.php
public function postDispatch()
{
if ($this->getFlag('', self::FLAG_NO_POST_DISPATCH)) {
return;
}
Mage::dispatchEvent(
'controller_action_postdispatch_'.$this->getFullActionName(),
array('controller_action'=>$this)
);
Mage::dispatchEvent(
'controller_action_postdispatch_'.$this->getRequest()->getRouteName(),
array('controller_action'=>$this)
);
Mage::dispatchEvent('controller_action_postdispatch', array('controller_action'=>$this));
}
Specifically, the
Mage::dispatchEvent(
'controller_action_postdispatch_'.$this->getRequest()->getRouteName(),
array('controller_action'=>$this)
);
bit. ($this->getRequest()->getRouteName() returns customer_account_createpost). Notice that
array('controller_action'=>$this)
is passed into the event dispatch — this means you could access the controller object from your observer with the following
$observer->getControllerAction();
$observer->getData('controller_action');
You can also get a list of data keys variable to an observer with
var_dump(
array_keys($observer->getData())
);
The "other extension" (by which I assume you mean another extension's observer object) is probably listening to a different event, one that passes in a customer object to the event. For example, consider the customer_login event.
#File: app/code/core/Customer/Model/Session.php
public function setCustomerAsLoggedIn($customer)
{
$this->setCustomer($customer);
Mage::dispatchEvent('customer_login', array('customer'=>$customer));
return $this;
}
Here the event dispatch includes a customer object
array('customer'=>$customer)
which means the customer will be available in your observer.
I build a kernel listener some time ago, to redirect a user to a certain language.
There are several pages, that do not have a translation and where the user should not be redirected. As i use JMSI18nRoutingBundle, i figured, it would be the best way to use the 'options: { i18n: false }' setting from the bundle.
I would need to be able to read the options of the current route inside my kernel listener. It this possible?
For getting route options first you will have to get route collection and from route collection you will have to get the route object depending on the route name.
So your listener will have a dependency on router. Your constructor will look some what like this.
/**
* #var $routeCollection \Symfony\Component\Routing\RouteCollection
*/
private $_routeCollection;
function __construct(\Symfony\Bundle\FrameworkBundle\Routing\Router $router)
{
$this->_routeCollection = $router->getRouteCollection();
}
Now inside your listener method you will need request object to get current route name. For example if your listener method is onKernelController()
function onKernelController(FilterControllerEvent $event)
{
/**
* #var $route \Symfony\Component\Routing\Route
*/
$route = $this->_routeCollection->get($event->getRequest()->get('_route'));
// #var $allOptions will have all the options for current route.
$allOptions = $route->getOptions();
// To get specific option you can use getOption()
$someSpecificOption = $route->getOption('<key>');
}
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.