Zotero Integration – Import Templates?

Hi @mapiq I’m about to get a Boox device and I was worried about the same issue. Did you find a good workaround for this?

Hello @apfelstrudelig, I like very much the way annotations are rendered in callouts! But I’m struggling with 2 things:

  1. All the callouts are grey, are you using some css or special callouts to render them in different colors?
  2. I prefer having the annotations listes in the order they appear in the sources, how can I achieve this? (ie. which lines should I erase or modify to have them unordered?

Thanks! :slightly_smiling_face:

I have been working with this template a little while now, and after returning from fieldwork after a year, and starting to do literature reviews again, I needed to update the buttons plugin.
And here starts my problem, and I hope I can get some imput from you all.

The button template and the meta bind do work, I get updates when hitting the button.
However, there are two problems
When going back to the PDF in zotero, and higlights new parts and when hitting the update button in obsidian, the highlights are placed under the last one instead of grouped.

Secondly; when higlights are removed from the PDF in zotero, it is not removed from the literature note in zotero

I try to illustrate it with these screenshots

After deleting (1stred higlight )and re-higlighting (green, red, yellow) in zotero:

I Guess I should adjust some of the script in the template, which refers to this grouping.
Does some see where I do go wrong?:

{% persist “annotations” %}
{% set annotations = annotations | filterby(“date”, “dateafter”, lastImportDate) -%}
{% if annotations.length > 0 %}
Imported on [[{{importDate | format(“YYYY-MM-DD”)}}]] at {{importDate | format(“HH:mm”)}}

{%- set grouped_annotations = annotations | groupby(“color”) -%}
{%- for color, colorValue in colorValueMap -%}
{%- if color in grouped_annotations -%}
{%- set annotations = grouped_annotations[color] -%}
{%- for annotation in annotations -%}
{%- set citationLink = '[(p. ’ ~ annotation.pageLabel ~ ‘)](zotero://open-pdf/library/items/’ ~ annotation.attachment.itemKey ~ ‘?page=’ ~ annotation.pageLabel ~ ‘&annotation=’ ~ annotation.id ~ ‘)’ %}

{%- if annotation and loop.first %}

Hi, thanks for creating and sharing your very neat template! I am trying to setup the “Update literature note” button but I can’t seem to get it to work. If I run the “Import ‘import format’” (i.e. a Zotero integration shortcut), I can see the additional annotations and the date/time when they were added appear. However, clicking on the button does nothing. Is there anything wrong with my meta bind command:

actions:

  • type: command
    command: templater-obsidian:07 - Templates/runImport.md

Hey everyone.

I’ve created a patchwork template for my taste by copy-pasting what I like from the others. It works just fine so far but I’m trying to add one more feature. In the Related Works callout, I’d like to have a column for the authors.

Here is the relevant part:

{%- if relations.length > 0 %}
{{ "" }}
> [!example]- Related Works ({{ relations.length}}):  
>
> | Title | Author | Citekey | Links |
> | --- | --- | --- | --- |
{%- for r in relations %}
> | {{formatCell(r.title)}} | {{r.authors}} | [[{{r.citekey}}]] | [Zotero]({{r.desktopURI}}){%- for rAttachment in r.attachments | filterby("path", "endswith", ".pdf") %} [PDF](zotero://open-pdf/library/items/{{rAttachment.itemKey}}){% endfor %} |
{%- endfor -%}
{{ "" }}
{%- endif %}

What I seek for is a working code for {{r.authors}}. Is there anyone knowing how to fix this? Maybe @Feralflora ?

Here is the full template code:

---
citekey: {{citekey}}
aliases:
- "{%- if creators -%}
        {{creators[0].lastName or creators[0].name }}
        {%- if creators|length == 2 %} & {{creators[1].lastName or creators[1].name}}{% endif -%}
        {%- if creators|length > 2 %} et al.{% endif -%}
    {%- endif -%}
    {%- if date %} ({{date | format("YYYY")}}){% endif -%} 
    {%- if shortTitle %} {{shortTitle | safe}} {%- else %} {{title | safe}} {%- endif -%}"{% if itemType == "bookSection" %}
book-title: "{{bookTitle | replace('"',"'")}}"{% endif %}
title: "{{title | replace('"',"'")}}"
{%- set camelRegex = r/([a-z])([A-Z])/g %}
{%- for type, creators in creators | groupby("creatorType") %} 
{% if creators.length > 1 %}{{type | replace(camelRegex, "$1 $2") | lower | trim}}s:{%- for creator in creators %}{% if creator.name %}
- {{creator.name}}{% else%}
- {{creator.firstName}} {{creator.lastName}} {% endif %}{%- endfor %} {% else -%}
{{type | replace(camelRegex, "$1-$2") | lower | trim}}:{%- for creator in creators %}{% if creator.name %} "{{creator.name}}"{% else%} "{{creator.firstName}} {{creator.lastName}}"{% endif -%}{%- endfor -%}{% endif -%}{% endfor %}
year: {% if date %}{{date | format("YYYY")}}{% endif %}
item-type: {{itemType | replace(camelRegex, "$1 $2") | title | trim}}
publisher: {% if publicationTitle %}"{{publicationTitle}}"{% else %}"{{publisher}}"{% endif %}
{%- if notes.length > 0 -%}
{%- set longShortCutoff = 20 -%}
{%- set shortnotes = [] -%}
{%- set longnotes = [] -%}
{%- for note in notes -%}
{%- if note.note | wordcount <= longShortCutoff -%}
{%- set shortnotes = (shortnotes.push(note.note), shortnotes) -%} 
{%- else -%}
{%- set longnotes = (longnotes.push(note), longnotes) -%}
{%- endif -%}{%- endfor -%}{%- endif -%}
{%- for comment in shortnotes %}
{%- if comment and loop.first %}
comments:
{% endif -%}
- "{{comment|replace('"',"'")| replace("\n"," ")}}"{% endfor %}
tags:{% for t in tags %}
- {{t.tag | replace(r/\s+/g, "-")}}{% endfor %}{% if DOI %}
doi: https://doi.org/{{DOI}}{% endif %}{% if itemType == "book" %}
ISBN: {{ISBN}}{% endif %}
cssclasses: 
- literature-note{% if attachments.length > 0 %}{% for attachment in attachments %}{% if loop.first %}
attachments:{% endif %}
- {{attachment.path}}{% endfor %}{% endif %}
libraryID: {{libraryID}}
---
#### {{title}}
**Author(s):** [[{{authors}}]]
**Date:** {{date | format ("YYYY")}}
**Publisher:** {{publisher}}{{publicationTitle}}
**Format:** {{itemType | replace(camelRegex, "$1 $2") | title | trim}}
**Bibliography:** {{bibliography}}
**Collections**:: {% for collection in collections %}[[{{collection.name}}]]{% if not loop.last %}, {% endif %}{% endfor %}
**Keywords:** {% for t in tags %} [[{{t.tag}}]]{% if not loop.last %}, {% endif %}{% endfor %}
**Tags:** 

{#- handle | characters in zotero strings used in MD -#}
{%- macro formatCell(cellText) -%}
	{{ cellText | replace("|","❕")}}
{%- endmacro -%}

{#- Macros for handling annotations -#}
{%- macro formatAnnotation(annotation, calloutString) -%}
	{%- if annotation.imageRelativePath -%}
- {{calloutString}} ![[{{annotation.imageRelativePath}}]] [Zotero, p. {{annotation.pageLabel}}](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.page}}&annotation={{annotation.id}}){% if annotation.hashTags %} {{annotation.hashTags}}{% endif %} ^{{annotation.id-}}
{% if annotation.comment.substring(2) | trim | length > 0 %}
	- {{annotation.comment.substring(2) | trim}}
{% else %}
{% endif -%}
	{%- elif annotation.comment.substring(2, 3) === "-" -%}
- {{calloutString}} {{annotation.annotatedText}} 
	{%- elif annotation.comment.substring(2, 3) === "+" %} {{annotation.annotatedText}} [Zotero, p. {{annotation.pageLabel}}](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.page}}&annotation={{annotation.id}}){% if annotation.hashTags %} {{annotation.hashTags}}{% endif %} ^{{annotation.id-}}
{% if annotation.comment.substring(3) | trim | length > 0 %}
	- {{annotation.comment.substring(3) | trim}}
{% else %}
{% endif -%}
	{%- elif annotation.annotatedText -%}
- {{calloutString}} {{annotation.annotatedText}} [Zotero, p. {{annotation.pageLabel}}](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.page}}&annotation={{annotation.id}}){% if annotation.hashTags %} {{annotation.hashTags}}{% endif %} ^{{annotation.id-}}
{% if annotation.comment.substring(2) | trim | length > 0 %}
	- {{annotation.comment.substring(2) | trim}}
{% else %}
{% endif -%}
	{%- endif -%}
{%- endmacro %}

