How to make Twig template include file in same bundle? - symfony

I'm relatively new to Symfony. I have a bundle with twig templates that are automatically loaded with annotations in a directory structure like:
src/bundle/Resources/views/Default/
One of my templates has a big chunk of code repeated a bunch of times (with a few minor changes each instance) that I think doing an include a few times formatted like this:
{% include 'form_include.html' with {'foo': 'bar'} %}
with different variables for each instance should work well. But the debugger is telling me that it's looking for the include file in
/app/Resources/
But the template is really specific to this bundle and I wouldn't want it kept elsewhere. I tried using the ../../src.... method to specify its location with no luck. Is there a way to do this?

You can provide a path using the bundle's name:
{% include 'YourBundleNameBundle:Default:form_include.html.twig' with {
'foo': bar
} %}
Where each part is separated by : and:
YourBundleNameBundle corresponds to src/YourBundleNameBundle/Resources/views/
Default corresponds to the /Default directory in this folder
form_include.html.twig corresponds to form_include.html.twig in this folder
So, the 'YourBundleNameBundle:Default:form_include.html.twig' value will load the src/bundle/Resources/views/Default/form_include.html.twig file.
This syntax works for the different Twig functions: include, extends, etc.
It is useful for allowing templates inheritance.

Have you considered a macro instead?
From: http://twig.sensiolabs.org/doc/tags/macro.html
Macros are comparable with functions in regular programming languages. They are useful to put often used HTML idioms into reusable elements to not repeat yourself.
Here is a small example of a macro that renders a form element:
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
Macros differs from native PHP functions in a few ways:
Default argument values are defined by using the default filter in the macro body;
Arguments of a macro are always optional.
If extra positional arguments are passed to a macro, they end up in the special varargs variable as a list of values.
But as with PHP functions, macros don't have access to the current template variables.
You can pass the whole context as an argument by using the special _context variable.
Macros can be defined in any template, and need to be "imported" before being used (see the documentation for the import tag for more information):
{% import "forms.html" as forms %}
The above import call imports the "forms.html" file (which can contain only macros, or a template and some macros), and import the functions as items of the forms variable.
The macro can then be called at will:
<p>{{ forms.input('username') }}</p>
<p>{{ forms.input('password', null, 'password') }}</p>
If macros are defined and used in the same template, you can use the special _self variable to import them:
{% import _self as forms %}
<p>{{ forms.input('username') }}</p>
When you define a macro in the template where you are going to use it, you might be tempted to call the macro directly via _self.input() instead of importing it; even if seems to work, this is just a side-effect of the current implementation and it won't work anymore in Twig 2.x.
When you want to use a macro in another macro from the same file, you need to import it locally:
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
{% macro wrapped_input(name, value, type, size) %}
{% import _self as forms %}
<div class="field">
{{ forms.input(name, value, type, size) }}
</div>
{% endmacro %}

Related

Exporting imported macros

I want to be able to import macros from one central file, but have each macro in a separate file.
My current approach is having a macros.twig, which imports all the other macros.
{% from 'foo-macro.twig' import foo %}
{% from 'bar-macro.twig' import bar %}
{% from 'baz-macro.twig' import baz %}
But when importing it in example.twig:
{% from 'macros.twig' import foo, bar, baz %}
{{ foo('foo arg') }}
{{ bar('bar arg') }}
{{ baz('baz arg') }}
Error: Macro "foo" is not defined in template "macros.twig".
I also tried including each macro in macros.twig via include. Same issue.
I there a way I could not find in the docs?
Here's a TwigFiddle
You need to import the macro in each file you want to use it.
This has been changed in Twig 2.0 and has been documented
Macros
As of Twig 2.0, macros imported in a file are not available in child templates anymore (via an include call for instance). You need to import macros explicitly in each file where you are using them.
source

SaltStack: how do I repeat other states with context?

I created a complex state for API service, it involves git checkouts, python venv, uwsgi, nginx, etc etc. It works fine.
Now I would like to turn it into a template and execute it several times per minion, with variables supplied from pillar - i.e something like.
{% for apiserver in pillar.apiservers %}
include apiserver_template.sls, locals: apiserver.config
{% endfor %}
where apiserver_template will work with context supplied to it, with apiserver.config having all config data for each API instance. I know syntax is wrong but hopefully I am communicating the idea - ideally, something like executing ruby partials with supplying local variables.
How is it done properly in saltland?
It sounds to me like Jinja Macro is something you want to use for this. You can find more information about usage here: https://docs.saltstack.com/en/2015.8/topics/development/conventions/formulas.html#jinja-macros
In short what you will have in your case may look like:
{% macro api_server(git_repo, python_venv_path, python_venv_requirements) %}
{{python_venv_path}}:
virtualenv.managed:
- system_site_packages: False
- requirements: salt://{{python_venv_requirements}}
{{git_repo}}:
git.latest:
- name: {{git_repo}}
{% endmacro %}
Assuming you have a pillar apiservers where each api server has git_repo, python_venv_path and python_venv_requirements values, you can use the macro like this:
{% for server in salt.pillar.get('apiservers', []) %}
{{ api_server(server['git_repo'], server['python_venv_path'], server['python_venv_requirements']) }}
{% endfor %}
If you want - you can also put a macro in a separate state file and then import a marco as a regular salt resource.
Please also not that instead of pillar.apiservers I used salt.pillar.get('apiservers', []). This is a safer way to get data from pillar. If for some reason a pillar is unavailable - the later code will result in empty dict instead of failure in first case.

