redirect for only admin module - php

I have the following code inside my Admin Module.php
public function onBootstrap(MvcEvent $e)
{
$application = $e->getApplication();
$em = $application->getEventManager();
if(!SystemUtils::isApiRequest()){
$em->attach(\Zend\Mvc\MvcEvent::EVENT_DISPATCH, array($this,'initUser'),10000);
}
}
public function initUser(MvcEvent $e)
{
$isLoggedIn=false;
// determined elsewhere
$action = Pluto::registry('application_action');
if($action!=='login' && !$isLoggedIn){
$viewHelperManager = $e->getApplication()->getServiceManager()->get('ViewHelperManager');
$renderer = $viewHelperManager->getRenderer();
$url = $renderer->plutourl('login');
$response = $e->getApplication()->getServiceManager()->get('Response');
$response->getHeaders()->clearHeaders()->addHeaderLine('Location', $url);
$response->setStatusCode(302)->sendHeaders();
exit();
}
}
The problem is this is only supposed to activate when inside a page within the admin module and you are not logged in but its operating for every request regardless of the module the code is inside
I want to limit this check to just when accessing the admin module.
Update
$sm = $e->getApplication()->getServiceManager();
$router = $sm->get('router');
$request = $sm->get('request');
$matchedRoute = $router->match($request);
returns the admin controller class name which i can use but i want it for all of the admin controller classes

You can use the shared event manager to attach to events in a given name space. In the example below we attach to an event only for the current modules namespace.
use Zend\Mvc\MvcEvent;
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$em = $app->getEventManager()->getSharedManager();
$sm = $app->getServiceManager();
$em->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function ($e) use ($sm) {
/**
* this will be triggered only by controlers in
* this module/namespace...
*/
$this->doSomething();
});
}
You could add this inside your admin module and only this event will only be listened to for classes inside this module / namespace.

You should use the MvcEvent object!
If all controllers are under a single namespace, for instance \Admin\Controller:
$namespace = 'Admin\\Controller\\';
$matchController = $e->getRouteMatch()->getParam('controller');
if( $namespace === substr($matchController, 0, strlen($namespace))
{
// check here if user is logged in.
}
else {
// not an admin controller, do nothing.
}

Related

zend framework 3 - how to disable layout for entire controller

In Zend Framework 3, is it possible to disable the layout for an entire controller, preferably in the __construct() or onDispatch() methods?
I know that I can disable the layout for specific actions, for example:
public function indexAction()
{
$view = new \Zend\View\Model\ViewModel();
$view->setTerminal(true);
return $view;
}
However, I would like to disable the layout for all actions in the controller without having to copy and paste the above code in every action.
In your Module class :
public function onBootstrap(MvcEvent $e)
{
$sharedEvents = $e->getApplication()
->getEventManager()
->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch',
function ($e) {
if ($e->getRouteMatch()->getParam('controller') == '[your controller name in lowercase]') {
$result = $e->getResult();
if ($result instanceof \Zend\View\Model\ViewModel) {
$result->setTerminal(true);
} else {
throw new \Exception(
__METHOD__ . ' expected \Zend\View\Model\ViewModel');
}
}
});
}

phalconphp Access controllerName that triggered a event

