@myrmidon/cadmus-refs-zotero-lookup
v0.0.3
Published
Cadmus - Zotero lookup.
Readme
CadmusRefsZoteroLookup
This library provides an Angular service (ZoteroService) for interacting with the Zotero Web API v3. It allows you to search, retrieve, create, update, and delete items, collections, tags, and more from Zotero user and group libraries.
Installation
Add this library to your Angular project and ensure HttpClientModule is imported in your app module.
Configuration
Provide your Zotero API key, user ID, and (optionally) a default library ID in your app's providers:
import {
ZOTERO_API_KEY_TOKEN,
ZOTERO_USER_ID_TOKEN,
ZOTERO_LIBRARY_ID_TOKEN
} from '@myrmidon/cadmus-refs-zotero-lookup';
export const appConfig: ApplicationConfig = {
providers: [
// ...
// Zotero
{
provide: ZOTERO_API_KEY_TOKEN,
useFactory: (env: EnvService) => env.get('zoteroApiKey'),
deps: [EnvService],
},
{
provide: ZOTERO_USER_ID_TOKEN,
useFactory: (env: EnvService) => env.get('zoteroUserId'),
deps: [EnvService],
},
{
provide: ZOTERO_LIBRARY_ID_TOKEN,
useFactory: (env: EnvService) => env.get('zoteroLibraryId'),
deps: [EnvService],
},
]
};ZOTERO_API_KEY_TOKEN: Your Zotero API key (required).ZOTERO_USER_ID_TOKEN: Your Zotero user ID (required for user libraries).ZOTERO_LIBRARY_ID_TOKEN: The default library ID (optional, for convenience when working with a specific library).
If you do not provide a library ID when calling service methods, the default will be used if set.
In turn, add these settings to your env.js like:
// env.js
// https://www.jvandemo.com/how-to-use-environment-variables-to-configure-your-angular-application-without-a-rebuild/
(function (window) {
window.__env = window.__env || {};
// ...
// Zotero
window.__env.zoteroApiKey = "TODO:YOUR_ZOTERO_KEY";
window.__env.zoteroUserId = "TODO:YOUR_ZOTERO_USER_ID";
window.__env.zoteroLibraryId = "TODO:YOUR_ZOTERO_LIBRARY_ID";
})(this);💡 To avoid storing these sensitive data in your env.js, you can create a new env.local.js, exclude it from GitHub files (this is essential!), and then conditionally load it from index.html only when it's present:
<!-- index.html ... -->
<script src="env.js"></script>
<script>
// check if a local environment file exists and load it
function loadLocalEnv() {
var script = document.createElement("script");
script.onload = function () {
console.log("Loaded local environment overrides.");
};
script.onerror = function () {
console.warn(
"Local environment file not found, using default values."
);
};
script.src = "env.local.js";
document.head.appendChild(script);
}
loadLocalEnv();
</script>
<!-- ... -->If you want to use the Zotero lookup component, configure it in your app.ts like this:
import {
ZOTERO_API_KEY_TOKEN,
ZOTERO_USER_ID_TOKEN,
ZOTERO_LIBRARY_ID_TOKEN,
ZoteroRefLookupService,
} from '@myrmidon/cadmus-refs-zotero-lookup';
// ...
@Component({
selector: 'app-root',
imports: [
RouterModule,
MatButtonModule,
MatIconModule,
MatMenuModule,
MatToolbarModule,
MatTooltipModule,
GravatarPipe,
],
templateUrl: './app.html',
styleUrl: './app.scss',
})
export class App implements OnInit, OnDestroy {
constructor(
@Inject('itemBrowserKeys')
private _itemBrowserKeys: { [key: string]: string },
private _authService: AuthJwtService,
private _appRepository: AppRepository,
private _router: Router,
env: EnvService,
storage: RamStorageService
) {
this.version = env.get('version') || '';
// configure citation schemes
this.configureCitationService(storage);
// configure external lookup for asserted composite IDs
storage.store(LOOKUP_CONFIGS_KEY, [
// Zotero
{
name: 'Zotero',
iconUrl: '/img/zotero128.png',
description: 'Zotero bibliography',
label: 'ID',
service: inject(ZoteroRefLookupService),
itemIdGetter: (item: any) =>
// use a global ID by concatenating library ID and item key
item ? `${item.library?.id}/${item.key}` : '',
itemLabelGetter: (item: any) => {
// customize this as you prefer
if (!item) {
return '';
}
const sb: string[] = [];
if (item.data?.creators && Array.isArray(item.data.creators)) {
const creators = item.data.creators;
for (let i = 0; i < creators.length; i++) {
const c = creators[i];
if (i > 0) {
sb.push('; ');
}
if (c.lastName) {
sb.push(c.lastName);
}
if (c.firstName) {
sb.push(' ' + c.firstName.charAt(0) + '.');
}
}
}
sb.push(': ');
if (item.title) {
sb.push(item.title);
} else if (item.data?.title) {
sb.push(item.data?.title);
}
return sb.join('');
},
}
] as RefLookupConfig[]);
}
// ...
}Usage
Inject ZoteroService into your component or service:
constructor(private zotero: ZoteroService) {}API Overview
1. Search Items
Search for items in a library by query string.
this.zotero.search('userId', 'angular', ZoteroLibraryType.USER, {
itemType: ZoteroItemType.JOURNAL_ARTICLE,
limit: 10,
sort: ZoteroSortField.DATE,
direction: ZoteroDirection.DESC
}).subscribe(response => {
console.log(response.data); // array of items
console.log(response.totalResults); // total count
});Search Everything
Search all fields (not just title/creator/year):
this.zotero.searchEverything('userId', 'climate', ZoteroLibraryType.USER)
.subscribe(response => {
console.log(response.data);
});2. Get Items
Get all items in a library (with optional filters):
this.zotero.getItems('userId', ZoteroLibraryType.USER, { limit: 20 }).subscribe({
next: items => { /* ... */ },
error: err => {
// This will catch missing config, network, and API errors
console.error('Zotero lookup failed:', err.message);
}
});Get a single item by key:
this.zotero.getItem('userId', 'ITEM_KEY', ZoteroLibraryType.USER)
.subscribe(item => console.log(item));3. Create, Update, and Delete Items
Create an item:
const newItem = {
itemType: ZoteroItemType.BOOK,
title: 'My New Book',
creators: [{ creatorType: 'author', firstName: 'John', lastName: 'Doe' }]
};
this.zotero.createItem('userId', newItem, ZoteroLibraryType.USER)
.subscribe(item => console.log('Created:', item));Update an item:
this.zotero.updateItem('userId', 'ITEM_KEY', { title: 'Updated Title' }, 123, ZoteroLibraryType.USER)
.subscribe(item => console.log('Updated:', item));Delete an item:
this.zotero.deleteItem('userId', 'ITEM_KEY', 123, ZoteroLibraryType.USER)
.subscribe(() => console.log('Deleted'));4. Collections
Get all collections:
this.zotero.getCollections('userId', ZoteroLibraryType.USER)
.subscribe(response => console.log(response.data));Get a single collection:
this.zotero.getCollection(ZoteroLibraryType.USER, 'userId', 'COLLECTION_KEY')
.subscribe(collection => console.log(collection));5. Tags
Get all tags in a library:
this.zotero.getTags('userId', ZoteroLibraryType.USER)
.subscribe(tags => console.log(tags));6. Groups
Get all groups for the current user:
this.zotero.getGroups()
.subscribe(groups => console.log(groups));Get a single group by ID:
this.zotero.getGroup('GROUP_ID')
.subscribe(group => console.log(group));7. Item Templates and Metadata
Get a template for a new item:
this.zotero.getItemTemplate(ZoteroItemType.BOOK)
.subscribe(template => console.log(template));Get all item types:
this.zotero.getItemTypes()
.subscribe(types => console.log(types));Get all fields for an item type:
this.zotero.getItemFields(ZoteroItemType.BOOK)
.subscribe(fields => console.log(fields));Get creator types for an item type:
this.zotero.getCreatorTypes(ZoteroItemType.BOOK)
.subscribe(types => console.log(types));8. Key Permissions
Check the permissions of the current API key:
this.zotero.getKeyPermissions()
.subscribe(info => console.log(info));9. User Info
Get info about the current user:
this.zotero.getMyUserInfo()
.subscribe(info => console.log(info));Error Handling
All methods return RxJS Observables and handle errors with descriptive messages. Use .subscribe() with error handling as needed:
this.zotero.getItems('userId', ZoteroLibraryType.USER).subscribe({
next: response => { /* ... */ },
error: err => console.error('Zotero error:', err.message)
});Types and Enums
The service provides TypeScript interfaces and enums for all major Zotero entities and options, such as:
ZoteroLibraryType(USER,GROUP)ZoteroItemType(e.g.,BOOK,JOURNAL_ARTICLE)ZoteroSortField,ZoteroDirection,ZoteroFormatZoteroItem,ZoteroCollection,ZoteroTag, etc.
References
History
- 2025-09-04:
- better error handling.
- optional default library ID.
