How do I migrate the old header, annotation, and footer templates to the new single template format?
Previously, this plugin used three different template files. For the sake of this example, let's call the content of each template <header content>
, <annotation content>
, and <footer content>
. To migrate to the new single file template format all you need to do is create new markdown file that contains:
<header content>
{% persist "annotations" %}
<annotation content>
{% endpersist %}
<footer content>
You can view the data available to templates using the Data Explorer
command in Obsidian's command pallette.
Obsidian Zotero Integration uses the Nunjucks templating language. Nunjucks is a robust templating language, but also has a learning curve.
A basic template might looks something like:
## {{title}}
### Formatted Bibliography
{{bibliography}}
{% if abstractNote %}
### Abstract
{{abstractNote}}
{% endif %}
And a basic annotation template:
{% for annotation in annotations %}
{% if annotation.annotatedText %}
> {{annotation.annotatedText}}
> {% endif %}
> {% if annotation.comment %}
> {{annotation.comment}}
{% endif %}
{% endfor %}
Please see the Nunjucks docs for more detail on templating.
Templates can reside anywhere in your Obsidian vault. The path to the template is supplied in the import settings.
In the data explorer, you'll notice that annotations, tags, creators, and other values look something like:
The square brackets next to annotations
here means that this is a list of annotations. To format a list you can use what's called a for
loop (see the docs on this here). Which looks like:
{% for annotation in annotations %}
...do something...
{% endfor %}
Let's break this down a bit. Speaking this out loud, you might say, "for each annotation in the annotations list, '...do something...'". This will then loop through each item in the list.
annotations
specifically refers the list provided by the Template Data, and annotation
(singular) is what we're calling the current annotation, but we can call it whatever we want, for example:
{% for a in annotations %}
...do something...
{% endfor %}
But how do we access the fields of each annotation? This can be done using dot notation:
{% for a in annotations %}
{{a.annotatedText}}
{% endfor %}
Here, a
is the current annotation and annotatedText
is one field of this annotation. You can access all fields of the annotation this way:
{% for a in annotations %}
{{a.annotatedText}}
{{a.color}}
{{a.colorCategory}}
{{a.page}}
...ect...
{% endfor %}
Finally, there are special values that nunjucks provides. For example loop.first
and loop.last
will tell you if you are on the first item in a list or the last item. This can be useful if you're delimiting items in a list. Take tags, for example.
{% for t in tags %}{{t.tag}}{% if not loop.last %}, {% endif %}{% endfor %}
This will output each tag and place a comma after each except the last tag in the list, resulting in something like: Fear conditioning, Learning and memory, Long-term memory
Each time you import data from the same Zotero entry, it will overwrite the existing markdown file. You can prevent this using the persist
template tag.
This can be used to create a section for notes:
## {{title}}
### Notes
{% persist "notes" %}{% if isFirstImport %}
- [ ] first thing
- [ ] second thing
{% endif %}things to add each time you import
{% endpersist %}
Which will create a markdown file that looks like:
## The Boring Billion, a slingshot for Complex Life on Earth
### Notes
%% begin notes %%
- [ ] first thing
- [ ] second thing
things to add each time you import
%% end notes %%
Any content added between %% begin notes %%
and %% end notes %%
will not be overwritten.
Content between {% if isFirstImport %}
and {% endif %}
will not be overwritten. But things to add each time you import
will be appended to the end of the notes section each time you import as follows:
- [ ] first thing
- [ ] second thing
things to add each time you import
things to add each time you import
things to add each time you import
...
You can also use this to import only the annotations that were added since the last import.
{% persist "annotations" %}
{% set newAnnotations = annotations | filterby("date", "dateafter", lastImportDate) %}
{% if newAnnotations.length > 0 %}
### Imported: {{importDate | format("YYYY-MM-DD h:mm a")}}
{% for a in newAnnotations %}
> {{a.annotatedText}}
{% endfor %}
{% endif %}
{% endpersist %}
This would then allow you to add block IDs to annotations, edit annotations or annotation comments, and add additional notes to annotations.
Templates can be split into multiple files if that makes organization easier for you. You can include those files in your main template using obsidian links:
{% include "[[link to markdown file]]" %}
Note: if you want to include other markdown files in a for
loop, you need to use asyncEach
instead of for
:
{% asyncEach a in annotations %}
{% include "[[annotation template]]" %}
{% endeach %}
All dates support formatting via Moment. See the moment format reference for more details.
Allows filtering a list in several ways.
Reduce a list down to only the items where a property starts with the specified parameter:
{% set important = annotations | filterby("comment", "startswith", "important") %}
Reduce a list down to only the items where a property ends with the specified parameter:
{% set important = annotations | filterby("comment", "endswith", "(important)") %}
Reduce a list down to only the items where a property contains the specified parameter:
{% set tagged = annotations | filterby("comment", "contains", "#my-tag") %}
Reduce a list down to only the items where a date property occurs after the specified date parameter:
{% set annots = annotations | filterby("date", "dateafter", lastExportDate) %}
Reduce a list down to only the items where a date property occurs before the specified date parameter:
{% set previousAnnots = annotations | filterby("date", "datebefore", lastExportDate) %}
Allows modify an object.
{% set annot = annot|setAttribute('annotatedText', text) %}
It can be invaluable in complex scenarios where you need to change one annotation based on another. For instance, when some annotations ("yellow") represent sentences or paragraphs, and other annotations ("green") are highlighted terms within these paragraphs. By using this filter, you can transform occurrences of green annotations inside previous yellow ones into links.
{%- for entry in annotations -%}
{%- if entry['colorCategory'] === "Green" -%}
{%- if loop.index0 > 1 -%}
{%- set conceptIndex = loop.index0 -%}
{%- set findPrev = false -%}
{%- for i in range(1, loop.index0) -%}
{%- if findPrev === false -%}
{%- set prevAnnot = annotations[conceptIndex - i] -%}
{%- if prevAnnot['colorCategory'] === "Yellow" -%}
{%- set concept = entry['annotatedText'] -%}
{%- set text = prevAnnot['annotatedText'].replace(concept, '[[' + concept + ']]') -%}
{%- set prevAnnot = prevAnnot|setAttribute('annotatedText', text) -%}
{%- set findPrev = true -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
An example of highlighting a text fragment within the text and the same fragment in the exported note:
other expressions circulated at the time, from the [[sociology of translation]], to the [[anthropology of science and technology,]] to the [[sociology of mediation,]] marking the plural and fluctuating character of the intellectual project(s).