[Share] Copy Zotero link #115
Replies: 18 comments 30 replies
-
Firstly, thank you for your amazing plugins! Can someone implement copying the item link to show the item inside of a specific collection? Preferably automatically the one i selected the item from? I was able to construct the functioning select link using the collection-key found in the browser version, but every way i try to implement grabbing it into the script it gives the error "collection is not defined". Can someone more knowledgable help? I just recently moved to Zotero and it is still quite disorienting to see everything i ever saved at once when i click on an item link in obsidian. I can only really function inside of collections and smaller projects. |
Beta Was this translation helpful? Give feedback.
-
Thank you for your efforts! Could someone also implement the ability to copy multiple item links when multiple items are selected? |
Beta Was this translation helpful? Give feedback.
-
This action supports both |
Beta Was this translation helpful? Give feedback.
-
if (linkAction === "auto") {
if (item.isPDFAttachment() && !item.isAnnotation()) {
linkAction = "open-pdf"; Should it be this instead? if (linkAction === "auto") {
if (item.isPDFAttachment() || item.isAnnotation()) {
linkAction = "open-pdf"; |
Beta Was this translation helpful? Give feedback.
-
If I drag and drop a highlight annotation from the PDF viewer to a Zotero note, I get a citations that looks like:
The Is it possible to get a |
Beta Was this translation helpful? Give feedback.
-
Thank you very much. This is great. |
Beta Was this translation helpful? Give feedback.
-
The copy collection link is not working anymore for me. When I run the exact script as above, and use it from a right-click on a collection, I get the link to an item in the collection instead of the collection link.
Is it a Zotero bug? |
Beta Was this translation helpful? Give feedback.
-
I am having the same problem @mjtoraval reported Dec. 10. I also thought I recalled testing this successfully when the script was first written, so I'm not sure what's happened. When I right-click on a collection and choose this script, it creates a link to the first item in the collection, rather than to the collection itself. I use these links internally--to connect one item to a collection of items that relate or support it. |
Beta Was this translation helpful? Give feedback.
-
@mjthoraval and @DonnaCoxBaker, if I understand correctly, you want a link to the collection--not an item in the collection, but the collection itself. If that's correct, I think this will fix the issue. That said, I wasn't able to reproduce the TypeError mentioned here.
|
Beta Was this translation helpful? Give feedback.
-
Zotero7, how to set up an action implementation for actions&tags? After adding an entry to a category, it will automatically add a label with the same category name. When removing this entry, it will not be deleted |
Beta Was this translation helpful? Give feedback.
-
I am not able to copy the link to a saved search collection or any special collection (My Publications, Duplicate Items, Retracted Items, Unfiled Items). Is it possible to get it to work there also? |
Beta Was this translation helpful? Give feedback.
-
I don't know why, but this isn't working for me anymore - in that it creates a link (eg zotero://open-pdf/library/items/CXTEB3BQ) but clicking just switches to Zotero (or opens it), but doesn't open a pdf or take me to an item. Is this a Zotero problem (I'm using version 7.0.0-beta.63+965149fe0)? |
Beta Was this translation helpful? Give feedback.
-
I want to ask that how we can paste a link shown as a picture? |
Beta Was this translation helpful? Give feedback.
-
I adapted this script to also copy the annotation text and comment, when used on annotations. Furthermore, I added the It's necessary to leave Demo: From Zotero to ObsidianIn the demo, the Obsidian plugin Pandoc Reference List is rendering the citekeys as formatted citations in my chosen citation style (APA 7th edition), and opening a tooltip with the formatted bibliography when hovering over the citekey. Other changes
The adapted script// @author windingwind, garth74, FeralFlora
// @link https://github.com/windingwind/zotero-actions-tags/discussions/115
// @usage Select an item in the library and press the assigned shortcut keys
// @update Mon, 22 Jan 2024 00:10:18 GMT (by new Date().toGMTString())
// EDIT THESE SETTINGS
/** @type {string} Name of the field to use as the link text. To use the citation key, set this to "citationKey". */
let linkTextField = "citationKey";
/** @type {'html' | 'md' | 'plain'} What type of link to create. */
let linkType = "md";
/** @type {boolean} If true, make the link specific to the currently selected collection. */
let useColl = false;
/** @type {boolean} If true, use Better Notes zotero://note link when the selected item is a note. */
let useNoteLink = false;
/** @type {'select' | 'open-pdf' | 'auto'} Action of link*/
let linkAction = "auto"; // auto = open-pdf for PDFs and annotations, select for everything else
// END OF EDITABLE SETTINGS
// For efficiency, only execute once for all selected items
if (item) return;
item = items[0];
if (!item && !collection) return "[Copy Zotero Link] item is empty";
if (collection) {
linkAction = "select";
useColl = true;
}
if (linkAction === "auto") {
if (item.isPDFAttachment() || item.isAnnotation()) {
linkAction = "open-pdf";
} else {
linkAction = "select";
}
}
const uriParts = [];
let uriParams = "";
let pageIndex;
let targetItem = item;
if (linkAction === "open-pdf") {
uriParts.push("zotero://open-pdf");
if (item.isRegularItem()) {
targetItem = (await item.getBestAttachments()).find((att) =>
att.isPDFAttachment()
);
} else if (item.isAnnotation()) {
targetItem = item.parentItem;
// If the item is an annotation, we want to open the PDF at the page of the annotation
pageIndex = 1;
try {
pageIndex = JSON.parse(item.annotationPosition).pageIndex + 1;
} catch (e) {
Zotero.warn(e);
}
uriParams = `?page=${pageIndex}&annotation=${item.key}`;
}
} else {
uriParts.push("zotero://select");
if (item?.isAnnotation()) {
targetItem = item.parentItem;
}
}
if (!targetItem && !collection) return "[Copy Zotero Link] item is invalid";
// Get the link text using the `link_text_field` argument
let linkText;
let annotation = "";
let comment = "";
if (collection) {
// When `collection` is truthy, this script was triggered in the collection menu.
// Use collection name if this is a collection link
linkText = collection.name;
} else if (item.isAttachment()) {
// Try to use top-level item for link text
linkText = Zotero.Items.getTopLevel([item])[0].getField(linkTextField);
} else if (item.isAnnotation()) {
// Format link text as Pandoc citation with citekey and pagenumber
linkText = `@${Zotero.Items.getTopLevel([item])[0].getField(linkTextField)}${item.annotationPageLabel ? `, p. ${item.annotationPageLabel}` : `, p. ${pageIndex}`}`;
// Define comment and annotation, if present, including trailing space - otherwise empty string.
comment = item.annotationComment ? `**${item.annotationComment}**: ` : "";
annotation = item.annotationText ? `${item.annotationText} ` : "";
} else {
// Use the item's field
linkText = item.getField(linkTextField);
}
// Add the library or group URI part (collection must go first)
let libraryType = (collection || item).library.libraryType;
if (libraryType === "user") {
uriParts.push("library");
} else {
uriParts.push(
`groups/${Zotero.Libraries.get((collection || item).libraryID).groupID}`
);
}
// If useColl, make the link collection specific
if (useColl) {
// see https://forums.zotero.org/discussion/73893/zotero-select-for-collections
let coll = collection || Zotero.getActiveZoteroPane().getSelectedCollection();
// It's possible that a collection isn't selected. When that's the case,
// this will fall back to the typical library behavior.
// If a collection is selected, add the collections URI part
if (!!coll) uriParts.push(`collections/${coll.key}`);
}
if (!collection) {
// Add the item URI part
uriParts.push(`items/${targetItem.key}`);
}
// Join the parts together
let uri = uriParts.join("/");
// Add the URI parameters
if (uriParams) {
uri += uriParams;
}
if (useNoteLink && item?.isNote() && Zotero.BetterNotes) {
uri = Zotero.BetterNotes.api.convert.note2link(item);
}
// Format the link and copy it to the clipboard
const clipboard = new Zotero.ActionsTags.api.utils.ClipboardHelper();
if (linkType == "html") {
clipboard.addText(`<a href="${uri}">${linkText}</a>`, "text/unicode");
} else if (linkType == "md") {
clipboard.addText(`${comment}${annotation}[${linkText}](${uri})`, "text/unicode");
} else {
clipboard.addText(uri, "text/unicode");
}
clipboard.copy();
return `[Copy Zotero Link] link ${uri} copied.`; |
Beta Was this translation helpful? Give feedback.
-
Major RewriteGiven that Zotero is making its URL protocol more powerful and adding more document types, I've rewritten the script to make it easier to extend and edit. It is a major rewrite, though, so if people can test it out to ensure it works as expected, that'd be great. Additional feature
A note about annotationsI added the New Script// @author windingwind, garth74, FeralFlora, kassiansun
// @link https://github.com/windingwind/zotero-actions-tags/discussions/115
// @usage Select an item in the library and press the assigned shortcut keys
// @update Tue, 30 Apr 2024 15:42:21 GMT (by new Date().toGMTString())
// @note https://github.com/zotero/zotero/blob/main/components/zotero-protocol-handler.js
// Only execute once for all selected items
if (item) return;
// EDIT THESE SETTINGS
/** Name of the field to use as the link text. If '@citationKey' is used, the
* link will be formatted using a pandoc compatible style.
* @type {string} */
const linkTextField = "@citationKey";
/** @type {'html' | 'md' | 'plain'} What type of link to create. */
const linkType = "md";
/** @type {boolean} If true, make the link specific to the selected collection. */
const useColl = true;
/** @type {boolean} If true, use Better Notes zotero://note link when the selected item is a note. */
const useNoteLink = false;
/** @type {'select' | 'open-pdf' | 'auto'} Action of link */
const linkAction = "auto"; // auto = open-pdf for PDFs and annotations, select for everything else
// END OF EDITABLE SETTINGS
// ========================================================================== //
// Functions used to create a uniform object
// ========================================================================== //
async function _create_HandledObject(item, coll) {
if (coll) {
// When `collection` is truthy, this script has been triggered from the
// collection menu, implying that the link should be to the collection. However,
// `item` can still have a value, so let's just handle `collection` links
// separately.
return _handleCollection(coll);
}
if (item) {
let path = "items";
if (useColl) {
let coll = Zotero.getActiveZoteroPane().getSelectedCollection();
if (coll) path = `collections/${coll.key}/items`;
}
path = `${_getLibraryOrGroupPart(item)}/${path}`;
// Is it a regular item?
if (item.isRegularItem()) return await _handleRegularItem(item, path);
// Is it a note?
if (item.isNote()) return _handleNote(item, path);
// Is it a pdf?
if (item.isPDFAttachment()) return _handlePDF(item, path);
// Is it an annotation?
if (item.isAnnotation()) return _handleAnnotation(item, path);
// Extend with new types
throw `[Copy Zotero Link] unknown item type: ${item.itemType}`;
}
// How'd you get here?
throw "[Copy Zotero Link] item is empty";
}
/*
The functions in this section return a _HandledObject to simplify all the other
functions.
*/
class _HandledObject {
constructor({ obj, type, link, linkText }) {
this.obj = obj;
this.type = type;
this.link = link;
this.linkText = linkText;
}
}
function _handleCollection(obj) {
let libOrGroup = _getLibraryOrGroupPart(obj);
return new _HandledObject({
obj: obj,
type: "collection",
link: `zotero://select/${libOrGroup}/collections/${obj.key}`,
linkText: obj.name,
});
}
async function _handleRegularItem(obj, path) {
let action = _inferLinkAction("select");
let pdf;
if (action === "open-pdf") {
pdf = (await obj.getBestAttachments()).find((att) => att.isPDFAttachment());
if (!pdf) throw "[Copy Zotero Link] item does not have a PDF.";
}
return new _HandledObject({
obj: obj,
type: "item",
link: `zotero://${action}/${path}/${(pdf || obj).key}`,
linkText: _getField(obj, linkTextField),
});
}
function _handleNote(obj, path) {
let action = _inferLinkAction("select");
return new _HandledObject({
obj: obj,
type: "note",
link:
useNoteLink && Zotero.BetterNotes // top-level
? Zotero.BetterNotes.api.convert.note2link(obj)
: `zotero://${action}/${path}/${obj.key}`,
linkText: _getField(obj, linkTextField),
});
}
function _handlePDF(obj, path) {
let action = _inferLinkAction("open-pdf");
return new _HandledObject({
obj: obj,
type: "pdf",
link: `zotero://${action}/${path}/${obj.key}`,
linkText: _getField(obj, linkTextField),
});
}
function _handleAnnotation(obj, path) {
let action = _inferLinkAction("open-pdf");
let pageIndex = 1;
try {
pageIndex = JSON.parse(obj.annotationPosition).pageIndex + 1;
} catch (e) {
Zotero.warn(e);
}
let pageParam = `page=${pageIndex}`;
if (Number.isNaN(pageIndex)) {
pageParam = `cfi=${JSON.parse(obj.annotationPosition).pageIndex}`;
}
return new _HandledObject({
obj: obj,
type: "annotation",
link: `zotero://${action}/${path}/${obj.parentItem.key}?${pageParam}&annotation=${obj.key}`,
linkText: _getAnnotationText(obj),
});
}
function _getAnnotationText(item) {
switch (linkTextField) {
case "@citationKey":
// pandoc compatible formatting
let page = item.annotationPageLabel || item.pageIndex;
return `${_getField(item, "@citationKey")}, p. ${page}`;
default:
return `${item.parentItem.getField(linkTextField)}(${
item.annotationComment || item.annotationText || "annotation"
})`;
}
}
// ========================================================================== //
// helpers
function _inferLinkAction(fallback) {
return linkAction === "auto" ? fallback : linkAction;
}
function _getLibraryOrGroupPart(obj) {
if (obj.library.libraryType === "user") return "library";
return `groups/${Zotero.Libraries.get(obj.libraryID).groupID}`;
}
function _getField(item, field) {
if (item.isAttachment() || item.isAnnotation())
item = Zotero.Items.getTopLevel([item])[0];
if (field === "@citationKey") return "@" + item.getField("citationKey");
return item.getField(field);
}
function _getFormattedLink(handledObject) {
let rawLink = handledObject.link;
let linkText = handledObject.linkText;
switch (linkType) {
case "html":
return `<a href="${rawLink}">${linkText}</a>`;
case "md":
return `[${linkText}](${rawLink})`;
case "plain":
return rawLink;
default:
throw `[Copy Zotero Link] Unrecognized linkType: ${linkType}`;
}
}
// ========================================================================== //
// Main Invocation
async function copyLink(item, coll) {
try {
let handledObject = await _create_HandledObject(item, coll);
let formattedLink = _getFormattedLink(handledObject);
const clipboard = new Zotero.ActionsTags.api.utils.ClipboardHelper();
clipboard.addText(formattedLink, "text/unicode");
clipboard.copy();
return `[Copy Zotero Link] ${handledObject.link}`;
} catch (err) {
return err;
}
}
return await copyLink(items[0], collection); |
Beta Was this translation helpful? Give feedback.
-
There is a strange behaviour I stumbled upon. I can copy a dircet link for annotations via right click menu but when I use shortcut it just gives me the link to the PDF. Earlier this was working. My editable settings are as follows: ` // @author windingwind, garth74 // EDIT THESE SETTINGS /** @type {string} Name of the field to use as the link text. To use the citation key, set this to "citationKey". */ /** @type {'html' | 'md' | 'plain'} What type of link to create. */ /** @type {boolean} If true, make the link specific to the currently selected collection. */ /** @type {boolean} If true, use Better Notes zotero://note link when the selected item is a note. */ /** @type {'select' | 'open-pdf' | 'auto'} Action of link*/ |
Beta Was this translation helpful? Give feedback.
-
@windingwind. I see that there have been a lot of code changes presented in the discussion. Are we to assume the code at the top is the one to use, or should we be taking code from the discussions? |
Beta Was this translation helpful? Give feedback.
-
The link to an annotation copied from a collection does not work: Is that a bug from the script, the plugin or Zotero? A click on the link generates the following output: |
Beta Was this translation helpful? Give feedback.
-
Description
Copy Zotero link of the selected item/PDF/note/annotation/collection.
zotero://select
,zotero://open-pdf
, and Better Notes linkzotero://note
markdown
/html
/plain-text
Action Settings
Operation: Script
Data:
Beta Was this translation helpful? Give feedback.
All reactions