How could i reference the controllerName and actionName that triggered the beforeExecuteRoute from the event itself?
<?php
use Phalcon\Events\Manager as EventsManager;
//Create a events manager
$eventManager = new EventsManager();
//Listen all the application events
$eventManager->attach('micro', function($event, $app) {
if ($event->getType() == 'beforeExecuteRoute') {
//how to get controller name to handle acl stuff
}
});
From the documentation - http://docs.phalconphp.com/en/latest/api/Phalcon_Mvc_Dispatcher.html
getModuleName () - Gets the module where the controller class is
getControllerName () - Gets last dispatched controller name
getActionName () - Gets the lastest dispatched action name
Example:
<?php
use Phalcon\Events\Manager as EventsManager;
//Create a events manager
$eventManager = new EventsManager();
//Listen all the application events
$eventManager->attach('micro', function($event, $app) {
if ($event->getType() == 'beforeExecuteRoute') {
$controllerName = $app->getControllerName();
$moduleName = $app->getModuleName();
$actionName = $app->getActionName();
}
});
This way you can get the route in string format to parse it:
$router->getMatchedRoute()->getPattern();
Hope this help. I found no other way to do this.
If you don't have a dispatcher you must be getting those values from the router. I'm not very familiar with specifics of micro apps, but from looking at the docs it must be something like that.
<?php
use Phalcon\Events\Manager as EventsManager;
//Create a events manager
$eventManager = new EventsManager();
//Listen all the application events
$eventManager->attach('micro', function($event, $app) {
if ($event->getType() == 'beforeExecuteRoute') {
//how to get controller name to handle acl stuff
DI::getDefault()->get('router')->getControllerName();
DI::getDefault()->get('router')->getActionName();
// or
$app->getRouter()->getControllerName();
$app->getRouter()->getActionName();
}
});
Does this work?
I actually ran into a similar confusion, but the other answers didn't help, and Phalcon's official documentation didn't find the right way to get it, so if you're using Micro, it's hard to find an effective answer on the web.So I used my reflection to understand Phalcon's before, and finally came up with the following answer, which I hope will help you. Good luck!
<?php
$eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach(
'micro:beforeExecuteRoute',
function (\Phalcon\Events\Event $event, $app) {
$controllerName = $event->getSource()->getActiveHandler()[0]->getDefinition();
$actionName = $event->getSource()->getActiveHandler()[1];
}
);
$app = new \Phalcon\Mvc\Micro($di);
$app->setEventsManager($eventsManager);
If you want to implement the ACL with the before event, the following AclAnnotation code may help:
<?php
class AclAnnotation
{
protected $private = true;
protected $roles = [];
protected $component = [];
protected $access = [];
public function __construct($event, $app)
{
$controllerName = $event->getSource()->getActiveHandler()[0]->getDefinition();
$actionName = $event->getSource()->getActiveHandler()[1];
$reflector = $app->annotations->get($controllerName);
$annotations = $reflector->getClassAnnotations();
if (!$annotations) {
throw new ErrorException('The permission configuration is abnormal. Please check the resource permission comments.');
return;
}
if (
$annotations->has('Private')
&& ($annotation = $annotations->get('Private'))->numberArguments() > 0
) {
$this->private = $annotation->getArguments('Private')[0];
}
if (
$annotations->has('Roles')
&& ($annotation = $annotations->get('Roles'))->numberArguments() > 0
) {
$this->roles = $annotation->getArguments('Roles');
}
if (
$annotations->has('Components')
&& ($annotation = $annotations->get('Components'))->numberArguments() > 0
) {
$this->components = $annotation->getArguments('Components');
}
$annotations = $app->annotations->getMethod($controllerName, $actionName);
if (
$annotations->has('Access')
&& ($annotation = $annotations->get('Access'))->numberArguments() > 0
) {
$this->access = $annotation->getArguments('Access');
}
}
public function isPrivate()
{
return $this->private;
}
public function getRoles()
{
return $this->roles;
}
public function getComponents()
{
return $this->components;
}
public function getAccess()
{
return $this->access;
}
}
Create an event listener in index.php:
<?php
$eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach(
'micro:beforeExecuteRoute',
function (\Phalcon\Events\Event $event, $app) {
$aclAnnotation = new AclAnnotation($event, $app);
if (!$aclAnnotation->isPrivate()) {
return true;
}
$acl = $app->currentACL;
// Verify that you have access permission for the interface
if (!$acl->isAllowed($aclAnnotation->getRoles(), $aclAnnotation->getComponents(), $aclAnnotation->getAccess())) {
throw new \ErrorException('You do not have access to the resource', 40001);
return false;
}
return true;
}
);
$app = new \Phalcon\Mvc\Micro($di);
$app->setEventsManager($eventsManager);
As for the use of annotations, you can refer to here:
https://docs.phalcon.io/5.0/en/annotations

