We want to change the way we do the frontends in symfony and we'd like some level of "reflection":
We want the system to be able to detect "Template displayProduct.html.twig needs xxxx other file, defines yyyy block and uses the zzzz variable".
I would like a command similar to:
php bin/console debug:template displayProduct.html.twig
that responds something like this:
Template: displayProduct.html.twig
Requires: # And tells us what other files are needed
- widgetPrice.html.twig
- widgetAvailability.html.twig
Defines: # And tells us what {% block xxxx %} are defined
- body
- title
- javascripts_own
- javascripts_general
Uses these variables: # <= This is the most important for us now
- productTitle
- price
- stock
- language
We are now visually scanning complex templates for needed variables and it's a killer. We need to automatically tell "this template needs this and that to work".
PD: Functional tests are not the solution, as we want to apply all this to dynamically generated templates stored in databases, to make users able to modify their pages, so we can't write a test for every potential future unknown template that users will write.
Does this already exist somehow?
Related
I use ntc-templates for cisco output. In all of cisco's wisdom, from 16.x to 17.x IOS-XE, they have changed the output of 'show license status'
Is there a way to use one textfsm template with conditional matches to determine which part of the template to use?
Currently using 2 templates, which works, but adds 'overhead' as this method doesn't work with the netmiko usetextfsm implementation as it is based on the command being run.
Imagine that we have a single yaml file that describes roles of users in variety of systems. it may look like this:
username:
systemA:
- roleA
- roleB
systemB:
- roleC
I would like to use this file a source for all the minions to populate the list of users and roles for their respective systems. So the minion of systemA would have only this in it's pillar:
username:
- roleA
- roleB
I'm not sure that I want to make it kinda default pillar and rip parts out of it depending on the minion using jinja. But other options, like regenerating pillars using python from this file on every change or storing this data in DB and using ext_pillar, looks even worse to me. But may be I'm just don't see something obvious.
Thanks!
User roles aren't usually secret, so this doesn't need any transformation, and it doesn't need to be in pillars in the first place.
Simply lookup using the current minion id (or whatever "systemA" is) in your states and/or map.jinja:
{% set roles = data["username"][grains["id"]] %}
I was wondering if it is possible with Symfony 3.5 to use multiple translation files for a single language when using yml files.
Currently I have something like this:
AppBundle/Resources/translations/messages.en.yml
AppBundle/Resources/translations/messages.de.yml
which contains all my translations in either language. However I was wondering if it was possible to change this to the following structure:
AppBundle/Resources/translations/en/products.yml
AppBundle/Resources/translations/en/invoices.yml
AppBundle/Resources/translations/de/products.yml
AppBundle/Resources/translations/de/invoices.yml
I have been looking but I have been unable to find some kind of solution for this. I got it working for splitting up my routes.
AppBundle/Resources/config/routing.yml
appbundle_routes:
resource: '#AppBundle/Resources/config/routing'
type: directory
Inside that folder I got all my routes split like:
AppBundle/Resources/config/routing/products.yml
AppBundle/Resources/config/routing/users.yml
AppBundle/Resources/config/routing/invoices.yml
I was wondering if it was possible to achieve the same thing with translations?
Symfony's Translator requires files to by named in format domain.locale.loader. In case you have messages.en.yml:
messages is the default name of domain, you can also specify eg. invoices
en is the locale
yml is specifying YAML loader will be used
So your proposed use is not possible to achieve with standard set of configs and functionality. However, you can split your translations to different domain files. So paths would be:
AppBundle/Resources/translations/products.en.yml
AppBundle/Resources/translations/invoices.en.yml
And when you are using translator you specify the domain in which the translation should be looked for:
$translator->trans('translated.key', [], 'invoices');
Or in Twig:
{{ 'translated.key'|trans({},'invoices') }}
Preface: in ez4 i remember there was a tpl function to read ini settings, we used to use this to pass specific locations or id's with which we could then render certain content.
In ezplatform I am now doing the same thing but by using the PreContentViewListener (in the PreContentViewListener read a yml file and pass into the view as params), but this doesn't feel like the correct way as the PreContentViewListener doesn't always get triggered, in custom controllers for example.
Question
Is there a native way to read yaml files from within twig templates? After searching the docs and available packagists i cannot find anything :/
If your needs are simple (i.e. reading container parameters), you can also use eZ Publish config resolver component which is available in any Twig template with ezpublish.configResolver.
You can specify a siteaccess aware parameter in format <namespace>.<scope>.<param_name>, like this:
parameters:
app.default.param.name: 'Default param value'
app.eng.param.name: 'English param value'
app.cro.param.name: 'Croatian param value'
where default, eng and cro are different eZ Publish scopes.
You can then use the config resolver to fetch the parameter in current scope with:
{{ ezpublish.configResolver.parameter('param.name', 'app') }}
If you have Legacy Bridge installed, this even falls back to legacy INI settings if no Symfony container parameter exists:
{{ ezpublish.configResolver.parameter('SiteSettings.SiteName', 'site') }}
Disclaimer: Some say that using config resolver is bad practice, but for simpler usecases it is okay, IMO.
Have a look to our CjwPublishToolsBundle.
https://github.com/cjw-network/CjwPublishToolsBundle
https://github.com/cjw-network/CjwPublishToolsBundle/blob/master/Services/TwigConfigFunctionsService.php
Here we have 2 wrapper twig functions
{{cjw_config_resolver_get_parameter ( 'yamlvariablename', 'namespace default ezsettings') }}
=> ezpublish siteaccessmatching
{{cjw_config_get_parameter( 'mailer_transport' )}}
=> core symfony yaml reader without siteaccess
You could do a lot of things in eZ 4 and not always really good for your application design. ezini was able to read the configuration from the template but now in eZ Platform and by extension Symfony you need to respect more common patterns. IMO the view should not be that smart.
Then injecting variables to the view from a listener (PreContentViewListener or your own) is not a bad idea.
You can also use the Twig Globals that could allow you to do 2 global things:
inject variables (1)
inject a service (2)
Look here: https://symfony.com/doc/current/templating/global_variables.html
(2): please don't inject the service container globally it is bad
(1): I don't remember if the Twig Globals are Site Access aware, if not injecting your own service (2) to manage access to the config might be better.
And finally, I think that the use case is not a common one:
we used to use this to pass specific locations or id's with which we could then render certain content.
Most of the time it is a bad idea to pass ids coming from the configuration to render something, it is much better to organize the content structure to let you pull the location you want using the PHP API. (no id in configuration no hassle with dev, stage, preprod and prod architecture)
Chef has a very elaborate (maybe too much so) scheme for cookbooks to provide default values of attributes. I think Puppet does something similar with class parameters where defaults usually go into params.pp. With Salt, I've seen:
specifying default value in dictionary/pillar lookups.
the grains.filter_by merging of default attribute values with user-provided pillar data (e.g., map.jinja in apache-formula)
in a call to file.managed state, specifying default attribute values as the defaults parameter and user-specified pillar data as context.
Option 1 seems to be the most common, but has the drawback that the template file becomes very hard to read. It also requires repeating the default value whenever the lookup is done, making it very easy to make a mistake.
Option 2 feels closest in spirit to Chef's approach, but seems to expect the defaults broken down into a dictionary of cases based on some filtering attribute (e.g., the OS type recorded in grains).
Option 3 is not bad, but puts attribute defaults into the state file, instead of separating them into their own file as they are with option 2.
Saltstack's best practices doc endorses Option 2, except that it doesn't address how to merge defaults with user-specified values without having to use grains.filter_by. Is there any way around it?
Note: The behavior of defaults.get changed in version 2015.8, and so the method described here no longer works. I am leaving this answer for users of older versions and will post a similar method for current versions.
defaults.get coupled with a defaults.yaml file should do what you want. Assume your formula tree looks like this:
my-formula/
files/
template.jinja
init.sls
defaults.yaml
# my-formula/init.sls
my-formula-conf-file:
file.managed:
- name: {{ salt['defaults.get']('conf_location') }}
- source: {{ salt['defaults.get']('conf_source') }}
... and so on.
# defaults.yaml
conf_location: /etc/my-formula.conf
conf_source: salt://my-formula/files/template.jinja
# pillar/my-formula.sls
my-formula:
conf_location: /etc/my-formula/something.conf
This will end with the configuration file placed at /etc/my-formula/something.conf (the pillar value) using salt://my-formula/files/template.jinja as the source (the default, for which no pillar override was supplied).
Note the unintuitive structure of the pillar and defaults files; defaults.get expects defaults.yaml to have its values at the root of the file, but expects the pillar overrides to be in a dictionary named after the formula, because consistency is for the weak.
The documentation for defaults.get gives its example using defaults.json instead of defaults.yaml. That works but I find yaml much more readable. And writable.
There is a bug using defaults.get from inside a managed template rather than within the state file, and as far as I know it's still open. It can still be made to work; the workaround is behind the link.
The behavior of defaults.get changed in 2015.8, possibly due to a bug. This answer describes a compatible method of getting the same results in (at least) 2015.8 and later.
Suppose your formula tree looks like this:
something/
files/
template.jinja
init.sls
defaults.yaml
# defaults.yaml
conf_location: /etc/something.conf
conf_source: salt://something/files/template.jinja
# pillar/something.sls
something:
conf_location: /etc/something/something.conf
The idea is that formula defaults are in defaults.yaml, but can be overridden in pillar. Anything not provided in pillar should use the value in defaults. You can accomplish this with a few lines at the top of any given .sls:
# something/init.sls
{%- set pget = salt['pillar.get'] %} # Convenience alias
{%- import_yaml slspath + "/defaults.yaml" as defaults %}
{%- set something = pget('something', defaults, merge=True) %}
something-conf-file:
file.managed:
- name: {{ something.conf_location }}
- source: {{ something.conf_source }}
- template: jinja
- context:
slspath: {{ slspath }}
... and so on.
What this does: The contents of defaults.yaml are loaded in as a nested dictionary. That nested dictionary is then merged with the contents of the something pillar key, with the pillar winning conflicts. The result is a nested dictionary containing both the defaults and any pillar overrides, which can then be used directly without concern to where a particular value came from.
slspath is not strictly required for this to work; it's a magic variable that contains the directory path to the currently-running sls. I like to use it because it decouples the formula from any particular location in the directory tree. It is not normally available from managed templates, which is why I pass it on as explicit context above. It may not work as expected in older versions, in which case you'll have to provide a path relative to the root of the salt tree.
The downside to this method is that, so far as I know, you can't access the final dictionary with salt's colon-based nested-keys syntax; you need to descend through it one level at a time. I have not had problems with that (dot syntax is easier to type anyway), but it is a downside. Another downside is the need for a few lines of boilerplate at the top of any .sls or template using the technique.
There are a few upsides. One is that you can loop over the final dictionary or its sub-dicts with .items() and the Right Thing will happen, which was not the case with defaults.get and which drove me insane. Another is that, if and when the salt team restores defaults.get's old functionality, the defaults/pillar structure suggested here is already compatible and they'll work fine side by side.