zotero/chrome/content/zotero/xpcom/file.js
Dan Stillman 884e5474fe Zotero File Storage megacommit
- Group file sync via Zotero File Storage
- Split file syncing into separate modules for ZFS and WebDAV
- Dragging items between libraries copies child notes, snapshots/files, and links based on checkboxes for each (enabled by default) in the Zotero preferences
- Sync errors now trigger an exclamation/error icon separate from the sync icon, with a popup window displaying the error and an option to report it
- Various errors that could cause perpetual sync icon spinning now stop the sync properly
- Zotero.Utilities.md5(str) is now md5(strOrFile, base64)
- doPost(), doHead(), and retrieveSource() now takes a headers parameter instead of requestContentType
- doHead() can now accept an nsIURI (with login credentials), is a background request, and isn't cached
- When library access or file writing access is denied during sync, display a warning and then reset local group to server version
- Perform additional steps (e.g., removing local groups) when switching sync users to prevent errors
- Compare hash as well as mod time when checking for modified local files
- Don't trigger notifications when removing groups from the client
- Clear relation links to items in removed groups
- Zotero.Item.attachmentHash property to get file MD5
- importFromFile() now takes libraryID as a third parameter
- Zotero.Attachments.getNumFiles() returns the number of files in the attachment directory
- Zotero.Attachments.copyAttachmentToLibrary() copies an attachment item, including files, to another library
- Removed Zotero.File.getFileHash() in favor of updated Zotero.Utilities.md5()
- Zotero.File.copyDirectory(dir, newDir) copies all files from dir into newDir
- Preferences shuffling: OpenURL to Advanced, import/export character set options to Export, "Include URLs of paper articles in references" to Styles
- Other stuff I don't remember

Suffice it to say, this could use testing.
2009-09-13 07:23:29 +00:00

307 lines
9.0 KiB
JavaScript

