Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

include parameters supported? #382

Closed
bonesoul opened this issue Mar 5, 2015 · 24 comments
Closed

include parameters supported? #382

bonesoul opened this issue Mar 5, 2015 · 24 comments

Comments

@bonesoul
Copy link

bonesoul commented Mar 5, 2015

i'm considering a switch from swig to nunjucks and have been reading if all my requirements are covered. one thing though, can i use parameters with include?

like with swig i can use this;

    {% set parameters = {'contentType': 'tournament'} %}
    {% include "../common/editor/content.htm" with parameters only %}

and then in content.htm can reference it

{{ contentType }}

is this possible with nujucks as the documentation doesn't mention about it? http://mozilla.github.io/nunjucks/templating.html#include

@ricordisamoa
Copy link
Contributor

AFAICS all parameters are automatically passed to included templates, but it is not possible to only pass some of them:

{% set contentType = 'tournament' %}
{% include "../common/editor/content.htm" %}

and then in content.htm

{{ contentType }}

@airtonix
Copy link

Actually you can't even do that.

We've hit a problem in our patternlab where the variables are not available to the included file.

To be brutally honest, the macro tag should be merged into the include tag.

edit: can't access objects by reference, only literals

@jlongster
Copy link
Contributor

edit: can't access objects by reference, only literals

Can you expand on this? I can't reproduce this at all

@jlongster
Copy link
Contributor

Please re-open if you have a reproducible test case. This works:

{% set parameters = {'contentType': 'tournament'} %}
{% include "./foo.html" %}

foo.html

parameters: {{ parameters.contentType }}

output: "parameters: tournament"

@legutierr
Copy link

The functionality that seems to be missing from Nunjucks is the following:

{% set somevar = "should not be accessible in include" %}
{% include "file.html" with { "parameter" : "value" } only %}

file.html

parameter: {{ parameter }}
should be inaccessible: {{ somevar }}

The output should be:

parameter: value
should be inaccessible:

If you want certain values of the context to be accessible inside the included file, and other values to be excluded from its scope, you need to have something akin to Twig and Swig's with...only syntax. A use case for this feature might be where the included template is user-supplied and you want to make sure that it is not able to access context beyond a specific whitelist.

This functionality is also available in Twig and Django (albeit with a slightly different syntax in the case of Django). Jinja provides the {% include "file.html" without context %} syntax, but doesn't allow arguments to be passed in in that case.

@carljm
Copy link
Contributor

carljm commented Jul 28, 2015

@legutierr IMO in Jinja/nunjucks those use cases are intended to be handled with macros instead of includes.

@legutierr
Copy link

@carljm I agree that include and macro have some overlap in functionality. There are some relevant differences between the two tags, however--differences that make macro a poor replacement for include...with...only.

What it mostly comes down to is that macro is a poor tool to use when trying to sandbox user-provided (or otherwise less-trusted) templates--the use case that I mentioned above.

If a user defines a macro, they would have to give it the name that you intend to import. And because the order and number of a macro's parameters matters, a user must be careful to list all of the parameters in the right order.

For a typical "mail merge" type application, these requirements are the difference between asking the user to create the following template on one hand...

{{ address }}
Dear {{ full_name }}:
...

...and the following template on the other...

{% macro mylettertext(address, first_name, last_name, full_name, date, price, ...) %}
{{ address }}
Dear {{ full_name }}:
...
{% endmacro %}

My users would have a real problem getting this right.

Could it be possible to make it easier for the user by calling include inside of a macro (hiding the macro concept from the user)? Unfortunately, in both Jinja and Nunjucks, macros are able to access the context of the template that contains macro definition. To illustrate, while the proposed syntax will provide code only with access to key...

{% set protected = "must not access" %}
{% include code with { "key" : "val" } only %}

...wrapping include in a macro under the current regime cannot prevent code from getting access to protected...

