Symfony Twig Sandboxing with ArrayLoader - symfony

My purpose is to sandbox a generated html.
$loader = new \Twig\Loader\ArrayLoader([
'test1.html.twig' => $this->getMessageObject()->getBody()
]);
$twig = new \Twig\Environment($loader);
$output = "{% sandbox %}{% include ("'.$twig->getLoader()->getSourceContext('test1.html.twig')->getPath().'") %} {% endsandbox %}"
I'm not able to get any path results from getPath() method. It's empty string.

To sandbox a twig template without using a template file can be done with using chainloader as follows:
Creating a Sandbox Policy:
$tags = ['if'];
$filters = ['filter1', 'filter2'];
$methods = [
'Customer' => ['getCustomer'],
'Person' => ['getPerson']
];
$properties = [];
$functions = ['max'];
$policy = new \Twig\Sandbox\SecurityPolicy($tags, $filters, $methods, $properties, $functions);
$sandbox = new \Twig\Extension\SandboxExtension($policy);
To Sandbox a Twig Template without Creating A Twig File:
$loader1 = new \Twig\Loader\ArrayLoader([
'test1.html.twig' => "{% if 1==1 %} Tag Test {%endif%} test content here"
]);
$loader2 = new \Twig\Loader\ArrayLoader([
'sandbox.html.twig' => '{% sandbox %}{% include "test1.html.twig" %} {% endsandbox %}'
]);
$loader = new \Twig\Loader\ChainLoader([$loader1, $loader2]);
$twig = new \Twig\Environment($loader);
$twig->addExtension($sandbox);
$renderedOutput = $twig->render('sandbox.html.twig');

Related

Twigpress: Can't pass variables to twig template

I am using Twigpress with Wordpress. According to the Twigpress doc, you can pass variables to the template with twigpress_render_twig_template($vals = array(), $template = false, $echo = true).
I'm trying to pass variables to the template with the following code but it doesn't work. What am I doing wrong?
single.php:
$vals = array( 'foo' => 'bar' );
twigpress_render_twig_template($vals);
single.twig:
{{ vals.foo }} # Does not print anything #
{{ foo }} # Same #
{{ dump(vals) }} # Prints 'null' #
Please enlighten a n00b! Thanks. :)
You have to enable debug in your twig setting.
This is how I do it for my Twig initialization. I put this in a separate file call twig-init.php and just require_once where i need to use twig.
$loader = new Twig_Loader_Filesystem('/blah/twig/templates');
$settings = array(
'cache' => '/blah/twig/compiled',
'debug' => true
);
$twig = new Twig_Environment($loader, $settings);
if ($settings['debug'])
$twig->addExtension(new Twig_Extension_Debug());
When you dump it, you can just do {{ dump() }} to dump everything instead.
In addition, you may need to access your array value via the _context. Try to {{ dump(_context['foo']) }}
If you really want to access the variable via vals, you will have to do the following to your array:
$blah = array('vals' => array('foo' => 'bar'));
Then {{ vals.foo }} will work.
See: http://twig.sensiolabs.org/doc/functions/dump.html

Nested menu with parameters in Symfony and KNPmenu

