I'm trying to add a custom block to the dashboard of SonataAdminBundle. I followed the instructinos here (How to add custom link or button to SonataAdminBundle Dashboard in Symfony2) and I'm getting the following error :
RuntimeException: The block service `sonata.block.service.processManagement` does not exist
Here's what I did. I have a file named "ProcessManagementBlockService.php" which contains the following:
<?php
namespace IMA\ProcessManagementBundle\Block;
use Symfony\Component\HttpFoundation\Response;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\BlockBundle\Block\BaseBlockService;
class ProcessManagementBlockService extends BaseBlockService
{
public function getName()
{
return 'My Newsletter';
}
public function getDefaultSettings()
{
return array();
}
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
{
}
public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
{
}
public function execute(BlockContextInterface $block, Response $response = null)
{
// merge settings
$settings = array_merge($this->getDefaultSettings(), $block->getSettings());
return $this->renderResponse('IMAProcessManagement:Block:blockProcessManagement.html.twig', array(
'block' => $block,
'settings' => $settings
), $response);
}
}
I also created a file (views/Block/blockProcessManagement.html.twig) that contains the template of the block I want to add to SonataAdmin's dashboard :
{% extends 'SonataBlockBundle:Block:block_base.html.twig' %}
{% block block %}
<table class="table table-bordered table-striped sonata-ba-list">
<thead>
<tr>
<th colspan="3">Newsletter - inviare</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="btn-group" align="center">
<a class="btn btn-small" href="#">Servizio Newsletter</a>
</div>
</td>
</tr>
</tbody>
</table>
{% endblock %}
Also, I in the services.yml file of my bundle, I have the following
services:
# ima_process_management.example:
# class: %ima_process_management.example.class%
# arguments: [#service_id, "plain_value", %parameter%]
sonata.block.service.processManagement:
class: IMA\ProcessManagementBundle\Block\ProcessManagementBlockService
arguments: [ "sonata.block.service.processManagement", #templating ]
tags:
- { name: sonata.block }
I know this file is properly loaded because I tried to put the upper lines directly in config.yml and got the same result.
Finally, I added in the main config.yml file of my project
sonata_block:
default_contexts: [cms]
blocks:
# Enable the SonataAdminBundle block
sonata.admin.block.admin_list:
contexts: [admin]
# Your other blocks
sonata.block.service.text:
sonata.block.service.rss:
sonata.admin.block.search_result:
sonata.block.service.processManagement: ~
and
sonata_admin:
templates:
dashboard: SonataAdminBundle:Core:dashboard.html.twig
dashboard:
blocks:
- { position: left, type: sonata.admin.block.admin_list }
- { position: left, type: sonata.block.service.processManagement}
I really don't know why I'm getting the error that the service does not exist...
The problem is how you did the letter casing in the config.yml. Always use lowercase for defining service names if you don't then Symfony converts it to lowercase.
Take a look at the coding standards for Symfony.
Service Naming Conventions:
A service name contains groups, separated by dots
The DI alias of the bundle is the first group (e.g. fos_user)
A service name contains groups, separated by dots
Use lowercase letters for service and parameter names
Related
After updating from Symfony 3.4 to 4.0 and verifying the operation, the following error occurred.
Do you have any idea?
I added the tride code to routes.yaml by referring to the post below, but it didn't change.
Override a controller Symfony 3.4/4.0
Error
An exception has been thrown during the rendering of a template
("Controller not found: service "#AhiSpAdminBundle/Hq/Image/manager" does not exist.").
Resources/views/Hq/Staff/input.html.twig
{# Image management dialog #}
<div id="imageDialog" title="Image management">
{{ render(controller("#AhiSpAdminBundle/Hq/Image/manager", {"modal": true})) }}
</div>
routes.yaml
ahi_sp_admin:
resource: "../src/Ahi/Sp/AdminBundle/Controller/"
type: annotation
prefix: /admin/
schemes: "%secure_scheme%"
#Tried code
ahi_sp_admin_hq_image_manager:
path: /admin/hq/image/manager/
defaults: { _controller: "#AhiSpAdminBundle/Hq/Image/manager" }
ImageController.php
/**
* Image management panel
*
* #Method("GET")
* #Route("/manager")
*
* #Template("#AhiSpAdminBundle/Hq/Image/manager.html.twig")
*/
public function managerAction(Request $request)
{
$routeParams = $request->get('_route_params');
$uploadUrl = $this->generateUrl("ahi_sp_admin_hq_image_save");
return array(
'modal' => isset($routeParams["modal"]) ? $routeParams["modal"] : false,
'form' => $this->createUploadForm($uploadUrl)->createView(),
);
}
That's not how you render a controller.
{{ render(controller(
'App\\Controller\\ArticleController::recentArticles',
{ 'max': 3 }
)) }}
It's clear from the doc
To include the controller, you’ll need to refer to it using the standard string syntax for controllers (i.e. controllerNamespace::action):
https://symfony.com/doc/4.0/templating/embedding_controllers.html
After a lot of effort I was finally able to configure Sonata Admin with ACL following this guide:
https://sonata-project.org/bundles/admin/master/doc/reference/security.html
I wanted to users to be able to view and edit only items with the same country property as the user.
This is my config.yml:
parameters:
locale: en
sonata.user.admin.user.class: AppBundle\Admin\UserAdmin
sonata.admin.security.mask.builder.class: Sonata\AdminBundle\Security\Acl\Permission\MaskBuilder
# SonataAdminBundle Configuration
sonata_admin:
security:
handler: sonata.admin.security.handler.acl
role_admin: ROLE_ADMIN
role_super_admin: ROLE_SUPER_ADMIN
# acl security information
information:
GUEST: [VIEW, LIST]
STAFF: [EDIT, LIST, CREATE]
EDITOR: [OPERATOR, EXPORT]
ADMIN: [MASTER]
# permissions not related to an object instance and also to be available when objects do not exist
# the DELETE admin permission means the user is allowed to batch delete objects
admin_permissions: [CREATE, LIST, DELETE, UNDELETE, EXPORT, OPERATOR, MASTER]
# permission related to the objects
object_permissions: [VIEW, EDIT, DELETE, UNDELETE, OPERATOR, MASTER, OWNER]
I created an AclVoter in order to show/hides elements:
services:
security.acl.voter.country_owned_permissions:
class: AppBundle\Security\Authorization\Voter\CountryOwnedAclVoter
arguments:
- "#security.acl.provider"
- "#security.acl.object_identity_retrieval_strategy"
- "#security.acl.security_identity_retrieval_strategy"
- "#security.acl.permission.map"
- "#logger"
tags:
- { name: monolog.logger, channel: security }
- { name: security.voter, priority: 255 }
public: false
This is the actual class:
<?php
namespace AppBundle\Security\Authorization\Voter;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Acl\Voter\AclVoter;
class CountryOwnedAclVoter extends AclVoter
{
public function supportsClass($class)
{
// support the Class-Scope ACL for votes with the custom permission map
// return $class === 'Sonata\UserBundle\Admin\Entity\UserAdmin' || is_subclass_of($class, 'FOS\UserBundle\Model\UserInterface');
// if you use php >=5.3.7 you can check the inheritance with is_a($class, 'Sonata\UserBundle\Admin\Entity\UserAdmin');
// support the Object-Scope ACL
return is_subclass_of($class, 'AppBundle\Model\CountryOwnedInterface');
}
public function supportsAttribute($attribute)
{
return in_array($attribute, array(
'LIST',
'VIEW',
'EDIT',
'DELETE',
'EXPORT',
));
}
public function vote(TokenInterface $token, $object, array $attributes)
{
if (!$this->supportsClass(get_class($object))) {
return self::ACCESS_ABSTAIN;
}
foreach ($attributes as $attribute) {
if ($this->supportsAttribute($attribute)) {
if ($object->getCountry() != $token->getUser()->getCountry()) {
//if ($object->isSuperAdmin() && !$token->getUser()->isSuperAdmin()) {
// deny a non super admin user to edit a super admin user
return self::ACCESS_DENIED;
}
}
}
// use the parent vote with the custom permission map:
// return parent::vote($token, $object, $attributes);
// otherwise leave the permission voting to the AclVoter that is using the default permission map
return self::ACCESS_ABSTAIN;
}
}
It seems to work fine since a user can only edit items which have the same country as the users. The problem is that he can still view the items in the list.
What am I doing wrong?
As specified in the official documentation I just needed to install a specific bundle:
5.4.6. LIST FILTERING
List filtering using ACL is available as a third party bundle: CoopTilleulsAclSonataAdminExtensionBundle. When enabled,
the logged in user will only see the objects for which it has the VIEW
right (or superior).
This will suffice:
composer require tilleuls/acl-sonata-admin-extension-bundle
In AppKernel.php:
// ACL list filter
new CoopTilleuls\Bundle\AclSonataAdminExtensionBundle\CoopTilleulsAclSonataAdminExtensionBundle(),
I refer this solution https://stackoverflow.com/a/15167450/2910183 ,and an error occur:
Compile Error: Declaration of
AppBundle\Block\NewsletterBlockService::execute() must be compatible
with
Sonata\BlockBundle\Block\BlockServiceInterface::execute(Sonata\BlockBundle\Block\BlockContextInterface
$blockContext, Symfony\Component\HttpFoundation\Response $response =
NULL)
Code
Part of my app/config/config.yml
sonata_block:
default_contexts: [cms]
blocks:
# enable the SonataAdminBundle block
sonata.admin.block.admin_list:
contexts: [admin]
sonata.user.block.menu: ~ # used to display the menu in profile pages
sonata.user.block.account: ~ # used to display menu option (login option)
sonata.block.service.text: ~
sonata.block.service.rss: ~
sonata.block.service.newsletter: ~
sonata_admin:
dashboard:
blocks:
-
position: left
type: sonata.admin.block.admin_list
settings:
groups: [default, app.admin.group.content]
-
position: right
type: sonata.admin.block.admin_list
settings:
groups: [default]
- { position: left, type: sonata.block.service.newsletter}
Part of my /private/var/www/learning_sonata/app/config/services.yml
sonata.block.service.newsletter:
class: AppBundle\Block\NewsletterBlockService
arguments: [ "sonata.block.service.newsletter", #templating ]
tags:
- { name: sonata.block }
My /private/var/www/learning_sonata/src/AppBundle/Block/NewsletterBlockService.php
namespace AppBundle\Block;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\BlockBundle\Block\BlockContext;
use Sonata\BlockBundle\Block\BlockServiceInterface;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\CoreBundle\Validator\ErrorElement;
use Sonata\BlockBundle\Block\BaseBlockService;
//use Sonata\BlockBundle\Block;
class NewsletterBlockService extends BaseBlockService
{
public function getName()
{
return 'My Newsletter';
}
public function getDefaultSettings()
{
return array();
}
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
{
}
public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
{
}
public function execute(BlockInterface $block, Response $response = null)
{
// merge settings
$settings = array_merge($this->getDefaultSettings(), $block->getSettings());
return $this->renderResponse('InstitutoStoricoNewsletterBundle:Block:block_my_newsletter.html.twig', array(
'block' => $block,
'settings' => $settings
), $response);
}
}
Thanks devilcius!
I refer your answer,clear the error above,but new erro occur:
Method "id" for object "Sonata\BlockBundle\Block\BlockContext" does
not exist in SonataBlockBundle:Block:block_base.html.twig at line 11
File path vendor/sonata-project/block-bundle/Resources/views/Block/block_base.html.twig
<div id="cms-block-{{ block.id }}" class="cms-block cms-block-element">
{% block block %}EMPTY CONTENT{% endblock %}
</div>
Sonata Version
sonata-project/admin-bundle 3.1.0 The missing Symfony Admin ...
sonata-project/block-bundle 3.0.0 Symfony SonataBlockBundle
sonata-project/cache 1.0.7 Cache library
sonata-project/core-bundle 3.0.0 Symfony SonataCoreBundle
sonata-project/doctrine-orm-admin-bundle 3.0.1 Symfony Sonata / Integrate...
sonata-project/exporter 1.4.1 Lightweight Exporter library
I read this resource,it said it's the version pro.
Could u give some idea,guys?
You're not implementing the method signature when extending BaseBlockService.
use Sonata\BlockBundle\Block\BlockContextInterface;
public function execute(BlockContextInterface $blockContext, Response $response = null)
{
return $this->renderResponse('InstitutoStoricoNewsletterBundle:Block:block_my_newsletter.html.twig', array(
'block' => $blockContext->getBlock(),
'settings' => $blockContext->getSettings()
), $response);
}
I'm having the same problem symfony2 is describing here
This comes in handy when you have a bundle but don't want to manually
add the routes for the bundle to app/config/routing.yml. This may be
especially important when you want to make the bundle reusable
TLDR; im trying to implement a custom Route Loader using this part of the symfony2 documentation
http://symfony.com/doc/current/cookbook/routing/custom_route_loader.html#more-advanced-loaders
However it doesn't seem to be working, the route cannot be found;
This is what I've tried so far:
The loader:
<?php
//namespace Acme\DemoBundle\Routing;
namespace Gabriel\AdminPanelBundle\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;
class AdvancedLoader extends Loader
{
public function load($resource, $type = null)
{
$collection = new RouteCollection();
$resource = '#GabrielAdminPanelBundle/Resources/config/routing.yml';
$type = 'yaml';
$importedRoutes = $this->import($resource, $type);
$collection->addCollection($importedRoutes);
return $collection;
}
public function supports($resource, $type = null)
{
return $type === 'advanced_extra';
}
}
here is my routing.yml
located in: src/Gabriel/AdminPanelBundle/Resources/config/routing.yml
the routing.yml
gabriel_admin_panel:
resource: "#GabrielAdminPanelBundle/Controller/"
type: annotation
prefix: /superuser
The Routes of the bundle can't be found unless I put the Routes back in the main app/config/routing.yml file, how to fix this?
Edit:
FileLoaderImportCircularReferenceException: Circular reference
detected in "/app/config/routing_dev.yml"
("/app/config/routing_dev.yml" > "/app/config/routing.yml" > "." >
"#GabrielAdminPanelBundle/Controller/" >
"/app/config/routing_dev.yml").
You must also configure service
# src/Gabriel/AdminPanelBundle/Resources/config/services.yml
your_bundle.routing_loader:
class: Gabriel\AdminPanelBundle\Routing\AdvancedLoader
tags:
- { name: routing.loader }
And routing file
# app/config/routing.yml
YourBundle_extra:
resource: .
type: advanced_extra
I am trying to have a language switcher on my symfony 2.1 website.
I followed the official documentation, set the translation files but setting the locale with $request->setLocale('en_US'); doesn't seem to work. After some research, I found this question which provides a beginning of an answer with this listener technique.
However, I still don't manage to have it working, I'm not so sure about my listener declaration, is something wrong with it?
My controller:
public function englishAction(Request $request)
{
$this->get('session')->set('_locale', 'en_US');
return $this->redirect($request->headers->get('referer'));
}
Service declaration in config.yml:
services:
my_listener:
class: "FK\MyWebsiteBundle\Listener\LocaleListener"
My routing:
homepage:
pattern: /{_locale}
defaults: { _controller: FKMyWebsiteBundle:Default:index, _locale: en }
requirements:
_locale: en|fr|cn
about:
pattern: /{_locale}/about
defaults: { _controller: FKMyWebsiteBundle:Default:about, _locale: en }
requirements:
_locale: en|fr|cn
The declaration of the LocaleListener in yml (inspired by the current declaration of the new LocaleListener: \vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Resources\config\web.xml)
services:
my_listener:
class: "FK\MyWebsiteBundle\Listener\LocaleListener"
arguments: [%locale%]
tags:
- { name: kernel.event_subscriber }
Some snippets:
A language switcher in your template:
{% for locale in ['en', 'fr', 'cn'] %}
<li {% if locale == app.request.locale %}class="active"{% endif %}>
{{ locale }}
</li>
{% endfor %}
A redirection with locale change from a controller:
$LocalizedUrl = $this->get('router')->generate(
$request->attributes->get('_route'),
['_locale' => $locale] + $request->attributes->get('_route_params')
);
return new \Symfony\Component\HttpFoundation\RedirectResponse($LocalizedUrl);
You should get the translator instance linked to your symfony kernel container:
$this->container->get('translator')->setLocale('fr');