{% set protected = "must not access" %}
{% macro encapsulate(key) %}
{% include code %} {# fail: protected is accessible from inside code #}
{% endmacro %}
{{ encapsulate("val") }}

This approach is also much more verbose. Now, one possible alternative might be to implement a sort of "include.html" macro file as a sort of intermediary...

{# include.html #}
{% macro include(code, key) %}
{% include code %}
{% endmacro %}

...which, in turn, would be imported...

{% from "include.html" import include %}
{% set protected = "must not access" %}
{{ include(code, val) }}

...but this approach is even more verbose and convoluted. In this case, code will only have access to val and its own source/filename--effectively replicating the include...with...only functionality--but it takes five lines and two separate templates to do it.

I understand that Nunjucks typically tracks closest to Jinja, rather than Django and Twig, but I think that the fact that both Django and Twig implement this feature shows that it is useful to people. Sure, Django doesn't have the macro tag to compete with include, but Twig does, Swig does, and so does Pongo2, the Go implementation of the language. And all three allow you to specify parameters to include.

@carljm
Copy link
Contributor

carljm commented Jul 29, 2015

@legutierr If I were implementing the "sandbox" or mail-merge type functionality you're discussing, I would render the user template directly from code (thereby allowing me to fully control the context and prevent any interaction with other templates) and then pass the rendered user content as part of the context to my application templates as needed.

That said, I'm not strongly opposed to adding this feature. I'm just personally only interested in nunjucks' Jinja-compatibility, so I won't spend time reviewing or merging non-Jinja features. But if another core dev wants to add it, I won't stand in the way.

@carljm
Copy link
Contributor

carljm commented Jul 29, 2015

(It's also important to note if we're using the word "sandbox" that it is not safe to allow untrusted users to write nunjucks templates for your application. You have to at least trust the users not to be actively malicious. Nunjucks is not designed to support true secure sandboxing; a template author can almost certainly find a way to DoS your system if motivated enough.)

@legutierr
Copy link

@carljm You're right, I think I might have been a little loose in my use of the term "sandbox".

Yes, for the typical mail-merge-type functionality, separately rendering would probably make sense. What I myself am doing is a little different than that. My typical users would better be described as designers working under contract, so "less trusted" rather than "untrusted". And I agree that it would be relatively trivial for an actively malicious user to DoS nunjucks, which is why if I ever intend to give template authorship rights to end-users I would use a library like tripwire for node.js or interruptingcow for python in the case of jinja, in addition to taking other preventative steps.

(I believe, however, that a privilege escalation-type attack should not be possible from inside nunjucks or jinja; I'd be curious as to your thoughts on that.)

Now, regarding your personal interest in focusing only on Nunjucks and Jinja compatibility...there are a few areas where Nunjucks' is currently incompatible:

  • {% include contextvar %} where contextvar is a compiled template, or other object with a render() method implemented, rather than a file location.
  • {% extends contextvar %} where contextvar is a compiled template, or other object with a render() method implemented (fixed with the same patch as the above).
  • {% include "file.html" without context %} which prevents the included template from accessing the context of the containing template.

If I produced pull requests that incorporated this functionality into Nunjucks, would you be able & willing to work with me to review them and get them merged?

@carljm
Copy link
Contributor

carljm commented Jul 29, 2015

Are you sure about without context? I'm pretty sure that's already implemented (it may be only in master, not the last official release).

Regarding the other two, sure, I'd be happy to take a look at a PR.

@legutierr
Copy link

@carljm I did a code search for without context and the only thing I found was the use of without context with import but not include.

https://github.com/mozilla/nunjucks/search?p=1&q=without+context&type=Code

Looking through the implementation itself, I also don't see where Nunjucks would be handling include...without context.

I'll ping you back when I have the pull requests together.

@carljm
Copy link
Contributor

carljm commented Jul 29, 2015

Ah yes, I was remembering seeing the pull request for adding support to import.

Sounds good. FYI I'm leaving for a ten-day vacation tomorrow so if you're quick with the PRs I won't be as quick with the reviews :-)

@asabhaney
Copy link

I believe a PR was already opened for this a while ago. It would need some adaptation to work with the latest code base, but might provide a good starting point: #277

I am in favour of this feature for a different reason. I think it makes rendering an array of objects cleaner and less verbose. For example, this code might be used when rendering a page server-side:

{% for comment in comments %}
   {% include 'comment.html' with comment %}
{% endfor %}

Then later on, when a new comment is added after the initial page render, just the comment partial can be rendered client side quite nicely:

nunjucks.render('comment.html', newComment);

@legutierr
Copy link

@carljm The pull requests should be ready for your review when you get back :)

@asabhaney I don't actually think you need the feature being discussed here. If all you do is this:

{% for comment in comments %}
  {% include 'comment.html' %}
{% endfor %}

Then comment should still be available inside of 'comment.html', and you should still be able to re-use the template. Please correct me if I'm wrong.

@asabhaney
Copy link

@legutierr That's true, it's not necessary - it's more of a convenience. Like many nunjucks users, I come from using swig.js, where being able to do this was arguably one of its nicest features. I do realize that nunjucks aims to follow jinja, and not Twig, but nunjucks has taken subtle liberties where it makes certain use cases easier for its users.

Using your example, inside of comment.html, one would have to explicitly access properties of comment, as follows:

<span class="date">{{ comment.date }}</span>

It would be nice to simply write the following in comment.html:

<span class="date">{{ date }}</span>

I find that having to write comment. every time a property is required is a little redundant, since the scope of the partial view file implies that the context is a comment object.

@legutierr
Copy link

@asabhaney Ah, yes, I see how that would be more convenient. Good point.

@obarannikov
Copy link

This feature will be very helpful for rendering similar information from different datasources. For example we may have a template to render a list of articles:

{% for article in articles %}
    <h1>{{article.title}}</h1>
    <p>{{article.short_text}}</p>
{% endfor %}

and then use this as include for following:

<h1>Latest articles:</h1>
{% include 'articles_list' with {articles: latest_articles} %}
<h1>Popular articles:</h1>
{% include 'articles_list' with {articles: popular_articles} %}

@slavafomin
Copy link

Plus one for with properties statement. It will really help to migrate from Twig.

@dviedma
Copy link

dviedma commented Aug 31, 2016

Hi, any progress on this?

@timkelty
Copy link

timkelty commented Sep 1, 2016

As someone who is attempting to share server-side (Twig) templates and JS rendered (Nunjucks) templates, this would be really great. (Twig includes have with and only params)

@zetareticoli
Copy link

Any news on this?

@luishdez
Copy link

it's open here #539

@yuvilio
Copy link

yuvilio commented Dec 30, 2020

Still another technique. Setting variables in a partial and then importing it before calling an include.

You could set variables somewhere, say content/hero-vars.nunj

{% set hero_image_title %}
Stories of Success and Creativity
{% endset %}

{% set hero_img = "/files/hero-01.jpg" %}

Your calling template imports them as a variable: and then calls the partial template that needs them:

  {# page sections  pass content (c) to each partial #}
  {% import "content/hero-vars.nunj" as c %}
  {% include "partials/hero-image.nunj" %}

And here's your partial that uses them. Notice it uses the c variable with the variables you set:

<section class="section component-hero-image">

  <img class="hero-img" src="{{c.hero_img}}" alt="">
  <div class="overlay">
    <h1 class="title text-uppercase text-center">{{  c.hero_image_title }}</h1>
  </div>

</section>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests