Overriding SonataUserBundle's ProfileFormType - php

I'm trying to override SonataUserBundle's ProfileFormType.
I have added some extra fields to that form and all the fields render on the page. So that works. But now I'm wondering why the user's data doesn't load, as the firstname, lastname, ... is already known but just not shown in the textboxes of the form.
The overridden ProfileController class's editProfileAction:
/**
* #return Response
*
* #throws AccessDeniedException
*/
public function editProfileAction()
{
$user = $this->container->get('security.context')->getToken()->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
$form = $this->container->get('sonata.user.profile.form');
$formHandler = $this->container->get('sonata.user.profile.form.handler');
$process = $formHandler->process($user);
if ($process) {
$this->setFlash('fos_user_success', 'profile.flash.updated');
return new RedirectResponse($this->generateUrl('sonata_user_profile_show'));
}
// This doesn't show the firstname
die($form->getData()->getFirstname());
return $this->render('SonataUserBundle:Profile:edit_profile.html.twig', array(
'form' => $form->createView(),
'theme' => $this->container->getParameter('fos_user.template.theme')
));
}
The overridden ProfileFormHandler class's process function:
public function process(UserInterface $user)
{
$this->form->setData($user);
// This DOES show the firstname
die($this->form->getData()->getFirstname());
if ('POST' == $this->request->getMethod()) {
$this->form->bindRequest($this->request);
if ($this->form->isValid()) {
$user->upload();
$this->onSuccess($user);
return true;
}
// Reloads the user to reset its username. This is needed when the
// username or password have been changed to avoid issues with the
// security layer.
$this->userManager->reloadUser($user);
}
return false;
}
Services.yml:
services:
application_sonata_user.registration.form.type:
class: Application\Sonata\UserBundle\Form\Type\RegistrationFormType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: application_sonata_user_registration }
application_sonata_user.profile.form.type:
class: Application\Sonata\UserBundle\Form\Type\ProfileType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: application_sonata_user_profile }
application_sonata_user.search.form.type:
class: Application\Sonata\UserBundle\Form\Type\SearchFormType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: application_sonata_user_search }
application_sonata_user.form.handler.profile:
class: Application\Sonata\UserBundle\Form\Handler\ProfileFormHandler
arguments: ["#fos_user.profile.form", "#request", "#fos_user.user_manager", "#ewz_search.lucene"]
scope: request
public: false

In the services.yml file, I had to put:
arguments: ["#sonata.user.profile.form", "#request", "#fos_user.user_manager", "#ewz_search.lucene"]
instead of
arguments: ["#fos_user.profile.form", "#request", "#fos_user.user_manager", "#ewz_search.lucene"]

Related

Symfony2: Time-based Controller Action

Is it possible, to make a time-based link / controller action in symfony2 in the annotation? With a start and a stopdate!?
For example:
/**
*#Route("/mylink", start="14.10.2015" stop="20.12.2015", name="mylink", schemes= { "http" })
public function myLinkAction()
{
.....
}
You cannot extend #Route that way but with defaults and I think the best solution without boilerplate code is a controller filter:
services.yml
services:
time_range_route_filter:
class: AppBundle\Services\TimeRangeRouteFilter
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onFilterController }
DefaultController.php
class DefaultController
{
/**
* #Route("/", name="homepage", defaults={"start"="2015-01-01", "end"="2016-01-01"})
*/
public function indexAction()
{
}
}
TimeRangeRouteFilter.php
class TimeRangeRouteFilter
{
public function onFilterController(FilterControllerEvent $event) {
$request = $event->getRequest();
$attributes = $request->attributes;
$routeParams = $attributes->get('_route_params');
$end = $routeParams['end'];
$start = $routeParams['start'];
if(!/* in range */) {
throw new NotFoundHttpException();
}
}
}

Symfony2: how to set form action in services.yml

I use a service to get a basic search form on my website. I need to set the action form to a search result page. I can set the form action to be a specific url (/search) but can't use a "generateUrl"-like method in a yaml.
I want to be able to test my form in dev environment /app_dev.php as well as in prod environment. Any suggestion or idea?
services.yml
parameters:
form.search.default.value: "search for things"
services:
app_bundle.form.type.search:
class: AppBundle\Form\SearchType
arguments: [AppBundle\Entity\Search]
tags:
- { name: form.type, alias: tab_search }
app_bundle.form.search:
factory_method: create
factory_service: form.factory
class: Symfony\Component\Form\Form
arguments:
- tab_search
- #app_bundle.form.entity.search
- { action: /search } # I'd like something similar to $this->generateUrl("search")
app_bundle.form.entity.search:
class: AppBundle\Entity\Search
arguments: [%form.search.default.value%]
DefaultController.php
/**
* #Route("/", name="homepage")
* #Template()
*/
public function indexAction(Request $request)
{
$form = $this->get('app_bundle.form.search');
// [...]
return array('search' => $form->createView());
}
Untested, but I've done something like this before with this code:
protected $action;
public function setAction($action)
{
$this->action = $action;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// rest of form configuration
$builder->setAction($this->action);
}
Then in your services.yml:
services:
app_bundle.form.type.search:
class: AppBundle\Form\SearchType
arguments: [AppBundle\Entity\Search]
calls:
- [setAction, ["%app_bundle.form.type.search.action%"]]
tags:
- { name: form.type, alias: tab_search }
And then set the app_bundle.form.type.search.action value in parameters somewhere.
If you want to allow the method to be overridden but set a default, set the action in setDefaultOptions and then override from the controller or a listener.

