Skip to content

Commit

Permalink
Correctifs dans la gestion des imports par URL Zotero (#1328)
Browse files Browse the repository at this point in the history
* fix(zotero): corrige la sauvegarde du jeton Zotero

Régression introduite dans 92d1ba2 / #1272

* fix: traduction dynamique de la pagination de bibliographie

* fix(zotero): convertit en URL d'API Zotero lors de l'import

Gère quelques cas à la marge en plus, dont la sauvegarde d'article individuel.

---------

Co-authored-by: Thomas Parisot <thom4parisot@users.noreply.github.com>
  • Loading branch information
thom4parisot and thom4parisot authored Mar 11, 2025
1 parent 93616fe commit 63866e9
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 27 deletions.
2 changes: 1 addition & 1 deletion front/src/components/Credentials.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ mutation changePassword($old: String!, $new: String!, $user: ID!) {
}
}

mutation setAuthToken($service: String!, $token: String) {
mutation setAuthToken($service: AuthTokenService!, $token: String) {
setAuthToken(service: $service, token: $token) {
zoteroToken
}
Expand Down
2 changes: 1 addition & 1 deletion front/src/components/Write/ReferenceList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function ReferenceList() {
))}
{!showAll && bibliographyEntries.length > 25 && (
<Button className={styles.showAll} onClick={handleShowAll}>
{t('write.showBiblio.button')} {bibliographyEntries.length} references
{t('write.showBiblio.button', { length: bibliographyEntries.length })}
</Button>
)}
</>
Expand Down
3 changes: 2 additions & 1 deletion front/src/components/Write/bibliographe/ZoteroPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
fetchAllCollectionsPerLibrary,
fetchBibliographyFromCollectionHref,
prefixLegacyUrl,
toApiUrl,
} from '../../../helpers/zotero'
import { useGraphQLClient } from '../../../helpers/graphQL'
import { linkToZotero as linkToZoteroQuery } from '../../Article.graphql'
Expand Down Expand Up @@ -133,7 +134,7 @@ export default function ZoteroPanel({
try {
const result = await fetchBibliographyFromCollectionHref({
token,
collectionHref,
collectionHref: await toApiUrl(collectionHref),
})
onChange(result)
} catch (err) {
Expand Down
20 changes: 12 additions & 8 deletions front/src/helpers/zotero.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ async function fetchAllJSON(url, key, agg = []) {
}

/**
* @param {string} url
* @param {URL} url
* @param {string} key Zotero API key
* @param {string[]=} agg
* @returns {Promise<string[]>} - a list of bibliographical references (as BibTeX)
Expand Down Expand Up @@ -200,9 +200,7 @@ export async function fetchBibliographyFromCollectionHref({
collectionHref,
token: key = null,
}) {
return (await fetchAllBibTeX(new URL(collectionHref + '/items'), key)).join(
'\n'
)
return (await fetchAllBibTeX(new URL(collectionHref), key)).join('\n')
}

/**
Expand All @@ -214,14 +212,15 @@ export async function fetchBibliographyFromCollectionHref({
* @returns {Promise<string>} Zotero API URL
*/
export async function toApiUrl(plainUrl, token) {
// https://www.zotero.org/{username}
// https://www.zotero.org/{username}/library
// https://www.zotero.org/{username}/collections/{collectionId}
// https://www.zotero.org/{username}/collections/{collectionId}/items/{itemId}/collection
// https://www.zotero.org/groups/{groupId}/articlesamroute/library
// https://www.zotero.org/groups/{groupId}/article_durassavoie-bernard/collections/{collectionId}
// https://www.zotero.org/groups/{groupId}/article_durassavoie-bernard/collections/{collectionId}/items/{itemId}/collection
const GROUP_RE =
/zotero.org\/(groups\/(?<groupId>\d+)(\/[a-zA-Z0-9_%-]+)?|(?<username>[^/]+))(\/collections\/(?<collectionId>[A-Z0-9]+))?(\/items\/(?<itemId>[A-Z0-9]+))?(\/tags\/(?<tag>[^/]+))?(\/(?<action>collection|library|item-list|trash))?$/
/zotero.org\/(groups\/(?<groupId>\d+)(\/[^/]+)?|(?<username>[^/]+))(\/collections\/(?<collectionId>[A-Z0-9]+))?(\/items\/(?<itemId>[A-Z0-9]+))?(\/tags\/(?<tag>[^/]+))?(\/(?<action>collection|library|item-list|trash))?$/

if (/https:\/\/api.zotero.org\/(users|groups)\/\d+\//i.test(plainUrl)) {
return plainUrl
Expand All @@ -236,6 +235,7 @@ export async function toApiUrl(plainUrl, token) {
}

const result = plainUrl.match(GROUP_RE)

let userId = null
const { username, groupId, collectionId, itemId, tag, action } = result.groups

Expand All @@ -258,7 +258,7 @@ export async function toApiUrl(plainUrl, token) {
groupId && `groups/${groupId}`,
collectionId || itemId ? '' : 'items',
collectionId && !itemId && `collections/${collectionId}/items`,
itemId && `items/${itemId}/children`,
itemId && `items/${itemId}`,
]
.filter(Boolean)
.join('/')
Expand All @@ -274,8 +274,12 @@ export async function toApiUrl(plainUrl, token) {
export function prefixLegacyUrl(urlSuffix) {
const MAYBE_ZOTERO_RE = /^\d+\/([^/]+\/)?collections\/[A-Z0-9]+/

if (typeof urlSuffix === 'string' && MAYBE_ZOTERO_RE.test(urlSuffix)) {
return `${baseWebUrl}groups/${urlSuffix}`
if (typeof urlSuffix === 'string') {
const suffix = urlSuffix.replace(/[[\]]/g, '')

if (MAYBE_ZOTERO_RE.test(suffix)) {
return `${baseWebUrl}groups/${suffix}`
}
}

return urlSuffix
Expand Down
83 changes: 70 additions & 13 deletions front/src/helpers/zotero.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ describe('toApiUrl', () => {
)
})

test('converts legacy url', () => {
const url = '[2381910]/revue_f%C3%A9mur/collections/[P3JEQVU4]'

return expect(toApiUrl(prefixLegacyUrl(url))).resolves.toBe(
'https://api.zotero.org/groups/2381910/collections/P3JEQVU4/items'
)
})

test('converts library url', async () => {
const url = 'https://www.zotero.org/mattheyje/collections/DITT533A'

Expand All @@ -115,16 +123,26 @@ describe('toApiUrl', () => {
const url =
'https://www.zotero.org/mattheyje/collections/DITT533A/items/LXVEBKSN/collection'
await expect(toApiUrl(url, fakeToken)).resolves.toBe(
'https://api.zotero.org/users/4922242/items/LXVEBKSN/children'
'https://api.zotero.org/users/4922242/items/LXVEBKSN'
)

await expect(toApiUrl(url)).rejects.toThrow()
})

test('groupe', () => {
return expect(
test('groupe', async () => {
await expect(
toApiUrl('https://www.zotero.org/groups/3822124/articlesamroute/library')
).resolves.toBe('https://api.zotero.org/groups/3822124/items')

await expect(
toApiUrl('https://www.zotero.org/groups/1612889/ethnocomptabilites')
).resolves.toBe('https://api.zotero.org/groups/1612889/items')

await expect(
toApiUrl(
'https://www.zotero.org/groups/1612889/ethnocomptabilites/library'
)
).resolves.toBe('https://api.zotero.org/groups/1612889/items')
})

test('(sous)-collection dans un groupe', async () => {
Expand All @@ -136,6 +154,30 @@ describe('toApiUrl', () => {
'https://api.zotero.org/groups/2373533/collections/FYFFI3VG/items'
)

await expect(
toApiUrl(
'https://www.zotero.org/groups/1612889/ethnocomptabilites/collections/8ZJKU2RM'
)
).resolves.toBe(
'https://api.zotero.org/groups/1612889/collections/8ZJKU2RM/items'
)

await expect(
toApiUrl(
'https://www.zotero.org/groups/2381910/revue_fémur/collections/P3JEQVU4'
)
).resolves.toBe(
'https://api.zotero.org/groups/2381910/collections/P3JEQVU4/items'
)

await expect(
toApiUrl(
'https://www.zotero.org/groups/2381910/revue_f%C3%A9mur/collections/P3JEQVU4'
)
).resolves.toBe(
'https://api.zotero.org/groups/2381910/collections/P3JEQVU4/items'
)

await expect(
toApiUrl(
'https://www.zotero.org/groups/2317031/emattheyfren615/collections/3LDNMKGB'
Expand All @@ -151,14 +193,24 @@ describe('toApiUrl', () => {
)
})

test("item sélectionné dans une collection d'un groupe", () => {
return expect(
test("item sélectionné dans une collection d'un groupe", async () => {
await expect(
toApiUrl(
'https://www.zotero.org/groups/2373533/article_durassavoie-bernard/collections/FYFFI3VG/items/V4H7CRG5/collection'
)
).resolves.toBe(
'https://api.zotero.org/groups/2373533/items/V4H7CRG5/children'
)
).resolves.toBe('https://api.zotero.org/groups/2373533/items/V4H7CRG5')

await expect(
toApiUrl(
'https://www.zotero.org/groups/1612889/ethnocomptabilites/collections/6F2QIM32/items/WM2LXQB9/collection'
)
).resolves.toBe('https://api.zotero.org/groups/1612889/items/WM2LXQB9')

await expect(
toApiUrl(
'https://www.zotero.org/groups/2381910/revue_fémur/collections/P3JEQVU4/items/6VR4JGQX/collection'
)
).resolves.toBe('https://api.zotero.org/groups/2381910/items/6VR4JGQX')
})

test('items liés à un tag', () => {
Expand Down Expand Up @@ -199,11 +251,16 @@ describe('prefixLegacyUrl', () => {
)
})

test('do not prefix API URLs', () => {
const url = 'https://api.zotero.org/users/4922242/collections/ZWU5CAC8'

expect(prefixLegacyUrl(url)).toBe(url)
})
test.each(
[
'https://api.zotero.org/users/4922242/collections/ZWU5CAC8',
'https://www.zotero.org/groups/2381910/revue_f%C3%A9mur/collections/P3JEQVU4/items/6VR4JGQX/collection',
],
'do not prefix fully resolved URLs',
(url) => {
expect(prefixLegacyUrl(url)).toBe(url)
}
)

test("do not prefix something that's shady", () => {
expect(prefixLegacyUrl('5025104/aaaa/ZLPY5WLF')).toBe(
Expand Down
2 changes: 1 addition & 1 deletion front/src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
"write.searchFieldBiblio.placeholder": "Search",
"write.copyReferenceToClipboard.Button": "Copy '{{text}}' to clipboard",
"write.copyReferenceToClipboard.successToast": "'{{text}}' copied to clipboard",
"write.showBiblio.button": "Show all",
"write.showBiblio.button": "Show all {{length}} references",
"write.titleVersion.sidebar": "Versions",
"write.newVersion.button": "New version",
"write.createVersion.button": "New version",
Expand Down
2 changes: 1 addition & 1 deletion front/src/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
"write.searchFieldBiblio.placeholder": "Buscar",
"write.copyReferenceToClipboard.Button": "Copiar '{{text}}' al portapapeles",
"write.copyReferenceToClipboard.successToast": "'{{text}}' copiado al portapapeles",
"write.showBiblio.button": "Ver todos",
"write.showBiblio.button": "Ver todas al {{length}} portapapeles",
"write.titleVersion.sidebar": "Versiones",
"write.newVersion.button": "Nueva versión",
"write.createVersion.button": "Nueva versión",
Expand Down
2 changes: 1 addition & 1 deletion front/src/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
"write.searchFieldBiblio.placeholder": "Rechercher",
"write.copyReferenceToClipboard.Button": "Copier '{{text}}' dans le presse-papiers",
"write.copyReferenceToClipboard.successToast": "'{{text}}' copié dans le presse-papiers",
"write.showBiblio.button": "Afficher tout",
"write.showBiblio.button": "Afficher les {{length}} références",
"write.titleVersion.sidebar": "Versions",
"write.newVersion.button": "Nouvelle version",
"write.createVersion.button": "Nouvelle version",
Expand Down

0 comments on commit 63866e9

Please sign in to comment.