I have a contact page which contains contacts. When clicking on a contact a detailed page with this contact information should appear.
The detail page is called in the general contact page:
Details
In the config.yml i added the following:
Name: mySite
After: framework/routes#coreroutes
---
Director:
rules:
'detail/contact/$ID': 'ContactDetailPage_Controller'
// The Contact Detail controller
class ContactDetailPage_Controller extends Page_Controller {
private $id;
public function init() {
parent::init();
$Params = $this->getURLParams();
$URLSegment = Convert::raw2sql($Params['ID']);
$this->id = $URLSegment;
}
Unfortunately by clicking on the link an error appears that the current page doesn't exist.
What could be wrong?
Thank you
Link:
Details
config.yml:
Director:
rules:
'detail//$Action/$ID': 'ContactDetailPage_Controller'
ContactDetailPage_Controller:
private static $allowed_actions = array(
'contact'
);
public function contact( $arguments ) {
var_dump( $arguments->allParams() );
}
Hope it helps.
Related
Recently while building my CMS in Symfony, I've run into a problem. I have 2 controllers, publicationcontroller and contactcontroller. In both controllers, an instance of the entity Page is loaded whenever a corresponding slug matches. Below is the code:
class ContactController extends AbstractController
{
/**
* #Route("/{slug}", name="contact")
*/
public function index(PageRetriever $pageRetriever, $slug)
{
$page = $pageRetriever->getPage($slug);
return $this->render('contact/index.html.twig', [
'page' => $page
]);
}
}
class PublicationController extends AbstractController
{
/**
* #Route("/{slug}", name="publications")
*/
public function index(PageRetriever $pageRetriever, $slug)
{
$page = $pageRetriever->getPage($slug);
return $this->render('publication/index.html.twig', [
'page' => $page
]);
}
}
My problem is that both the content of publication and contact are loaded in the same template, depending on which controller is initialized first.
Does anyone here have an idea or some tips on how to load the proper template, depending on which slug is called?
Any hope is greatly appreciated
There is no way Symfony could know which controller should be called. You have same route for both contact and publication controller.
There is 2 possible solutions which I can think of.
1. Use different routes
#Route("/publication/{slug}", name="publications")
#Route("/contact/{slug}", name="contact")
2. Use one controller but write your own logic to choose template
$page = $pageRetriever->getPage($slug);
if ($page->getType() === 'publication') {
return $this->render('publication/index.html.twig', [
'page' => $page
]);
}
return $this->render('contact/index.html.twig', [
'page' => $page
]);
I try to override layout template in Sonata Admin but depends of logged user. If logged user belong to group customers has some ROLE - show other layout.
I want change -
layout" => "#SonataAdmin/standard_layout.html.twig"
Where is best place to do it ?
I found that i can do this in admin class - override getTemplate.
But is possible to do this is some listener and switch globaly without edit admin classes ?
UPDATE 1
i create class
class SonataTemplateRegistry implements MutableTemplateRegistryInterface
{
/**
* #var string[]
*/
private $templates = [];
/**
* #param string[] $templates
* #param ContactService $contactService
*/
public function __construct(array $templates = [], ContactService $contactService)
{
$templates['layout']= '#SonataAdmin/layout1.html.twig';
// $templates['layout']= '#SonataAdmin/standard_layout.html.twig';
// echo '<pre>'; var_dump($templates); die();
$this->templates = $templates;
}
register it
sonata.admin.global_template_registry:
class: App\Service\SonataTemplateRegistry
public: true
arguments: ['%sonata.admin.configuration.templates%', '#mea.contact']
class is fired - die() show templates but main template is not changed when i change here.
Update 2
in admin class when i get layout template i get correct #SonataAdmin/layout1.html.twig
protected function configureListFields(ListMapper $listMapper)
{
var_dump($this->configurationPool->getTemplate('layout'));
but it is not loaded, still see #SonataAdmin/standard_layout.html.twig
UPDATE 3
I found a strange behavior - main page sonata admin - switching template works but already under the pages use the default template
UPDATE 4
I found something interesting , each admin panel has sub service like here :
php bin/console debug:container |grep app.admin.social
app.admin.social.accounts App\SocialManager\Admin\SocialAccountAdmin
app.admin.social.accounts.template_registry Sonata\AdminBundle\Templating\TemplateRegistry
app.admin.social.order App\SocialManager\Admin\SocialManagementOrderAdmin
app.admin.social.order.template_registry Sonata\AdminBundle\Templating\TemplateRegistry
i override parameters :
parameters:
sonata.admin.global_template_registry: App\Service\SonataTemplateRegistry
and service
sonata.admin.global_template_registry:
class: App\Service\SonataTemplateRegistry
public: true
arguments: ['%sonata.admin.configuration.templates%', '#mea.contact']
so why sonata still use Sonata\AdminBundle\Templating\TemplateRegistry
protected function configureListFields(ListMapper $listMapper)
{
$this->getTemplateRegistry()
give Sonata\AdminBundle\Templating\TemplateRegistry
You can use workaround by service tags.
First create your own Template registry and implement MutableTemplateRegistryInterface.
Then create compiler pass.
<?php
namespace App\DependencyInjection\Compiler;
use App\Registry\TemplateRegistry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Class SonataAdminTemplateRegistryPass
* #package App\DependencyInjection\Compiler
*/
class SonataAdminTemplateRegistryPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
foreach ($container->findTaggedServiceIds('sonata.admin.template_registry') as $id => $tags)
{
//because #see src/vendor/sonata-project/admin-bundle/src/DependencyInjection/Compiler/AddDependencyCallsCompilerPass.php:405
$adminServiceId = str_replace(".template_registry", "", $id);
$def = $container->getDefinition($adminServiceId);
$def->removeMethodCall('setTemplateRegistry');
$def->addMethodCall('setTemplateRegistry', [new Reference(TemplateRegistry::class)]);
}
}
}
then add compiler pass to src/Kernel.php like this
protected function build(ContainerBuilder $container)
{
$container->addCompilerPass(new SonataAdminTemplateRegistryPass());
}
And you will get always override TemplateRegistry in any admin class.
So you can implement your own logic in your own TemplateRegistry :)
You can do this using single Twig template:
{# layout.html.twig #}
{# mutatis mutandis #}
{% extends app.user ?
('ROLE_ADMIN' in app.user.role ?
'admin_layout.html.twig' :
'customer_layout.html.twig'
) :
'fallback.html.twig'
%}
What I want to do is show logged user only content that he has access to.
First thing I was doing access_control in security.yml & redirect but i must do at least 30 deferent accounts;/
Next i create twig extension that will connect to DB and get current logged user specific settings -
access to panels. Is this good way?
The problem is
$user = $this->getUser()->getId();
$currentUser = $this->em->getRepository('AppBundle:User')->find($user);
It will not work, Blank page appears in dev env
But when i put 1
$currentUser = $this->em->getRepository('AppBundle:User')->find(1);
& 1 is user id everything is ok.
services.yml
app.twig.users_extension:
class: AppBundle\Twig\Extension\AccesExtension
arguments: ["#doctrine.orm.entity_manager","#security.token_storage"]
tags:
- { name: twig.extension }
Twig Extension
class AccesExtension extends \Twig_Extension
{
protected $em;
protected $tokenStorage;
public function __construct(EntityManager $em, TokenStorage $tokenStorage)
{
$this->em = $em;
$this->tokenStorage = $tokenStorage;
}
public function getUser()
{
return $this->tokenStorage->getToken()->getUser();
}
public function getGlobals()
{
$user = $this->getUser()->getId();
$currentUser = $this->em->getRepository('AppBundle:User')->find($user);
return array (
"acces" => $currentUser,
);
}
public function getName()
{
return "AppBundle:AccesExtension ";
}
}
As people in the comments already told you, you can use security voters.
Here is a simple tutorial with video/text that explains the use of voters and also how to use them in twig, this should help you with the problem you have.
Does somebody know how to create a notification system for the menu items in the sidebar?
For example if you have a sidebar entry
Articles
and in the background, a new article has been added (e.g. by importing via a sql script). Then the menu entry should be displayed as
Articles (1)
Is there a tutorial for my concern?
You can modify the sidebar menu items.
To do this, you must create a listener that configure the menu in the manner that you want. You can do this with this code:
app.menu_listener:
class: AppBundle\EventListener\MenuBuilderListener
tags:
- { name: kernel.event_listener, event: sonata.admin.event.configure.menu.sidebar, method: addMenuItems }
calls:
- [ setDependencies, [ #doctrine.orm.entity_manager ] ]
After that you can write the class that modify the menu:
namespace AppBundle\EventListener;
use AppBundle\Entity\Configuration;
use Sonata\AdminBundle\Event\ConfigureMenuEvent;
use Doctrine\ORM\EntityManager;
class MenuBuilderListener {
/** #var EntityManager $em */
private $em;
public function addMenuItems(ConfigureMenuEvent $event)
{
$articles = $this->em->getRepo('AppBundle:Article')->findAll();
$menu = $event->getMenu();
$articleMenu = $menu->getChild('sonata.admin.group.articles');
$articleMenu->setLabel('Articles <span>' . $articles->count() . '</span>')
}
public function setDependencies(EntityManager $em, Translator $translator) {
$this->em = $em;
}
}
This is only an example, but is the way that i will take if i need to do this feature, i hope this may help you
You have more info about this here: https://sonata-project.org/bundles/admin/master/doc/cookbook/recipe_knp_menu.html
I'm need to do add custom access law to my access control on Symfony, I try to explain me.
I have a web application with some customer, and I want to able the access of some part of the code when that customer have the right plugin. So this is my customer:
namespace AppBundle\Entity;
class CustomerProfile{
private $id;
private $user;
private $plugins;
}
The Entity for the Plugin
namespace AppBundle\Entity;
class Plugin{
private $id;
private $name;
private $customerProfiles;
}
I use doctrine for the relations so from customer i can get his plugins. For example, we have 2 customer and 2 plugin:
AppBundle\Entity\CustomerProfile:
customer_1:
user: '#user_1'
plugins: ['#plugin_1','#plugin_2']
customer_2:
user: '#user_2'
plugins: ['#plugin_1']
AppBundle\Entity\Plugin:
plugin_1:
name: 'plugin 1'
plugin_2:
name: 'plugin 2'
In my project all the code about customer is under the /customer namespace, Symfony like, and all work.
access_control:
- { path: ^/customer, roles: ROLE_CUSTOMER }
But, for this customer with different plugin i would set an dinamic access control, but i don't know how. I need to control something like this:
access_control:
- { path: ^/code_for_plugin_1, roles: ROLE_CUSTOMER_WHIT_PLUGIN_1}
- { path: ^/code_for_plugin_2, roles: ROLE_CUSTOMER_WHIT_PLUGIN_2}
but I think the good way is set a "sub role" (if exist) to set for each customer that have a plugin a role to access in that namespace.
I hope I was clear enough, thanks for help.
I would suggest to use custom voter rather than the access_control and role approach as
this is in my opinion more flexible for your use case. Your suggested solution requires
a generated role (ROLE_CUSTOMER_WHIT_PLUGIN_{X}) for every plugin, which in case
of adding pluggins dynamically would just not work.
Check the How to Use Voters to Check User Permissions article in Symfony documentation for more detail.
You basically need to implement a user voter which will check if the logged user has an access
to requested resource. In your case it would look similar to this:
/src/YourBundle/Controller/YourController.php
<?php
...
class YourController extends Controller
{
public function getFooAction($id)
{
$this->denyAccessUnlessGranted(YourVoter::VIEW_FOO);
// ...method logic
}
public function getBarAction($id)
{
$this->denyAccessUnlessGranted(YourVoter::VIEW_BAR);
// ...method logic
}
}
/src/YourBundle/Security/YourVoter.php
<?php
...
class YourVoter extends AbstractVoter
{
const VIEW_FOO = 'YOUR_VIEW_FOO';
const VIEW_BAR = 'YOUR_VIEW_BAR';
public function getVoterAttributes()
{
return [self::VIEW_FOO, self::VIEW_BAR,];
}
protected function supports($attribute, $subject)
{
...
}
protected function voteOnAttribute($attribute, $item, TokenInterface $token)
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
switch ($attribute) {
case self::VIEW_FOO:
return $this->canViewFoo($user);
case self::VIEW_BAR:
return $this->canViewBar($user);
}
throw new \Exception(sprintf(
'Invalid vote attribute "%s".',
$attribute
));
}
private function canViewFoo(User $user)
{
return $user->getProfile()->hasRoleFooXYZ()
}
private function canViewBar(User $user)
{
return $user->getProfile()->hasRoleBarXYZ()
}
}