How can I write username to log in Symfony2

I want to write username like extra field to production log.
How can I do it?
I have this config in config.yml:
services:
monolog.formatter.logprocessor:
class: Monolog\Formatter\LineFormatter
arguments:
- "[%%datetime%%] [%%username%%] %%channel%%.%%level_name%%: %%message%%\n"
monolog.processor.logprocessor:
class: Acme\CoreBundle\Monolog\LogProcessor
tags:
- { name: monolog.processor, method: processRecord }
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: warning
formatter: monolog.formatter.logprocessor
When I tried inject security.context into LogProcessor, I get error:
[Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException]
Circular reference detected for service "monolog.processor.logprocessor", path: "router ->
monolog.logger.router -> monolog.processor.logprocessor -> security.context -> security.a
uthorization_checker -> security.authentication.manager -> security.user.provider.concrete
.chain_provider -> security.user.provider.concrete.main -> doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> monolog.logger.doctrine".
Here is the configuration
# Service Configuration
services:
monolog.formatter.html:
class: Monolog\Formatter\HtmlFormatter
monolog.processor.web_processor:
class: Monolog\Processor\WebProcessor
tags:
- { name: monolog.processor, method: __invoke }
monolog.processor.user:
class: Company\ToolBoxBundle\Services\Monolog\ExtraProcessor
arguments: ["#security.token_storage"]
tags:
- { name: monolog.processor }
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
And the service code
namespace Company\ToolBoxBundle\Services\Monolog;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class ExtraProcessor
{
private $tokenStorage = null;
private $user = null;
private $postParams = null;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function __invoke(array $record)
{
if (null !== $this->user) {
$record['extra']['user'] = $this->user->getUserDetails();
}
if (null !== $this->postParams) {
$record['extra']['postParams'] = $this->postParams;
}
return $record;
}
public function onKernelRequest(GetResponseEvent $event)
{
$postParams = $event->getRequest()->request->all();
if(false === empty($postParams)){
$this->postParams = serialize($postParams);
}
if (null === $token = $this->tokenStorage->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
$this->user = $user;
}
}
if you are using a symfony version >= 2.4, you can have a look at this bundle
that should help you doing what you want :)

Symfony2: Check user authentication based on path

