concrete5 way for $_FILES, $_POST, $_SERVER - concrete5

What's the concrete5 equivalent to
if (empty($_FILES) &&
empty($_POST) &&
isset($_SERVER['REQUEST_METHOD']) &&
strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
in way of requests?
[UPDATE] Looks like this is working:
if (empty($this->request->files->all()) &&
empty($this->request->request->all()) &&
null !== $this->request->server->get('REQUEST_METHOD') &&
strtolower($this->request->server->get('REQUEST_METHOD')) == 'post') {

You should get the current Request instance.
In a controller method, it's as simple as writing
$request = $this->request;
If you are not using a controller but a custom class, you can mark the Request as a dependency of your class:
use Concrete\Core\Http\Request;
class YourClass
{
/**
* #var \Concrete\Core\Http\Request
*/
private $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function TheMethodWhereYouNeedRequest()
{
$request = $this->request;
// ...
}
}
You can also get the Request instance by writing
$request = \Core::make(\Concrete\Core\Http\Request::class);
Once you have the Request instance, you can write:
if (
// Same as empty($_FILES)
$this->request->files->count() === 0
&&
// Same as empty($_POST)
$this->request->request->count() === 0
&&
// Same as isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) == 'post'
$this->request->getMethod() === 'POST'
) {
...
}
Please remark that the concrete5 Request extends the Symfony Request (version 3.4), so you may want to take a look at https://symfony.com/doc/3.4/components/http_foundation.html#accessing-request-data

Related

Get route path '/blog/{slug}' of current request in Symfony

For some logging/monitoring i would like to get the current route path including placeholders.
If my route is /blog/{slug} and the request is to http://localhost/blog/foobar what i need is "/blog/{slug}"
In request listeners this value seem to not be inside the request object. I only find the resolved path which i am not interested in.
In Compiler passes I have the issue that any Router related service I try to get from the ContainerBuilder returns an exception. If i had the
What is a clean way to obtain this?
For routes with one param:
$routeWithoutParam = substr($request->getRequestUri(),0,strrpos($request->getRequestUri(),'/'));
$routeParams = $request->attributes->get('_route_params');
$routeDefinition = $routeWithoutParam.'/{' . array_key_first($paramsArray) . '}';
echo $routeDefinition;
For routes with multiple params:
$routeParams = $request->attributes->get('_route_params');
$routeWithoutParam = '';
for ($i = 0; $i < count($routeParams); $i++) {
$routeWithoutParam = $i === 0
? substr($request->getRequestUri(), 0, strrpos($request->getRequestUri(), '/'))
: substr($routeWithoutParam, 0, strrpos($routeWithoutParam, '/'))
;
}
$routeDefinition = $routeWithoutParam.'/';
foreach (array_keys($routeParams) as $key => $param) {
$routeDefinition.= '{' . $param . '}' . ($key < count($routeParams)-1 ? '/' : '');
}
echo $routeDefinition;
You can obtain the Router and RouteCollection, that holds all the routes in your app:
// src/Listener/RouteLoggerListener.php -- namespace and use ommited
class RouteLoggerListener implements EventSubscriberInterface
{
/**
* #var LoggerInterface
*/
private $logger;
/**
* #var RouterInterface
*/
private $router;
public function __construct(LoggerInterface $logger, RouterInterface $router)
{
$this->logger = $logger;
$this->router = $router;
}
public static function getSubscribedEvents()
{
// Trigger after the RouterListener
return [KernelEvents::REQUEST => ['onKernelRequest', 50]];
}
public function onKernelRequest(RequestEvent $event)
{
// This is for sf53 and up, for previous versions, use isMasterRequest()
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
if (null == $request) {
return;
}
$matchedRoute = $request->attributes->get('_route');
if (null == $matchedRoute) {
// Bail gracefully
return;
}
$routeCollection = $this->router->getRouteCollection();
$route = $routeCollection->get($matchedRoute);
$this->logger->debug('Request route: '.$route->getPath());
}
}
Note: Using RouteCollection at runtime is highly discouraged because it triggers a recompile. As indicated in this GitHub comment, the proper way to do it would be to use ConfigCache while cache warming.

How to toggle Symfony's php-translation/symfony-bundle EditInPlace

I followed this documentation for Edit In Place, and setup the Activator, and it works!
However, I will be using this on the production site and allowing access via a ROLE_TRANSLATOR Authorization. This is also working, but I don't want the web interface always "on"
How would I go about enabling it via some sort of link or toggle?
My thoughts, it would be simple to just add a URL parameter, like ?trans=yes and then in the activator;
return ($this->authorizationChecker->isGranted(['ROLE_TRANSLATOR']) && $_GET['trans'] == 'yes');
Obviously, $_GET would not work, I didn't even try.
How do I generate a link to simply reload THIS page with the extra URL parameter
How do I check for that parameter within the "Activator"
or, is there a better way?
The "proper" way to do this, as I have discovered more about "services" is to do the logic diectly in the RoleActivator.php file.
referencing documentation for How to Inject Variables into all Templates via Referencing Services I came up with the following solution;
src/Security/RoleActivator.php
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
use Translation\Bundle\EditInPlace\ActivatorInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\HttpFoundation\RequestStack;
class RoleActivator implements ActivatorInterface
{
/**
* #var AuthorizationCheckerInterface
*/
private $authorizationChecker;
/**
* #var TranslatorInterface
*/
private $translate;
/**
* #var RequestStack
*/
private $request;
private $params;
private $path;
private $flag = null;
public function __construct(AuthorizationCheckerInterface $authorizationChecker, TranslatorInterface $translate, RequestStack $request)
{
$this->authorizationChecker = $authorizationChecker;
$this->translate = $translate;
$this->request = $request;
}
/**
* {#inheritdoc}
*/
public function checkRequest(Request $request = null)
{
if ($this->flag === null) { $this->setFlag($request); }
try {
return ($this->authorizationChecker->isGranted(['ROLE_TRANSLATOR']) && $this->flag);
} catch (AuthenticationCredentialsNotFoundException $e) {
return false;
}
}
public function getText()
{
if ($this->flag === null) { $this->setFlag(); }
return ($this->flag) ? 'linkText.translate.finished' : 'linkText.translate.start'; // Translation key's returned
}
public function getHref()
{
if ($this->flag === null) { $this->setFlag(); }
$params = $this->params;
if ($this->flag) {
unset($params['trans']);
} else {
$params['trans'] = 'do';
}
$queryString = '';
if (!empty($params)) {
$queryString = '?';
foreach ($params as $key => $value) {
$queryString.= $key.'='.$value.'&';
}
$queryString = rtrim($queryString, '&');
}
return $this->path.$queryString;
}
private function setFlag(Request $request = null)
{
if ($request === null) {
$request = $this->request->getCurrentRequest();
}
$this->flag = $request->query->has('trans');
$this->params = $request->query->all();
$this->path = $request->getPathInfo();
}
}
config\packages\twig.yaml
twig:
# ...
globals:
EditInPlace: '#EditInPlace_RoleActivator'
config\services.yaml
services:
# ...
EditInPlace_RoleActivator:
class: App\Security\RoleActivator
arguments: ["#security.authorization_checker"]
So What I added over and above the php-translation example is the getText and getHref methods and corresponding private variables being set in the checkRequest and read there after.
Now in my twig template (in the header) I just use
{% if is_granted('ROLE_TRANSLATOR') %}
{{ EditInPlace.Text }}
{% endif %}
Add the new keys to the translation files and your done. the trans=do query parameter is toggled on and off with each click of the link. You could even add toggling styles with a class name, just copy the getText method to something like getClass and return string a or b with the Ternary.

Redirect to page prior to login form Symfony 3.4 is resolving to liip_imagine route in a custom listener

I'm having problems getting a redirect after login to work in Symfony.
It works for some pages but for others, the last_route session variable is being set to a users profile picture that uses the liip_imagine_filter:
"last_route" => [
"name" => "liip_imagine_filter",
"params" => [
"filter" => "profile_picture"
"path" => "frederick-jacobson/5ba60fc93056b.png"
]
]
LoginFormAuthenticator:
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
/*...*/
protected function getDefaultSuccessRedirectURL()
{
/** #var Session $session */
$session = $this->container->get('session');
$priorPage = $session->get('last_route');
return $this->router->generate($priorPage['name'], $priorPage['params']);
// return $this->router->generate('poll_index');
}
}
This means that it tries to redirect to an image URL.
services.yml:
poll.last_route_event_listener:
class: PollBundle\Services\LastRouteListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 30 }
LastRouteListener:
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;
class LastRouteListener
{
public function onKernelRequest(GetResponseEvent $event)
{
// Do not save subrequests
if ($event->getRequestType() !== HttpKernel::MASTER_REQUEST) {
return;
}
$request = $event->getRequest();
$session = $request->getSession();
$routeName = $request->get('_route');
$routeParams = $request->get('_route_params');
if ($routeName[0] == '_') {
return;
}
$routeData = ['name' => $routeName, 'params' => $routeParams];
// Do not save same matched route twice
$thisRoute = $session->get('this_route', []);
if ($thisRoute == $routeData) {
return;
}
$session->set('last_route', $thisRoute);
$session->set('this_route', $routeData);
}
}
Can someone please help me work out what I'm doing wrong and/or tell me the correct way to handle redirecting to the page prior to login?
As pointed out in the comments below my question. I was using a custom listener that was picking up the liip_imagine_filter route and setting it to the last_route session variable.
I could just add a check in to the listener, like this:
if ($routeName[0] == '_' || $routeName == 'liip_imagine_filter') {
return;
}
But a better way to handle it is to use the built in Symfony\Component\Security\Http\Util\TargetPathTrait
It is usually set automatically when a user hits a restricted page, but it can be set manually with $this->saveTargetPath($request->getSession(), $providerKey, $request->getUri());
Then you can use $targetPath to find the route to redirect to in LoginFormAuthenticator:
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
/*...*/
use TargetPathTrait;
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$targetPath = $this->getTargetPath($request->getSession(), $providerKey);
if (!$targetPath || $request->getBaseUrl() && !strpos($targetPath, $request->getBaseUrl())) {
$targetPath = $this->container->get('router')
->generate('poll_index');
}
return new RedirectResponse($targetPath);
}
}

