├── LICENSE ├── README.md ├── chrome.manifest ├── chrome └── content │ ├── addPaper.js │ ├── addPaperDialog.html │ ├── options.html │ ├── options.js │ ├── overlay.xul │ ├── references.html │ └── semanticZotero.js └── install.rdf /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tomáš Daniš 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Semantic Zotero Plugin 2 | Semantic Zotero integrates Zotero with Semantic Scholar to fetch and display references related to a selected paper. You can then add them to your library directly from Zotero along with the full-text PDF. For now, the visuals are pretty barebones, I will update when I have time. 3 | 4 | ![refs](https://github.com/AgiNetz/semantic-zotero/assets/29703385/d99ca766-182e-4d10-8c5d-5ee5199615dd) 5 | 6 | ## Usage 7 | 8 | ### Show References: 9 | 10 | To fetch references for a given paper, right click on it, select "Semantic Zotero" and then "Show references" 11 | ![show-ref](https://github.com/AgiNetz/semantic-zotero/assets/29703385/bb52135d-fa33-4f5f-b3c5-5c674b103d24) 12 | 13 | ### Configure options 14 | 15 | In the Zotero Menu bar, choosing Tools -> Semantic Zotero Options allows you to configure options such as custom Semantic Scholar API key 16 | 17 | ## Installation 18 | 19 | 1. Download the .xpi file from one of the releases 20 | 2. In Zotero menu bar, select Tools -> Add-ons -> Settings icon on upper right -> Install add-on from file 21 | -------------------------------------------------------------------------------- /chrome.manifest: -------------------------------------------------------------------------------- 1 | content semanticZotero chrome/content/ 2 | overlay chrome://zotero/content/zoteroPane.xul chrome://semanticZotero/content/overlay.xul 3 | 4 | -------------------------------------------------------------------------------- /chrome/content/addPaper.js: -------------------------------------------------------------------------------- 1 | async function populateDialog() { 2 | const subcollectionsDiv = document.getElementById("subcollections"); 3 | const tagsDiv = document.getElementById("tags"); 4 | 5 | // Fetch subcollections and tags from Zotero 6 | var zp = Zotero.getActiveZoteroPane(); 7 | const subcollections = await Zotero.Collections.getByLibrary(zp.getSelectedLibraryID()); 8 | const tags = await Zotero.Tags.getAll(); 9 | 10 | // Populate subcollections 11 | for (let subcollection of subcollections) { 12 | const checkbox = document.createElement("input"); 13 | checkbox.type = "checkbox"; 14 | checkbox.value = subcollection.key; 15 | checkbox.id = "subcollection_" + subcollection.id; 16 | 17 | const label = document.createElement("label"); 18 | label.htmlFor = checkbox.key; 19 | label.textContent = subcollection.name; 20 | 21 | subcollectionsDiv.appendChild(checkbox); 22 | subcollectionsDiv.appendChild(label); 23 | subcollectionsDiv.appendChild(document.createElement("br")); // New line 24 | } 25 | 26 | // Populate tags 27 | tags.forEach((tag, index) => { 28 | const checkbox = document.createElement("input"); 29 | checkbox.type = "checkbox"; 30 | checkbox.value = tag.tag; 31 | checkbox.id = "tag_" + index; 32 | 33 | const label = document.createElement("label"); 34 | label.htmlFor = checkbox.id; 35 | label.textContent = tag.tag; 36 | 37 | tagsDiv.appendChild(checkbox); 38 | tagsDiv.appendChild(label); 39 | tagsDiv.appendChild(document.createElement("br")); // New line 40 | }); 41 | } 42 | 43 | async function onAddButtonClick() { 44 | const selectedTags = getSelectedTags(); 45 | const selectedCollections = getSelectedCollections(); 46 | 47 | const item = window.arguments[0]; 48 | const reference = window.arguments[1]; 49 | 50 | document.getElementById("statusMessage").style.display = "block"; 51 | 52 | try { 53 | await addToCollection(reference, item, selectedCollections, selectedTags); 54 | window.opener.SemanticZotero.handleReferenceAdded(reference); 55 | } catch(error) { 56 | console.error("Failed to add item:", error); 57 | document.getElementById("statusMessage").innerText = error; 58 | } 59 | 60 | window.close(); 61 | } 62 | 63 | function getSelectedTags() { 64 | const tagsDiv = document.getElementById("tags"); 65 | const checkboxes = tagsDiv.querySelectorAll("input[type='checkbox']:checked"); 66 | const selectedTags = Array.from(checkboxes).map(cb => cb.value); 67 | return selectedTags; 68 | } 69 | 70 | function getSelectedCollections() { 71 | const collectionsDiv = document.getElementById("subcollections"); 72 | const checkboxes = collectionsDiv.querySelectorAll("input[type='checkbox']:checked"); 73 | const selectedCollections = Array.from(checkboxes).map(cb => cb.value); 74 | return selectedCollections; 75 | } 76 | 77 | async function addToCollection(reference, item, selectedCollections, selectedTags) { 78 | var newItem = new Zotero.Item('preprint'); 79 | var arxivID = reference.externalIds ? reference.externalIds["ArXiv"] : null; 80 | var pdfUrl = undefined; 81 | if(arxivID) { 82 | pdfUrl = "https://arxiv.org/pdf/" + arxivID + '.pdf'; 83 | } else if(reference.isOpenAccess) { 84 | pdfUrl = reference.openAccessPdf.url; 85 | } 86 | // Set fields for the item using the provided reference data 87 | newItem.setField('title', reference.title); 88 | newItem.setField('date', reference.publicationDate || reference.year); 89 | newItem.setField('abstractNote', reference.abstract); 90 | if (arxivID) { 91 | newItem.setField('repository', "arXiv"); 92 | newItem.setField('archiveID', "arXiv:" + arxivID); 93 | } 94 | newItem.setField('url', reference.url); 95 | 96 | // Set authors 97 | if (reference.authors && reference.authors.length) { 98 | let creators = reference.authors.map(author => ({ firstName: author.name.split(' ').slice(0, -1).join(' '), lastName: author.name.split(' ').slice(-1).join(' '), creatorType: "author" })); 99 | newItem.setCreators(creators); 100 | } 101 | 102 | // Once the paper is added, add tags 103 | for (let tag of selectedTags) { 104 | newItem.addTag(tag); 105 | } 106 | 107 | newItem.setCollections(selectedCollections); 108 | 109 | // Save the new item to the database 110 | await newItem.saveTx(); 111 | 112 | //Make items related 113 | let relateItems = Zotero.Prefs.get('SemanticZotero.relateItems') === undefined ? true : Zotero.Prefs.get('SemanticZotero.relateItems'); 114 | if (relateItems) { 115 | item.addRelatedItem(newItem); 116 | await item.saveTx(); 117 | 118 | newItem.addRelatedItem(item); 119 | await newItem.saveTx(); 120 | } 121 | 122 | // Attach the PDF if available 123 | if (pdfUrl) { 124 | var zp = Zotero.getActiveZoteroPane(); 125 | var libraryID = zp.getSelectedLibraryID(); 126 | await Zotero.Attachments.importFromURL({ 127 | url: pdfUrl, 128 | parentItemID: newItem.id, 129 | contentType: 'application/pdf', 130 | libraryID: libraryID 131 | }); 132 | } 133 | return newItem; 134 | } 135 | 136 | document.getElementById("addButton").addEventListener("click", onAddButtonClick); 137 | 138 | 139 | window.onload = function() { 140 | populateDialog(); 141 | }; 142 | -------------------------------------------------------------------------------- /chrome/content/addPaperDialog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Add Paper 5 | 6 | 7 |
8 |

Select Subcollections

9 |
10 |
11 | 12 |

Select Tags

13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /chrome/content/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Semantic Zotero Options 6 | 19 | 20 | 21 | 22 | 23 | 27 | 31 |
32 | 33 | 34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /chrome/content/options.js: -------------------------------------------------------------------------------- 1 | var SemanticZoteroOptions = { 2 | saveOptions: function() { 3 | let apiKey = document.getElementById('apiKey').value; 4 | let relateItems = document.getElementById('relateItemsCheckbox').checked; 5 | 6 | Zotero.Prefs.set('SemanticZotero.apiKey', apiKey); 7 | Zotero.Prefs.set('SemanticZotero.relateItems', relateItems); 8 | 9 | window.close(); 10 | }, 11 | 12 | cancel: function() { 13 | window.close(); 14 | }, 15 | 16 | loadOptions: function() { 17 | let apiKey = Zotero.Prefs.get('SemanticZotero.apiKey') || ''; 18 | let relateItems = Zotero.Prefs.get('SemanticZotero.relateItems') === undefined ? true : Zotero.Prefs.get('SemanticZotero.relateItems'); 19 | 20 | document.getElementById('apiKey').value = apiKey; 21 | document.getElementById('relateItemsCheckbox').checked = relateItems; 22 | } 23 | } 24 | 25 | window.onload = SemanticZoteroOptions.loadOptions; 26 | -------------------------------------------------------------------------------- /chrome/content/overlay.xul: -------------------------------------------------------------------------------- 1 | 3 | 4 |