I am struggling with Symfony2 and Knpmenu to build a menu that handles:
breadcrumbs
routing with dynamic parameters
rendering of separate menus starting with different children
My Menu/Builder.php file looks like this (the extra bits like navbar, pull-nav etc are part of the mopa_bootstrap extension that handles the rendering using bootstrap classes):
namespace My\AppBundle\Menu;
use Knp\Menu\FactoryInterface;
class Builder
{
public function mainMenu(FactoryInterface $factory, array $options)
{
$menu = $factory->createItem(
'root', array(
'navbar' => true,
'pull-right' => true,
)
);
// Main Menu -> Config
// no link here, it's just a placeholder
$dropdown = $menu->addChild(
'Config', array(
'dropdown' => true,
'caret' => true,
)
);
// Menu -> Config -> User
$dropdown2 = $dropdown ->addChild(
'User', array(
'route' => 'user',
)
);
// Secondary Menu -> Edit (but child of Menu -> Config -> User)
$dropdown2->addChild(
'Edit',
array(
'route' => 'user_edit',
'routeParameters' => array('name' => $options['id']),
)
);
The idea is to have a main menu that prints the first two levels only, and a separate menu that gets rendered somewhere else to allow users moving between the edit/delete/whatever views of that specific element being viewed.
What I am trying to achieve, is to have a single structure thus to handle the parenting structure, not only to have sign parents as active in the menu, but also to be able to handle a working breadcrumb structure.
InResources/views/base.html.twig I am calling the main menu like this:
{{ mopa_bootstrap_menu('MyAppBundle:Builder:mainMenu', {depth: 2}) }}
And ideally the sub menu like this:
{% set id = app.request.attributes.get('id') %}
{% if app.request.attributes.get('_route') starts with 'user_' %}
{% set menu = knp_menu_get('MyAppBundle:Builder:mainMenu', ['User'], {'id': id }) %}
{{ knp_menu_render(menu) }}
{% endif %}
However:
knpmenu returns error when rendering the main menu, as $options['id'] isn't defined
I still can't render the secondary menu (therefore by passing the parameter 'User') - the page just returns a black output in that block
Is this approach correct?
I'm using "knplabs/knp-menu": "2.0.*#dev" and "symfony/symfony": "2.5.*"
As it turns out, using this method allowed to access effectively $request and add the conditional in the MenuBuilder.php file to prompt the sub-sub-items only under certain conditions.
The final code looks like this:
// Menu/MenuBuilder.php
namespace My\AppBundle\Menu;
use Knp\Menu\FactoryInterface;
use Symfony\Component\HttpFoundation\Request;
class MenuBuilder
{
private $factory;
/**
* #param FactoryInterface $factory
*/
public function __construct(FactoryInterface $factory)
{
$this->factory = $factory;
}
public function createMainMenu(Request $request)
{
$menu = $this->factory->createItem(
'root', array(
'navbar' => true,
'pull-right' => true,
)
);
// Main Menu -> Config
// no link here, it's just a placeholder
$dropdown = $menu->addChild(
'Config', array(
'dropdown' => true,
'caret' => true,
)
);
// Menu -> Config -> User
$dropdown2 = $dropdown ->addChild(
'User', array(
'route' => 'user',
)
);
// Secondary Menu -> Edit (but child of Menu -> Config -> User)
// Skip this part if we are not in the relevant page, thus to avoid errors
if (false !== strpos($request->get('_route'), 'user_') && $request->get('id')):
$dropdown2->addChild(
'Edit',
array(
'route' => 'user_edit',
'routeParameters' => array('id' => $request->get('id')),
)
);
endif;
And the services configuration file:
// Resources/config/services.yml
services:
my_app.menu_builder:
class: My\AppBundle\Menu\MenuBuilder
arguments: ["#knp_menu.factory"]
my_app.menu.main:
class: Knp\Menu\MenuItem
factory_service: my_app.menu_builder
factory_method: createMainMenu
arguments: ["#request"]
scope: request
tags:
- { name: knp_menu.menu, alias: main }
And then the Resources/views/base.html.twig template:
// Main menu
{% set menu = knp_menu_get('main', ['Config', 'Users'], {'id': id }) %}
{{ knp_menu_render(menu) }}
// Secondary menu
{% if app.request.attributes.get('_route') starts with 'user_' %}
{% set menu = knp_menu_get('main', ['Config', 'Users'], {'id': id }) %}
{{ mopa_bootstrap_menu(menu, {'automenu': 'navbar'}) }}
{% endif %}
The solution seems to work, but if you have a better approach please do let me know!

Is it possible to embed twig in Sonata Adminform field options?

Is it possible to add twig directives to field options in a Sonata Admin field option?
$fileFieldOptions = array('required' => false, 'data_class' => null);
if ($image && ($webPath = $image->getS3PathToImage())) {
// get the container so the full path to the image can be set
$container = $this->getConfigurationPool()->getContainer();
$fullPath = 'http://'.$webPath;
// add a 'help' option containing the preview's img tag
//$fileFieldOptions['help'] = '<img src="'.$fullPath.'" class="admin-preview" />';
$fileFieldOptions['help'] = "<img src='{{ '/relative/path/to/image.jpg' | imagine_filter('gi_thumb') }}' />";
I tried safe => true as suggested here but it does not seem to work with form fields.

Symfony2 form in base.html.twig

I am new to Symfony2 and I am just trying some stuff to learn Symfony2.
Currently, I have a few controllers which can perfrom some simple tasks. A simple crud controller for example.
Al my pages extend a base.html.twig file and insert their block into it. Now I want to add a login part to my base file, because I want to be able to login at every page. I was thinking to use the formbuilder for this, but this is php code which can not be in a html.twig file. I have absolutly no idea how to handle this. Can someone push me in the right direction of give my some working example?
Thanking in advance
Actions:
public function loginFormAction(Request $request){
// create a task and give it some dummy data for this example
$message = "";
$account = new Account();
$account->setEmail('login#login.nl');
$form = $this->createFormBuilder($account)
->add('email', 'text')
->add('wachtwoord', 'text')
->add('login', 'submit')
->getForm();
$form->handleRequest($request);
if ($_POST != null && $_POST['form']['email'] != null) {
$repository = $this->getDoctrine()->getRepository('AcmeBlogBundle:Account');
$email =$_POST['form']['email'];
$wachtwoord =$_POST['form']['wachtwoord'];
$foundAccount = $repository->findOneByemail($email);
if (!$foundAccount) {
$message = "Ingevuld email en wachtwoord komen niet overeen.";
}else if($foundAccount->getWachtwoord() == $wachtwoord){
$session = $request->getSession();
$session->set('name', $foundAccount->getVoornaam() . " " . $foundAccount->getAchternaam());
$session->set('email', $foundAccount->getEmail());
//return $this->redirect($this->generateUrl('melding', array('melding' => 'mooiman')));
//return $this->render('AcmeBlogBundle:Account:melding.html.twig', array('melding' => 'Hallo, '.$foundAccount->getVoornaam() . " " . $foundAccount->getAchternaam()));
$message = 'Hallo, '.$foundAccount->getVoornaam() . " " . $foundAccount->getAchternaam();
$form = $this->createFormBuilder()
->add('logout', 'submit')
->getForm();
return $this->render('AcmeBlogBundle:Account:login.html.twig', array('form'=> $form->createView(), 'message' => $message));
}else{
$message = "Ingevuld email en wachtwoord komen niet overeen.";
}
}
return $this->render('AcmeBlogBundle:Account:login.html.twig', array('form'=> $form->createView(), 'message' => $message));
}
public function logoutFormAction(Request $request){
// create a task and give it some dummy data for this example
$session = $request->getSession();
$message = 'Hallo, '.$session->get('name');
$form = $this->createFormBuilder()
->add('logout', 'submit')
->getForm();
$form->handleRequest($request);
if ($_POST != null) {
$account = new Account();
$account->setEmail('login#login.nl');
$form = $this->createFormBuilder($account)
->add('email', 'text')
->add('wachtwoord', 'text')
->add('login', 'submit')
->getForm();
$session->set('name', null);
$session->set('email', null);
$message = "U bent nu uitgelogd";
}
return $this->render('AcmeBlogBundle:Account:login.html.twig', array('form'=> $form->createView(), 'message' => $message));
}
Base.html.twig:
{% if(app.session.get('name') == null) %}
{{ render(controller('AcmeBlogBundle:Account:loginForm')) }}
{% else %}
{{ render(controller('AcmeBlogBundle:Account:logoutForm')) }}
{% endif %}
You can include whole output of other controller/action in template. Just include this in your taplate:
<div id="sidebar">
{{ render(controller('YourBundle:User:login')) }}
</div>
This will inject whole output of YourBundle/UserController/loginAction to the #sidebar div. Remember that YourBundle/UserController/loginAction template can't extend other twig templates and hould be simple.
You can read more about embedding controllers here http://symfony.com/doc/current/book/templating.html#embedding-controllers
[EXAMPLE]:
An form action:
/**
* #Route("/contact", name="contact")
*/
public function contactAction(Request $request){
$form = $this->createFormBuilder()
->setAction($this->generateUrl('contact'))
->add('email', 'email', array(
'constraints' => array(
new \Symfony\Component\Validator\Constraints\Email()
)
))
->add('add', 'submit')
->getForm()
;
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isValid()) {
//do whatever you want and redirect
return $this->redirect($this->generateUrl('homepage'));
}else{
// this must be a full page (extending from base tamplate)
// that show form and errors
return $this->render(
'YourBundle:ControllerName:contact_faild.html.twig',
array('form' => $form->createView())
);
}
}
return $this->render(
'YourBundle:ControllerName:contact.html.twig',
array('form' => $form->createView())
);
}
There must be action URL in form "->setAction($this->generateUrl('contact'))" because form should be handled on separate request.
Next a form template (YourBundle:ControllerName:contact.html.twig):
{{ form(form) }}
Then we need an extra template for showing form errors when something went wrong. That should extend base template and probably override place where form appear in the first time
A form with errors tempalte (YourBundle:ControllerName:contact_faild.html.twig):
{% extends '::base.html.twig' %}
{% block sidebar -%}
{{ form(form) }}
{% endblock sidebar %}
And base teplate:
(...)
{% block sidebar %}
<div id="sidebar">
{{ render(controller('ClickAdUserBundle:User:contact')) }}
</div>
{% endblock %}
(...)
read this
you will find some html/twig code, put that in a "login.html.twig" and include it where you want
If you're completly new to Symfony2 i can suggest the symfony2 symblock tutorial
You'll work with Doctrine 2, creating Forms and design them, Twig Layouts and all the other stuff while creating a little blog.
Taken from the example, you would create the form in your controller like this
// src/Blogger/BlogBundle/Controller/PageController.php
public function contactAction()
{
$enquiry = new Enquiry(); //This creates an Instance of the Entity you need for the form
$form = $this->createForm(new EnquiryType(), $enquiry);
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// Perform some action, such as sending an email
// Redirect - This is important to prevent users re-posting
// the form if they refresh the page
return $this->redirect($this->generateUrl('BloggerBlogBundle_contact'));
}
}
return $this->render('BloggerBlogBundle:Page:contact.html.twig', array(
'form' => $form->createView()
));
}
and the form as .html.twig would look like this
{# src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig #}
{% extends 'BloggerBlogBundle::layout.html.twig' %}
{% block title %}Contact{% endblock%}
{% block body %}
<header>
<h1>Contact symblog</h1>
</header>
<p>Want to contact symblog?</p>
<form action="{{ path('BloggerBlogBundle_contact') }}" method="post" {{ form_enctype(form) }} class="blogger">
{{ form_errors(form) }}
{{ form_row(form.name) }}
{{ form_row(form.email) }}
{{ form_row(form.subject) }}
{{ form_row(form.body) }}
{{ form_rest(form) }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
I hardly suggest working through this tutorial to have a nice start with symfony2

Symfony2 & Twig : display all fields and keys

I have a problem with Symfony2 and Twig: I don't know how to display all fields of my entity which is loaded dynamically. Here is my code (displays nothing!!)
Controller :
public function detailAction($id)
{
$em = $this->container->get('doctrine')->getEntityManager();
$node = 'testEntity'
$Attributes = $em->getRepository('TestBetaBundle:'.$node)->findOneById($id);
return $this->container->get('templating')->renderResponse('TestBetaBundle:test:detail.html.twig',
array(
'attributes' => $Attributes
));
}
detail.html.twig :
{% for key in attributes %}
<p>{{ value }} : {{ key }}</p>
{% endfor %}
Don't settle for just the public properties! Get the private/protected as well!
public function detailAction($id){
$em = $this->container->get('doctrine')->getEntityManager();
$node = 'testEntity'
$Attributes = $em->getRepository('TestBetaBundle:'.$node)->findOneById($id);
// Must be a (FQCN) Fully Qualified ClassName !!!
$MetaData = $em->getClassMetadata('Test\Beta\Bundle\Entity\'. $node);
$fields = array();
foreach ($MetaData->fieldNames as $value) {
$fields[$value] = $Attributes->{'get'.ucfirst($value)}();
}
return $this->container
->get('templating')
->renderResponse('TestBetaBundle:test:detail.html.twig',
array(
'attributes' => $fields
));
}
OK. What you are trying to do cannot be done with a Twig for loop over your attributes object. Let me try to explain:
The Twig for loop iterates over an ARRAY of objects, running the inside of the loop for each of the objects in the array. In your case, $attributes is NOT an array, it is an OBJECT which you retrived with your findOneById call. So the for loop finds that this is not an array and does not run the inside of the loop, not even once, that is why you get no output.
The solution proposed by #thecatontheflat does not work either, as it is just the same iteration over an array, only that you have access to both the keys and values of the array, but since $attributes is not an array, nothing is accomplished.
What you need to do is pass the template an array with the properties of the $Attributes object. You can use the php get_object_vars() function for this. Do something like:
$properties = get_object_vars ($Attributes);
return $this->container->get('templating')->renderResponse('TestBetaBundle:test:detail.html.twig',
array(
'attributes' => $Attributes
'properties' => $properties
));
And in the Twig template:
{% for key, value in properties %}
<p>{{ value }} : {{ key }}</p>
{% endfor %}
Take into account that this will only show the public properties of your object.
For Symfony3
$em = $this->getDoctrine()->getEntityManager();
$MetaData = $em->getClassMetadata('TestBetaBundle:Node');
$fields = $MetaData->getFieldNames();
return $this->render('test/detail.html.twig', array('fields'=>fields));
You should change it to
{% for key, value in attributes %}
<p>{{ value }} : {{ key }}</p>
{% endfor %}

Resources