Overriding FOSUserBundle loginAction() to redirect to custom routes

I am working on a Symfony 3.3.8 project with FOSUserBundle. I have created two registration and two profile pages for student and provider respectively. Now I am trying to override the FOSUserBundle's loginAction() method, where I am checking the logged-in user for it's role. If the role is ROLE_STUDENT I am trying to redirect the user to student profile page after successful login and if the role is ROLE_PROVIDER I want the user to be redirected to the provider profile page after successful login. Here is my overridden SecurityController with loginAction():
<?php
namespace Epita\HousingBundle\Controller;
use FOS\UserBundle\Controller\SecurityController as BaseController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class SecurityController extends BaseController {
/**
*
*/
public function loginAction(Request $request) {
/** #var $session \Symfony\Component\HttpFoundation\Session\Session */
$session = $request->getSession();
$key = '_security.main.target_path';
$securityContext = $this->container->get('security.authorization_checker');
if ($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$user = $this->getUser();
}
if ($this->container->get('session')->has($key))
{
if ($this->get('security.authorization_checker')->isGranted('ROLE_STUDENT')) {
return $this->redirectToRoute('student_profile');
} else if ($this->get('security.authorization_checker')->isGranted('ROLE_PROVIDER')) {
return $this->redirectToRoute('provider_profile');
}
}
else{
return $this->redirectToRoute('student_profile');
}
if (class_exists('\Symfony\Component\Security\Core\Security')) {
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
} else {
// BC for SF < 2.6
$authErrorKey = SecurityContextInterface::AUTHENTICATION_ERROR;
$lastUsernameKey = SecurityContextInterface::LAST_USERNAME;
}
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
// last username entered by the user
$lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);
if ($this->has('security.csrf.token_manager')) {
$csrfToken = $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue();
} else {
// BC for SF < 2.4
$csrfToken = $this->has('form.csrf_provider')
? $this->get('form.csrf_provider')->generateCsrfToken('authenticate')
: null;
}
return $this->renderLogin(array(
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
));
}
/**
*
* #param array $data
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function renderLogin(array $data)
{
$template = sprintf('EpitaHousingBundle:Security:login.html.twig');
return $this->container->get('templating')->renderResponse($template, $data);
}
public function checkAction()
{
throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
}
public function logoutAction()
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
}
}
But this solution does not work for me. My guess is I have to play around with the session. Since I am new to this can anyone help me?
Thanks in advance. Let me know if you need more details.
Here is some code that might help you.
1 - This is the Event/LoginSuccessHandler, your logic is in this class
<?php
namespace AppBundle\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
class LoginSuccessHandler implements AuthenticationSuccessHandlerInterface
{
protected $router;
protected $security;
public function __construct(Router $router, AuthorizationChecker $security)
{
$this->router = $router;
$this->security = $security;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$url = 'homepage';
if ($this->security->isGranted('ROLE_STUDENT')) {
$url = 'student_route';
}
if ($this->security->isGranted('ROLE_PROVIDER')) {
$url = 'provider_route';
}
$response = new RedirectResponse($this->router->generate($url));
return $response;
}
}
2 - We set up the config that listens the login event in your services.yml
login_success_handler:
class: AppBundle\Event\LoginSuccessHandler
arguments:
- "#router"
- "#security.authorization_checker"
tags:
- { name: 'monolog.logger', channel: 'security' }
3 - That should be it. Try and tell me if something is wrong

an easier way of the redirect process according to state of user data in Symfony2

I want to add a process to redirect by the user data.
For example it is a point system.
It is forcibly redirect to static page if a user has not points.
I have on my mind that add process to construct function of common BaseController.
class BaseController extends Controller
{
public function __construct()
{
if ($this->container->get('security.context')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$user = $this->container->get('security.context')->getToken()->getUser();
$manager = $this->container->get('doctrine')->getManager();
if (! $manager->getRepository('MyUserBundle:User')->hasPoints($user))
return new RedirectResponse($this->container->get('router')->generate('static_page_route'));
}
}
But I can't use a container.
Error: Call to a member function get() on a non-object
point at issue:
1. How do I use user object at constructor.
2. Is there an easier way of the redirect process according to state of user data(like a points)?
(The such as changing the role by state of user points though I still not know ACL details.)
add example:
public function onKernelRequest(GetResponseEvent $event){
if (!$this->securityContext->getToken()) {
return;
}
$isUser = $this->securityContext->isGranted('ROLE_USER');
if (!$isUser) {
return;
}
$user = $this->securityContext->getToken()->getUser();
$request = $event->getRequest();
$_route = $request->attributes->get('_route');
if (! $user->hasPoints() && $_route != 'static_page_route') {
// exit; <= page rendering is done even if process exit.
// Redirect process be called after rendering?
$redirectUrl = $this->router->generate('static_page_route');
$event->setResponse(new RedirectResponse($redirectUrl)); // <= `The page isn't redirecting properly` error now.
}
}
One solution:
<?php
namespace My\ExampleBundle\EventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;
class BeforeController {
protected $securityContext;
protected $router;
public function setRouter(Router $router){
$this->router = $router;
}
public function setSecurityContext(SecurityContext $securityContext){
$this->securityContext = $securityContext;
}
public function onKernelRequest(GetResponseEvent $event){
if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) { return; }
if (! $this->securityContext->getToken()) { return; }
if (! $this->securityContext->isGranted('ROLE_USER')) { return; }
$user = $this->securityContext->getToken()->getUser();
$request = $event->getRequest();
$_route = $request->attributes->get('_route');
if (! $user->hasPoints() && $_route != 'static_page_route') {
$redirectUrl = $this->router->generate('static_page_route');
$event->setResponse(new RedirectResponse($redirectUrl));
}
}
}

Resources