File Issues: Greatest Hits, 2006-2015
- When relinking a missing stored file, copy it into the attachment's storage directory automatically - Previously, selecting a file outside the attachment subdir would just result in a missing attachment, since it only looks for stored files within the subdir - Display an error message if a Windows shortcut (.lnk) is added via drag-and-drop or via a file dialog on non-Windows systems, until we can figure out how to determine the original file - Shortcuts can cause errors during syncing, for unclear reasons - Neither nsIFile::copyToFollowingLinks() nor nsIFile::target work for me to get the original file, even when nsIFile::isSymlink() returns true - Windows file dialogs seem to automatically resolve shortcuts, so it's only an issue there for drag-and-drop - Disallow hidden files from being selected in relink dialog - I think some people on Windows with hidden files shown relink the .zotero* files that show up when they click Locate, which causes file sync errors. Which brings us to... - Fix file sync errors for *.lnk and .zotero* files - Ignore existing .zotero* attachment files, treating the files as missing instead to encourage relinking - Strip leading period in getValidFileName() to prevent added files from being hidden - This allows hidden files to be added explicitly; they just won't stay that way in the storage directory (These things should have tests, but that will have to happen on the 5.0 branch.)
This commit is contained in:
parent
0511d37b07
commit
dfb28ff3f6
8 changed files with 128 additions and 34 deletions
|
@ -644,7 +644,7 @@ var wpdCommon = {
|
|||
|
||||
aDir.initWithPath(destdir);
|
||||
|
||||
aFile.copyTo(aDir, destfile);
|
||||
aFile.copyToFollowingLinks(aDir, destfile);
|
||||
return true; // Added by Dan S. for Zotero
|
||||
},
|
||||
|
||||
|
|
|
@ -55,6 +55,10 @@ Zotero.Attachments = new function(){
|
|||
throw ("'" + file.leafName + "' must be a file in Zotero.Attachments.importFromFile()");
|
||||
}
|
||||
|
||||
if (file.leafName.endsWith(".lnk")) {
|
||||
throw new Error("Cannot add Windows shortcut");
|
||||
}
|
||||
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
try {
|
||||
|
@ -165,7 +169,7 @@ Zotero.Attachments = new function(){
|
|||
var storageDir = Zotero.getStorageDirectory();
|
||||
var destDir = this.getStorageDirectory(itemID);
|
||||
_moveOrphanedDirectory(destDir);
|
||||
file.parent.copyTo(storageDir, destDir.leafName);
|
||||
file.parent.copyToFollowingLinks(storageDir, destDir.leafName);
|
||||
|
||||
// Point to copied file
|
||||
var newFile = destDir.clone();
|
||||
|
|
|
@ -2863,6 +2863,11 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
|||
if (Zotero.isWin || path.indexOf('/') != -1) {
|
||||
path = Zotero.File.getValidFileName(path, true);
|
||||
}
|
||||
// Ignore .zotero* files that were relinked before we started blocking them
|
||||
if (path.startsWith(".zotero")) {
|
||||
Zotero.debug("Ignoring attachment file " + path, 2);
|
||||
return false;
|
||||
}
|
||||
var file = Zotero.Attachments.getStorageDirectory(this.id);
|
||||
file.QueryInterface(Components.interfaces.nsILocalFile);
|
||||
file.setRelativeDescriptor(file, path);
|
||||
|
@ -3109,15 +3114,47 @@ Zotero.Item.prototype.relinkAttachmentFile = function(file, skipItemUpdate) {
|
|||
throw('Cannot relink linked URL in Zotero.Items.relinkAttachmentFile()');
|
||||
}
|
||||
|
||||
if (file.leafName.endsWith(".lnk")) {
|
||||
throw new Error("Cannot relink to Windows shortcut");
|
||||
}
|
||||
|
||||
var newName = Zotero.File.getValidFileName(file.leafName);
|
||||
if (!newName) {
|
||||
throw ("No valid characters in filename after filtering in Zotero.Item.relinkAttachmentFile()");
|
||||
}
|
||||
|
||||
// Rename file to filtered name if necessary
|
||||
if (file.leafName != newName) {
|
||||
Zotero.debug("Renaming file '" + file.leafName + "' to '" + newName + "'");
|
||||
file.moveTo(null, newName);
|
||||
try {
|
||||
// If selected file isn't in the attachment's storage directory,
|
||||
// copy it in and use that one instead
|
||||
var storageDir = Zotero.Attachments.getStorageDirectory(this.id);
|
||||
if (this.isImportedAttachment() && !file.parent.equals(storageDir)) {
|
||||
// If file with same name already exists in the storage directory,
|
||||
// move it out of the way
|
||||
let targetFile = storageDir.clone();
|
||||
targetFile.append(newName);
|
||||
let renamedFile;
|
||||
if (targetFile.exists()) {
|
||||
renamedFile = targetFile.clone();
|
||||
renamedFile.moveTo(null, newName + ".bak");
|
||||
}
|
||||
|
||||
Zotero.File.copyToUnique(file, targetFile);
|
||||
file = targetFile;
|
||||
|
||||
// Delete backup file
|
||||
if (renamedFile) {
|
||||
renamedFile.remove(false);
|
||||
}
|
||||
}
|
||||
// Rename file to filtered name if necessary
|
||||
else if (file.leafName != newName) {
|
||||
Zotero.debug("Renaming file '" + file.leafName + "' to '" + newName + "'");
|
||||
file.moveTo(null, newName);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
var path = Zotero.Attachments.getPath(file, linkMode);
|
||||
|
@ -3128,7 +3165,7 @@ Zotero.Item.prototype.relinkAttachmentFile = function(file, skipItemUpdate) {
|
|||
skipClientDateModifiedUpdate: skipItemUpdate
|
||||
});
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -3342,7 +3379,8 @@ Zotero.Item.prototype.__defineGetter__('attachmentPath', function () {
|
|||
|
||||
var sql = "SELECT path FROM itemAttachments WHERE itemID=?";
|
||||
var path = Zotero.DB.valueQuery(sql, this.id);
|
||||
if (!path) {
|
||||
// Ignore .zotero* files that were relinked before we started blocking them
|
||||
if (!path || path.startsWith('.zotero')) {
|
||||
path = '';
|
||||
}
|
||||
this._attachmentPath = path;
|
||||
|
|
|
@ -426,7 +426,7 @@ Zotero.File = new function(){
|
|||
newFile.remove(null);
|
||||
|
||||
// Copy file to unique name
|
||||
file.copyTo(newFile.parent, newName);
|
||||
file.copyToFollowingLinks(newFile.parent, newName);
|
||||
return newFile;
|
||||
}
|
||||
|
||||
|
@ -442,7 +442,7 @@ Zotero.File = new function(){
|
|||
while (otherFiles.hasMoreElements()) {
|
||||
var file = otherFiles.getNext();
|
||||
file.QueryInterface(Components.interfaces.nsIFile);
|
||||
file.copyTo(newDir, null);
|
||||
file.copyToFollowingLinks(newDir, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -505,6 +505,8 @@ Zotero.File = new function(){
|
|||
// Strip characters not valid in XML, since they won't sync and they're probably unwanted
|
||||
fileName = fileName.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\ud800-\udfff\ufffe\uffff]/g, '');
|
||||
}
|
||||
// Don't allow hidden files
|
||||
fileName = fileName.replace(/^\./, '');
|
||||
// Don't allow blank or illegal filenames
|
||||
if (!fileName || fileName == '.' || fileName == '..') {
|
||||
fileName = '_';
|
||||
|
|
|
@ -2810,7 +2810,7 @@ Zotero.ItemTreeView.fileDragDataProvider.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
parentDir.copyTo(destDir, newName ? newName : dirName);
|
||||
parentDir.copyToFollowingLinks(destDir, newName ? newName : dirName);
|
||||
|
||||
// Store nsIFile
|
||||
if (useTemp) {
|
||||
|
@ -2851,7 +2851,7 @@ Zotero.ItemTreeView.fileDragDataProvider.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
file.copyTo(destDir, newName ? newName : null);
|
||||
file.copyToFollowingLinks(destDir, newName ? newName : null);
|
||||
|
||||
// Store nsIFile
|
||||
if (useTemp) {
|
||||
|
@ -3219,6 +3219,14 @@ Zotero.ItemTreeView.prototype.drop = function(row, orient, dataTransfer) {
|
|||
var itemID = Zotero.Attachments.linkFromFile(file, sourceItemID);
|
||||
}
|
||||
else {
|
||||
if (file.leafName.endsWith(".lnk")) {
|
||||
let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
let win = wm.getMostRecentWindow("navigator:browser");
|
||||
win.ZoteroPane.displayCannotAddShortcutMessage(file.path);
|
||||
Zotero.DB.commitTransaction();
|
||||
continue;
|
||||
}
|
||||
var itemID = Zotero.Attachments.importFromFile(file, sourceItemID, targetLibraryID);
|
||||
// If moving, delete original file
|
||||
if (dragData.dropEffect == 'move') {
|
||||
|
|
|
@ -1486,6 +1486,10 @@ Zotero.Sync.Storage = new function () {
|
|||
if (!file) {
|
||||
throw ("Empty path for item " + item.key + " in " + funcName);
|
||||
}
|
||||
// Don't save Windows aliases
|
||||
if (file.leafName.endsWith('.lnk')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var fileName = file.leafName;
|
||||
var renamed = false;
|
||||
|
@ -1899,8 +1903,9 @@ Zotero.Sync.Storage = new function () {
|
|||
params.push(Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD);
|
||||
}
|
||||
sql += ") "
|
||||
// Skip attachments with empty path, which can't be saved
|
||||
+ "AND path!=''";
|
||||
// Skip attachments with empty path, which can't be saved, and files with .zotero*
|
||||
// paths, which have somehow ended up in some users' libraries
|
||||
+ "AND path!='' AND path NOT LIKE 'storage:.zotero%'";
|
||||
var itemIDs = Zotero.DB.columnQuery(sql, params);
|
||||
if (!itemIDs) {
|
||||
return [];
|
||||
|
|
|
@ -3116,8 +3116,16 @@ var ZoteroPane = new function()
|
|||
var attachmentID;
|
||||
if(link)
|
||||
attachmentID = Zotero.Attachments.linkFromFile(file, id);
|
||||
else
|
||||
else {
|
||||
if (file.leafName.endsWith(".lnk")) {
|
||||
let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
let win = wm.getMostRecentWindow("navigator:browser");
|
||||
win.ZoteroPane.displayCannotAddShortcutMessage(file.path);
|
||||
continue;
|
||||
}
|
||||
attachmentID = Zotero.Attachments.importFromFile(file, id, libraryID);
|
||||
}
|
||||
|
||||
if(attachmentID && !id)
|
||||
{
|
||||
|
@ -3757,6 +3765,16 @@ var ZoteroPane = new function()
|
|||
}
|
||||
|
||||
|
||||
// TODO: Figure out a functioning way to get the original path and just copy the real file
|
||||
this.displayCannotAddShortcutMessage = function (path) {
|
||||
Zotero.alert(
|
||||
null,
|
||||
Zotero.getString("general.error"),
|
||||
Zotero.getString("file.error.cannotAddShortcut") + (path ? "\n\n" + path : "")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function showAttachmentNotFoundDialog(itemID, noLocate) {
|
||||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
createInstance(Components.interfaces.nsIPromptService);
|
||||
|
@ -3951,25 +3969,43 @@ var ZoteroPane = new function()
|
|||
throw('Item ' + itemID + ' not found in ZoteroPane_Local.relinkAttachment()');
|
||||
}
|
||||
|
||||
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpen);
|
||||
|
||||
|
||||
var file = item.getFile(false, true);
|
||||
var dir = Zotero.File.getClosestDirectory(file);
|
||||
if (dir) {
|
||||
dir.QueryInterface(Components.interfaces.nsILocalFile);
|
||||
fp.displayDirectory = dir;
|
||||
}
|
||||
|
||||
fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
|
||||
|
||||
if (fp.show() == nsIFilePicker.returnOK) {
|
||||
var file = fp.file;
|
||||
file.QueryInterface(Components.interfaces.nsILocalFile);
|
||||
item.relinkAttachmentFile(file);
|
||||
while (true) {
|
||||
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpen);
|
||||
|
||||
|
||||
var file = item.getFile(false, true);
|
||||
var dir = Zotero.File.getClosestDirectory(file);
|
||||
if (dir) {
|
||||
dir.QueryInterface(Components.interfaces.nsILocalFile);
|
||||
fp.displayDirectory = dir;
|
||||
}
|
||||
|
||||
fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
|
||||
|
||||
if (fp.show() == nsIFilePicker.returnOK) {
|
||||
let file = fp.file;
|
||||
file.QueryInterface(Components.interfaces.nsILocalFile);
|
||||
|
||||
// Disallow hidden files
|
||||
// TODO: Display a message
|
||||
if (file.leafName.startsWith('.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Disallow Windows shortcuts
|
||||
if (file.leafName.endsWith(".lnk")) {
|
||||
this.displayCannotAddShortcutMessage(file.path);
|
||||
continue;
|
||||
}
|
||||
|
||||
item.relinkAttachmentFile(file);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -963,6 +963,7 @@ file.accessError.message.windows = Check that the file is not currently in use,
|
|||
file.accessError.message.other = Check that the file is not currently in use and that its permissions allow write access.
|
||||
file.accessError.restart = Restarting your computer or disabling security software may also help.
|
||||
file.accessError.showParentDir = Show Parent Directory
|
||||
file.error.cannotAddShortcut = Shortcut files cannot be added directly. Please select the original file.
|
||||
|
||||
lookup.failure.title = Lookup Failed
|
||||
lookup.failure.description = Zotero could not find a record for the specified identifier. Please verify the identifier and try again.
|
||||
|
|
Loading…
Reference in a new issue