====== Zotero 8 for Developers ====== Zotero 8 includes an internal upgrade of the Mozilla platform on which Zotero is based, incorporating changes from Firefox 115 through Firefox 140. Most of the guidance from [[Zotero 7 for Developers]] is still relevant. ===== Feedback ===== If you have questions about anything on this page or encounter other problems while updating your plugin, let us know on the [[https://groups.google.com/g/zotero-dev|dev list]]. Please don't post to the Zotero Forums about Zotero 8 at this time. ===== Dev Builds ===== The ''dev'' channel has been paused. Use [[:beta_builds|Zotero 8 beta builds]] for development. ===== Platform Changes ===== ==== Mozilla Platform ==== The following list includes nearly all Mozilla changes that affected Zotero code. You may encounter other breaking changes if you use APIs not used in Zotero. [[https://searchfox.org/|Searchfox]] is the best resource for identifying current correct usage in Mozilla code and changes between Firefox 115 and Firefox 140. Earlier Zotero 8.0 (formerly 7.1) beta releases were based on Firefox 128, so we've listed changes for Firefox 128 and 140 separately. === Firefox 115 → Firefox 128 ("Zotero 7.1" beta) === * Manual ''Services.jsm'' imports must be removed ([[https://github.com/zotero/zotero/commit/97329752a1839210d636b7b8d37deae854d42410|example]]) * ''nsIScriptableUnicodeConverter'' was removed; replace ''convertToByteArray()'' and ''convertToInputStream()'' ([[https://github.com/zotero/zotero/commit/24bf583b787d21117000336e4f67ddc9ac7b1deb|example]]) * ''nsIOSFileConstantsService'' was removed ([[https://github.com/zotero/zotero/commit/7f619e3fe3044ea968a9bd0fb31d98f45d5cb75b|example]]) * ''XPCOMUtils.defineLazyGetter'' → ''ChromeUtils.defineLazyGetter'' ([[https://github.com/zotero/zotero/commit/9606fca3ad39a52d29d0c74ae96fb680c14ad4e3|example]]) * ''nsIDOMChromeWindow'' was removed ([[https://github.com/zotero/zotero/commit/45e681431bfd6b77bfaf8f55ce87f04fedb4a475|example]]) * Login manager: ''addLogin'' → ''addLoginAsync'' ([[https://github.com/zotero/zotero/commit/fa2073335342f5f8f067b0e2cad12611012bcab5|example]]) * ''BrowsingContext'' should be passed to ''nsIFilePicker'' init ([[https://github.com/zotero/zotero/commit/72b56e7a592a6793817e2a7479b08b6f2e334252|example]], but we recommend using Zotero's ''FilePicker'' module instead) * ''DataTransfer#types'': ''contains()'' → ''includes()'' — now a standard array ([[https://github.com/zotero/zotero/commit/f28fa763ccfc5aac4575c1a2b28f532d84c89bf2|example]]) * ''-moz-nativehyperlinktext'' → ''LinkText'' ([[https://github.com/zotero/zotero/commit/b85864595754fe54ae841902df2e4f3276a7574f|example]]) === Firefox 128 → Firefox 140 (Zotero 8) === * All JSMs (''.jsm'' files) in Firefox and Zotero were converted to ESMs (''.mjs'' files, or ''.sys.mjs'' files for Firefox code) ([[https://github.com/zotero/zotero/commit/6f96fa9da8368080001143898f6c3ece60939359|example]]) * Zotero now uses [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules|standard JavaScript modules]] and ''import'' statements ([[https://github.com/zotero/zotero/commit/09fc93f9772a267f581198d5ce6a768744ed9d9c|example]]) * See Mozilla's [[https://groups.google.com/a/mozilla.org/g/firefox-dev/c/od7Bd1QpCuU|primer on ESMification]] — in particular, the "Renamed New API" section * **Zotero includes a script that can update most JSM-based code automatically.** Copy the ''[[https://github.com/zotero/zotero/tree/main/scripts/migrate-fx140|migrate-fx140]]'' directory into your plugin's Git repo and run ''migrate-fx140/migrate.py esmify path/to/Module.jsm''. To update the imports in a non-JSM file, run ''migrate-fx140/migrate.py esmify %%--%%imports path/to/file.js''. Both commands also accept a directory for batch conversion. * Global imports are no longer supported. Imported modules need to be assigned to a variable. ([[https://github.com/zotero/zotero/commit/5e64365ce1ad07bd8f9308c76c4c39658883d0a7|example]]) * All ESMs run in [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode|strict mode]] * Bluebird was removed — Zotero now uses standard JavaScript promises everywhere ([[https://github.com/zotero/zotero/commit/b090f5d4eb10108052f95b6afb7599301a81f125|example]]) * **Zotero provides a script that can update most Bluebird-based code automatically.** Copy the ''[[https://github.com/zotero/zotero/tree/fx140/scripts/migrate-fx140|migrate-fx140]]'' directory into your plugin's Git repo and run ''migrate-fx140/migrate.py asyncify path/to/file.js''. This command also accepts a directory for batch conversion. * For compatibility, ''Zotero.Promise.delay()'' and ''Zotero.Promise.defer()'' are still supported. ''defer()'' can no longer be called as a constructor ([[https://github.com/zotero/zotero/commit/8d6a853a3fe11d4ad560dde8aa270f8aecee35ba|example]]). * Bluebird Promise instance methods like ''map()'', ''filter()'', ''each()'', ''isResolved()'', ''isPending()'', and ''cancel()'' are no longer available. Collection methods can usually be replaced with iteration ([[https://github.com/zotero/zotero/commit/692312b419dfb7f0aaa8759230a9aafe62bbdc60|example]], [[https://github.com/zotero/zotero/commit/d734d78812c841f7adffdbb35b20b80606b67331|example]]) or awaits ([[https://github.com/zotero/zotero/commit/cf18fe69a9ffef4945841d62d4031c9137f2c5f2|example]]). Complicated ''isPending()'' logic will need to be rewritten ([[https://github.com/zotero/zotero/commit/8ffdb8bca2585408334c7dad7b1dce366fd6cd1a|example]]). * ''ZoteroProtocolHandler'' extensions: ''new AsyncChannel()'' now takes an async function, not a generator ([[https://github.com/zotero/zotero/commit/ccc5dba03220b20722e94a1936a709ffc7a6c025|example]]) * ''Zotero.spawn()'' was removed. * ''Services.appShell.hiddenDOMWindow'' was removed outside macOS — use only as a fallback ([[https://github.com/zotero/zotero/commit/cd813171e7d29b164d3760698274790e888c8fbb|example]]) * ''ZOTERO_CONFIG'' needs to be imported ([[https://github.com/zotero/zotero/commit/5e64365ce1ad07bd8f9308c76c4c39658883d0a7|example]]) * Preference panes now run in their own global scope, so a ''var'' defined in one preference pane's script won't automatically be accessible to other preference panes. Set variables on ''window'' explicitly if you need to share them between preference panes. * Update button labels using the ''label'' property, not attribute ([[https://github.com/zotero/zotero/commit/b828cd5281e8a3ef5c86349b13d0796d8b08caf0|example]]) * The first segment of a zotero: URI is now parsed as its host, not as part of its path ([[https://github.com/zotero/zotero/commit/9cad92b817e56beb108a57b00929ce756dda4125|example]]) ===== Plugin Changes ===== ==== Custom Menu Items ==== A new API allows plugins to create custom menu items in Zotero's menu popups. Plugins should use this official API if possible rather than manually injecting content. The example below shows registering a custom menu item and a submenu in Zotero's items list context menu. let registeredID = Zotero.MenuManager.registerMenu({ menuID: "test", pluginID: "example@example.com", target: "main/library/item", menus: [ { menuType: "menuitem", l10nID: "menu-print", onShowing: (event, context) => { Zotero.debug("onShowing"); Zotero.debug(Object.keys(context)); }, onCommand: (event, context) => { Zotero.debug("onCommand"); Zotero.debug(Object.keys(context)); }, }, { menuType: "submenu", l10nID: "menu-print", menus: [ { menuType: "menuitem", l10nID: "menu-print", onShowing: (event, context) => { Zotero.debug("onShowing submenu"); Zotero.debug(Object.keys(context)); // Only for regular items context.setVisible(context.items?.every((item) => item.isRegularItem())); }, onCommand: (event, context) => { Zotero.debug("onCommand submenu"); Zotero.debug(Object.keys(context)); }, }, ], }, ], }); Available targets: * **Main window menubar menus** * main/menubar/file — File menu in the main window menubar * main/menubar/edit — Edit menu in the main window menubar * main/menubar/view — View menu in the main window menubar * main/menubar/go — Go menu in the main window menubar * main/menubar/tools — Tools menu in the main window menubar * main/menubar/help — Help menu in the main window menubar * **Main window library context menus** * main/library/item — Context menu for library items * main/library/collection — Context menu for library collections * **Main window toolbar & file menu submenus** * main/library/addAttachment — “Add attachment” button/menu * main/library/addNote — “New note” button/menu * **Main window tab context menus** * main/tab — Context menu for main window tabs * **Reader window menubar menus** * reader/menubar/file — File menu in reader window * reader/menubar/edit — Edit menu in reader window * reader/menubar/view — View menu in reader window * reader/menubar/go — Go menu in reader window * reader/menubar/window — Window menu in reader window * **Item pane context menus** * itemPane/info/row — Context menu for item pane info rows * **Notes pane add note buttons** * notesPane/addItemNote — Add item note button * notesPane/addStandaloneNote — Add standalone note button * **Sidenav buttons** * sidenav/locate — Locate button in side navigation More advanced options are documented in the [[https://github.com/zotero/zotero/blob/main/chrome/content/zotero/xpcom/pluginAPI/menuManager.js|source code]]. When the plugin is disabled or uninstalled, custom menus with the corresponding ''pluginID'' will be automatically removed. If you want to unregister a custom menu manually, you can use ''unregisterMenu()'': Zotero.MenuManager.unregisterMenu(registeredID); If you encounter any problem with this API, please let us know on the [[https://groups.google.com/g/zotero-dev|dev list]].