zend rest route messing up routing for default controller - zend-framework

I have implemented a RESTful service by extending the Zend_Rest_Controller. The service works great. I have only one controller that actually has RESTful behavior. So I added the rest route for just that controller in the bootstrap.
protected function _initRestRoute()
{
$this->bootstrap('frontController');
$frontController = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($frontController, array() , array('default' => array('MyserviceController')));
$frontController->getRouter()->addRoute('rest', $restRoute);
}
problem starts when I run my portal zend app. The links in the layout for the index controller miss out on the action parameter when I construct the URL. For example the link on the index layout for a action homepage in the network controller is as follows.
$this->url(array('controller'=>'network','action'=>'homepage','module'=>'default'));
this should return "localhost/projectname/public/network/homepage" instead it returns "localhost/projectname/public/network". This behavior is only when the layout is loaded by the default controller i.e. IndexController. The routing problem vanishes if I remove the zend rest route from the bootstrap. As long as I am mentioning what controller the rest request has to route to it should not be a problem right. But this is affecting the default controller routing.

This seems to be a common issue and usually resolved with something like:
$this->url(array('controller'=>'network','action'=>'homepage','module'=>'default'), 'default');
or:
$this->url(array('controller'=>'network','action'=>'homepage','module'=>'default'), NULL, TRUE);
might work.
if your 'module'=>'default' is the default controllers directory at/application/controllers you can omit the module option from the route.
Here is the whole url method note the comments for the reset option.
/**
* Generates an url given the name of a route.
*
* #access public
*
* #param array $urlOptions Options passed to the assemble method of the Route object.
* #param mixed $name The name of a Route to use. If null it will use the current Route
* #param bool $reset Whether or not to reset the route defaults with those provided
* #return string Url for the link href attribute.
*/
public function url(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
{
$router = Zend_Controller_Front::getInstance()->getRouter();
return $router->assemble($urlOptions, $name, $reset, $encode);
}

Related

Laravel Backpack - Impersonate Operation

Im trying to create an impersonate operation within my user controller, I have been following this guide..
impersonate for backpack
The setupImpersonateDefaults function gets called ok but i get a 404 error, after some testing i figured out the setupImpersonateRoutes is not getting triggered
Any ideas on why?
<?php
namespace App\Http\Controllers\Admin\Operations;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
use Illuminate\Support\Facades\Route;
use Session;
use Alert;
trait ImpersonateOperation
{
/**
* Define which routes are needed for this operation.
*
* #param string $segment Name of the current entity (singular). Used as first URL segment.
* #param string $routeName Prefix of the route name.
* #param string $controller Name of the current CrudController.
*/
protected function setupImpersonateRoutes($segment, $routeName, $controller)
{
Route::get($segment.'/{id}/impersonate', [
'as' => $routeName.'.impersonate',
'uses' => $controller.'#impersonate',
'operation' => 'impersonate',
]);
}
/**
* Add the default settings, buttons, etc that this operation needs.
*/
protected function setupImpersonateDefaults()
{
CRUD::allowAccess('impersonate');
CRUD::operation('impersonate', function () {
CRUD::loadDefaultOperationSettingsFromConfig();
});
CRUD::operation('list', function () {
// CRUD::addButton('top', 'impersonate', 'view', 'crud::buttons.impersonate');
CRUD::addButton('line', 'impersonate', 'view', 'crud::buttons.impersonate');
});
}
/**
* Show the view for performing the operation.
*
* #return Response
*/
public function impersonate()
{
CRUD::hasAccessOrFail('impersonate');
// prepare the fields you need to show
$this->data['crud'] = $this->crud;
$this->data['title'] = CRUD::getTitle() ?? 'Impersonate '.$this->crud->entity_name;
$entry = $this->crud->getCurrentEntry();
backpack_user()->setImpersonating($entry->id);
Alert::success('Impersonating '.$entry->name.' (id '.$entry->id.').')->flash();
// load the view
return redirect('dashboard');
// load the view
//return view('crud::operations.impersonate', $this->data);
}
}
Have tried following the guides and the routes are not getting added.
for anyone else looking at this, you need to call the route from the \routes\backpack\custom.php file, if its not called from this file it wont trigger the setupXXXRoute function
One of the official Backpack team members has created an add-on for impersonating users. You can use his add-on or get inspiration from it:
https://github.com/maurohmartinez/impersonate-users-backpack-laravel

Symfony 4 setter injection in parent class

Just a quick question. I'm building some API. I was thinking about creating simple parent class that would deal with form requests.
So for example if you would like to easily handle form request you just extend this class and you get access to request object, request data extracted from that object and bunch of methods that do some things for you out of the box. It doesn't matter what and why exactly.
The problem is:
I send request through postman.
I try to use request object in class that extends parent class but instead of request I get null.
How do I set up the whole thing?:
Now in Symfony every controller is by default registered as a service so I override this definition like this:
#generic api form controller
App\Controller\Api\ApiFormController:
calls:
- [setDependencies, ['#request_stack', '#App\Service\Serialization\Serializer']]
So as you can see I am using setter injection.
I extend above class in my other class. Let's call it PostController. So:
<?php
namespace App\Controller\Api;
use Symfony\Component\HttpFoundation\RequestStack;
class ApiFormController
{
/**
* #var Request
*/
public $request;
/**
* #param RequestStack $requestStack
*/
public function setDependencies(
RequestStack $requestStack
) {
$this->request = $requestStack;
}
}
And now PostController:
public function get(int $post = null)
{
dump($this->request); exit;
}
I was expecting to get access like this and I think I understand why I don't have access to this object. I'm looking for some ideas how I could achieve this goal in cleanest possible way. I'm not expecting ready answers but hints.
I was thinking about using events to set it up in the background?
I also think it has something to do with the way I'm hooking up my controller as a service.
The core of it all: Symfony does not pick up service definition for subclasses. So if you define dependencies for a class and extend it in another class, you have to define the dependencies for this second class too.
The easiest way is to use the parent keyword for this, so your example would work in the following way:
App\Controller\Api\ApiFormController:
calls:
- [setDependencies, ['#request_stack', '#App\Service\Serialization\Serializer']]
PostController:
parent: App\Controller\Api\ApiFormController
If you are using autowiring, you can use #required to make Symfony call the setter automatically. https://symfony.com/doc/current/service_container/autowiring.html#autowiring-other-methods-e-g-setters
/**
* #param RequestStack $requestStack
* #required
*/
public function setDependencies(
RequestStack $requestStack
) {
$this->request = $requestStack;
}
This should do the trick.
I see several problems here.
If you want to inject dependencies in such a way you should define controller as service. You can read more here.
Routing should be something like this:
# config/routes.yaml
get_something:
path: /
defaults: { _controller: App\Controller\Api\PostController:get }
Also, you should define PostController as service, not ApiFormController.
You injected RequestStack but type hint for the attribute is Request.
Instead of:
$this->request = $requestStack;
You need to use:
$this->request = $requestStack->getMasterRequest();

TYPO3: Backend Module render action of another Controller

I created a simple backend module for TYPO3 (7.6.15), with help of the ExtensionBuilder. The UserController and MediaController have a createAction, showAction and listAction. The PanelController just has the showAction which is the main view of the module and should look like this:
Now, I want to render the listActions from the other controllers in the template of the PanelController.showAction and I would like to do it in the template of the view (MyExt/Resources/Private/Templates/Panel/Show.html), if possible.
I appreciate all help in advance and wish everyone a nice day!
/**
* Redirects the request to another action and / or controller.
*
* #param string $actionName Name of the action to forward to
* #param string $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
* #param string $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
* #param array $arguments Arguments to pass to the target action
* #param integer $pageUid Target page uid. If NULL, the current page uid is used
* #param integer $delay (optional) The delay in seconds. Default is no delay.
* #param integer $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other"
*/
protected function redirect(
$actionName,
$controllerName = NULL,
$extensionName = NULL,
array $arguments = NULL,
$pageUid = NULL,
$delay = 0,
$statusCode = 303
)
You have to pass controller name and action name in redirect method to call action of another controller.
Please review above method that helps you.
or if you do it in template itself than may be you have to call viewhelper.
Okay, I haven't even realized for years that the question was still open. I simply loaded both external controllers to get all the data I needed. Then I passed the data to the correct view templates within the view itself, which were the views of the original list actions.

Sudzc for Magento web services

I am in very bad situation, I got the objectiveC classes from the sudzc website.
using "http://www.xxx.in/mstore/api/soap/?wsdl"
in "SDZMagentoServiceExample.m" class I get one method
[service call:self action:#selector(callHandler:) sessionId: #"" resourcePath: #"catalog_category.level" args: (id)args];
It always gives me parameter error like
"Invalid website code requested:" if I pass dictionary or array in the args.
Please help me, I am in very bad situation.
thanks in advance.
From
/**
* Catalog category api
*
* #category Mage
* #package Mage_Catalog
* #author Magento Core Team <core#magentocommerce.com>
*/
class Mage_Catalog_Model_Category_Api extends Mage_Catalog_Model_Api_Resource
{
Following code:
/**
* Retrieve level of categories for category/store view/website
*
* #param string|int|null $website
* #param string|int|null $store
* #param int|null $categoryId
* #return array
*/
public function level($website = null, $store = null, $categoryId = null)
{
So nor array, nor dictionary would be accepted. Only raw string or int value.
I will not be able to help you in Objective C code, but I can show you some light with PHP. You can try out this type of call:-
$proxy = new SoapClient('http://www.iphone5case.in/mstore/api/soap/?wsdl');
$sessionId = $proxy->login('apiUser', 'apiKey');
/**
* As defined in the "Manage Stores" section of Admin panel,
* where you need to use the specific Website Code and/or Store Code
*/
$websiteCode = null;
$storeCode = 'german';
// Parent Category ID
$parentCategoryId = 2;
$firstLevel = $proxy->call($sessionId, 'category.level', array($websiteCode, $storeCode, $parentCategoryId));
Now if you print this variable "$firstLevel", you will get your required output, from this Web Service API.
Also whenever you are using Magento SOAP API v1, then each of the arguments will need to be as an array element. In this case, following are the main parameters expected for this API call "category.level":-
Website Code or ID
Store View Code or ID
Parent Category ID
So you need to create an array, and put sequentially each of the above arguments as array elements, like:-
array(
$websiteCode,
$storeCode,
$parentCategoryId
)
Lastly, please make sure that you reference this article always, as you can get the usage of almost all the Web Service API methods here.
Hope it helps.

Zend Framework Authentication and Redirection

What is the best method in Zend Framework to provide restricted areas and redirect users to a login page? What I want to do is set a flag on my controllers for restricted pages:
class AdminController extends Zend_Controller_Action
{
protected $_isRestricted = true;
....
and have a plugin check to see if the controller is restricted and if the user has authenticated, otherwise redirect them to the login page. If I do this directly in the controller's preDispatch I can use $this->_redirect(), but looking at Action Helpers they won't have access to that. It's also a lot of duplicate code to copy/paste the authentication check code in every controller that needs it.
Do I need an Action Controller linked to preDispatch, or a Front Controller plugin? How would I do the redirect and still preserve things like the base URL?
Use Zend_Acl (best combined with Zend_Auth)
See also Practical Zend_ACL + Zend_Auth implementation and best practices
For one project, I've extended Zend_Controller_Action, and in that class's preDispatch put a check for logged-in-ness. I can override it on a per-action basis with an init() that checks the actionname and turns off the requirement (or preDispatch() that calls it's parent for the actual checks).
In a project I was working on, I had trouble with various users experiencing timeouts from their browsers. This meant the Zend_Auth no longer existed in the registry and users lost access to required pages/functions.
In order to stop this from occuring, I setup a Plugin (as you suggest) and have this plugin perform checks in the preDispatch(). An example is below:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
public function run()
{
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new App_Controller_Plugin_Timeout());
parent::run();
}
}
with the timeout class implementing any Zend_Auth or Zend_Acl requirements, using a check via the function below.
class App_Controller_Plugin_Timeout extends Zend_Controller_Plugin_Abstract
{
/**
* Validate that the user session has not timed out.
* #param Zend_Controller_Request_Abstract $request
* #return void
* #todo Validate the user has access to the requested page using Zend_Acl
*/
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$frontController = Zend_Controller_Front::getInstance();
$controllerName = $frontController->getRequest()->getControllerName();
$actionName = $frontController->getRequest()->getActionName();
$authInstance = Zend_Auth::getInstance();
/** If the controller is not the Auth or Error controller, then check for
* a valid authorized user and redirect to the login page if none found */
if (($controllerName !== 'auth') && ($controllerName !== 'index') && ($controllerName !== 'error')) {
if (!$authInstance->hasIdentity()) {
$this->_response->setRedirect('/index/timeout')->sendResponse();
exit;
}
} else if (($controllerName == 'index') || (($controllerName == 'auth') && ($actionName !== 'logout'))) {
/** If running the Auth or Index (default) controller (and not the logout
* action), check if user already signed in and redirect to the welcome page */
if ($authInstance->hasIdentity()) {
$this->_response->setRedirect('/general/welcome')->sendResponse();
exit;
}
}
}
}
....
/**
* Test that the input user belongs to a role based on the user input and
* the values loaded into the Acl registry object setup when the site first
* loads
*
* #param mixed|Zend_Auth $userData
* #param string $userRole
* #return boolean
* #throws Zend_Exception When invalid input is provided
*/
public function isUserMemberOfRole($userData, $userRole)
{
if (empty($userData)) {
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()) {
$userData = $auth->getIdentity();
} else {
return FALSE;
}
}
if (!is_string($userRole)){
throw new Zend_Exception('Invalid input provided to ' . __METHOD__);
}
// Setup the required variables and access the registry for the Acl values
$rolesTable = new App_Model_Internal_UsersToRoles();
$registry = Zend_Registry::getInstance();
$acl = $registry->get('acl');
$roles = $rolesTable->getUserRoles($userData); // returns an array of values
foreach ($roles as $value) {
if ($value['Name'] == $userRole) {
return $acl->isAllowed($value['Name'], null, $userRole);
}
}
}
I had the user access implemented in a database table and then initialized as an "_init" function at Bootstrap->run() as follows:
protected function _initAclObjectForUserRoles()
{
$userTable = new App_Model_Internal_Roles();
$acl = new Zend_Acl();
$userRoles = $userTable->fetchAll();
$roles = $userRoles->toArray();
// Cycle through each Role and set the allow status for each
foreach($roles as $value) {
$department = $value['Name'];
$acl->addRole(new Zend_Acl_Role($department));
$acl->allow($department, null, $department);
}
// Add the new Acl to the registry
$registry = Zend_Registry::getInstance();
$registry->set('acl', $acl);
}
So, using this method you could put access restrictions via the roles loaded via from a database into an Zend_Acl object, or you could load the controller class attribute via the Timeout plugin and check it's value. Although, I've found it's easier to maintain access policies in a database than spread them throughout your code base... :-)