how to render dynamic menus based on logged user role on symfony2 - templates

Which is the best practice to generate in a template a dynamic menu (the menu will be present in all the other app's pages) depending on the user role?
i'm using this in my main template:
{{render(controller('AcmeMainBundle:Sidebar:show'))}}
and this is the controller
class SidebarController extends Controller {
public function showAction() {
$userRole = $this->getUser()->getRoles();
if (in_array("ROLE_ADMIN", $userRole)) {
return $this->render('AcmeMainBundle:Sidebar:sidebarAdmin.html.twig');
} else {
return $this->render('AcmeMainBundle:Sidebar:sidebarUser.html.twig');
}
}
}
but i think it isn't good; what do you think? thanks!

You can achieve this at the view level too. In the template, check the active user's role and hide/show menus depending on the role
{% if is_granted('ROLE_ADMIN') and not is_granted('ROLE_USER') %}
//Show admin stuff
{% else if is_granted('ROLE_USER') %}
//Show user stuff
{% endif %}

If you really want to use the same template for both and not have logic in the template, you can pass parameters to the render method to provide the elements that will be present in your menu.

Related

In flask admin, add confirmation prompt when Save new record

Our users have the usual CRUD actions in various flask pages.
For one scenario, the Create action can be an intensive process, with lots of domain logic performed, and on occasion, items have been created accidentally.
Our users would now like to add a simple "Are you sure you want to create this Account?" prompt when they click the "Save" button. The next response would simply be Yes and No buttons.
It's not apparent how to add this - are we missing something obvious?
Override the Flask-Admin create template for the particular view and add a JavaScript confirmation to the form's onSubmit event. Adding the confirm to the submit event handles the three submit buttons; 'Save', 'Save and Add Another' and 'Save and Continue'.
For example:
class UserView(ModelView):
create_template = 'create_user.html'
# ...
In the /templates/admin folder add html file 'create_user.html':
{% extends 'admin/model/create.html' %}
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='admin/js/helpers.js', v='1.0.0') }}" type="text/javascript"></script>
<script>
(function ($) {
$('form:first').on('submit', function() {
return faHelpers.safeConfirm('{{ _gettext('Are you sure you want to save this record?') }}');
});
})(jQuery);
</script>
{% endblock %}
Note there's a bug in Flask-Admin 1.5.3, helpers.js is missing from the distribution and you will need to add the safeConfirm function.
{% extends 'admin/model/create.html' %}
{% block tail %}
{{ super() }}
<script>
(function ($) {
function safeConfirm(msg) {
try {
var isconfirmed = confirm(msg);
if (isconfirmed == true) {
return true;
}
else {
return false;
}
}
catch (err) {
return false;
}
}
$('form:first').on('submit', function () {
return safeConfirm('{{ _gettext('Are you sure you want to save this record?') }}');
});
})(jQuery);
</script>
{% endblock %}

Extends base.twig when it is with passed entity

I am making some small project with Symfony, but now I have a question about base.twig.html, and does it should be passed with some entity in it. I am using Symfony 4.
Lets say I have some entity MainCategory with:
id
name
info
slug
Then from my Route /, I use MainCategoryRepository to get all records for that entity, then render the base.twig with the passed records.
Then in navigation bar, I loop over all records and make links with the entity.name.
After that in another Route "/{slug}", i render another twig file which extends the base one. But here i am not using the repository, i use just the Entity - MainCategory, because the 'slug' is unique field and it will get only that record.
And here is the problem, i am just passing one record, but the navigation bar from the 'base.twig' loop over all records and here I get an error:
Variable "mainCategories" does not exist.
Here is my code:
Route "/"
/**
* #Route("/", name="app_homepage")
*/
public function homepage(MainCategoryRepository $mainCategoryRepository)
{
$mainCategories = $mainCategoryRepository->findAll();
return $this->render('base.html.twig', [
'mainCategories' => $mainCategories,
]);
}
"base.html.twig"
{% for mainCategory in mainCategories %}
<li>
<a href="{{ path('category_show', {'slug': mainCategory.slug}) }}">
{{ mainCategory.name }}</a>
</li>
{% endfor %}
Route "/{slug}"
/**
* #Route("/{slug}", name="category_show")
*/
public function categoryShow(MainCategory $mainCategory)
{
return $this->render('show.html.twig',
[
'mainCategory' => $mainCategory,
]
);
}
"show.html.twig"
{% extends 'base.html.twig' %}
{% block body %}
<p>{{ mainCategory.Info }}</p>
{% endblock %}
ERROR:
Variable "mainCategories" does not exist.
So it is good idea to pass any entity in base.twig? I can pass all records in the second twig file, but that means I have to do it in every template which extends the base.twig.
I found one solution, dont know is it perfect or the right solution, but it works.
I made one Service with a method in which I return all records from MainCategory Entity.
MainCategoryLoaderService
protected $mainCategoryRepository;
public function __construct(MainCategoryRepository $mainCategoryRepository)
{
$this->mainCategoryRepository = $mainCategoryRepository;
}
public function getAllMainCategories()
{
return $this->mainCategoryRepository->findAll();
}
Then in the file config/packages/twig.yaml I made one global variable:
Global Variable
globals:
globalMainCategory: '#App\Service\MainCategoryLoaderService'
After that in base.html.twig I made one variable in which I will have all main categories:
Twig Variable
{% set mainCategories = globalMainCategory.getAllMainCategories() %}
And then just loop over that variable.

