Sonata Admin - Disable list view - symfony

I'm facing a problem with Sonata Admin. Is there a way to disable the "list view" ? I would like to fetch the first entity in the database and to go on it when clicking on the link in the sidebar. But not for all entry.
Is there a clean way to do it ? (I have the idea to check the entity in a custom controller, and to redirect to list view or edit view depending on the entity, but that's not really clean)

If you want to do a custom query for your list view, you could override the createQuery method in your Admin class like this :
class EntityAdmin
{
public function createQuery($context = 'list')
{
$query = parent::createQuery($context);
$query->andWhere(
$query->expr()->eq($query->getRootAlias() . '.id', ':id')
);
$query->setParameter('id', 1);
return $query;
}
}
You will have only your first entity in your list view.
UPDATE
You could override the standard_layout.html.twig to change the link in your sidebar :
First you need to set where your template is located:
app/config/config.yml
sonata_admin:
templates:
layout: ApplicationSonataAdminBundle::standard_layout.html.twig
Change the behaviour of the sidebar, for Sonata Admin 2.3 this is how you do it :
src/Application/Sonata/AdminBundle/Resources/Views/standard_layout.html.twig l.224
<ul class="treeview-menu{% if active %} active{% endif %}">
{% for admin in group.items %}
{% if admin.code == 'sonata.admin.entity' and
admin.hasroute('edit') and
admin.isGranted('EDIT') %}
<li{% if app.request.get('_sonata_admin') == admin.code %} class="active"{% endif %}><i class="fa fa-angle-double-right"></i> {{ admin.label|trans({}, admin.translationdomain) }}</li>
{% else %}
{% if admin.hasroute('list') and admin.isGranted('LIST') %}
<li{% if app.request.get('_sonata_admin') == admin.code %} class="active"{% endif %}><i class="fa fa-angle-double-right"></i> {{ admin.label|trans({}, admin.translationdomain) }}</li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
You must change 'sonata.admin.entity' by the identifier of your admin service.
Also if you want to remove access to the list you should add in your Admin class
use Sonata\AdminBundle\Route\RouteCollection;
class EntityAdmin
{
protected function configureRoutes(RouteCollection $collection)
{
$collection->remove('list');
}
}

Related

Sonata admin bundle: exclude custom admin from global search

Within my Symfony 3.4 project, I have 2 custom admins. Specially created for reporting services. Those admins do not have specific entities.
For the custom admins, I followed the Symfony recipe:
https://symfony.com/doc/3.x/bundles/SonataAdminBundle/cookbook/recipe_custom_view.html
Now, when searching items through the sonata global search, I get a
"Class does not exist" error in
vendor/sonata-project/admin-bundle/src/Resources/views/Core/search.html.twig.
This error is related to the custom admins.
Is there a solution to exclude these custom admins from the global search or to resolve this error?
Admin:
<?php
namespace MainBundle\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Route\RouteCollection;
class AccessRightsAdmin extends AbstractAdmin
{
protected $baseRoutePattern = 'accessrights';
protected $baseRouteName = 'Accessrights';
protected function configureRoutes(RouteCollection $collection)
{
$collection->clearExcept(array('list'));
$collection->add('accesRights', 'accessrights');
}
}
Service
services:
system.admin.accessrights:
class: MainBundle\Admin\AccessRightsAdmin
arguments: [~, ~, MainBundle:AccessRightsAdmin]
tags:
- { name: sonata.admin, manager_type: orm, group: sonata.admin.group.System, label: Accessrights }
calls:
- [ setTranslationDomain, [SonataAdminBundle]]
public: true
I found a solution and I'm going to leave it here in case someone need it.
The solution basically is to override the search.html.twig and ignore the admin you want from the search like so:
{% extends base_template %}
{% block title %}{{ 'title_search_results'|trans({'%query%': query}, 'SonataAdminBundle') }}{% endblock %}
{% block breadcrumb %}{% endblock %}
{% block content %}
<h2 class="page-header">{{ 'title_search_results'|trans({'%query%': query}, 'SonataAdminBundle') }}</h2>
{% if query is defined and query is not same as(false) %}
{% set count = 0 %}
<div class="row" data-masonry='{ "itemSelector": ".search-box-item" }'>
{% for group in groups %}
{% set display = group.roles is empty or is_granted(sonata_admin.adminPool.getOption('role_super_admin')) or group.roles|filter(role => is_granted(role))|length > 0 %}
{% if display %}
{% for admin in group.items %}
{% set count = count + 1 %}
{% if admin.code != 'bundle.admin.admin_to_ignore' %}{# in this line right here add the admin you want to ignore in your search #}
{% if admin.hasRoute('create') and admin.hasAccess('create') or admin.hasRoute('list') and admin.hasAccess('list') %}
{{ sonata_block_render({
'type': 'sonata.admin.block.search_result'
}, {
'query': query,
'admin_code': admin.code,
'page': 0,
'per_page': 10,
'icon': group.icon
}) }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endblock %}
To override the file you need to put it under the following path:
app -> Resources -> SonataAdminBundle -> views -> Core -> search.html.twig

Using raw Cypher to query Neo4j in Symfony

I am trying to go throught this tutorial: http://www.sitepoint.com/adding-social-network-features-php-app-neo4j/ But using the Symfony Framework instead of Silex.
I have been able to set up Neo4j to run with Symfony and am able to right user data to the graph. Now I would like to display all user email addresses in a list. I have taken this script:
public function home(Application $application, Request $request)
{
$neo = $application['neo'];
$q = 'MATCH (user:User) RETURN user';
$result = $neo->sendCypherQuery($q)->getResult();
$users = $result->get('user');
return $application['twig']->render('index.html.twig', array(
'users' => $users
));
}
And adapted it to read:
public function showUsersAction()
{
$em = $this->container->get('neo4j.manager');
$query = 'MATCH (n:`User`) RETURN n';
$users = $em->cypherQuery($query);
//print_r($users);
return $this->render('UserBundle:Account:showUsers.html.twig', array('users' =>$users));
}
And The twig looks as follows:
{% extends '::base.html.twig' %}
{% block content %}
<h1>get all users:</h1>
<ul>
{% for user in users %}
<li>{{ user.property('email') }}</li>
{% endfor %}
</ul>
{% endblock %}
But something in the twig is wrong, im getting the error:
Method "property" for object "Everyman\Neo4j\Query\Row" does not exist in UserBundle:Account:showUsers.html.twig at line 6
The problem was found in the syntax of the twig file. After consulting this page: https://github.com/jadell/neo4jphp/wiki/Cypher-and-gremlin-queries it became clear, that I had to include user['n'] in my twig template. The twig template now looks as such:
{% extends '::base.html.twig' %}
{% block content %}
<h1>get all users:</h1>
<ul>
{% for user in users %}
<li>{{ user['n'].getProperty('email') }}</li>
{% endfor %}
</ul>
{% endblock %}
I'm the author of the article you mentioned. The thing is that you use a different neo4j library than the one used in the article, hence neoclient, so the methods used in the article are different than the methods provided with neo4jphp.
As NeoClient uses heavily the Symfony components, integrating it in Symfony is really easy, you just need to override the DI. Example here : https://github.com/graphaware/GithubNeo4j/tree/master/src/GraphAware/Neo4jBundle
You'll then be able to use the methods illustrated in the 3 articles I wrote on Sitepoint.
So your problem with the twig template is that he doesn't find the getProperty method of the node object class, which is normal as neo4jphp returns Row object classes.
If you switch back to neoclient, as in the article, in the Twig template you can just write :
{% for user in users %}
<li>{{ user.getProperty('email') }}</li>
{% endfor %}

Symfony2: How to display admin-account name while impersonating user-account?

I want to display something like that:
Case 1: "logged in as USER"
# UserName [ logout ]
No problems here, i just do:
# {{ app.user.username}} [ logout ]
Case 2: "logged in as ADMIN"
# AdminName [ logout ]
The same works here:
# {{ app.user.username}} [ logout ]
Case 3: "logged in as ADMIN impersonating a USER"
AdminName # UserName [ return ]
Now thats a problem:
{{ ??..what here..?? }} # {{ app.user.username}} [ return ]
This is the only solution I know... it seems a lot of code for a sipmle displaying username :/
{# iterating through user roles to find ROLE_PREVIOUS_ADMIN #}
{% for role in app.security.token.roles %}
{% if role.source is defined %}
{{ role.source.user.username }}
{% endif %}
{% endfor %}
# {{ app.user.username }} [ return ]
Is there any other way? I need a pure TWIG solution -> this is supposed to be part of my main twig template (that is extended by all other templates) -> I can't add controller code to all actions, just to display username.
With the idea you have proposed above,.. can you not just create a custom twig extension that encompasses your logic from your twig template so that you can just call myCustomTwigFunction within your twig template and it will output the original users name?
See http://symfony.com/doc/current/cookbook/templating/twig_extension.html for more info about custom twig extensions
The code you'd have in your Twig extension file would be...
$roles = $this->container->get('security.context')->getToken()->getRoles();
foreach ($roles as $role) {
if (method_exists($role, 'getSource')) {
return ($role->getSource()->getUser()->getUsername());
}
}
Where $container is a class variable of the DI Container on your twig extension class
For anyone looking for a solution for Symfony 4.3/4.4/5.0:
{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
{% for role in app.token.roles %}
{% if role.role == 'ROLE_PREVIOUS_ADMIN' %}
Admin username is {{ role.source.user.username }}
{% endif %}
{% endfor %}
{% endif %}
From Symfony 5.1 onwards, use IS_IMPERSONATOR in place of ROLE_PREVIOUS_ADMIN.

Symfony2 FOSUserBundle Overriding Forms

I am trying to change the template for the registration form in my application so that I can add some other HTML to it. Here is the /My/UserBundle/Resources/views/Registration/register.html.twig file:
{% extends "MyUserBundle::layout.html.twig" %}
{% block fos_user_content %}
<section class="register site-content">
<header>
<h1>{{ 'layout.register'|trans({}, 'FOSUserBundle') }}</h1>
</header>
<div class="block">
{% include "FOSUserBundle:Registration:register_content.html.twig" %}
</div>
</section>
{% endblock fos_user_content %}
And I have successfully overridden the layout.html.twig:
{% extends 'MyMainBundle::layout.html.twig' %}
{% block title %}{{ site_name }}{% endblock %}
{% block content %}
{% for key, message in app.session.getFlashes() %}
<div class="{{ key }}">
{{ message|trans({}, 'FOSUserBundle') }}
</div>
{% endfor %}
{% block fos_user_content %}{% endblock %}
{% endblock %}
as well as form.html.twig:
{% extends 'FOSUserBundle::form.html.twig' %}
{% block field_row %}
<li class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</li>
{% endblock field_row %}
{% block form_widget %}
<ul {{ block('container_attributes') }}>
{{ block('field_rows') }}
{{ form_rest(form) }}
</ul>
{% endblock form_widget %}
config parts:
# FOS User Configuration
fos_user:
db_driver: orm
firewall_name: main
user_class: My\UserBundle\Entity\User
from_email:
address: %admin_email%
sender_name: %site_name%
template:
engine: twig
theme: MyUserBundle::form.html.twig
I have cleared my cache.
Whenever I go to
http://localhost/register/
apache just hangs until it times out.
The best I can figure out, is the PHP maximum execution message says it crashes on a twig template in the cache on line 16. That line is function doGetParent(...) The file is:
<?php
/* FOSUserBundle::form.html.twig */
class __TwigTemplate_9cf68a2af1db50466c556a735bcdeba0 extends Twig_Template
{
public function __construct(Twig_Environment $env)
{
parent::__construct($env);
$this->blocks = array(
'field_row' => array($this, 'block_field_row'),
'form_widget' => array($this, 'block_form_widget'),
);
}
protected function doGetParent(array $context)
{
return "FOSUserBundle::form.html.twig";
}
protected function doDisplay(array $context, array $blocks = array())
{
$this->getParent($context)->display($context, array_merge($this->blocks, $blocks));
}
// line 3
public function block_field_row($context, array $blocks = array())
{
// line 4
echo " <li class=\"form_row\">
";
// line 5
echo $this->env->getExtension('form')->renderLabel($this->getContext($context, "form"));
echo "
";
// line 6
echo $this->env->getExtension('form')->renderErrors($this->getContext($context, "form"));
echo "
";
// line 7
echo $this->env->getExtension('form')->renderWidget($this->getContext($context, "form"));
echo "
</li>
";
}
// line 11
public function block_form_widget($context, array $blocks = array())
{
// line 12
echo " <ul ";
$this->displayBlock("container_attributes", $context, $blocks);
echo ">
";
// line 13
$this->displayBlock("field_rows", $context, $blocks);
echo "
";
// line 14
echo $this->env->getExtension('form')->renderRest($this->getContext($context, "form"));
echo "
</ul>
";
}
public function getTemplateName()
{
return "FOSUserBundle::form.html.twig";
}
public function isTraitable()
{
return false;
}
}
It has also timed out on \vendor\twig\lib\Twig\Template.php on line 65 Which is public function getParent(array $context)
So clearly there is some problem with getParent but I don't know what that means or how to fix it.
According to the FOSUserBundle documentation:
The easiest way to override a bundle's template is to simply place a
new one in your app/Resources folder. To override the layout template
located at Resources/views/layout.html.twig in the FOSUserBundle
directory, you would place you new layout template at
app/Resources/FOSUserBundle/views/layout.html.twig.
As you can see the pattern for overriding templates in this way is to
create a folder with the name of the bundle class in the app/Resources
directory. Then add your new template to this folder, preserving the
directory structure from the original bundle.
In my project I override FOSUserBundle's layout as they said and it's work like a charm.
So doing it at the same way you will need to create app/Resources/FOSUserBundle/views/Registration/register.html.twig. (or the form you want to override)
EDIT
Ok, I just realize that you've chosen to extend the FOSUserBundle. In that case instead of app/Resources/ you need to do it inside your bundle. But you don't need to put
{% extends 'FOSUserBundle::form.html.twig' %}
The FOSUserBundle will detect that you are overriding the bundle and will be extended automatically.
And you also need to tell your bundle that FOSUserBundle is its parent.
class YourBundle extends Bundle
{
public function getParent()
{
return 'FOSUserBundle';
}
}
Copy the file:
{% include "FOSUserBundle:Registration:register_content.html.twig" %}
to the: app/Resources/FOSUserBundle/views/ catalogue. Then it will overwrite the base FOS form. You can edit the copied file.
Otherwise you must override the FOS RegisterController.
I feel stupid, just removed the {% extends ... %} line in form.html.twig and it worked. I guess I don't understand enough about what is inherited in bundles. (I did want to inherit from the FOSUserBundle form.html.twig, but I don't know how to access that from a bundle which inherits from FOSUserBundle)

How to identify if a user is being impersonated in Symfony2?

In an application built with Symfony2 we want superadmins to be able to impersonate other users. This is easily done by giving the superadmin user the ROLE_ALLOWED_TO_SWITCH role. The switching is implemented with a call to "somewhere?_switch_user=" as suggesed in the reference documentation.
The problem however, is to detect in a template if the current user is actually impersonated so as to print a link to "somewhere?_switch_user=_exit" on the page, thus enabling the impersonating user to return to her real user.
I haven't been using Symfony2 for a while so I'm not sure, but when you switch to another user you gain all roles assigned to that user and one extra role: ROLE_PREVIOUS_ADMIN. So I guess all you need to do is to use voter to check whether such a role is assigned to the current user using voter.
// Twig
{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
EXIT
{% endif %}
// PHP
<?php if ($view['security']->isGranted('ROLE_PREVIOUS_ADMIN')): ?>
EXIT
<?php endif ?>
An example of how to get more details about the impersonator:
use Symfony\Component\Security\Core\Role\SwitchUserRole;
$sec = $this->get('security.context');
if($sec->isGranted('ROLE_PREVIOUS_ADMIN')) {
foreach($sec->getToken()->getRoles() as $role) {
if ($role instanceof SwitchUserRole) {
$admin_user = $role->getSource()->getUser();
}
}
}
You then have admin_user as the original user object. Remember to use the SwitchUserRole.
An example of how to display impersonator in twig:
{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
{% for role in app.security.token.roles %}
{% if role.role == 'ROLE_PREVIOUS_ADMIN' %}
{{ role.source.user.username }}
{% endif %}
{% endfor %}
{% endif %}
If you need to test role from the previous admin user :
Working on Symfony 3.4
{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
{% for role in app.token.roles %}
{% if role.role == 'ROLE_PREVIOUS_ADMIN' %}
{% for role_from_previous in role.source.roles if role_from_previous.role == "ROLE_DELETE" %}
{{ role.source.user.username }} has "ROLE_DELETE"
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}

Resources