Creating customer specific routes in Zend Framework 2

I'm developing a WebApp which (as usual) must support customer specific functionalities.
To achieve it I plan to set the customer name in the local app configuration (config/autoload/local.php )
configuration file so that I can use it to call the specialized code later on.
The module folder structure is this:
/module/Application
/module/Application/config
/module/Application/src
/module/Application/src/Application
/module/Application/src/Application/Controller
/module/Application/src/Application/Controller/[customer_instance_name]
/module/Application/src/Application/Model
/module/Application/src/Application/Model/[customer_instance_name]
/module/Application/view
/module/Application/view/Application
/module/Application/view/Application/[action]
/module/Application/view/Application/[action]/[customer_instance_name]
Using a custom ViewModel I inject the specific customer name to the template path:
namespace Application\Model;
use Zend\View\Model\ViewModel;
use Zend\View\Resolver\TemplatePathStack;
use Zend\Mvc\Service\ViewTemplatePathStackFactory;
class MyViewModel extends ViewModel
{
private $customInstanceName;
private $pathStack;
/**
* Constructor
*
* #param null|array|Traversable $variables
* #param array|Traversable $options
*/
public function __construct($variables = null, $options = null)
{
parent::__construct ( $variables, $options );
$serviceLocator = MySingleton::instance()->serviceLocator;
$factory = new ViewTemplatePathStackFactory();
$this->pathStack = $factory->createService($serviceLocator);
$config = $serviceLocator->get('config');
if (isset($config['custom_instance_name']) AND ($config['custom_instance_name']!='')) {
$this->customInstanceName = $config['custom_instance_name'];
} else {
$this->customInstanceName = false;
}
}
/**
* Set the template to be used by this model
*
* #param string $template
* #return ViewModel
*/
public function setTemplate($template)
{
$this->template = (string) $template;
if ( $this->customInstanceName === false) {
return $this;
}
$pathComponents = explode('/', (string) $template);
$last = array_pop($pathComponents);
array_push($pathComponents, $this->customInstanceName);
array_push($pathComponents, $last);
$customTemplate = implode('/', $pathComponents);
if ($this->pathStack->resolve($customTemplate) !== false) {
$this->template = $customTemplate;
}
return $this;
}
}
Using the "Decorator Pattern" I can achieve the same customization level on my Models.
I'm having problem to handle specific behavior. In this case I plan to create custom Controllers extending
my base controller class, but I'unable to call those controllers since the routing is defined on the module
config (and I was unable to change it in runtime).
My questions are:
1) Is this approach correct, or there is a better way to do it?
2) If the approach is correct, how can I define a custom router to be used when the ServiceManager reads my routing config?
Just found a solution. Will register it here hoping someone will benefit from it.
All I had to do was to create a specific router class with a match method which returns the correct routing target for each customer controller, and add it to my module.config.php as the type for each action.
namespace TARGETNAMESPACE;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\Mvc\Router\Http\RouteMatch;
use Zend\Mvc\Router\Http\Literal;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
class MyRouterLiteral extends Literal {
public function match(Request $request, $pathOffset = null) {
if (! method_exists($request, 'getUri')) {
return null;
}
$uri = $request->getUri();
$path = $uri->getPath();
if ($pathOffset !== null) {
if ($pathOffset >= 0 && strlen($path) >= $pathOffset && ! empty($this->route)) {
if (strpos($path, $this->route, $pathOffset) === $pathOffset) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
}
return null;
}
if ($path === $this->route) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
return null;
}
private function getDefaults() {
$aux = explode('\\', $this->defaults['controller']);
$last = array_pop($aux);
array_push($aux, '[CUSTOM_INSTANCE_NAME]');
array_push($aux, '[CUSTOM_INSTANCE_NAME]'.$last);
$result = $this->defaults;
$result['controller'] = implode('\\', $aux);
return $result;
}
}
To address all cases I had to create a second custom router (for segment routes) which follows the same rules and can be easily derived from the above code.