in Symfony2, is it possible to check if user is authenticated to access the URl he requested.
What I want to do is, i dont want to allow a logged in user to go back to registration or login or recover password pages.
here is my security.yml:
access_control:
- { path: ^/signup/, roles: IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICATED_FULLY}
- { path: ^/register/, roles: IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICATED_FULLY}
- { path: ^/recover/, roles: IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICATED_FULLY}
but this is showing, access denied page to current user. So i think it would be nice if I can redirect the user to home page on such request, by checking if he is not allowed. Can I check by providing path that user is authenticated or not in listener?
public function onKernelResponse(FilterResponseEvent $event)
{
$request = $event->getRequest();
$path = $request->getPathInfo();
if($this->container->get('security.context')->getToken() != null) {
// To check if user is authenticated or anonymous
if( ($this->container->get('security.context')->getToken() instanceof UsernamePasswordToken) &&
($this->container->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY') == true) ) {
// HOW TO CHECK PATH ?
// set response to redirect to home page
}
}
}
The security.access_map service
The configuration of security.access_control is processed by ...
SecurityBundle\DependencyInjection\SecurityExtension
... which creates RequestMatchers for the routes (path,hosts,ip,...) and then invokes the service's add() method with the matcher, the allowed roles and the channel (i.e. https ).
The service is usually used by i.e. the AccessListener.
You can use the security.access_map service to access the
security.access_control parameters in your application.
The class used for the security.access_map service is defined by the parameter security.access_map.class and defaults to
Symfony\Component\Security\Http\AccessMap ( implements
AccessMapInterface )
You can use the parameter security.access_map.class to override the service with a custom class (must implement AccessMapInterface):
# i.e. app/config/config.yml
parameters:
security.access_map.class: My\Custom\AccessMap
How to access the service
The security.access_map service is a private service as you can see by it's definition here.
This means you can't request it from the container directly like this:
$this->container->get('security.access_map')
You will have to inject it into another service (i.e. a listener service) explicitly to be able to access it.
A listener example
services:
my_listener:
class: My\Bundle\MyListenerBundle\EventListener\ForbiddenRouteListener
arguments: [ #security.access_map ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Then you can call the getPatterns() method to obtain the RequestMatchers, allowed roles and required channel from there.
namespace My\Bundle\MyListenerBundle\EventListener;
use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class ForbiddenRouteListener
{
protected $accessMap;
public function __construct(AccessMapInterface $access_map)
{
$this->accessMap = $access_map;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$patterns = $this->accessMap->getPatterns($request);
// ...
Maybe this will help someone. I just catch route name and check if they are in array. If yes just redirect. This is event listener.
services.yml
project.loggedin_listener:
class: Project\FrontBundle\EventListener\LoggedInListener
arguments: [ "#router", "#service_container" ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
listener:
namespace Project\FrontBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
class LoggedInListener {
private $router;
private $container;
public function __construct($router, $container)
{
$this->router = $router;
$this->container = $container;
}
public function onKernelRequest(GetResponseEvent $event)
{
$container = $this->container;
$accountRouteName = "_homepage";
if( $container->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY') ){
$routeName = $container->get('request')->get('_route');
$routes = array("admin_login","fos_user_security_login","fos_user_registration_register","fos_user_resetting_request");
if(in_array($routeName, $routes)){
$url = $this->router->generate($accountRouteName);
$event->setResponse(new RedirectResponse($url));
}
}
}
}
You can do not only inside security.yml options, but also via controller, like this:
if($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return $this->redirect($this->generateUrl('homepage'));
}

Google maps infowindow not showing on click in Symfony2 with VichGeographicalBundle

I have successfully set up VichGeographicalBundle to display a bunch of places in a Google Map. Everything works ok, except the infowindows that do not show on click.
$this->setShowInfoWindowsForMarkers(true); is set but does not seem to work.
Any ideas ?
EDIT:
class allShopsMap extends Map
{
/**
* Constructs a new instance of LocationMap.
*/
public function __construct(EntityManager $em)
{
parent::__construct();
// configure your map in the constructor
// by setting the options
$this->setShowZoomControl(true);
$this->setZoom(13);
$this->setAutoZoom(false);
$this->setContainerId('map_canvas');
$this->setWidth(980);
$this->setHeight(360);
$this->setShowInfoWindowsForMarkers(true);
$this->setCenter(23.232323,23.232323);
$this->setShowMapTypeControl(true);
$query = $em->createQuery("SELECT st
FROM acme\ShopBundle\Entity\Shop sh
WHERE sh.published = 1 ");
$shops = $query->getResult();
foreach ($shops as $shop) {
$this->addMarker(new MapMarker($shop->getLatitude(), $shop->getLongitude(),$icon='images/map_marker.png'));
}
}
}
Called from twig template:
{{ vichgeo_map('allShops') }}
config.yml
vich_geographical:
db_driver: orm
query_service: vich_geographical.query_service.default
map_renderer: vich_geographical.map_renderer.google
templating:
engine: twig
info_window: msgrShopBundle:Map:infoWindow.html.twig
services:
msgr.map.allShops:
class: msgr\ShopBundle\Map\allShopsMap
tags:
- { name: vichgeo.map, alias: allShops }
arguments:
entityManager: "#doctrine.orm.entity_manager"
HTML Code generated by {{ vichgeo_map('allShops') }} : http://pastebin.com/jqvzG67N
Try this:
class allShopsMap extends Map
{
/**
* Constructs a new instance of LocationMap.
*/
public function __construct(EntityManager $em, $infoWindowBuilder)
{
parent::__construct();
// configure your map in the constructor
// by setting the options
$this->setShowZoomControl(true);
$this->setZoom(13);
$this->setAutoZoom(false);
$this->setContainerId('map_canvas');
$this->setWidth(980);
$this->setHeight(360);
$this->setShowInfoWindowsForMarkers(true);
$this->setCenter(23.232323,23.232323);
$this->setShowMapTypeControl(true);
$query = $em->createQuery("SELECT st
FROM acme\ShopBundle\Entity\Shop sh
WHERE sh.published = 1 ");
$shops = $query->getResult();
foreach ($shops as $shop) {
$marker = new MapMarker($shop->getLatitude(), $shop->getLongitude(),$icon='images/map_marker.png');
$marker->setInfoWindow($infoWindowBuilder->build($marker));
$this->addMarker($marker);
}
}
}
infoWindowBuilder is vich_geographical.info_window_builder service that is available in the container.
And modify your config:
services:
msgr.map.allShops:
class: msgr\ShopBundle\Map\allShopsMap
tags:
- { name: vichgeo.map, alias: allShops }
arguments:
entityManager: "#doctrine.orm.entity_manager"
infoWindowBuilder: "#vich_geographical.info_window_builder"

Categories