How to extend a core template macro

In some cases we need to customise part of a template, the template block mechanism is not granular enough. I noticed that in several cases there is some widget template, which contains a lot of macros. I don’t want to override the whole template, which implies all the widgets in the template. I need to override a couple of line in the macro.
Is there a way to import the core-template from the plugin-template?

An example:

from Indico-core you have a template A with the following content:

{% from 'B.html' import foo %}
{% macro A %} 
   do_this
   do_that
  {{ B() }}
{% endmacro %}
{% macro B %} .... {% endmacro %}

I want to change do_this, so in my template_override i would need something like:

{% from 'core:A' import B %}
{% macro A %} 
   do_something_else
   do_that
   {{ B() }}
{% endmacro %}

In this way if from another core-template someone uses {% from ‘A’ import A %} will be call the plugin-template A macro, which uses other parts of core-template A macro

Is it possible? is there some special convention to invoke explicitly a core-template?

Thanks

1 Like

If you enable CUSTOMIZATION_DEBUG in indico.conf you should see the information on the original path of each overridden template.

If the template is called as a.html, then you’ll import the original macro from '~a.html'.

Good, thank You very much, it worked, it worked!
Is there a way to force a jinja-template to import a macro starting with underscore?

or alternatively, is there a way to replace a single macro from another template?

no, i don’t think so

Hi – an additional question to the discussion above:

  • I need to adjust/redefine the {% block title %} of the CORE template in our plugin: indico/modules/designer/templates/list.html
  • I have added another list.html template in our plugin’s template_overrides folder, trying several path locations within that, as well as several template extends ref e.g. {% extends ‘list.html’ %} – but this template is never picked up (always only the core list.html is executed).

What is missing? What should the file name, location, extends ref name, etc be for this template to be picked up and extend the core template?

Thanks for any clarification!

core/designer/list.html should work

and to inherit from the original one use {% extends '~core/designer/list.html' %}

Thanks – that combo was one of the many I have tried, but it does not work, the core list.html! What is the logic for knowing what the extending template filename and location should be, as well as how to refer to the extended template?

The logic is to use whatever path the CUSTOMIZATION_DEBUG output gives you :slight_smile:

Actually, it looks like there’s a bug, since the output contains this:

Customizable: core/designerlist.html (original: /home/adrian/dev/indico/src/indico/modules/designer/templates/list.html, reference: ~designerlist.html) 

Of course that’s wrong - it sohuld be core/designer/list.html. But for now you can use the incorrect path and it should work. But once we fixed it (2.2.1) you will need to change it to core/designer/list.html.

Ah, that one has worked :wink:

Very interesting logic though – who is CUSTOMIZATION_DEBUG ;-?

https://docs.getindico.io/en/latest/config/settings/#CUSTOMIZATION_DEBUG

Thanks for that. Maybe my last remark was not so clear – the logic of a program is usually given to it by a programmer :wink:

The actual logic is to use /<core|(plugins/<pluginname>)>/<template_path> - but since the path is not 100% obvious in all cases (generally it’s <modulename>/<path inside that module's templates dir> except for indico/web/templates/ where there is no modulename prefix) it’s easier to just look at those log messages.

Here’s the fix for it:

We’ll include it in 2.2.1, but since this is a minor and very limited issue (only happens for templates in the designer module) we’ll wait with releasing that version until there are more important fixes. Anyway, since you are doing development you will already have the fix if you run from the 2.2-maintenance branch.

Awesome, thanks! Updated to this and it works as expected.

While we’re in this specific template area – just noticed that the list.html template puts out invalid html, specifically referring to multiple html id=“template-list” attributes… maybe can also look into this? Thanks!

Oops! That shouldn’t happen. Do you want to send a PR against 2.2-maintenance to fix it? (in that case please do verify that the AJAX updates from data-update="#template-list" in _list.html still work correctly afterwards)

For the record, carried this last issue over to: https://github.com/indico/indico/issues/4036

Sorry if I insist on the macro extension via child templates, but this is what a plugin should do in principle.
Is there a way to override a macro defined in a template from the core code-base without surrounding it with an {% if not macro %}?

core-foo.html

{% macro bar() %}Core - bar{% end %}
{% macro foo() %}Core - foo{% end %}

plugin-foo.html

{% extends '~foo.html' %}

{% macro bar() %}
     PLUGIN - bar
     {{ foo() }}
{% endmacro %}

this works only if change the core template surrounding the definition with an “if statement”. in file core-foo.html as follows:

{% if not bar %}
     {% macro bar() %}Core - bar{% end %}
{% endif %}

{% if not foo %}
     {% macro foo() %}Core - foo{% end %}
{% endif %}

This is a suggested strategy I found surfing the web. This is not applied anywhere in the core code-base.

I think this is standard jinja behavior

In case you are dealing with a template that only exports macros, don’t {% extend %} it but fully replace it, import the original macros (with the module-style import or aliased names), and then create your own macros that call the original ones (or not, depending on what you want to change).

Because it’s pretty ugly and overriding macros is not that common… You could possibly see if you can hack the Jinja core to automatically include this condition around all macro definitions. This would probably require subclassing the Jinja CodeGenerator (flask_pluginengine.templating.PluginCodeGenerator in our case), overriding visit_Macro to wrap it with custom code generating the conditional, and setting it as the code_generator_class of the jinja_environment on the Flask instance.