Allow users to edit only their enities in sonata - php

I want to allow some users to add news, but also I don't want them to edit each others newses. So after loggin to admin panel they should be able to see only theirs newses. I'm currently doing this with sonata.
Is there any configuration for this?
My service.yml:
services:
sonata.admin.news:
class: MyBundle\Admin\NewsAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Content", label: "News" }
arguments:
- ~
- MyBundle\Entity\News
- ~
- "#doctrine.orm.entity_manager"
calls:
- [ setTranslationDomain, [MyBundle]]
NewsAdmin contains regular stuff so there is no point in putting it here.

You can inject the security context service into your NewsAdmin
class NewsAdmin extends Admin
{
protected $security;
protected $em;
public function __construct($code, $class, $baseControllerName, $entityManager, SecurityContext $security)
{
parent::__construct($code, $class, $baseControllerName);
$this->em = $entityManager;
$this->security = $security;
}
public function getNewInstance()
{
$news = parent::getNewInstance();
$news->setUser($this->security->getToken()->getUser());
return $formDefinition;
}
public function createQuery($context = 'list')
{
$queryBuilder = $this->getModelManager()->getEntityManager($this->getClass())->createQueryBuilder();
$queryBuilder
->select('news')
->from($this->getClass(), 'news')
->andWhere('news.user = :user')
->setParameter(':user', $this->security->getToken()->getUser());
$proxyQuery = new ProxyQuery($queryBuilder);
return $proxyQuery;
}
And in your Admin definition:
services:
sonata.admin.news:
class: MyBundle\Admin\NewsAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Content", label: "News" }
arguments:
- ~
- MyBundle\Entity\News
- ~
- "#doctrine.orm.entity_manager"
- "#security.context"
calls:
- [ setTranslationDomain, [MyBundle]]

Related

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.

Sonata Bundle Error when configuring child admin

I'm using Symfony and the Sonata Bundle to generate my admin interface. I have 3 classes:
Restaurant
Service
RestaurantService
With classes Restaurant and Service having a OneToMany relationship with RestaurantService.
I try RestaurantService as a child admin in Restaurant but I've got those errors:
ContextErrorException in RestaurantAdmin.php line 143:
Runtime Notice: Declaration of
GSG\AdminBundle\Admin\RestaurantAdmin::configureSideMenu() should be
compatible with
Sonata\AdminBundle\Admin\Admin::configureSideMenu(Knp\Menu\ItemInterface
$menu, $action, Sonata\AdminBundle\Admin\AdminInterface $childAdmin =
NULL)
and
FileLoaderLoadException in classes.php line 13757:
Runtime Notice: Declaration of
GSG\AdminBundle\Admin\RestaurantAdmin::configureSideMenu() should be
compatible with
Sonata\AdminBundle\Admin\Admin::configureSideMenu(Knp\Menu\ItemInterface
$menu, $action, Sonata\AdminBundle\Admin\AdminInterface $childAdmin =
NULL) in /Volumes/Data/ge0ra/www/admin_gsg/app/config/. (which is
being imported from
"/Volumes/Data/ge0ra/www/admin_gsg/app/config/routing.yml").
Here is my services.yml file:
services:
sonata.admin.restaurant:
class: GSG\AdminBundle\Admin\RestaurantAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Gestion des restaurants", label: "Restaurants" }
arguments:
- ~
- GSG\AdminBundle\Entity\Restaurant
- ~
calls:
- [ addChild, [#sonata.admin.restaurantservice]]
sonata.admin.service:
class: GSG\AdminBundle\Admin\ServiceAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Gestion des restaurants", label: "Services" }
arguments:
- ~
- GSG\AdminBundle\Entity\Service
- ~
sonata.admin.restaurantservice:
class: GSG\AdminBundle\Admin\RestaurantServiceAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Gestion des restaurants", label: "RestaurantServices" }
arguments:
- ~
- GSG\AdminBundle\Entity\RestaurantService
- ~
in my RestaurantAdmin class:
protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
{
if (!$childAdmin && !in_array($action, array('edit'))) {
return;
}
$admin = $this->isChild() ? $this->getParent() : $this;
$id = $admin->getRequest()->get('id');
$menu->addChild(
'Voir/Editer',
array('uri' => $admin->generateUrl('edit', array('id' => $id)))
);
$menu->addChild(
'Services',
array('uri' => $admin->generateUrl('sonata.admin.restaurantservice.list', array('id' => $id)))
);
}
and my RestaurantServiceAdmin class:
class RestaurantServiceAdmin extends Admin
{
protected $parentAssociationMapping = 'Restaurant';
// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('service', 'sonata_type_model')
;
}
// Fields to be shown on filter forms
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
}
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
if (!$this->isChild())
$listMapper->addIdentifier('id')->addIdentifier('Restaurant');
$listMapper
->add('service', 'sonata_type_model')
;
}
}
Do someone have an idea from where those errors can come?
Thanks!
Your RestaurantAdmin class has the first parameter of configureSideMenu set as MenuItemInterface $menu when it should be an instance of \Knp\Menu\ItemInterface.
The Menu part of the type hint is wrong.

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 :)

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"

Overriding SonataUserBundle's ProfileFormType

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"]

Categories