wshanks / Zutilo

Zotero plugin providing some additional editing features
Other
1.53k stars 72 forks source link

Request that 'change attachment path' can change to a completely new path #173

Open teodor-a opened 3 years ago

teodor-a commented 3 years ago

I would love to not be limited by having to specify which path to replace with what. All my linked files have different paths (which are all wrong - long story) and I would love to change them all to the one single path they are actually in. I don't really see a way to do it in the method that is given. Is it possible to have a function which would just overwrite all files' paths with one of my choosing? Thanks!

qqobb commented 3 years ago

You could try using Zotero's JavaScript API to fix your file paths.

The script below assumes that your broken linked files are saved to a single new directory, newDirectory. It tries to fix the paths of linked files that are selected in Zotero's middle pane. Selecting parent items etc. is OK. The script checks for the existence of files at the new paths. File paths in Zotero's database will only be updated if the files exist in the file system. To specify newDirectory, replace the line with C:\Folder1\Folder2\ in the code with your path. For Mac or Linux, this could be /Folder1/Folder2/. Note that the directory string should end with a slash or backslash. If it includes backticks (`), you need to escape them, so replace (`) with (\`).

If you're not sure how Zutilo's "Modify attachment paths" function works, please read this comment on the Zotero forum. The script below is adapted from code that I posted in the same discussion, see here.

Close Zotero and make a backup of the zotero.sqlite file in your Zotero data directory. Then reopen Zotero, go to "Tools" -> "Developer" -> "Run JavaScript", and paste in and run this code:

// https://regex101.com/r/ZdzzFC/1
const re = /^(.*?)([^\/\\]+)$/;
const mode = Zotero.Attachments.LINK_MODE_LINKED_FILE;
var attArray = ZutiloChrome.zoteroOverlay.getSelectedAttachments(mode);
var attItem = new Zotero.Item("attachment");
attItem.attachmentLinkMode = mode;
var m, n;
m = n = 0;

// New directory which contains broken linked files without subfolders.
var newDirectory = String.raw`
C:\Folder1\Folder2\
`.trim();

for (let i = 0; i < attArray.length; i++) {
    if (await attArray[i].fileExists()) continue
    m++;
    let oldFullPath = attArray[i].attachmentPath;
    let newFullPath = newDirectory + oldFullPath.replace(re, '$2');
    if (newFullPath === oldFullPath) continue
    attItem.attachmentPath = newFullPath;
    if (!await attItem.fileExists()) continue
    attArray[i].attachmentPath = newFullPath;
    await attArray[i].saveTx();
    n++;
}
return `New directory: ${newDirectory}
${attArray.length} item(s) selected
${m} item(s) broken
${n} item(s) fixed
`;

It could help to use this script together with the Zotero Storage Scanner add-on, which adds #broken_attachments tags to broken attachments. (I think selecting "Tools" -> "Storage Scanner" will update these tags.)

qqobb commented 3 years ago

I edited the code above. It's now using String.raw() for newDirectory, so most paths should work literally, without escaping.

(One could prompt for the directory string. However, while backticks are allowed characters, I think not many people use them in folder names.)

wshanks commented 3 years ago

Thanks for the detailed suggestions, @qqobb! @teodor-a, I was going to suggest something similar to @qqobb -- that we just hack a temporary change into Zutilo's modify attachments function for you to use once -- because I think the particular use case (replacing all attachment directories with a single directory) isn't common enough to be a permanent feature.

qqobb commented 3 years ago

The function's use case is indeed very narrow, so I agree with @wshanks that it isn't worth including this as a Zutilo function.

@wshanks: It might be worth adding some file existence checks to the "Modify attachment paths" function, though. I'm not sure about the best approach. One could perhaps add a check box with a label such as "Verify paths" which could be checked by default. With this option, only valid paths, i.e., paths that correspond to existing files, would be updated. The function without file verification might still be useful in case of performance issues. Such a new default option could help avoiding bad paths.

(BTW, I edited the code above. It's now using String.raw().)