### :luc_lightbulb:<mark style="background: #D2B3FFA6;">Key Ideas</mark>
- The author

### :luc_search:<mark style="background: #ADCCFFA6;">Definitions</mark>
- 1. 

### :luc_arrow_left_right:<mark style="background: #BBFABBA6;">Related Issues</mark>
- 1. 

### :luc_expand:<mark style="background: #FF5582A6;">Subtle Points</mark>
- 1. 

### :luc_quote:<mark style="background: #FFF3A3A6;">Key Quotes</mark>
- 1. 
### ...
---
> [!info]- Abstract
> {% if abstractNote %}
> {{abstractNote|replace("\n"," ")}}
> {% endif %}

{%- set headingRegex = r/^#+/ -%}
{%- set titleRegex = r/^#+.*/ -%}
{%- set lineRegex = r/^.*$/m %}
{%- if longnotes.length > 0 -%}
{%- for n in longnotes -%}
{%- if n and loop.first %}

> [!note]- Notes ({{longnotes.length}})
{%- endif %}
>> [!example]- Note {{loop.index}} |{%- if headingRegex.test(n.note) == true %}[{{n.note | replace(n.note,titleRegex.exec(n.note))|replace(headingRegex,"")}}]({{n.uri}}){% else %} [{{lineRegex.exec(n.note | truncate(30))}}]({{n.uri}})
>> {% endif %}
>> {{n.note | replace("\n", "\n>> ")| replace(titleRegex, "")}}{% if n.tags.length > 0 %}
>>
>> Tags:{% for t in n.tags %} #{{t.tag}}{% if not loop.last %}, {% endif %}{% endfor %}{% endif -%}{%- if not loop.last %}
>{%- endif -%}
{%- endfor -%}{%- endif %}

> [!quote]- Highlights ({{ annotations.length}}): 
> {% persist "annotations" %}
> {% set annots = annotations | filterby("date", "dateafter", lastImportDate) -%}
> {% if annots.length > 0 %}
> {% for annot in annots -%}
> 
> {#-**Annotations that start with #, to be turned into markdown style section headers**-#}
> {% if annot.annotatedText and "#" in annot.annotatedText %}
> {{annot.annotatedText|nl2br|lower|title}}
> 
> {#-**Regular text annotations w/ page number and page link**-#}
> {%- elif annot.annotatedText %}
> 
> - <mark style="background: {{annot.color}}">{{annot.annotatedText | nl2br}}</mark>  [(p. {{annot.page}})](zotero://open-pdf/library/items/{{annot.attachment.itemKey}}?page={{annot.page}}) {#**page link**#}
> 
> {%- endif -%}
> 
> {#-**Image annotations**-#}
> {%- if annot.imageRelativePath %}
>  ![[{{annot.imageRelativePath}}]]
> {%- endif %}
> 
> {#-**comment annotations**-#}
> {%- if annot.comment %}
> >{{annot.comment | nl2br}}
>  {%- endif %}
> 
> {%- endfor %}
> {%- endif -%}
> {%- endpersist -%}

{%- if relations.length > 0 %}
{{ "" }}
> [!example]- Related Works ({{ relations.length}}):  
>
> | Title | Author | Citekey | Links |
> | --- | --- | --- | --- |
{%- for r in relations %}
> | {{formatCell(r.title)}} | {{r.authors}} | [[{{r.citekey}}]] | [Zotero]({{r.desktopURI}}){%- for rAttachment in r.attachments | filterby("path", "endswith", ".pdf") %} [PDF](zotero://open-pdf/library/items/{{rAttachment.itemKey}}){% endfor %} |
{%- endfor -%}
{{ "" }}
{%- endif %}


----
### ...

### :luc_fast_forward: <mark style="background: #FF5582A6;">Progress Log</mark>
- 1. 
1 Like

I have been reading along here for some time because I use zotero to manage my literature and obsidian to analyse my annotations and notes in zotero and prepare and sort them for publication.
From the number of posts in this thread I see that there is obviously a need for exchange on the connection between zotero and obsidian, but no real place for such an exchange.
That would be a separate topic for obsidian templates, for example, where working templates and template development issues could be discussed.
People like me don’t need highly differentiated templates in which red and yellow annotations are compared by date. They need very simple templates that import annotations from zotero into obsidian.
It should be possible to set up a directory of templates somewhere that people who don’t speak a programming language can easily access and use.
For me as an obsidian user this would be a fundamental help.

Does anyone know how to update this template so it would group by color? I found this template here on the forum and works very well, but doesn’t group. It uses a CSS snippet as well which I updated a little bit as some of my Zotero colors weren’t picked up.
Non grouping template

---
{% if title %}Title: "{{title}}"{% endif %}
Authors: {{authors}}{{directors}}
{% if publicationTitle %}Publication: "{{publicationTitle}}"{% endif %}
{% if date %}Date: {{date | format("YYYY-MM-DD")}}{% endif %}
citekey: {{citekey}}
tags: {{hashTags}}
---
## {{title}}
**Bibliographie :** {{bibliography}}

**Link to publication :** {{url}}

**Link Zotero :** {{pdfZoteroLink}}

**Tags :** {{hashTags}} 

>[!abstract]+
>*« {{abstractNote}} »*

{% for annotation in annotations -%}
>[!Annotation|{{annotation.color}}]+ 
>{%- if annotation.annotatedText -%}*« {{annotation.annotatedText}} »*([{{annotation.page}}](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.page}}&annotation={{annotation.id}})){% endif %}{% if annotation.imageRelativePath %}![[{{annotation.imageRelativePath}}]]{% endif %}{% if annotation.comment %} 
>
>{{annotation.comment}}{%- endif %}

{% endfor %}

CSS snippet

/* Red */
.callout[data-callout-metadata="#ff6666"],
.callout[data-callout-metadata="#ff4c4c"],
.callout[data-callout-metadata="#ff8080"] {
    --callout-color: 205, 52, 92;
}
/* Green */
.callout[data-callout-metadata="#5fb236"],
.callout[data-callout-metadata="#6fbf47"],
.callout[data-callout-metadata="#4fa224"] {
    --callout-color: 114, 162, 100;
}
/* Blue */
.callout[data-callout-metadata="#2ea8e5"],
.callout[data-callout-metadata="#4caff0"],
.callout[data-callout-metadata="#1e92d1"] {
    --callout-color: 52, 82, 255;
}
/* Purple */
.callout[data-callout-metadata="#a28ae5"],
.callout[data-callout-metadata="#b19ae8"],
.callout[data-callout-metadata="#9377d9"] {
    --callout-color: 157, 131, 242;
}
/* Orange */
.callout[data-callout-metadata="#f19837"],
.callout[data-callout-metadata="#ffa347"],
.callout[data-callout-metadata="#e68b27"] {
    --callout-color: 244, 164, 96;
}
/* Yellow */
.callout[data-callout-metadata="#ffff11"],
.callout[data-callout-metadata="#ffffe0"],
.callout[data-callout-metadata="##fff8dc"],
.callout[data-callout-metadata="#fffacd"],
.callout[data-callout-metadata="#fff700"],
.callout[data-callout-metadata="##ffef00"],
.callout[data-callout-metadata="#ffffd4"],
.callout[data-callout-metadata="#ffffdd"],
.callout[data-callout-metadata="#ffd400"],
.callout[data-callout-metadata="#ffffd4"],
.callout[data-callout-metadata="#ffeb33"],
.callout[data-callout-metadata="#ffe600"] {
    --callout-color: 255, 236, 0;
}
/* Magenta */
.callout[data-callout-metadata="#e56eee"],
.callout[data-callout-metadata="#f080f0"],
.callout[data-callout-metadata="#d154d8"] {
    --callout-color: 255, 33, 255;
}
/* Gray */
.callout[data-callout-metadata="#aaaaaa"],
.callout[data-callout-metadata="#bbbbbb"],
.callout[data-callout-metadata="#999999"] {
    --callout-color: 152, 152, 152;
}

/* Hide callout icons */
.callout-icon {
    display: none;
}

Wow, this is an amazing thread. And a long thread. And a thread with much innovation and change spanning almost three years.

Like many, I’ve cobbled together a template based on snippets from @Feralflora in particular. My issue is, as a non-coder, I don’t know how the template actually works. And that’s not where I want to be as templates are a major building block of an academic workflow.

If anyone finds time and has an interest, I’d be so very grateful for an introductory document or video on coding templates. Or perhaps such basic documentation already exists? If so, I’d be grateful for a pointer.

Thanks very much to all the contributors in this thread

2 Likes

As a start I’ve found a video and related document from Danny Hatcher which are pitched at my level. And of course there’s the Zotero Integration documentation which is clear and quite understandable. The documentation for the template language that the plugin uses, Nunjucks, is more a reference guide to individual template functions for general web use, rather than a ‘How To’ specific to Obsidian templates. Still, it helps in deconstructing the very useful template that @Feralflora has so kindly provided (thank you!).

2 Likes

sure, I can help you with this!

In your template, duplicate this section multiple times, once for each color you want to separate:

{% for annotation in annotations -%}
>[!Annotation|{{annotation.color}}]+ 
>{%- if annotation.annotatedText -%}*« {{annotation.annotatedText}} »*([{{annotation.page}}](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.page}}&annotation={{annotation.id}})){% endif %}{% if annotation.imageRelativePath %}![[{{annotation.imageRelativePath}}]]{% endif %}{% if annotation.comment %} 
>
>{{annotation.comment}}{%- endif %}

{% endfor %}

Next, in each section, change this line

{% for annotation in annotations -%}

to:

{% for annotation in annotations | filterby("colorCategory", "contains", "Blue") -%}

substituting "Blue" in each block for the name of whatever color of annotations you want to have appear in that block.

Instead of “colorCategory”, you could also technically filter by “color”, but this does not work with the zotero-integrated names of colors (Blue, Green, Yellow, Magenta etc.), and you would then need to specify the color exactly as a hexcode value (e.g. “#5fb236”). For readability, I therefore suggest you use colorCategory.

Try using the zotero integration plugin’s data explorer to inspect the properties of the “annotations” object to see what other properties you might filter for. You can also chain filters together to add multiple conditions, e.g. {% for annotation in annotations | filterby("colorCategory", "contains", "Blue") | filterby("type", "contains", "highlight") -%} to only get the blue highlights. More info here.

Happy templating!

请问,目前obsidian支持zotero的版本是哪一种?谢谢

I am getting this error. I don’t know much about programming. Can you help me solve this?

In the Zotero Data Explorer, there is something to select on called “relations” that shows any zotero items you have placed in the “related” field. I can’t figure out how to loop through these so that I can import a link to them in Obsidian. Something like:

{% for item in relations[0].attachments %}
{% item %}
{% endfor %}

I can’t seem to get it to work.

If you want to open the PDF in Zotero, you can do this:

{% for item in relations %}
[{{item.title}}]({{item.attachments[0].pdfURI}})
{% endfor %}

If you want to open the PDF outside Zotero, you can do this:

{% for item in relations %}
[{{item.title}}](file:///{{item.attachments[0].path | replace(" ", "%20")}})
{% endfor %}

For future reference, you need:

  1. To use a {{variable}}, not a {% tag %} to output something.
  2. to loop correctly through the data structure. You were looping through the attachments of the first relation. I think what you needed is the above, looping through all the relations, and then accessing their attachments. If you also need to access multiple attachments for each related item, you can add a nested loop over those.
2 Likes

Feral! You are the master! Thank you.

1 Like

If you have multiple .pdf attachments (say, chapters of a book), what is the best way to loop through those to export the all the annotations? I suppose another strategy would be to merge the .pdfs at the start, then do annotations. Sadly, I didn’t think of that!

At the moment, my import file looks like this:

# Annotations

{% for annotation in annotations -%}
>[!Note|{{annotation.color}}]+ 
>{%- if annotation.annotatedText -%}« {{annotation.annotatedText}} » ([{{annotation.page}}](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.page}}&annotation={{annotation.id}})){% endif %}{% if annotation.imageRelativePath %}![[{{annotation.imageRelativePath}}]]{% endif %}{% if annotation.comment %} 
>
> ---
>
>- {{annotation.comment}}{%- endif %}

{% endfor %}


# Related

{% for item in relations %}
- [{{relations[0].bibliography}}]({{item.attachments[0].pdfURI}})
{% endfor %}


1 Like

Hello! Yesterday I’ve discovered [Feralflora] import template and update template. It worked perfectly. But today i’m getting these problem with newly attached images. And if I delete the whole file and run import again all the new images are attached in the right folder. Can someone help?

Hi QWxleA, thanks a lot for your template, it’s awesome and a game changer to fully integrate the Zotero-Obsidian workflow.

I use the last version of your template (gist / Github).
I have 2 questions, and as a total noob with any piece of code, I’ll need some help…

  1. In my Obsidian vault, I have organized my files with a template for any person I create a file for, as follows : DOE, Joe (NAME, Surname)
    On your template, line 104, there is the following code for authors’names and files :
    > Authors: {% for c in creators %}[[{{c.firstName}} {{c.lastName}}]] {% endfor %}
    Can I change it to something like :
    > Authors: {% for c in creators %}[[{{c.lastName}}, {{c.firstName}}]] {% endfor %}
    to create a file with the “NAME, Surname” name syntaxe, or should I do otherwise ? Any suggestion in such a case ?

  2. Is it possible to add the “update button” that @FeralFlora uses in the template he created ?
    I’ve tried to add the following code at the very beginning of your template, but it broke it with a parse-ish error :slight_smile:
    BUTTON[update-litnote] {% persist "notes" -%} {%- if isFirstImport %}
    (of course, all plugins and parameters are set up in Meta Bind and Templater to be able to use this button)
    Is there a simple way to update a Zotero import file when using your template ? Is it enough to re-import it ?

Thanks a lot !

Does anybody know how to add the file creation date to a zotero integration template, i.e. code that corresponds to the following in templater:


date created: <%tp.file.creation_date()%>

It appears the closest I can come to this is using something like

date created: {{importDate | format("YYYY-MM-DD HH:mm")}}

But the problem with that is that it will get overwriten every time the template is executed (while I want to persist the date when the note was first created).

1 Like

wow, so many great templates to learn from. Helped me out quiet a bit.

But i am here with a question/request, too.

Has anybody any idea how to get the CitationNumber from the GoogleScholarCitationCount-Plugin into the Zotero integration template? Like, how the code would look like?

i would really love to add this number to my properties.