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
|
@ -644,7 +644,7 @@ var wpdCommon = {
|
||||||
|
|
||||||
aDir.initWithPath(destdir);
|
aDir.initWithPath(destdir);
|
||||||
|
|
||||||
aFile.copyTo(aDir, destfile);
|
aFile.copyToFollowingLinks(aDir, destfile);
|
||||||
return true; // Added by Dan S. for Zotero
|
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()");
|
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();
|
Zotero.DB.beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -165,7 +169,7 @@ Zotero.Attachments = new function(){
|
||||||
var storageDir = Zotero.getStorageDirectory();
|
var storageDir = Zotero.getStorageDirectory();
|
||||||
var destDir = this.getStorageDirectory(itemID);
|
var destDir = this.getStorageDirectory(itemID);
|
||||||
_moveOrphanedDirectory(destDir);
|
_moveOrphanedDirectory(destDir);
|
||||||
file.parent.copyTo(storageDir, destDir.leafName);
|
file.parent.copyToFollowingLinks(storageDir, destDir.leafName);
|
||||||
|
|
||||||
// Point to copied file
|
// Point to copied file
|
||||||
var newFile = destDir.clone();
|
var newFile = destDir.clone();
|
||||||
|
|
|
@ -2863,6 +2863,11 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
||||||
if (Zotero.isWin || path.indexOf('/') != -1) {
|
if (Zotero.isWin || path.indexOf('/') != -1) {
|
||||||
path = Zotero.File.getValidFileName(path, true);
|
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);
|
var file = Zotero.Attachments.getStorageDirectory(this.id);
|
||||||
file.QueryInterface(Components.interfaces.nsILocalFile);
|
file.QueryInterface(Components.interfaces.nsILocalFile);
|
||||||
file.setRelativeDescriptor(file, path);
|
file.setRelativeDescriptor(file, path);
|
||||||
|
@ -3109,15 +3114,47 @@ Zotero.Item.prototype.relinkAttachmentFile = function(file, skipItemUpdate) {
|
||||||
throw('Cannot relink linked URL in Zotero.Items.relinkAttachmentFile()');
|
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);
|
var newName = Zotero.File.getValidFileName(file.leafName);
|
||||||
if (!newName) {
|
if (!newName) {
|
||||||
throw ("No valid characters in filename after filtering in Zotero.Item.relinkAttachmentFile()");
|
throw ("No valid characters in filename after filtering in Zotero.Item.relinkAttachmentFile()");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename file to filtered name if necessary
|
try {
|
||||||
if (file.leafName != newName) {
|
// If selected file isn't in the attachment's storage directory,
|
||||||
Zotero.debug("Renaming file '" + file.leafName + "' to '" + newName + "'");
|
// copy it in and use that one instead
|
||||||
file.moveTo(null, newName);
|
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);
|
var path = Zotero.Attachments.getPath(file, linkMode);
|
||||||
|
@ -3128,7 +3165,7 @@ Zotero.Item.prototype.relinkAttachmentFile = function(file, skipItemUpdate) {
|
||||||
skipClientDateModifiedUpdate: 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 sql = "SELECT path FROM itemAttachments WHERE itemID=?";
|
||||||
var path = Zotero.DB.valueQuery(sql, this.id);
|
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 = '';
|
path = '';
|
||||||
}
|
}
|
||||||
this._attachmentPath = path;
|
this._attachmentPath = path;
|
||||||
|
|
|
@ -426,7 +426,7 @@ Zotero.File = new function(){
|
||||||
newFile.remove(null);
|
newFile.remove(null);
|
||||||
|
|
||||||
// Copy file to unique name
|
// Copy file to unique name
|
||||||
file.copyTo(newFile.parent, newName);
|
file.copyToFollowingLinks(newFile.parent, newName);
|
||||||
return newFile;
|
return newFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +442,7 @@ Zotero.File = new function(){
|
||||||
while (otherFiles.hasMoreElements()) {
|
while (otherFiles.hasMoreElements()) {
|
||||||
var file = otherFiles.getNext();
|
var file = otherFiles.getNext();
|
||||||
file.QueryInterface(Components.interfaces.nsIFile);
|
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
|
// 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, '');
|
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
|
// Don't allow blank or illegal filenames
|
||||||
if (!fileName || fileName == '.' || fileName == '..') {
|
if (!fileName || 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
|
// Store nsIFile
|
||||||
if (useTemp) {
|
if (useTemp) {
|
||||||
|
@ -2851,7 +2851,7 @@ Zotero.ItemTreeView.fileDragDataProvider.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.copyTo(destDir, newName ? newName : null);
|
file.copyToFollowingLinks(destDir, newName ? newName : null);
|
||||||
|
|
||||||
// Store nsIFile
|
// Store nsIFile
|
||||||
if (useTemp) {
|
if (useTemp) {
|
||||||
|
@ -3219,6 +3219,14 @@ Zotero.ItemTreeView.prototype.drop = function(row, orient, dataTransfer) {
|
||||||
var itemID = Zotero.Attachments.linkFromFile(file, sourceItemID);
|
var itemID = Zotero.Attachments.linkFromFile(file, sourceItemID);
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
Zotero.DB.commitTransaction();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
var itemID = Zotero.Attachments.importFromFile(file, sourceItemID, targetLibraryID);
|
var itemID = Zotero.Attachments.importFromFile(file, sourceItemID, targetLibraryID);
|
||||||
// If moving, delete original file
|
// If moving, delete original file
|
||||||
if (dragData.dropEffect == 'move') {
|
if (dragData.dropEffect == 'move') {
|
||||||
|
|
|
@ -1486,6 +1486,10 @@ Zotero.Sync.Storage = new function () {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
throw ("Empty path for item " + item.key + " in " + funcName);
|
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 fileName = file.leafName;
|
||||||
var renamed = false;
|
var renamed = false;
|
||||||
|
@ -1899,8 +1903,9 @@ Zotero.Sync.Storage = new function () {
|
||||||
params.push(Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD);
|
params.push(Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD);
|
||||||
}
|
}
|
||||||
sql += ") "
|
sql += ") "
|
||||||
// Skip attachments with empty path, which can't be saved
|
// Skip attachments with empty path, which can't be saved, and files with .zotero*
|
||||||
+ "AND path!=''";
|
// 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);
|
var itemIDs = Zotero.DB.columnQuery(sql, params);
|
||||||
if (!itemIDs) {
|
if (!itemIDs) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -3116,8 +3116,16 @@ var ZoteroPane = new function()
|
||||||
var attachmentID;
|
var attachmentID;
|
||||||
if(link)
|
if(link)
|
||||||
attachmentID = Zotero.Attachments.linkFromFile(file, id);
|
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);
|
attachmentID = Zotero.Attachments.importFromFile(file, id, libraryID);
|
||||||
|
}
|
||||||
|
|
||||||
if(attachmentID && !id)
|
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) {
|
function showAttachmentNotFoundDialog(itemID, noLocate) {
|
||||||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
|
||||||
createInstance(Components.interfaces.nsIPromptService);
|
createInstance(Components.interfaces.nsIPromptService);
|
||||||
|
@ -3951,25 +3969,43 @@ var ZoteroPane = new function()
|
||||||
throw('Item ' + itemID + ' not found in ZoteroPane_Local.relinkAttachment()');
|
throw('Item ' + itemID + ' not found in ZoteroPane_Local.relinkAttachment()');
|
||||||
}
|
}
|
||||||
|
|
||||||
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
while (true) {
|
||||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||||
.createInstance(nsIFilePicker);
|
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||||
fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpen);
|
.createInstance(nsIFilePicker);
|
||||||
|
fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpen);
|
||||||
|
|
||||||
|
|
||||||
var file = item.getFile(false, true);
|
var file = item.getFile(false, true);
|
||||||
var dir = Zotero.File.getClosestDirectory(file);
|
var dir = Zotero.File.getClosestDirectory(file);
|
||||||
if (dir) {
|
if (dir) {
|
||||||
dir.QueryInterface(Components.interfaces.nsILocalFile);
|
dir.QueryInterface(Components.interfaces.nsILocalFile);
|
||||||
fp.displayDirectory = dir;
|
fp.displayDirectory = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
|
fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
|
||||||
|
|
||||||
if (fp.show() == nsIFilePicker.returnOK) {
|
if (fp.show() == nsIFilePicker.returnOK) {
|
||||||
var file = fp.file;
|
let file = fp.file;
|
||||||
file.QueryInterface(Components.interfaces.nsILocalFile);
|
file.QueryInterface(Components.interfaces.nsILocalFile);
|
||||||
item.relinkAttachmentFile(file);
|
|
||||||
|
// 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.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.restart = Restarting your computer or disabling security software may also help.
|
||||||
file.accessError.showParentDir = Show Parent Directory
|
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.title = Lookup Failed
|
||||||
lookup.failure.description = Zotero could not find a record for the specified identifier. Please verify the identifier and try again.
|
lookup.failure.description = Zotero could not find a record for the specified identifier. Please verify the identifier and try again.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user