Is it possible to load bootstrap modal directly with flask

In flask we can render a page using render_template('home.html, data=data).
In the same way is possible to load bootstrap modal without having to use javascrpt?
For e.g if an element is clicked in the page,
<span class="example" data-toggle="modal" data-target="#showExample" Testing</span>
A call is made to function in flask function and it trigger the modal pop-up and pass data that can used to populate field
If i'm understand what you mean, you can do this in JavaScript (just paste it in bottom of your page, in the tag "script":
document.getElementById('showExample').addEventListener('click', function() {
// Do some stuff, that you need to do...
});
Or, if you want, you can do this without JavaScript (i guess that you using Jinja2):
Insert it into your page template:
{% if show_example_modal %}
<span class="example" data-toggle="modal" data-target="#showExample" Testing</span>
{% endif %}
Python view code:
#app.add_route('/something/')
def something():
return render_template("something.html", show_example_modal=True)

How to have template tags recognized in a context variable in DJango

I have a Django template that displays a link generated with template tags, and I want to send it as a context variable:
views.py:
def my_view(request):
...
link = '''Drop'''
context = {
'link': link,
}
return render(request, "my_template.html" , context)
I am trying this in my_template.html:
{{ link | safe }}
but the link is literally rendered as "{% url 'quests:drop' s.id %}"
Is there a way to have the template tags sent in the context variable recognized and executed by the template engine?
EDIT:
Why I am trying to do this is because I will actually be sending a list of buttons to the template, each with a different link and text, which will be looped through (in this example, looping through various 's' objects), and multiple views will be using the template (each view with their own button set), so the buttons are extremely context specific.
My template was becoming a spaghetti of logic to handle all the situations, and sending the buttons in the context seems like it would be much more DRY and result in a cleaner, easier to read template.
EDIT 2:
Another option that could solve the problem is if I could just pass the changing parts:
link_data = ['quests:drop','Drop']
context = {
'link_data': link_data,
}
return render(request, "my_template.html" , context)
And then the template would have:
{{link_data.1}}
However, as is, {{link_data}} doesn't render and I get the error:
Reverse for '{{ link_data.0 }}' ... not found.
You shouldn't is the answer. To achieve what are trying to do fits perfectly inline with how templates are supposed to work and you are over complicating it.
context = {
'buttons': {
'button1': {
'text': 'This is a button',
'link': reverse('quests:drop', args=[...])
},
'button2':{...}
}
}
And in the template:
{% for button in buttons %}
<a class="btn btn-primary" href={{button.link}}>{{button.text}}</a>
{% endfor %}
You could also create a Button class and use it similarly:
context = {
'buttons': [
Button('This is text', reverse('quests:drop', args=[...]),
...
],
}
Referencing to your 2nd edit:
The code in your template should look like this:
{{ link_data.1 }}
Notice the omitted '{{ }}' tags.

How to test a symfony2 embedded controller with phpunit?

I am using embedded controllers to generate dynamic content in side templates(eg: a menu)
Usually, I implement functionnal tests to assert controllers.
So far, functionnal tests are passing and phpunit considers my embedded controllers code-covered
I am wondering how to test the embedded controller with different inputs and evaluate the outputs... Is that Unit Testing right ?
I know Unit Testing controller is a bad practice, but how can I function test an embedded controller when there is no Request Object ?
The route/url is something that the Twig render() function takes care of.
{{ render(controller('AppSuperBundle:Default:generateMenu', {'params': ... } )) }}
An example to illustrate:
class DefaultController extends Controller
{
public function testAction()
{
return $this->render('AppSuperBundle::index.html.twig');
}
public function generateMenuAction($route, Array $RouteParams)
{
$repo = $this->getDoctrine()->getRepository(...
//some process to generate params of menu items (eg:locale, url, name...)
return $this->render('AppSuperBundle::menu.html.twig', array('menuItems' => $menuItemsWithParams));
}
}
The template index.html.twig
<html>
<body>
{% block menu %}
{{ render(controller('AppSuperBundle:Default:generateMenu', {'route': app.request.attributes.get('_route'), 'RouteParams': app.request.attributes.get('_route_params')} )) }}
{% endblock %}
{% block content %}
...
{% endblock %}
</body>
</html>
What are your thoughts on this ?
Your embedded controllers do not exist in a vaccum. They are being loaded by templates used in your main controllers.
I would say that it is sufficient to only check main controllers. If you really want to check different output from embedded controllers just test the main controller with appropriate params. In the end it is the main controller that is pasing different values to your embedded controllers.
Since render a view is the response, and you are talking about unit test,
And i would highly recommend unit test the controllers, since in some projects the controllers can have a lot of logic there.
i would unit test a controller to its behavour,so it won't throw strange error from the code in the controller. so what i suggest you to do is create a test method for each case in each action, and you will probably need to mock some of the objects the controller is using, here is an example:
public function testIndexAction()
{
$this->employeeRepository->expects($this->once())->method('findByFilter')->will($this->returnValue($this->employee));
$this->entityManager->expects($this->once())->method('getRepository')->will(
$this->returnValue($this->employeeRepository)
);
$this->employeeManager->expects($this->once())->method('formatEmployeeData')->will(
$this->returnValue($this->formattedJson)
);
$this->mockContainer($this->object);
$this->object->indexAction();
}