/*
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
*/
Zotero.File = new function(){
this.getExtension = getExtension;
this.getClosestDirectory = getClosestDirectory;
this.getSample = getSample;
this.getContents = getContents;
this.getContentsFromURL = getContentsFromURL;
this.putContents = putContents;
this.getValidFileName = getValidFileName;
this.copyToUnique = this.copyToUnique;
this.getCharsetFromFile = getCharsetFromFile;
this.addCharsetListener = addCharsetListener;
function getExtension(file){
var pos = file.leafName.lastIndexOf('.');
return pos==-1 ? '' : file.leafName.substr(pos+1);
}
/*
* Traverses up the filesystem from a file until it finds an existing
* directory, or false if it hits the root
*/
function getClosestDirectory(file) {
var dir = file.parent;
while (dir && !dir.exists()) {
var dir = dir.parent;
}
if (dir && dir.exists()) {
return dir;
}
return false;
}
/*
* Get the first 128 bytes of the file as a string (multibyte-safe)
*/
function getSample(file){
var fis = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
fis.init(file, 0x01, 0664, 0);
const replacementChar
= Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
var is = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Components.interfaces.nsIConverterInputStream);
is.init(fis, "UTF-8", 128, replacementChar);
var str = {};
var numChars = is.readString(128, str);
is.close();
return str.value;
}
/**
* Get contents of a binary file
*/
this.getBinaryContents = function(file) {
var iStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
iStream.init(file, 0x01, 0664, 0);
var bStream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
bStream.setInputStream(iStream);
var string = bStream.readBytes(file.fileSize);
iStream.close();
return string;
}
function getContents(file, charset, maxLength){
var fis = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
fis.init(file, 0x01, 0664, 0);
if (charset){
charset = Zotero.CharacterSets.getName(charset);
}
if (!charset){
charset = "UTF-8";
}
const replacementChar
= Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
var is = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Components.interfaces.nsIConverterInputStream);
is.init(fis, charset, 4096, replacementChar);
var chars = 4096;
var contents = [], str = {};
while (is.readString(4096, str) != 0) {
if (maxLength) {
chars += 4096;
if (chars >= maxLength) {
Zotero.debug('Stopping at ' + (chars - 4096)
+ ' characters in File.getContents()');
break;
}
}
contents.push(str.value);
}
is.close();
return contents.join('');
}
/*
* Return the contents of a URL as a string
*
* Runs synchronously, so should only be run on local (e.g. chrome) URLs
*/
function getContentsFromURL(url) {
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance();
xmlhttp.open('GET', url, false);
xmlhttp.send(null);
return xmlhttp.responseText;
}
/*
* Write string to a file, overwriting existing file if necessary
*/
function putContents(file, str) {
if (file.exists()) {
file.remove(null);
}
var fos = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
fos.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
var os = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream);
os.init(fos, "UTF-8", 4096, "?".charCodeAt(0));
os.writeString(str);
os.close();
fos.close();
}
function copyToUnique(file, newFile) {
newFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
var newName = newFile.leafName;
newFile.remove(null);
// Copy file to unique name
file.copyTo(newFile.parent, newName);
return file;
}
/**
* Copies all files from dir into newDir
*/
this.copyDirectory = function (dir, newDir) {
var otherFiles = dir.directoryEntries;
while (otherFiles.hasMoreElements()) {
var file = otherFiles.getNext();
file.QueryInterface(Components.interfaces.nsIFile);
file.copyTo(newDir, null);
}
}
this.createDirectoryIfMissing = function (dir) {
if (!dir.exists() || !dir.isDirectory()) {
if (dir.exists() && !dir.isDirectory()) {
dir.remove(null);
}
dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
}
}
// Strip potentially invalid characters
// See http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
function getValidFileName(fileName) {
// TODO: use space instead, and figure out what's doing extra
// URL encode when saving attachments that trigger this
fileName = fileName.replace(/[\/\\\?%\*:|"<>]/g, '');
// Replace newlines (which shouldn't be in the string in the first place) with spaces
fileName = fileName.replace(/\n/g, ' ');
// 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 blank filename
if (!fileName) {
fileName = '_';
}
return fileName;
}
/*
* Not implemented, but it'd sure be great if it were
*/
function getCharsetFromByteArray(arr) {
}
/*
* An extraordinarily inelegant way of getting the character set of a
* text file using a hidden browser
*
* I'm quite sure there's a better way
*
* Note: This is for text files -- don't run on other files
*
* 'callback' is the function to pass the charset (and, if provided, 'args')
* to after detection is complete
*/
function getCharsetFromFile(file, mimeType, callback, args){
if (!file || !file.exists()){
return false;
}
if (mimeType.substr(0, 5)!='text/' ||
!Zotero.MIME.hasInternalHandler(mimeType, this.getExtension(file))){
return false;
}
var browser = Zotero.Browser.createHiddenBrowser();
var url = Components.classes["@mozilla.org/network/protocol;1?name=file"]
.getService(Components.interfaces.nsIFileProtocolHandler)
.getURLSpecFromFile(file);
this.addCharsetListener(browser, function (charset, args) {
callback(charset, args);
Zotero.Browser.deleteHiddenBrowser(browser);
}, args);
browser.loadURI(url);
}
/*
* Attach a load listener to a browser object to perform charset detection
*
* We make sure the universal character set detector is set to the
* universal_charset_detector (temporarily changing it if not--shhhh)
*
* 'callback' is the function to pass the charset (and, if provided, 'args')
* to after detection is complete
*/
function addCharsetListener(browser, callback, args){
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var oldPref = prefService.getCharPref('intl.charset.detector');
var newPref = 'universal_charset_detector';
//Zotero.debug("Default character detector is " + (oldPref ? oldPref : '(none)'));
if (oldPref != newPref){
//Zotero.debug('Setting character detector to universal_charset_detector');
prefService.setCharPref('intl.charset.detector', 'universal_charset_detector');
}
var onpageshow = function(){
// ignore spurious about:blank loads
if(browser.contentDocument.location.href == "about:blank") return;
browser.removeEventListener("pageshow", onpageshow, false);
var charset = browser.contentDocument.characterSet;
Zotero.debug("Detected character set '" + charset + "'");
//Zotero.debug('Resetting character detector to ' + (oldPref ? oldPref : '(none)'));
prefService.setCharPref('intl.charset.detector', oldPref);
callback(charset, args);
};
browser.addEventListener("pageshow", onpageshow, false);
}
}