Twig safe html does not work inside macro

Using a twig macro on safe content seems to remove the safe flags
{% macro identity(value) %}
{{ value }}
{% endmacro %}
{{ "<br>"| raw }}
{{ _self.identity( "<br>"| raw) }}
{% autoescape false %}
{{ _self.identity( "<br>"| raw) }}
{% endautoescape %}
The code above show only 2 <br>. Is there a way to disable escaping, or marking my content as safe without editing my twig macro ?
You are indeed correct that you must put the raw filter in the macro in order to keep whatever tags inside the macro from escaping their input and this would, at least in my opinion, be the expected behavior.
In Twig, macros are not function or filters, they are simply shortcuts that keep you from repeating boilerplate tags and code. It helps, when you create and use a macro, to think of writing the whole code each time you will use it i.e.
Instead of thinking of your macro as a call {{ _self.identity( "<br>"| raw ) }} actually think that what you are writing is {{ {{ "<br>"| raw }} }} because you are passing the output of the macro's input as a literal, not the expression you have written.
If you would like your macros to operate using the provided input as a template, rather than, as designed, operating on the output of whatever you pass in, you may look at enabling the template_from_string function.

Passing variables between salt states

In Saltstack, I have the following use case:
There is a state redis.sls which can be included by other states. The result of redis.sls should be configured differently, depending on the state which included redis.sls.
For example:
redis.sls:
--------
{% if x==1 %}
#do something
{% else %}
#do something else
{% endif %}
state_a.sls
-----------
{% set x=1 %}
include:
- redis
state_b.sls
-----------
{% set x=2 %}
include:
- redis
But x is not recognized in *state_a* and *state_b*
I also tried setting a pillar value with something like this:
{{salt['pillar.set']('x', 1)}}
but that didn't work either.
Any other ideas?
I'd like to hear what the experts say but I have a similar use case. What I did was use the jinja template to extend a base template then my child templates populated the variables.
{% extends "base.template.sls" %}
{% block x %}1{% endblock %}
Only problem might be that you now have to call state_a and state_b separately but you can always put them in a comma separated list if you want both called.
Make your redis state a jinja macro.
redis.sls:
--------
{% macro redis(x) %}
{% if x==1 %}
#do something
{% else %}
#do something else
{% endif %}
{% endmacro %}
state_a.sls
-----------
{% from 'redis.sls' import redis %}
{{ redis(1) }}
state_b.sls
-----------
{% from 'redis.sls' import redis %}
{{ redis(2) }}
For clarity redis.sls should be renamed to redis.jinja here.
This, and many other ways of managing state customization is best explained in the Salt Formulas conventions guide. Specifically the part about Jinja macros
Note that your if x==1 logic can be probably avoided altogether, take a look at the 'better' version of haproxy example in the guide.
It looks like you want to parameterize a state based on either what depends on it, or where it is used. That sounds like whatever is setting the parameter(s) on which the redis.sls state is supposed to mutate, depends on a specific configuration of redis.
To me, that seems like there are more than one distinct states in which redis could be, and that some of your states depend on one state of redis, and others of your states depend on other states of redis.
So, give the installation of redis one state, and the specific configurations of redis would each get their own state. Your state_a could depend on redis_state_1 and your state_b would in turn depend on redis_state_2. Both redis_state_1 and redis_state_2 would depend on redis. It seems to me that the parameter passing you're asking about would be less explicit.
SALT.STATES.ENVIRON might work for you:
set_secret_key:
environ.setenv:
- name: SECRET_KEY
- value: ABC123!##abc
- update_minion: True
[..]
settings_secret_key:
file.replace:
- name: {{ salt['pillar.get']('data:source_folder') }}superlists/settings.py
- pattern: "SECRET_KEY =.+$"
- repl: 'SECRET_KEY = os.environ["SECRET_KEY"]'

Symfony2 HTML in the trans twig filter

I use the Symfony2.1 and have the default config.yml
Documentation said:
{# but static strings are never escaped #}
{{ '<h3>foo</h3>'|trans }}
But if I copy and paste it into the my empty template (without any additional autoescapes or another) I got the escaped string <h3>foo</h3>. What I do wrong?
Try it with the twig raw filter:
{{ '<h3>foo</h3>' | trans | raw }}
However, do not use the raw filter if you are processing any user input! It allows for cross-site-scripting attacks, according to the creators of Symfony. See this similar question for a secure but more tedious alternative.
Holding HTML stuff in translations is wrong, because translators usually break it. But if you really need it:
{% trans %}<h3>foo</h3>{% endtrans %}
https://github.com/symfony/symfony/issues/2713#issuecomment-12510417

Resources