Cannot attach Events in module init()-method

I read about some of the best practices for ZF2. There, it was explained to attach the events from MVC in the init()-Method of the module's Module class:
class Module {
public function getAutoloaderConfig() {
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
);
}
public function init(ModuleManager $moduleManager) {
echo 'init<br>';
$em = $moduleManager->getEventManager();
$em->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
$em->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'));
}
public function onDispatch(MvcEvent $e){
echo 'onDispatch<br>';
}
...
It results in getting no error, nice. But the event is not caught...
Any ideas? I tried the SharedManager too, but it only worked for the EVENT_DISPATCH ...
Unless for specific cases, it's better to register your events in onBootstrap.
init is for "early events".
I found a link that is quite clear : http://samsonasik.wordpress.com/2013/03/30/zend-framework-2-getting-closer-with-eventmanager/
You can find the order of defaults MVC events in Zend\ModuleManager\Listener\DefaultListenerAggregate::attch :
public function attach(EventManagerInterface $events)
{
$options = $this->getOptions();
$configListener = $this->getConfigListener();
$locatorRegistrationListener = new LocatorRegistrationListener($options);
// High priority, we assume module autoloading (for FooNamespace\Module classes) should be available before anything else
$this->listeners[] = $events->attach(new ModuleLoaderListener($options));
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE_RESOLVE, new ModuleResolverListener);
// High priority, because most other loadModule listeners will assume the module's classes are available via autoloading
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new AutoloaderListener($options), 9000);
if ($options->getCheckDependencies()) {
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new ModuleDependencyCheckerListener, 8000);
}
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new InitTrigger($options));
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new OnBootstrapListener($options));
$this->listeners[] = $events->attach($locatorRegistrationListener);
$this->listeners[] = $events->attach($configListener);
return $this;
}
You'll be better using the shared manager. The example bellow is for disabling layout when we got a xmlHttpRequest and the priority -95 is a key point to make things work.
public function onBootstrap(MvcEvent $e) {
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
// Hybrid view for ajax calls (disable layout for xmlHttpRequests)
$eventManager->getSharedManager()->attach('Zend\Mvc\Controller\AbstractController', MvcEvent::EVENT_DISPATCH, function(MvcEvent $event){
/**
* #var Request $request
*/
$request = $event->getRequest();
$viewModel = $event->getResult();
if($request->isXmlHttpRequest()) {
$viewModel->setTerminal(true);
}
return $viewModel;
}, -95);
}
See http://akrabat.com/zend-framework-2/module-specific-bootstrapping-in-zf2/
Only found this solution: attach listeners in onBootstrap()-Method in the Module.php class:
...
public function onBootstrap(MvcEvent $e){
echo 'onBootstrap<br>';
$em = $e->getApplication()->getEventManager();
$em->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
$em->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'));
}
...

zf2 get route and module name etc. in plugin

How can I get the route-, module-, controller- and actionname in a plugin in ZF2?
The plugin is being used by a viewHelper.
I tried different methods but none seem to work.
These are two of the methods i tried
Method 1:
public function __construct(MvcEvent $e)
{
$this->routename = $e->getRouteMatch()->getMatchedRouteName();
}
Method 2:
public function __construct()
{
$e = new MvcEvent();
$this->routename = $e->getRouteMatch()->getMatchedRouteName();
}
You can see in source code how it works this helper.
Current route helper
$routematch = $routeMatch = $serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch();
if($routeMatch) {
$controller = $routeMatch->getParam('controller');
$action = $routeMatch->getParam('action');
$module = $routeMatch->getParam('__NAMESPACE__');
$route = $routeMatch->getMatchedRouteName();
}

Categories