Merge branch '4.0'

Conflicts:
	chrome/content/zotero/xpcom/zotero.js
This commit is contained in:
Dan Stillman 2013-08-03 18:12:36 -04:00
commit 1117332177
35 changed files with 1219 additions and 682 deletions

View File

@ -54,6 +54,7 @@ overlay chrome://zotero/content/preferences/preferences.xul chrome://zotero/cont
overlay chrome://zotero/content/preferences/preferences.xul#cite chrome://zotero/content/preferences/preferences_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} overlay chrome://zotero/content/preferences/preferences.xul#cite chrome://zotero/content/preferences/preferences_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
overlay chrome://zotero/content/preferences/preferences_general.xul chrome://zotero/content/preferences/preferences_general_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} overlay chrome://zotero/content/preferences/preferences_general.xul chrome://zotero/content/preferences/preferences_general_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
overlay chrome://zotero/content/preferences/preferences_export.xul chrome://zotero/content/preferences/preferences_export_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} overlay chrome://zotero/content/preferences/preferences_export.xul chrome://zotero/content/preferences/preferences_export_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
overlay chrome://zotero/content/preferences/preferences_keys.xul chrome://zotero/content/preferences/preferences_keys_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
overlay chrome://zotero/content/preferences/preferences_advanced.xul chrome://zotero/content/preferences/preferences_advanced_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} overlay chrome://zotero/content/preferences/preferences_advanced.xul chrome://zotero/content/preferences/preferences_advanced_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
overlay chrome://zotero/content/preferences/preferences_advanced.xul chrome://zotero/content/preferences/preferences_advanced_standalone.xul application=zotero@chnm.gmu.edu overlay chrome://zotero/content/preferences/preferences_advanced.xul chrome://zotero/content/preferences/preferences_advanced_standalone.xul application=zotero@chnm.gmu.edu

View File

@ -538,7 +538,7 @@
<label id="index-status-label"/> <label id="index-status-label"/>
<hbox> <hbox>
<label id="index-status"/> <label id="index-status"/>
<toolbarbutton id="reindex" oncommand="this.hidden = true; setTimeout(function () { ZoteroPane_Local.reindexItem(); }, 50)"/> <image id="reindex" onclick="this.hidden = true; setTimeout(function () { ZoteroPane_Local.reindexItem(); }, 50)"/>
</hbox> </hbox>
</row> </row>
</rows> </rows>

View File

@ -94,6 +94,10 @@
<key id="key_openZotero" <key id="key_openZotero"
key="Z" key="Z"
oncommand="ZoteroOverlay.toggleDisplay();" oncommand="ZoteroOverlay.toggleDisplay();"
modifiers="accel alt" /> modifiers="accel shift" />
<key id="key_saveToZotero"
key="S"
oncommand="Zotero_Browser.scrapeThisPage();"
modifiers="accel shift" />
</keyset> </keyset>
</overlay> </overlay>

View File

@ -27,10 +27,10 @@
Zotero_Preferences.Keys = { Zotero_Preferences.Keys = {
init: function () { init: function () {
// Display the appropriate modifier keys for the platform
var rows = document.getElementById('zotero-prefpane-keys').getElementsByTagName('row'); var rows = document.getElementById('zotero-prefpane-keys').getElementsByTagName('row');
for (var i=0; i<rows.length; i++) { for (var i=0; i<rows.length; i++) {
rows[i].firstChild.nextSibling.value = Zotero.isMac ? 'Cmd+Shift+' : 'Ctrl+Alt+'; // Display the appropriate modifier keys for the platform
rows[i].firstChild.nextSibling.value = Zotero.isMac ? 'Cmd+Shift+' : 'Ctrl+Shift+';
} }
} }
}; };

View File

@ -31,50 +31,27 @@
helpTopic="shortcut_keys"> helpTopic="shortcut_keys">
<preferences> <preferences>
<preference id="pref-keys-openZotero" name="extensions.zotero.keys.openZotero" type="string"/> <preference id="pref-keys-openZotero" name="extensions.zotero.keys.openZotero" type="string"/>
<preference id="pref-keys-saveToZotero" name="extensions.zotero.keys.saveToZotero" type="string"/>
<preference id="pref-keys-toggleFullscreen" name="extensions.zotero.keys.toggleFullscreen" type="string"/> <preference id="pref-keys-toggleFullscreen" name="extensions.zotero.keys.toggleFullscreen" type="string"/>
<preference id="pref-keys-library" name="extensions.zotero.keys.library" type="string"/> <preference id="pref-keys-library" name="extensions.zotero.keys.library" type="string"/>
<preference id="pref-keys-quicksearch" name="extensions.zotero.keys.quicksearch" type="string"/> <preference id="pref-keys-quicksearch" name="extensions.zotero.keys.quicksearch" type="string"/>
<preference id="pref-keys-toggleTagSelector" name="extensions.zotero.keys.toggleTagSelector" type="string"/>
<preference id="pref-keys-newItem" name="extensions.zotero.keys.newItem" type="string"/> <preference id="pref-keys-newItem" name="extensions.zotero.keys.newItem" type="string"/>
<preference id="pref-keys-newNote" name="extensions.zotero.keys.newNote" type="string"/> <preference id="pref-keys-newNote" name="extensions.zotero.keys.newNote" type="string"/>
<preference id="pref-keys-toggleTagSelector" name="extensions.zotero.keys.toggleTagSelector" type="string"/> <preference id="pref-keys-importFromClipboard" name="extensions.zotero.keys.importFromClipboard" type="string"/>
<preference id="pref-keys-copySelectedItemCitationsToClipboard" name="extensions.zotero.keys.copySelectedItemCitationsToClipboard" type="string"/> <preference id="pref-keys-copySelectedItemCitationsToClipboard" name="extensions.zotero.keys.copySelectedItemCitationsToClipboard" type="string"/>
<preference id="pref-keys-copySelectedItemsToClipboard" name="extensions.zotero.keys.copySelectedItemsToClipboard" type="string"/> <preference id="pref-keys-copySelectedItemsToClipboard" name="extensions.zotero.keys.copySelectedItemsToClipboard" type="string"/>
<preference id="pref-keys-importFromClipboard" name="extensions.zotero.keys.importFromClipboard" type="string"/>
</preferences> </preferences>
<grid> <grid id="zotero-keys-grid">
<columns> <columns>
<column flex="1"/> <column flex="1"/>
<column/> <column/>
<column/> <column/>
</columns> </columns>
<rows> <rows id="zotero-keys-rows">
<row> <row id="zotero-keys-new-item">
<label value="&zotero.preferences.keys.openZotero;" control="key-textbox-openZotero"/>
<label/>
<textbox id="textbox-openZotero" maxlength="1" size="1" preference="pref-keys-openZotero"/>
</row>
<row>
<label value="&zotero.preferences.keys.toggleFullscreen;" control="textbox-toggleFullscreen"/>
<label/>
<textbox id="textbox-toggleFullscreen" maxlength="1" size="1" preference="pref-keys-toggleFullscreen"/>
</row>
<row>
<label value="&zotero.preferences.keys.focusLibrariesPane;" control="textbox-library"/>
<label/>
<textbox id="textbox-library" maxlength="1" size="1" preference="pref-keys-library"/>
</row>
<row>
<label value="&zotero.preferences.keys.quicksearch;" control="textbox-quicksearch"/>
<label/>
<textbox id="textbox-quicksearch" maxlength="1" size="1" preference="pref-keys-quicksearch"/>
</row>
<row>
<label value="&zotero.preferences.keys.newItem;" control="textbox-newItem"/> <label value="&zotero.preferences.keys.newItem;" control="textbox-newItem"/>
<label/> <label/>
<textbox id="textbox-newItem" maxlength="1" size="1" preference="pref-keys-newItem"/> <textbox id="textbox-newItem" maxlength="1" size="1" preference="pref-keys-newItem"/>
@ -87,9 +64,21 @@
</row> </row>
<row> <row>
<label value="&zotero.preferences.keys.toggleTagSelector;" control="textbox-toggleTagSelector"/> <label value="&zotero.preferences.keys.importFromClipboard;" control="textbox-importFromClipboard"/>
<label/> <label/>
<textbox id="textbox-toggleTagSelector" maxlength="1" size="1" preference="pref-keys-toggleTagSelector"/> <textbox id="textbox-importFromClipboard" maxlength="1" size="1" preference="pref-keys-importFromClipboard"/>
</row>
<row id="zotero-keys-focus-libraries-pane">
<label value="&zotero.preferences.keys.focusLibrariesPane;" control="textbox-library"/>
<label/>
<textbox id="textbox-library" maxlength="1" size="1" preference="pref-keys-library"/>
</row>
<row>
<label value="&zotero.preferences.keys.quicksearch;" control="textbox-quicksearch"/>
<label/>
<textbox id="textbox-quicksearch" maxlength="1" size="1" preference="pref-keys-quicksearch"/>
</row> </row>
<row> <row>
@ -109,9 +98,9 @@
</row> </row>
<row> <row>
<label value="&zotero.preferences.keys.importFromClipboard;" control="textbox-importFromClipboard"/> <label value="&zotero.preferences.keys.toggleTagSelector;" control="textbox-toggleTagSelector"/>
<label/> <label/>
<textbox id="textbox-importFromClipboard" maxlength="1" size="1" preference="pref-keys-importFromClipboard"/> <textbox id="textbox-toggleTagSelector" maxlength="1" size="1" preference="pref-keys-toggleTagSelector"/>
</row> </row>
</rows> </rows>
</grid> </grid>

View File

@ -0,0 +1,52 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 20062013 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
-->
<!DOCTYPE prefwindow SYSTEM "chrome://zotero/locale/preferences.dtd">
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<prefpane id="zotero-prefpane-keys">
<grid id="zotero-keys-grid">
<rows id="zotero-keys-rows">
<row position="1">
<label value="&zotero.preferences.keys.openZotero;" control="key-textbox-openZotero"/>
<label/>
<textbox id="textbox-openZotero" maxlength="1" size="1" preference="pref-keys-openZotero"/>
</row>
<row position="2">
<label value="&zotero.preferences.keys.toggleFullscreen;" control="textbox-toggleFullscreen"/>
<label/>
<textbox id="textbox-toggleFullscreen" maxlength="1" size="1" preference="pref-keys-toggleFullscreen"/>
</row>
<row insertbefore="zotero-keys-new-item">
<label value="&zotero.preferences.keys.saveToZotero;" control="key-textbox-saveToZotero"/>
<label/>
<textbox id="textbox-saveToZotero" maxlength="1" size="1" preference="pref-keys-saveToZotero"/>
</row>
</rows>
</grid>
</prefpane>
</overlay>

View File

@ -38,6 +38,7 @@ const ZoteroStandalone = new function() {
window.close(); window.close();
return; return;
} }
_checkRoot();
ZoteroPane.init(); ZoteroPane.init();
ZoteroPane.makeVisible(); ZoteroPane.makeVisible();
@ -146,6 +147,32 @@ const ZoteroStandalone = new function() {
this.onUnload = function() { this.onUnload = function() {
ZoteroPane.destroy(); ZoteroPane.destroy();
} }
/**
* Warn if Zotero Standalone is running as root and clobber the cache directory
*/
function _checkRoot() {
if(!Zotero.isWin) {
var env = Components.classes["@mozilla.org/process/environment;1"].
getService(Components.interfaces.nsIEnvironment);
var user = env.get("USER") || env.get("USERNAME");
if(user === "root") {
// Zap cache files
try {
Services.dirsvc.get("ProfLD", Components.interfaces.nsIFile).remove(true);
} catch(e) {}
// Warn user never to do this again
if(Services.prompt.confirmEx(null, "", Zotero.getString("standalone.rootWarning"),
Services.prompt.BUTTON_POS_0*Services.prompt.BUTTON_TITLE_IS_STRING |
Services.prompt.BUTTON_POS_1*Services.prompt.BUTTON_TITLE_IS_STRING,
Zotero.getString("standalone.rootWarning.exit"),
Zotero.getString("standalone.rootWarning.continue"),
null, null, {}) == 0) {
goQuitApplication();
}
}
}
}
} }
/** Taken from browser.js **/ /** Taken from browser.js **/

View File

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<overlay id="zotero-update"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<wizard id="updates">
<script type="application/javascript" src="chrome://zotero/content/include.js"/>
<script type="application/javascript">
document.getElementById("manualUpdateDesc").textContent = Zotero.getString("standalone.updateMessage");
</script>
</wizard>
</overlay>

View File

@ -11,7 +11,7 @@
style="padding:2em"> style="padding:2em">
<script src="../include.js"/> <script src="../include.js"/>
<script src="test.js"/> <script type="application/javascript;version=1.8" src="test.js"/>
<hbox><label>This is a test page.</label></hbox> <hbox><label>This is a test page.</label></hbox>
</window> </window>

View File

@ -57,7 +57,7 @@ if (!Array.indexOf) {
}; };
} }
var CSL = { var CSL = {
PROCESSOR_VERSION: "1.0.470", PROCESSOR_VERSION: "1.0.471",
CONDITION_LEVEL_TOP: 1, CONDITION_LEVEL_TOP: 1,
CONDITION_LEVEL_BOTTOM: 2, CONDITION_LEVEL_BOTTOM: 2,
PLAIN_HYPHEN_REGEX: /(?:[^\\]-|\u2013)/, PLAIN_HYPHEN_REGEX: /(?:[^\\]-|\u2013)/,
@ -2860,7 +2860,7 @@ CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
if (blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) { if (blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) {
span_split = ret.length; span_split = ret.length;
} }
var blobs_start = state.output.renderBlobs(ret.slice(0, span_split), blob_delimiter, true); var blobs_start = state.output.renderBlobs(ret.slice(0, span_split), blob_delimiter, true, blob);
if (blobs_start && blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) { if (blobs_start && blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) {
if (!state.tmp.suppress_decorations) { if (!state.tmp.suppress_decorations) {
for (i = 0, ilen = blob.decorations.length; i < ilen; i += 1) { for (i = 0, ilen = blob.decorations.length; i < ilen; i += 1) {
@ -2925,7 +2925,7 @@ CSL.Output.Queue.prototype.clearlevel = function () {
blob.blobs.pop(); blob.blobs.pop();
} }
}; };
CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite) { CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent) {
var state, ret, ret_last_char, use_delim, i, blob, pos, len, ppos, llen, pppos, lllen, res, str, params, txt_esc; var state, ret, ret_last_char, use_delim, i, blob, pos, len, ppos, llen, pppos, lllen, res, str, params, txt_esc;
txt_esc = CSL.getSafeEscape(this.state); txt_esc = CSL.getSafeEscape(this.state);
if (!delim) { if (!delim) {
@ -2936,6 +2936,11 @@ CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite) {
ret_last_char = []; ret_last_char = [];
use_delim = ""; use_delim = "";
len = blobs.length; len = blobs.length;
if (this.state.tmp.area === "citation" && !this.state.tmp.just_looking && len === 1 && typeof blobs[0] === "object" && parent) {
blobs[0].strings.prefix = parent.strings.prefix + blobs[0].strings.prefix;
blobs[0].strings.suffix = blobs[0].strings.suffix + parent.strings.suffix;
return blobs[0];
}
var start = true; var start = true;
for (pos = 0; pos < len; pos += 1) { for (pos = 0; pos < len; pos += 1) {
if (blobs[pos].checkNext) { if (blobs[pos].checkNext) {

View File

@ -1725,7 +1725,16 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient)
try { try {
Zotero.DB.beginTransaction(); Zotero.DB.beginTransaction();
var itemID = Zotero.Attachments.importFromFile(file, false, targetLibraryID); if (dragData.dropEffect == 'link') {
var itemID = Zotero.Attachments.linkFromFile(file);
}
else {
if (dragData.dropEffect != 'copy') {
Components.utils.reportError("Invalid dropEffect '" + dragData.dropEffect + "' dropping file");
}
var itemID = Zotero.Attachments.importFromFile(file, false, targetLibraryID);
}
if (parentCollectionID) { if (parentCollectionID) {
var col = Zotero.Collections.get(parentCollectionID); var col = Zotero.Collections.get(parentCollectionID);
if (col) { if (col) {
@ -1751,17 +1760,38 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient)
* Called by HTML 5 Drag and Drop when dragging over the tree * Called by HTML 5 Drag and Drop when dragging over the tree
*/ */
Zotero.CollectionTreeView.prototype.onDragEnter = function (event) { Zotero.CollectionTreeView.prototype.onDragEnter = function (event) {
//Zotero.debug("Storing current drag data");
Zotero.DragDrop.currentDataTransfer = event.dataTransfer; Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
return false;
} }
/* /*
* Called by HTML 5 Drag and Drop when dragging over the tree * Called by HTML 5 Drag and Drop when dragging over the tree
*/ */
Zotero.CollectionTreeView.prototype.onDragOver = function (event, dropdata, session) { Zotero.CollectionTreeView.prototype.onDragOver = function (event) {
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
if (event.dataTransfer.types.contains("application/x-moz-file")) {
// As of Aug. 2013 nightlies:
//
// - Setting the dropEffect only works on Linux and OS X.
//
// - Modifier keys don't show up in the drag event on OS X until the
// drop, so since we can't show a correct effect, we leave it at
// the default 'move', the least misleading option.
//
// - The cursor effect gets set by the system on Windows 7 and can't
// be overridden.
if (!Zotero.isMac) {
if (event.ctrlKey && event.shiftKey) {
event.dataTransfer.dropEffect = "link";
}
else {
event.dataTransfer.dropEffect = "copy";
}
}
}
// Show copy symbol when dragging an item over a collection // Show copy symbol when dragging an item over a collection
if (event.dataTransfer.getData("zotero/item")) { else if (event.dataTransfer.getData("zotero/item")) {
event.dataTransfer.dropEffect = "copy"; event.dataTransfer.dropEffect = "copy";
} }
return false; return false;
@ -1771,7 +1801,8 @@ Zotero.CollectionTreeView.prototype.onDragOver = function (event, dropdata, sess
/* /*
* Called by HTML 5 Drag and Drop when dropping onto the tree * Called by HTML 5 Drag and Drop when dropping onto the tree
*/ */
Zotero.CollectionTreeView.prototype.onDrop = function (event, dropdata, session) { Zotero.CollectionTreeView.prototype.onDrop = function (event) {
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
return false; return false;
} }

View File

@ -132,6 +132,7 @@ Zotero.Item.prototype.__defineSetter__('relatedItems', function (arr) { this._se
Zotero.Item.prototype.__defineGetter__('relatedItemsReverse', function () { var ids = this._getRelatedItemsReverse(); return ids; }); Zotero.Item.prototype.__defineGetter__('relatedItemsReverse', function () { var ids = this._getRelatedItemsReverse(); return ids; });
Zotero.Item.prototype.__defineGetter__('relatedItemsBidirectional', function () { var ids = this._getRelatedItemsBidirectional(); return ids; }); Zotero.Item.prototype.__defineGetter__('relatedItemsBidirectional', function () { var ids = this._getRelatedItemsBidirectional(); return ids; });
Zotero.Item.prototype.__defineGetter__('libraryKey', function () this.libraryIDInt + "/" + this.key);
Zotero.Item.prototype.getID = function() { Zotero.Item.prototype.getID = function() {
Zotero.debug('Item.getID() is deprecated -- use Item.id'); Zotero.debug('Item.getID() is deprecated -- use Item.id');
@ -2792,31 +2793,6 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
}; };
} }
// Update file existence state of this item
// and best attachment state of parent item
var self = this;
var updateAttachmentStates = function (exists) {
self._fileExists = exists;
if (self.isTopLevelItem()) {
return;
}
try {
var parentKey = self.getSource();
}
// This can happen during classic sync conflict resolution, if a
// standalone attachment was modified locally and remotely was changed
// into a child attachment
catch (e) {
Zotero.debug("Attachment parent doesn't exist for source key "
+ "in Zotero.Item.updateAttachmentStates()", 1);
return;
}
Zotero.Items.get(parentKey).updateBestAttachmentState();
};
// No associated files for linked URLs // No associated files for linked URLs
if (row.linkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { if (row.linkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) {
return false; return false;
@ -2824,7 +2800,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
if (!row.path) { if (!row.path) {
Zotero.debug("Attachment path is empty", 2); Zotero.debug("Attachment path is empty", 2);
updateAttachmentStates(false); this._updateAttachmentStates(false);
return false; return false;
} }
@ -2873,7 +2849,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
} }
catch (e) { catch (e) {
Zotero.debug('Invalid persistent descriptor', 2); Zotero.debug('Invalid persistent descriptor', 2);
updateAttachmentStates(false); this._updateAttachmentStates(false);
return false; return false;
} }
} }
@ -2883,7 +2859,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
row.path.indexOf(Zotero.Attachments.BASE_PATH_PLACEHOLDER) == 0) { row.path.indexOf(Zotero.Attachments.BASE_PATH_PLACEHOLDER) == 0) {
var file = Zotero.Attachments.resolveRelativePath(row.path); var file = Zotero.Attachments.resolveRelativePath(row.path);
if (!file) { if (!file) {
updateAttachmentStates(false); this._updateAttachmentStates(false);
return false; return false;
} }
} }
@ -2909,7 +2885,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
} }
catch (e) { catch (e) {
Zotero.debug('Invalid relative descriptor', 2); Zotero.debug('Invalid relative descriptor', 2);
updateAttachmentStates(false); this._updateAttachmentStates(false);
return false; return false;
} }
} }
@ -2917,15 +2893,41 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
if (!skipExistsCheck && !file.exists()) { if (!skipExistsCheck && !file.exists()) {
Zotero.debug("Attachment file '" + file.path + "' not found", 2); Zotero.debug("Attachment file '" + file.path + "' not found", 2);
updateAttachmentStates(false); this._updateAttachmentStates(false);
return false; return false;
} }
updateAttachmentStates(true); this._updateAttachmentStates(true);
return file; return file;
} }
/**
* Update file existence state of this item and best attachment state of parent item
*/
Zotero.Item.prototype._updateAttachmentStates = function (exists) {
this._fileExists = exists;
if (this.isTopLevelItem()) {
return;
}
try {
var parentKey = this.getSource();
}
// This can happen during classic sync conflict resolution, if a
// standalone attachment was modified locally and remotely was changed
// into a child attachment
catch (e) {
Zotero.debug("Attachment parent doesn't exist for source key "
+ "in Zotero.Item.updateAttachmentStates()", 1);
return;
}
Zotero.Items.get(parentKey).updateBestAttachmentState();
}
Zotero.Item.prototype.getFilename = function () { Zotero.Item.prototype.getFilename = function () {
if (!this.isAttachment()) { if (!this.isAttachment()) {
throw ("getFileName() can only be called on attachment items in Zotero.Item.getFilename()"); throw ("getFileName() can only be called on attachment items in Zotero.Item.getFilename()");

View File

@ -36,7 +36,7 @@ Zotero.Libraries = new function () {
break; break;
default: default:
throw ("Invalid library type '" + type + "' in Zotero.Libraries.add()"); throw new Error("Invalid library type '" + type + "' in Zotero.Libraries.add()");
} }
var sql = "INSERT INTO libraries (libraryID, libraryType) VALUES (?, ?)"; var sql = "INSERT INTO libraries (libraryID, libraryType) VALUES (?, ?)";
@ -57,7 +57,7 @@ Zotero.Libraries = new function () {
return group.name; return group.name;
default: default:
throw ("Unsupported library type '" + type + "' in Zotero.Libraries.getName()"); throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
} }
} }
@ -69,7 +69,7 @@ Zotero.Libraries = new function () {
var sql = "SELECT libraryType FROM libraries WHERE libraryID=?"; var sql = "SELECT libraryType FROM libraries WHERE libraryID=?";
var libraryType = Zotero.DB.valueQuery(sql, libraryID); var libraryType = Zotero.DB.valueQuery(sql, libraryID);
if (!libraryType) { if (!libraryType) {
throw ("Library " + libraryID + " does not exist in Zotero.Libraries.getType()"); throw new Error("Library " + libraryID + " does not exist in Zotero.Libraries.getType()");
} }
return libraryType; return libraryType;
} }
@ -87,7 +87,7 @@ Zotero.Libraries = new function () {
return group.editable; return group.editable;
default: default:
throw ("Unsupported library type '" + type + "' in Zotero.Libraries.getName()"); throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
} }
} }
@ -104,7 +104,7 @@ Zotero.Libraries = new function () {
return group.filesEditable; return group.filesEditable;
default: default:
throw ("Unsupported library type '" + type + "' in Zotero.Libraries.getName()"); throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
} }
} }
} }

View File

@ -1553,7 +1553,7 @@ Zotero.Integration.Fields.prototype._updateDocument = function(forceCitations, f
} catch(e) { } catch(e) {
Zotero.logError(e); Zotero.logError(e);
} }
yield; yield undefined;
} }
var citation = this._session.citationsByIndex[i]; var citation = this._session.citationsByIndex[i];
@ -1661,7 +1661,7 @@ Zotero.Integration.Fields.prototype._updateDocument = function(forceCitations, f
} catch(e) { } catch(e) {
Zotero.logError(e); Zotero.logError(e);
} }
yield; yield undefined;
} }
if(bibliographyText) { if(bibliographyText) {
@ -2649,7 +2649,7 @@ Zotero.Integration.Session.prototype._updateCitations = function() {
} }
this.citeprocCitationIDs[citation.citationID] = true; this.citeprocCitationIDs[citation.citationID] = true;
delete this.newIndices[index]; delete this.newIndices[index];
yield; yield undefined;
} }
} }

View File

@ -2990,7 +2990,15 @@ Zotero.ItemTreeView.prototype.drop = function(row, orient)
try { try {
Zotero.DB.beginTransaction(); Zotero.DB.beginTransaction();
var itemID = Zotero.Attachments.importFromFile(file, sourceItemID, targetLibraryID); if (dragData.dropEffect == 'link') {
var itemID = Zotero.Attachments.linkFromFile(file, sourceItemID);
}
else {
if (dragData.dropEffect != 'copy') {
Components.utils.reportError("Invalid dropEffect '" + dragData.dropEffect + "' dropping file");
}
var itemID = Zotero.Attachments.importFromFile(file, sourceItemID, targetLibraryID);
}
if (parentCollectionID) { if (parentCollectionID) {
var col = Zotero.Collections.get(parentCollectionID); var col = Zotero.Collections.get(parentCollectionID);
if (col) { if (col) {
@ -3012,21 +3020,46 @@ Zotero.ItemTreeView.prototype.drop = function(row, orient)
} }
Zotero.ItemTreeView.prototype.onDragEnter = function (event) { Zotero.ItemTreeView.prototype.onDragEnter = function (event) {
//Zotero.debug("Storing current drag data");
Zotero.DragDrop.currentDataTransfer = event.dataTransfer; Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
return false;
} }
/* /*
* Called by HTML 5 Drag and Drop when dragging over the tree * Called by HTML 5 Drag and Drop when dragging over the tree
*/ */
Zotero.ItemTreeView.prototype.onDragOver = function (event, dropdata, session) { Zotero.ItemTreeView.prototype.onDragOver = function (event) {
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
if (event.dataTransfer.types.contains("application/x-moz-file")) {
// As of Aug. 2013 nightlies:
//
// - Setting the dropEffect only works on Linux and OS X.
//
// - Modifier keys don't show up in the drag event on OS X until the
// drop, so since we can't show a correct effect, we leave it at
// the default 'move', the least misleading option.
//
// - The cursor effect gets set by the system on Windows 7 and can't
// be overridden.
if (!Zotero.isMac) {
if (event.ctrlKey && event.shiftKey) {
event.dataTransfer.dropEffect = "link";
}
else {
event.dataTransfer.dropEffect = "copy";
}
}
}
// Show copy symbol when dragging an item over a collection
else if (event.dataTransfer.getData("zotero/item")) {
event.dataTransfer.dropEffect = "copy";
}
return false; return false;
} }
/* /*
* Called by HTML 5 Drag and Drop when dropping onto the tree * Called by HTML 5 Drag and Drop when dropping onto the tree
*/ */
Zotero.ItemTreeView.prototype.onDrop = function (event, dropdata, session) { Zotero.ItemTreeView.prototype.onDrop = function (event) {
return false; return false;
} }

View File

@ -27,7 +27,7 @@ Zotero.Notifier = new function(){
var _observers = {}; var _observers = {};
var _disabled = false; var _disabled = false;
var _types = [ var _types = [
'collection', 'creator', 'search', 'share', 'share-items', 'item', 'collection', 'creator', 'search', 'share', 'share-items', 'item', 'file',
'collection-item', 'item-tag', 'tag', 'setting', 'group', 'trash', 'bucket', 'relation' 'collection-item', 'item-tag', 'tag', 'setting', 'group', 'trash', 'bucket', 'relation'
]; ];
var _inTransaction; var _inTransaction;

View File

@ -75,17 +75,23 @@ Zotero.Sync.Storage = new function () {
} }
} }
Zotero.Notifier.registerObserver(this, ['file']);
// //
// Private properties // Private properties
// //
var _maxCheckAgeInSeconds = 10800; // maximum age for upload modification check (3 hours)
var _syncInProgress; var _syncInProgress;
var _updatesInProgress; var _updatesInProgress;
var _itemDownloadPercentages = {}; var _itemDownloadPercentages = {};
var _uploadCheckFiles = [];
var _lastFullFileCheck = {};
this.sync = function (libraries) { this.sync = function (options) {
if (libraries) { if (options.libraries) {
Zotero.debug("Starting file sync for libraries " + libraries); Zotero.debug("Starting file sync for libraries " + options.libraries);
} }
else { else {
Zotero.debug("Starting file sync"); Zotero.debug("Starting file sync");
@ -100,7 +106,7 @@ Zotero.Sync.Storage = new function () {
return Q.fcall(function () { return Q.fcall(function () {
// TODO: Make sure modes are active // TODO: Make sure modes are active
if (libraries && libraries.indexOf(0) == -1) { if (options.libraries && options.libraries.indexOf(0) == -1) {
return; return;
} }
@ -116,7 +122,7 @@ Zotero.Sync.Storage = new function () {
if (Zotero.Sync.Storage.ZFS.includeGroupFiles) { if (Zotero.Sync.Storage.ZFS.includeGroupFiles) {
var groups = Zotero.Groups.getAll(); var groups = Zotero.Groups.getAll();
for each(var group in groups) { for each(var group in groups) {
if (libraries && libraries.indexOf(group.libraryID) == -1) { if (options.libraries && options.libraries.indexOf(group.libraryID) == -1) {
continue; continue;
} }
// TODO: if library file syncing enabled // TODO: if library file syncing enabled
@ -136,9 +142,9 @@ Zotero.Sync.Storage = new function () {
&& !Zotero.Sync.Storage.WebDAV.verified) { && !Zotero.Sync.Storage.WebDAV.verified) {
Zotero.debug("WebDAV file sync is not active"); Zotero.debug("WebDAV file sync is not active");
var promise = Zotero.Sync.Storage.checkServerPromise(Zotero.Sync.Storage.WebDAV) var promise = Zotero.Sync.Storage.checkServerPromise(Zotero.Sync.Storage.WebDAV)
.then(function () { .then(function () {
mode.cacheCredentials(); return mode.cacheCredentials();
}); });
} }
else { else {
var promise = mode.cacheCredentials(); var promise = mode.cacheCredentials();
@ -146,6 +152,7 @@ Zotero.Sync.Storage = new function () {
promises.push(Q.allResolved([mode, promise])); promises.push(Q.allResolved([mode, promise]));
} }
} }
return Q.all(promises) return Q.all(promises)
// Get library last-sync times // Get library last-sync times
.then(function (cacheCredentialsPromises) { .then(function (cacheCredentialsPromises) {
@ -212,79 +219,111 @@ Zotero.Sync.Storage = new function () {
} }
}); });
// Queue files to download and upload from each library // Check for updated files to upload in each library
var promises = [];
for (let libraryID in librarySyncTimes) { for (let libraryID in librarySyncTimes) {
var lastSyncTime = librarySyncTimes[libraryID]; let promise;
libraryID = parseInt(libraryID); libraryID = parseInt(libraryID);
self.checkForUpdatedFiles(null, libraryID); if (!Zotero.Libraries.isFilesEditable(libraryID)) {
Zotero.debug("No file editing access -- skipping file "
var downloadAll = self.downloadOnSync(libraryID); + "modification check for library " + libraryID);
continue;
// Forced downloads happen even in on-demand mode }
var sql = "SELECT COUNT(*) FROM items " // If this is a background sync, it's not the first sync of
+ "JOIN itemAttachments USING (itemID) " // the session, the library has had at least one full check
+ "WHERE libraryID=? AND syncState=?"; // this session, and it's been less than _maxCheckAgeInSeconds
var downloadForced = !!Zotero.DB.valueQuery( // since the last full check of this library, check only files
sql, // that were previously modified or opened recently
[ else if (options.background
libraryID == 0 ? null : libraryID, && !options.firstInSession
Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD && _lastFullFileCheck[libraryID]
] && (_lastFullFileCheck[libraryID] + (_maxCheckAgeInSeconds * 1000))
); > new Date().getTime()) {
let itemIDs = _getFilesToCheck(libraryID);
// If we don't have any forced downloads, we can skip promise = self.checkForUpdatedFiles(libraryID, itemIDs);
// downloads if the last sync time hasn't changed }
// or doesn't exist on the server (meaning there are no files) // Otherwise check all files in the library
if (downloadAll && !downloadForced) { else {
if (lastSyncTime) { _lastFullFileCheck[libraryID] = new Date().getTime();
var version = self.getStoredLastSyncTime( promise = self.checkForUpdatedFiles(libraryID);
libraryModes[libraryID], libraryID }
); promises.push(promise);
if (version == lastSyncTime) { }
Zotero.debug("Last " + libraryModes[libraryID].name return Q.all(promises)
+ " sync id hasn't changed for library " .then(function () {
+ libraryID + " -- skipping file downloads"); // Queue files to download and upload from each library
for (let libraryID in librarySyncTimes) {
libraryID = parseInt(libraryID);
var downloadAll = self.downloadOnSync(libraryID);
// Forced downloads happen even in on-demand mode
var sql = "SELECT COUNT(*) FROM items "
+ "JOIN itemAttachments USING (itemID) "
+ "WHERE libraryID=? AND syncState=?";
var downloadForced = !!Zotero.DB.valueQuery(
sql,
[
libraryID == 0 ? null : libraryID,
Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD
]
);
// If we don't have any forced downloads, we can skip
// downloads if the last sync time hasn't changed
// or doesn't exist on the server (meaning there are no files)
if (downloadAll && !downloadForced) {
let lastSyncTime = librarySyncTimes[libraryID];
if (lastSyncTime) {
var version = self.getStoredLastSyncTime(
libraryModes[libraryID], libraryID
);
if (version == lastSyncTime) {
Zotero.debug("Last " + libraryModes[libraryID].name
+ " sync id hasn't changed for library "
+ libraryID + " -- skipping file downloads");
downloadAll = false;
}
}
else {
Zotero.debug("No last " + libraryModes[libraryID].name
+ " sync time for library " + libraryID
+ " -- skipping file downloads");
downloadAll = false; downloadAll = false;
} }
} }
if (downloadAll || downloadForced) {
for each(var itemID in _getFilesToDownload(libraryID, !downloadAll)) {
var item = Zotero.Items.get(itemID);
self.queueItem(item);
}
}
// Get files to upload
if (Zotero.Libraries.isFilesEditable(libraryID)) {
for each(var itemID in _getFilesToUpload(libraryID)) {
var item = Zotero.Items.get(itemID);
self.queueItem(item);
}
}
else { else {
Zotero.debug("No last " + libraryModes[libraryID].name Zotero.debug("No file editing access -- skipping file uploads for library " + libraryID);
+ " sync time for library " + libraryID
+ " -- skipping file downloads");
downloadAll = false;
} }
} }
if (downloadAll || downloadForced) { // Start queues for each library
for each(var itemID in _getFilesToDownload(libraryID, !downloadAll)) { for (let libraryID in librarySyncTimes) {
var item = Zotero.Items.get(itemID); libraryID = parseInt(libraryID);
self.queueItem(item); libraryQueues.push(Q.allResolved(
} [libraryID, Zotero.Sync.Storage.QueueManager.start(libraryID)]
));
} }
// Get files to upload // The promise is done when all libraries are done
if (Zotero.Libraries.isFilesEditable(libraryID)) { return Q.all(libraryQueues);
for each(var itemID in _getFilesToUpload(libraryID)) { });
var item = Zotero.Items.get(itemID);
self.queueItem(item);
}
}
else {
Zotero.debug("No file editing access -- skipping file uploads for library " + libraryID);
}
}
// Start queues for each library
for (let libraryID in librarySyncTimes) {
libraryID = parseInt(libraryID);
libraryQueues.push(Q.allResolved(
[libraryID, Zotero.Sync.Storage.QueueManager.start(libraryID)]
));
}
// The promise is done when all libraries are done
return Q.all(libraryQueues);
}) })
.then(function (promises) { .then(function (promises) {
Zotero.debug('Queue manager is finished'); Zotero.debug('Queue manager is finished');
@ -642,209 +681,405 @@ Zotero.Sync.Storage = new function () {
* Scans local files and marks any that have changed for uploading * Scans local files and marks any that have changed for uploading
* and any that are missing for downloading * and any that are missing for downloading
* *
* @param {Object} [itemModTimes] Item mod times indexed by item ids; * @param {Integer} [libraryID]
* items with stored mod times * @param {Integer[]} [itemIDs]
* that differ from the provided * @param {Object} [itemModTimes] Item mod times indexed by item ids;
* time but file mod times * items with stored mod times
* matching the stored time will * that differ from the provided
* be marked for download * time but file mod times
* @param {Boolean} [includePersonalItems=false] * matching the stored time will
* @param {Boolean} [includeGroupItems=false] * be marked for download
* @return {Boolean} TRUE if any items changed state, * @return {Promise} Promise resolving to TRUE if any items changed state,
* FALSE otherwise * FALSE otherwise
*/ */
this.checkForUpdatedFiles = function (itemModTimes, libraryID) { this.checkForUpdatedFiles = function (libraryID, itemIDs, itemModTimes) {
var msg = "Checking for locally changed attachment files"; libraryID = parseInt(libraryID);
if (isNaN(libraryID)) {
if (typeof libraryID != 'undefined') { libraryID = false;
msg += " in library " + libraryID;
if (itemModTimes) {
throw new Error("libraryID is not allowed when itemModTimes is set");
}
} }
else {
if (!itemModTimes) {
return false;
}
}
Zotero.debug(msg);
var changed = false; Components.utils.import("resource://gre/modules/Task.jsm");
return Q(Task.spawn(function () {
var itemIDs = Object.keys(itemModTimes ? itemModTimes : {}); var msg = "Checking for locally changed attachment files";
// Can only handle 999 bound parameters at a time var memmgr = Components.classes["@mozilla.org/memory-reporter-manager;1"]
var numIDs = itemIDs.length; .getService(Components.interfaces.nsIMemoryReporterManager);
var maxIDs = 990; memmgr.init();
var done = 0; Zotero.debug("Memory usage: " + memmgr.resident);
var rows = [];
if (libraryID !== false) {
Zotero.DB.beginTransaction(); if (itemIDs) {
if (!itemIDs.length) {
do { var msg = "No files to check for local changes in library " + libraryID;
var chunk = itemIDs.splice(0, maxIDs); Zotero.debug(msg);
var sql = "SELECT itemID, linkMode, path, storageModTime, storageHash, syncState " throw new Task.Result(false);
+ "FROM itemAttachments JOIN items USING (itemID) " }
+ "WHERE linkMode IN (?,?) AND syncState IN (?,?)"; }
var params = []; if (itemModTimes) {
params.push( throw new Error("itemModTimes is not allowed when libraryID is set");
Zotero.Attachments.LINK_MODE_IMPORTED_FILE, }
Zotero.Attachments.LINK_MODE_IMPORTED_URL,
Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD,
Zotero.Sync.Storage.SYNC_STATE_IN_SYNC
);
if (typeof libraryID != 'undefined') {
sql += " AND libraryID=?";
params.push(libraryID == 0 ? null : libraryID);
}
if (chunk.length) {
sql += " AND itemID IN (" + chunk.map(function () '?').join() + ")";
params = params.concat(chunk);
}
var chunkRows = Zotero.DB.query(sql, params);
if (chunkRows) {
rows = rows.concat(chunkRows);
}
done += chunk.length;
}
while (done < numIDs);
// If no files, or everything is already marked for download,
// we don't need to do anything
if (!rows.length) {
var msg = "No in-sync or to-upload files found";
if (typeof libraryID != 'undefined') {
msg += " in library " + libraryID; msg += " in library " + libraryID;
} }
Zotero.debug(msg); else if (itemIDs) {
Zotero.DB.commitTransaction(); throw new Error("libraryID not provided");
return changed;
}
// Index attachment data by item id
var itemIDs = [];
var attachmentData = {};
for each(var row in rows) {
var id = row.itemID;
itemIDs.push(id);
attachmentData[id] = {
linkMode: row.linkMode,
path: row.path,
mtime: row.storageModTime,
hash: row.storageHash,
state: row.syncState
};
}
rows = null;
var updatedStates = {};
var items = Zotero.Items.get(itemIDs);
for each(var item in items) {
var lk = libraryID + "/" + item.key;
//Zotero.debug("Checking attachment file for item " + lk);
var file = item.getFile(attachmentData[item.id]);
if (!file) {
Zotero.debug("Marking attachment " + lk + " as missing");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
continue;
} }
else if (itemModTimes) {
// If file is already marked for upload, skip check. Even if this if (!Object.keys(itemModTimes).length) {
// is download-marking mode (itemModTimes) and the file was throw new Task.Result(false);
// changed remotely, conflicts are checked at upload time, so we
// don't need to worry about it here.
if (attachmentData[item.id].state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
continue;
}
var fmtime = item.attachmentModificationTime;
//Zotero.debug("Stored mtime is " + attachmentData[item.id].mtime);
//Zotero.debug("File mtime is " + fmtime);
// Download-marking mode
if (itemModTimes) {
Zotero.debug("Remote mod time for item " + lk + " is " + itemModTimes[item.id]);
// Ignore attachments whose stored mod times haven't changed
if (row.storageModTime == itemModTimes[id]) {
Zotero.debug("Storage mod time (" + row.storageModTime + ") "
+ "hasn't changed for item " + lk);
continue;
} }
msg += " in download-marking mode";
Zotero.debug("Marking attachment " + lk + " for download");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
}
var mtime = attachmentData[item.id].mtime;
// If stored time matches file, it hasn't changed locally
if (mtime == fmtime) {
continue;
}
// Allow floored timestamps for filesystems that don't support
// millisecond precision (e.g., HFS+)
if (Math.floor(mtime / 1000) * 1000 == fmtime || Math.floor(fmtime / 1000) * 1000 == mtime) {
Zotero.debug("File mod times are within one-second precision "
+ "(" + fmtime + " ≅ " + mtime + ") for " + file.leafName
+ " for item " + lk + " -- ignoring");
continue;
}
// Allow timestamp to be exactly one hour off to get around
// time zone issues -- there may be a proper way to fix this
if (Math.abs(fmtime - mtime) == 3600000
// And check with one-second precision as well
|| Math.abs(fmtime - Math.floor(mtime / 1000) * 1000) == 3600000
|| Math.abs(Math.floor(fmtime / 1000) * 1000 - mtime) == 3600000) {
Zotero.debug("File mod time (" + fmtime + ") is exactly one "
+ "hour off remote file (" + mtime + ") for item " + lk
+ "-- assuming time zone issue and skipping upload");
continue;
}
// If file hash matches stored hash, only the mod time changed, so skip
var f = item.getFile();
if (f) {
Zotero.debug(f.path);
} }
else { else {
Zotero.debug("File for item " + lk + " missing before getting hash"); throw new Error("libraryID, itemIDs, or itemModTimes must be provided");
} }
var fileHash = item.attachmentHash; Zotero.debug(msg);
if (attachmentData[item.id].hash && attachmentData[item.id].hash == fileHash) {
Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") " var changed = false;
+ "but hash did for " + file.leafName + " for item " + lk
+ " -- updating file mod time"); if (!itemIDs) {
try { itemIDs = Object.keys(itemModTimes ? itemModTimes : {});
file.lastModifiedTime = attachmentData[item.id].mtime;
}
catch (e) {
Zotero.File.checkFileAccessError(e, file, 'update');
}
continue;
} }
// Mark file for upload // Can only handle 999 bound parameters at a time
Zotero.debug("Marking attachment " + lk + " as changed " var numIDs = itemIDs.length;
+ "(" + mtime + " != " + fmtime + ")"); var maxIDs = 990;
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD; var done = 0;
} var rows = [];
for (var itemID in updatedStates) { Zotero.DB.beginTransaction();
Zotero.Sync.Storage.setSyncState(itemID, updatedStates[itemID]);
changed = true; do {
} var chunk = itemIDs.splice(0, maxIDs);
var sql = "SELECT itemID, linkMode, path, storageModTime, storageHash, syncState "
if (!changed) { + "FROM itemAttachments JOIN items USING (itemID) "
Zotero.debug("No synced files have changed locally"); + "WHERE linkMode IN (?,?) AND syncState IN (?,?)";
} var params = [];
params.push(
Zotero.DB.commitTransaction(); Zotero.Attachments.LINK_MODE_IMPORTED_FILE,
return changed; Zotero.Attachments.LINK_MODE_IMPORTED_URL,
} Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD,
Zotero.Sync.Storage.SYNC_STATE_IN_SYNC
);
if (libraryID !== false) {
sql += " AND libraryID=?";
params.push(libraryID == 0 ? null : libraryID);
}
if (chunk.length) {
sql += " AND itemID IN (" + chunk.map(function () '?').join() + ")";
params = params.concat(chunk);
}
var chunkRows = Zotero.DB.query(sql, params);
if (chunkRows) {
rows = rows.concat(chunkRows);
}
done += chunk.length;
}
while (done < numIDs);
Zotero.DB.commitTransaction();
// If no files, or everything is already marked for download,
// we don't need to do anything
if (!rows.length) {
var msg = "No in-sync or to-upload files found";
if (libraryID !== false) {
msg += " in library " + libraryID;
}
Zotero.debug(msg);
throw new Task.Result(changed);
}
// Index attachment data by item id
itemIDs = [];
var attachmentData = {};
for each(let row in rows) {
var id = row.itemID;
itemIDs.push(id);
attachmentData[id] = {
linkMode: row.linkMode,
path: row.path,
mtime: row.storageModTime,
hash: row.storageHash,
state: row.syncState
};
}
rows = null;
var t = new Date();
var items = Zotero.Items.get(itemIDs);
var numItems = items.length;
var updatedStates = {};
// OS.File didn't work reliably before Firefox 23, so use the old code
if (Zotero.platformMajorVersion < 23) {
Zotero.debug("Performing synchronous file update check");
for each(var item in items) {
// Spin the event loop during synchronous file access
yield Q.delay(1);
Zotero.debug("Memory usage: " + memmgr.resident);
let row = attachmentData[item.id];
let lk = item.libraryID + "/" + item.key;
Zotero.debug("Checking attachment file for item " + lk);
var file = item.getFile(row);
if (!file) {
Zotero.debug("Marking attachment " + lk + " as missing");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
continue;
}
// If file is already marked for upload, skip check. Even if this
// is download-marking mode (itemModTimes) and the file was
// changed remotely, conflicts are checked at upload time, so we
// don't need to worry about it here.
if (row.state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
continue;
}
var fmtime = item.attachmentModificationTime;
//Zotero.debug("Stored mtime is " + row.mtime);
//Zotero.debug("File mtime is " + fmtime);
// Download-marking mode
if (itemModTimes) {
Zotero.debug("Remote mod time for item " + lk + " is " + itemModTimes[item.id]);
// Ignore attachments whose stored mod times haven't changed
if (row.storageModTime == itemModTimes[id]) {
Zotero.debug("Storage mod time (" + row.storageModTime + ") "
+ "hasn't changed for item " + lk);
continue;
}
Zotero.debug("Marking attachment " + lk + " for download");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
}
var mtime = row.mtime;
// If stored time matches file, it hasn't changed locally
if (mtime == fmtime) {
continue;
}
// Allow floored timestamps for filesystems that don't support
// millisecond precision (e.g., HFS+)
if (Math.floor(mtime / 1000) * 1000 == fmtime || Math.floor(fmtime / 1000) * 1000 == mtime) {
Zotero.debug("File mod times are within one-second precision "
+ "(" + fmtime + " ≅ " + mtime + ") for " + file.leafName
+ " for item " + lk + " -- ignoring");
continue;
}
// Allow timestamp to be exactly one hour off to get around
// time zone issues -- there may be a proper way to fix this
if (Math.abs(fmtime - mtime) == 3600000
// And check with one-second precision as well
|| Math.abs(fmtime - Math.floor(mtime / 1000) * 1000) == 3600000
|| Math.abs(Math.floor(fmtime / 1000) * 1000 - mtime) == 3600000) {
Zotero.debug("File mod time (" + fmtime + ") is exactly one "
+ "hour off remote file (" + mtime + ") for item " + lk
+ "-- assuming time zone issue and skipping upload");
continue;
}
// If file hash matches stored hash, only the mod time changed, so skip
var f = item.getFile();
if (f) {
Zotero.debug(f.path);
}
else {
Zotero.debug("File for item " + lk + " missing before getting hash");
}
var fileHash = item.attachmentHash;
if (row.hash && row.hash == fileHash) {
Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") "
+ "but hash did for " + file.leafName + " for item " + lk
+ " -- updating file mod time");
try {
file.lastModifiedTime = row.mtime;
}
catch (e) {
Zotero.File.checkFileAccessError(e, file, 'update');
}
continue;
}
// Mark file for upload
Zotero.debug("Marking attachment " + lk + " as changed "
+ "(" + mtime + " != " + fmtime + ")");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD;
}
for (var itemID in updatedStates) {
Zotero.Sync.Storage.setSyncState(itemID, updatedStates[itemID]);
changed = true;
}
if (!changed) {
Zotero.debug("No synced files have changed locally");
}
Zotero.debug("Checked " + numItems + " files in " + (new Date() - t) + "ms");
throw new Task.Result(changed);
}
Components.utils.import("resource://gre/modules/osfile.jsm")
let checkItems = function () {
if (!items.length) return;
Zotero.debug("Memory usage: " + memmgr.resident);
let item = items.shift();
let row = attachmentData[item.id];
let lk = item.libraryKey;
Zotero.debug("Checking attachment file for item " + lk);
let nsIFile = item.getFile(row, true);
let file = null;
return Q(OS.File.open(nsIFile.path))
.then(function (promisedFile) {
file = promisedFile;
return file.stat()
.then(function (info) {
Zotero.debug("Memory usage: " + memmgr.resident);
var fmtime = info.lastModificationDate.getTime();
Zotero.debug("File modification time for item " + lk + " is " + fmtime);
if (fmtime < 1) {
Zotero.debug("File mod time " + fmtime + " is less than 1 -- interpreting as 1", 2);
fmtime = 1;
}
// If file is already marked for upload, skip check. Even if this
// is download-marking mode (itemModTimes) and the file was
// changed remotely, conflicts are checked at upload time, so we
// don't need to worry about it here.
if (row.state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
return;
}
//Zotero.debug("Stored mtime is " + row.mtime);
//Zotero.debug("File mtime is " + fmtime);
// Download-marking mode
if (itemModTimes) {
Zotero.debug("Remote mod time for item " + lk + " is " + itemModTimes[item.id]);
// Ignore attachments whose stored mod times haven't changed
if (row.storageModTime == itemModTimes[id]) {
Zotero.debug("Storage mod time (" + row.storageModTime + ") "
+ "hasn't changed for item " + lk);
return;
}
Zotero.debug("Marking attachment " + lk + " for download");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
}
var mtime = row.mtime;
// If stored time matches file, it hasn't changed locally
if (mtime == fmtime) {
return;
}
// Allow floored timestamps for filesystems that don't support
// millisecond precision (e.g., HFS+)
if (Math.floor(mtime / 1000) * 1000 == fmtime || Math.floor(fmtime / 1000) * 1000 == mtime) {
Zotero.debug("File mod times are within one-second precision "
+ "(" + fmtime + " ≅ " + mtime + ") for " + file.leafName
+ " for item " + lk + " -- ignoring");
return;
}
// Allow timestamp to be exactly one hour off to get around
// time zone issues -- there may be a proper way to fix this
if (Math.abs(fmtime - mtime) == 3600000
// And check with one-second precision as well
|| Math.abs(fmtime - Math.floor(mtime / 1000) * 1000) == 3600000
|| Math.abs(Math.floor(fmtime / 1000) * 1000 - mtime) == 3600000) {
Zotero.debug("File mod time (" + fmtime + ") is exactly one "
+ "hour off remote file (" + mtime + ") for item " + lk
+ "-- assuming time zone issue and skipping upload");
return;
}
// If file hash matches stored hash, only the mod time changed, so skip
return Zotero.Utilities.Internal.md5Async(file)
.then(function (fileHash) {
if (row.hash && row.hash == fileHash) {
Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") "
+ "but hash did for " + file.leafName + " for item " + lk
+ " -- updating file mod time");
try {
nsIFile.lastModifiedTime = row.mtime;
}
catch (e) {
Zotero.File.checkFileAccessError(e, nsIFile, 'update');
}
return;
}
// Mark file for upload
Zotero.debug("Marking attachment " + lk + " as changed "
+ "(" + mtime + " != " + fmtime + ")");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD;
});
});
})
.finally(function () {
if (file) {
Zotero.debug("Closing file for item " + lk);
file.close();
}
})
.catch(function (e) {
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
Zotero.debug("Marking attachment " + lk + " as missing");
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
return;
}
if (e instanceof OS.File.Error && e.becauseClosed) {
Zotero.debug("File was closed", 2);
}
else {
Zotero.debug(e);
Zotero.debug(e.toString());
}
throw new Error("Error " + e.operation + " " + nsIFile.path);
})
.then(function () {
return checkItems();
});
};
throw new Task.Result(checkItems()
.then(function () {
for (let itemID in updatedStates) {
Zotero.Sync.Storage.setSyncState(itemID, updatedStates[itemID]);
changed = true;
}
if (!changed) {
Zotero.debug("No synced files have changed locally");
}
Zotero.debug("Checked " + numItems + " files in " + (new Date() - t) + "ms");
return changed;
}));
}));
};
/** /**
@ -1144,6 +1379,20 @@ Zotero.Sync.Storage = new function () {
} }
this.notify = function(event, type, ids, extraData) {
if (event == 'open' && type == 'file') {
let timestamp = new Date().getTime();
for each(let id in ids) {
_uploadCheckFiles.push({
itemID: id,
timestamp: timestamp
});
}
}
}
// //
// Private methods // Private methods
// //
@ -1774,6 +2023,36 @@ Zotero.Sync.Storage = new function () {
} }
/**
* Get files to check for local modifications for uploading
*
* This includes files previously modified and files opened externally
* via Zotero within _maxCheckAgeInSeconds.
*/
function _getFilesToCheck(libraryID) {
var minTime = new Date().getTime() - (_maxCheckAgeInSeconds * 1000);
// Get files by modification time
var sql = "SELECT itemID FROM itemAttachments JOIN items USING (itemID) "
+ "WHERE libraryID=? AND linkMode IN (?,?) AND syncState IN (?) AND "
+ "storageModTime>=?";
var params = [
libraryID == 0 ? null : libraryID,
Zotero.Attachments.LINK_MODE_IMPORTED_FILE,
Zotero.Attachments.LINK_MODE_IMPORTED_URL,
Zotero.Sync.Storage.SYNC_STATE_IN_SYNC,
minTime
];
var itemIDs = Zotero.DB.columnQuery(sql, params) || [];
// Get files by open time
_uploadCheckFiles.filter(function (x) x.timestamp >= minTime);
itemIDs = itemIDs.concat([x.itemID for each(x in _uploadCheckFiles)])
return Zotero.Utilities.arrayUnique(itemIDs);
}
/** /**
* @inner * @inner
* @return {String[]|FALSE} Array of keys, or FALSE if none * @return {String[]|FALSE} Array of keys, or FALSE if none

View File

@ -74,8 +74,8 @@ Zotero.Sync.Storage.Mode.prototype.checkServerCallback = function (uri, status,
return this._checkServerCallback(uri, status, window, skipSuccessMessage); return this._checkServerCallback(uri, status, window, skipSuccessMessage);
} }
Zotero.Sync.Storage.Mode.prototype.cacheCredentials = function (callback) { Zotero.Sync.Storage.Mode.prototype.cacheCredentials = function () {
return this._cacheCredentials(callback); return this._cacheCredentials();
} }
Zotero.Sync.Storage.Mode.prototype.purgeDeletedStorageFiles = function (callback) { Zotero.Sync.Storage.Mode.prototype.purgeDeletedStorageFiles = function (callback) {

View File

@ -44,17 +44,12 @@ Zotero.Sync.Storage.StreamListener.prototype = {
// nsIProgressEventSink // nsIProgressEventSink
onProgress: function (request, context, progress, progressMax) { onProgress: function (request, context, progress, progressMax) {
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=451991 Zotero.debug("onProgress with " + progress + "/" + progressMax);
// (fixed in Fx3.1)
if (progress > progressMax) {
progress = progressMax;
}
//Zotero.debug("onProgress with " + progress + "/" + progressMax);
this._onProgress(request, progress, progressMax); this._onProgress(request, progress, progressMax);
}, },
onStatus: function (request, context, status, statusArg) { onStatus: function (request, context, status, statusArg) {
//Zotero.debug('onStatus'); Zotero.debug('onStatus with ' + status);
}, },
// nsIRequestObserver // nsIRequestObserver
@ -67,7 +62,7 @@ Zotero.Sync.Storage.StreamListener.prototype = {
}, },
onStopRequest: function (request, context, status) { onStopRequest: function (request, context, status) {
Zotero.debug('onStopRequest'); Zotero.debug('onStopRequest with ' + status);
switch (status) { switch (status) {
case 0: case 0:
@ -84,7 +79,7 @@ Zotero.Sync.Storage.StreamListener.prototype = {
// nsIWebProgressListener // nsIWebProgressListener
onProgressChange: function (wp, request, curSelfProgress, onProgressChange: function (wp, request, curSelfProgress,
maxSelfProgress, curTotalProgress, maxTotalProgress) { maxSelfProgress, curTotalProgress, maxTotalProgress) {
//Zotero.debug("onProgressChange with " + curTotalProgress + "/" + maxTotalProgress); Zotero.debug("onProgressChange with " + curTotalProgress + "/" + maxTotalProgress);
// onProgress gets called too, so this isn't necessary // onProgress gets called too, so this isn't necessary
//this._onProgress(request, curTotalProgress, maxTotalProgress); //this._onProgress(request, curTotalProgress, maxTotalProgress);
@ -108,8 +103,12 @@ Zotero.Sync.Storage.StreamListener.prototype = {
onStatusChange: function (progress, request, status, message) { onStatusChange: function (progress, request, status, message) {
Zotero.debug("onStatusChange with '" + message + "'"); Zotero.debug("onStatusChange with '" + message + "'");
}, },
onLocationChange: function () {}, onLocationChange: function () {
onSecurityChange: function () {}, Zotero.debug('onLocationChange');
},
onSecurityChange: function () {
Zotero.debug('onSecurityChange');
},
// nsIStreamListener // nsIStreamListener
onDataAvailable: function (request, context, stream, sourceOffset, length) { onDataAvailable: function (request, context, stream, sourceOffset, length) {
@ -119,7 +118,9 @@ Zotero.Sync.Storage.StreamListener.prototype = {
.createInstance(Components.interfaces.nsIScriptableInputStream); .createInstance(Components.interfaces.nsIScriptableInputStream);
scriptableInputStream.init(stream); scriptableInputStream.init(stream);
this._response += scriptableInputStream.read(length); var data = scriptableInputStream.read(length);
Zotero.debug(data);
this._response += data;
}, },
// nsIChannelEventSink // nsIChannelEventSink

View File

@ -1102,7 +1102,7 @@ Zotero.Sync.Storage.WebDAV = (function () {
obj._cacheCredentials = function () { obj._cacheCredentials = function () {
if (_cachedCredentials) { if (_cachedCredentials) {
Zotero.debug("Credentials are already cached"); Zotero.debug("WebDAV credentials are already cached");
return; return;
} }

View File

@ -86,7 +86,7 @@ Zotero.Sync.Storage.ZFS = (function () {
} }
else { else {
var msg = "Unexpected status code " + e.xmlhttp.status var msg = "Unexpected status code " + e.xmlhttp.status
+ " getting storage file info"; + " getting storage file info for item " + item.libraryKey;
} }
Zotero.debug(msg, 1); Zotero.debug(msg, 1);
Zotero.debug(e.xmlhttp.responseText); Zotero.debug(e.xmlhttp.responseText);
@ -1002,8 +1002,8 @@ Zotero.Sync.Storage.ZFS = (function () {
obj._cacheCredentials = function () { obj._cacheCredentials = function () {
if (_cachedCredentials) { if (_cachedCredentials) {
Zotero.debug("Credentials are already cached"); Zotero.debug("ZFS credentials are already cached");
return; return Q();
} }
var uri = this.rootURI; var uri = this.rootURI;

View File

@ -520,6 +520,7 @@ Zotero.Sync.Runner = new function () {
var _autoSyncTimer; var _autoSyncTimer;
var _queue; var _queue;
var _background; var _background;
var _firstInSession = true;
var _lastSyncStatus; var _lastSyncStatus;
var _currentSyncStatusLabel; var _currentSyncStatusLabel;
@ -533,7 +534,13 @@ Zotero.Sync.Runner = new function () {
this.IdleListener.init(); this.IdleListener.init();
} }
this.sync = function (background) { this.sync = function (options) {
if (!options) options = {};
if (_firstInSession) {
options.firstInSession = true;
_firstInSession = false;
}
_warning = null; _warning = null;
if (Zotero.HTTP.browserIsOffline()){ if (Zotero.HTTP.browserIsOffline()){
@ -549,7 +556,7 @@ Zotero.Sync.Runner = new function () {
// Purge deleted objects so they don't cause sync errors (e.g., long tags) // Purge deleted objects so they don't cause sync errors (e.g., long tags)
Zotero.purgeDataObjects(true); Zotero.purgeDataObjects(true);
_background = !!background; _background = !!options.background;
this.setSyncIcon('animate'); this.setSyncIcon('animate');
var finalCallbacks = { var finalCallbacks = {
@ -562,7 +569,7 @@ Zotero.Sync.Runner = new function () {
var storageSync = function () { var storageSync = function () {
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.syncingFiles')); Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.syncingFiles'));
Zotero.Sync.Storage.sync() Zotero.Sync.Storage.sync(options)
.then(function (results) { .then(function (results) {
Zotero.debug("File sync is finished"); Zotero.debug("File sync is finished");
@ -692,7 +699,9 @@ Zotero.Sync.Runner = new function () {
return; return;
} }
Zotero.Sync.Runner.sync(background); Zotero.Sync.Runner.sync({
background: background
});
} }
} }
@ -1143,8 +1152,10 @@ Zotero.Sync.Runner.IdleListener = {
Zotero.debug("Beginning idle sync"); Zotero.debug("Beginning idle sync");
Zotero.Sync.Runner.sync(true); Zotero.Sync.Runner.sync({
Zotero.Sync.Runner.setSyncTimeout(this._idleTimeout, true); background: true
});
Zotero.Sync.Runner.setSyncTimeout(this._idleTimeout, true, true);
}, },
_backObserver: { _backObserver: {
@ -1160,7 +1171,9 @@ Zotero.Sync.Runner.IdleListener = {
return; return;
} }
Zotero.debug("Beginning return-from-idle sync"); Zotero.debug("Beginning return-from-idle sync");
Zotero.Sync.Runner.sync(true); Zotero.Sync.Runner.sync({
background: true
});
} }
}, },
@ -1561,219 +1574,216 @@ Zotero.Sync.Server = new function () {
_error(e); _error(e);
} }
try { Components.utils.import("resource://gre/modules/Task.jsm");
var gen = Zotero.Sync.Server.Data.processUpdatedXML(
responseNode.getElementsByTagName('updated')[0], Task.spawn(Zotero.Sync.Server.Data.processUpdatedXML(
lastLocalSyncDate, responseNode.getElementsByTagName('updated')[0],
syncSession, lastLocalSyncDate,
libraryID, syncSession,
function (xmlstr) { libraryID,
Zotero.UnresponsiveScriptIndicator.enable(); function (xmlstr) {
Zotero.UnresponsiveScriptIndicator.enable();
if (Zotero.locked) {
Zotero.hideZoteroPaneOverlay(); if (Zotero.locked) {
} Zotero.hideZoteroPaneOverlay();
Zotero.suppressUIUpdates = false; }
_updatesInProgress = false; Zotero.suppressUIUpdates = false;
_updatesInProgress = false;
if (xmlstr === false) {
Zotero.debug("Sync cancelled"); if (xmlstr === false) {
Zotero.DB.rollbackTransaction(); Zotero.debug("Sync cancelled");
Zotero.reloadDataObjects(); Zotero.DB.rollbackTransaction();
Zotero.Sync.EventListener.resetIgnored(); Zotero.reloadDataObjects();
_syncInProgress = false; Zotero.Sync.EventListener.resetIgnored();
_callbacks.onStop(); _syncInProgress = false;
_callbacks.onStop();
return;
}
if (xmlstr) {
Zotero.debug(xmlstr);
}
if (Zotero.Prefs.get('sync.debugBreak')) {
Zotero.debug('===============');
throw ("break");
}
if (!xmlstr) {
Zotero.debug("Nothing to upload to server");
Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
Zotero.Sync.Server.nextLocalSyncDate = false;
Zotero.DB.commitTransaction();
_syncInProgress = false;
_callbacks.onSuccess();
return;
}
Zotero.DB.commitTransaction();
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadingData'));
var url = _serverURL + 'upload';
var body = _apiVersionComponent
+ '&' + Zotero.Sync.Server.sessionIDComponent
+ '&updateKey=' + updateKey
+ '&data=' + encodeURIComponent(xmlstr);
//var file = Zotero.getZoteroDirectory();
//file.append('lastupload.txt');
//Zotero.File.putContents(file, body);
var uploadCallback = function (xmlhttp) {
if (xmlhttp.status == 409) {
Zotero.debug("Upload key is no longer valid -- restarting sync");
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
return; return;
} }
if (xmlstr) { _checkResponse(xmlhttp);
Zotero.debug(xmlstr);
Zotero.debug(xmlhttp.responseText);
var response = xmlhttp.responseXML.childNodes[0];
if (_checkServerLock(response, function (mode) {
switch (mode) {
// If the upload was queued, keep checking back
case 'queued':
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadAccepted'));
var url = _serverURL + 'uploadstatus';
var body = _apiVersionComponent
+ '&' + Zotero.Sync.Server.sessionIDComponent;
Zotero.HTTP.doPost(url, body, function (xmlhttp) {
uploadCallback(xmlhttp);
});
break;
// If affected libraries were locked, restart sync,
// since the upload key would be out of date anyway
case 'locked':
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
break;
default:
throw ("Unexpected server lock mode '" + mode + "' in Zotero.Sync.Server.upload()");
}
})) { return; }
if (response.firstChild.tagName == 'error') {
// handle error
_error(response.firstChild.firstChild.nodeValue);
} }
if (Zotero.Prefs.get('sync.debugBreak')) { if (response.firstChild.localName != 'uploaded') {
Zotero.debug('==============='); _error("Unexpected upload response '" + response.firstChild.localName
throw ("break"); + "' in Zotero.Sync.Server.sync()");
} }
if (!xmlstr) { Zotero.DB.beginTransaction();
Zotero.debug("Nothing to upload to server"); Zotero.Sync.purgeDeletedObjects(nextLocalSyncTime);
Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp'); Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime; Zotero.Sync.Server.nextLocalSyncDate = false;
Zotero.Sync.Server.nextLocalSyncDate = false; Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
Zotero.DB.commitTransaction();
_syncInProgress = false; var sql = "UPDATE syncedSettings SET synced=1";
_callbacks.onSuccess(); Zotero.DB.query(sql);
return;
} //throw('break2');
Zotero.DB.commitTransaction(); Zotero.DB.commitTransaction();
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadingData')); // Check if any items were modified during /upload,
// and restart the sync if so
var url = _serverURL + 'upload'; if (Zotero.Items.getNewer(nextLocalSyncDate, true)) {
var body = _apiVersionComponent Zotero.debug("Items were modified during upload -- restarting sync");
+ '&' + Zotero.Sync.Server.sessionIDComponent Zotero.Sync.Server.sync(_callbacks, true, true);
+ '&updateKey=' + updateKey return;
+ '&data=' + encodeURIComponent(xmlstr);
//var file = Zotero.getZoteroDirectory();
//file.append('lastupload.txt');
//Zotero.File.putContents(file, body);
var uploadCallback = function (xmlhttp) {
if (xmlhttp.status == 409) {
Zotero.debug("Upload key is no longer valid -- restarting sync");
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
return;
}
_checkResponse(xmlhttp);
Zotero.debug(xmlhttp.responseText);
var response = xmlhttp.responseXML.childNodes[0];
if (_checkServerLock(response, function (mode) {
switch (mode) {
// If the upload was queued, keep checking back
case 'queued':
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadAccepted'));
var url = _serverURL + 'uploadstatus';
var body = _apiVersionComponent
+ '&' + Zotero.Sync.Server.sessionIDComponent;
Zotero.HTTP.doPost(url, body, function (xmlhttp) {
uploadCallback(xmlhttp);
});
break;
// If affected libraries were locked, restart sync,
// since the upload key would be out of date anyway
case 'locked':
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
break;
default:
throw ("Unexpected server lock mode '" + mode + "' in Zotero.Sync.Server.upload()");
}
})) { return; }
if (response.firstChild.tagName == 'error') {
// handle error
_error(response.firstChild.firstChild.nodeValue);
}
if (response.firstChild.localName != 'uploaded') {
_error("Unexpected upload response '" + response.firstChild.localName
+ "' in Zotero.Sync.Server.sync()");
}
Zotero.DB.beginTransaction();
Zotero.Sync.purgeDeletedObjects(nextLocalSyncTime);
Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
Zotero.Sync.Server.nextLocalSyncDate = false;
Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
var sql = "UPDATE syncedSettings SET synced=1";
Zotero.DB.query(sql);
//throw('break2');
Zotero.DB.commitTransaction();
// Check if any items were modified during /upload,
// and restart the sync if so
if (Zotero.Items.getNewer(nextLocalSyncDate, true)) {
Zotero.debug("Items were modified during upload -- restarting sync");
Zotero.Sync.Server.sync(_callbacks, true, true);
return;
}
_syncInProgress = false;
_callbacks.onSuccess();
} }
var compress = Zotero.Prefs.get('sync.server.compressData'); _syncInProgress = false;
// Compress upload data _callbacks.onSuccess();
if (compress) { }
// Callback when compressed data is available
var bufferUploader = function (data) { var compress = Zotero.Prefs.get('sync.server.compressData');
var gzurl = url + '?gzip=1'; // Compress upload data
if (compress) {
var oldLen = body.length; // Callback when compressed data is available
var newLen = data.length; var bufferUploader = function (data) {
var savings = Math.round(((oldLen - newLen) / oldLen) * 100) var gzurl = url + '?gzip=1';
Zotero.debug("HTTP POST " + newLen + " bytes to " + gzurl
+ " (gzipped from " + oldLen + " bytes; " var oldLen = body.length;
+ savings + "% savings)"); var newLen = data.length;
var savings = Math.round(((oldLen - newLen) / oldLen) * 100)
if (Zotero.HTTP.browserIsOffline()) { Zotero.debug("HTTP POST " + newLen + " bytes to " + gzurl
Zotero.debug('Browser is offline'); + " (gzipped from " + oldLen + " bytes; "
return false; + savings + "% savings)");
}
if (Zotero.HTTP.browserIsOffline()) {
var req = Zotero.debug('Browser is offline');
Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]. return false;
createInstance(); }
req.open('POST', gzurl, true);
req.setRequestHeader('Content-Type', "application/octet-stream"); var req =
req.setRequestHeader('Content-Encoding', 'gzip'); Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance();
req.onreadystatechange = function () { req.open('POST', gzurl, true);
if (req.readyState == 4) { req.setRequestHeader('Content-Type', "application/octet-stream");
uploadCallback(req); req.setRequestHeader('Content-Encoding', 'gzip');
}
}; req.onreadystatechange = function () {
try { if (req.readyState == 4) {
req.sendAsBinary(data); uploadCallback(req);
} }
catch (e) { };
_error(e); try {
} req.sendAsBinary(data);
} }
catch (e) {
// Get input stream from POST data _error(e);
var unicodeConverter = }
Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] }
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
unicodeConverter.charset = "UTF-8"; // Get input stream from POST data
var bodyStream = unicodeConverter.convertToInputStream(body); var unicodeConverter =
Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
// Get listener for when compression is done .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
var listener = new Zotero.BufferedInputListener(bufferUploader); unicodeConverter.charset = "UTF-8";
var bodyStream = unicodeConverter.convertToInputStream(body);
// Initialize stream converter
var converter = // Get listener for when compression is done
Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=gzip"] var listener = new Zotero.BufferedInputListener(bufferUploader);
.createInstance(Components.interfaces.nsIStreamConverter);
converter.asyncConvertData("uncompressed", "gzip", listener, null); // Initialize stream converter
var converter =
// Send input stream to stream converter Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=gzip"]
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]. .createInstance(Components.interfaces.nsIStreamConverter);
createInstance(Components.interfaces.nsIInputStreamPump); converter.asyncConvertData("uncompressed", "gzip", listener, null);
pump.init(bodyStream, -1, -1, 0, 0, true);
pump.asyncRead(converter, null); // Send input stream to stream converter
} var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
createInstance(Components.interfaces.nsIInputStreamPump);
// Don't compress upload data pump.init(bodyStream, -1, -1, 0, 0, true);
else { pump.asyncRead(converter, null);
Zotero.HTTP.doPost(url, body, uploadCallback); }
}
// Don't compress upload data
else {
Zotero.HTTP.doPost(url, body, uploadCallback);
} }
);
try {
gen.next();
} }
catch (e if e.toString() === "[object StopIteration]") {} ))
Zotero.pumpGenerator(gen, false, errorHandler); .then(
} null,
catch (e) { function (e) {
errorHandler(e, true); errorHandler(e);
} }
);
} }
catch (e) { catch (e) {
_error(e); _error(e);
@ -2024,7 +2034,9 @@ Zotero.Sync.Server = new function () {
Zotero.Sync.Server.resetClient(); Zotero.Sync.Server.resetClient();
Zotero.Sync.Server.canAutoResetClient = false; Zotero.Sync.Server.canAutoResetClient = false;
Zotero.Sync.Runner.sync(background); Zotero.Sync.Runner.sync({
background: background
});
}, 1); }, 1);
break; break;
@ -2127,7 +2139,9 @@ Zotero.Sync.Server = new function () {
Zotero.Sync.Server.canAutoResetClient = false; Zotero.Sync.Server.canAutoResetClient = false;
} }
Zotero.Sync.Runner.sync(background); Zotero.Sync.Runner.sync({
background: background
});
}, 1); }, 1);
break; break;
@ -2362,7 +2376,9 @@ Zotero.Sync.Server = new function () {
} }
Zotero.Sync.Server.resetClient(); Zotero.Sync.Server.resetClient();
Zotero.Sync.Server.canAutoResetClient = false; Zotero.Sync.Server.canAutoResetClient = false;
Zotero.Sync.Runner.sync(background); Zotero.Sync.Runner.sync({
background: background
});
}, 1); }, 1);
break; break;
@ -3433,14 +3449,14 @@ Zotero.Sync.Server.Data = new function() {
// Check mod times and hashes of updated items against stored values to see // Check mod times and hashes of updated items against stored values to see
// if they've been updated elsewhere and mark for download if so // if they've been updated elsewhere and mark for download if so
if (type == 'item' && Object.keys(itemStorageModTimes).length) { if (type == 'item' && Object.keys(itemStorageModTimes).length) {
Zotero.Sync.Storage.checkForUpdatedFiles(itemStorageModTimes); yield Zotero.Sync.Storage.checkForUpdatedFiles(null, null, itemStorageModTimes);
} }
} }
if (_timeToYield()) yield true; if (_timeToYield()) yield true;
callback(Zotero.Sync.Server.Data.buildUploadXML(syncSession)); callback(Zotero.Sync.Server.Data.buildUploadXML(syncSession));
} };
/** /**

View File

@ -80,9 +80,6 @@ Zotero.Utilities.Internal = {
return hash; return hash;
} }
/*
// This created 36-character hashes
// return the two-digit hexadecimal code for a byte // return the two-digit hexadecimal code for a byte
function toHexString(charCode) { function toHexString(charCode) {
return ("0" + charCode.toString(16)).slice(-2); return ("0" + charCode.toString(16)).slice(-2);
@ -90,18 +87,98 @@ Zotero.Utilities.Internal = {
// convert the binary hash data to a hex string. // convert the binary hash data to a hex string.
return [toHexString(hash.charCodeAt(i)) for (i in hash)].join(""); return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
*/ },
/**
* @param {OS.File|nsIFile|String} file File or file path
* @param {Boolean} [base64=FALSE] Return as base-64-encoded string
* rather than hex string
*/
"md5Async": function (file, base64) {
Components.utils.import("resource://gre/modules/osfile.jsm");
const CHUNK_SIZE = 16384;
// From http://rcrowley.org/2007/11/15/md5-in-xulrunner-or-firefox-extensions/ var deferred = Q.defer();
var ascii = [];
var ii = hash.length; function toHexString(charCode) {
for (var i = 0; i < ii; ++i) { return ("0" + charCode.toString(16)).slice(-2);
var c = hash.charCodeAt(i);
var ones = c % 16;
var tens = c >> 4;
ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48)));
} }
return ascii.join('');
var ch = Components.classes["@mozilla.org/security/hash;1"]
.createInstance(Components.interfaces.nsICryptoHash);
ch.init(ch.MD5);
// Recursively read chunks of the file, and resolve the promise
// with the hash when done
let readChunk = function readChunk(file) {
file.read(CHUNK_SIZE)
.then(
function readSuccess(data) {
ch.update(data, data.length);
if (data.length == CHUNK_SIZE) {
readChunk(file);
}
else {
let hash = ch.finish(base64);
// Base64
if (base64) {
deferred.resolve(hash);
}
// Hex string
else {
deferred.resolve(
[toHexString(hash.charCodeAt(i))
for (i in hash)].join("")
);
}
}
},
function (e) {
try {
ch.finish(false);
}
catch (e) {}
deferred.reject(e);
}
)
.then(
null,
function (e) {
try {
ch.finish(false);
}
catch (e) {}
deferred.reject(e);
}
);
}
if (file instanceof OS.File) {
readChunk(file);
}
else {
if (file instanceof Components.interfaces.nsIFile) {
var path = file.path;
}
else {
var path = file;
}
OS.File.open(path)
.then(
function opened(file) {
readChunk(file);
},
function (e) {
deferred.reject(e);
}
);
}
return deferred.promise;
}, },

View File

@ -219,6 +219,11 @@ Components.utils.import("resource://gre/modules/Services.jsm");
Zotero.Debug.init(); Zotero.Debug.init();
this.mainThread = Services.tm.mainThread; this.mainThread = Services.tm.mainThread;
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
this.platformVersion = appInfo.platformVersion;
this.platformMajorVersion = parseInt(appInfo.platformVersion.match(/^[0-9]+/)[0]);
this.isFx = true; this.isFx = true;
this.isStandalone = Services.appinfo.ID == ZOTERO_CONFIG['GUID']; this.isStandalone = Services.appinfo.ID == ZOTERO_CONFIG['GUID'];
@ -1892,13 +1897,13 @@ Components.utils.import("resource://gre/modules/Services.jsm");
'[JavaScript Error: "this._uiElement is null', '[JavaScript Error: "this._uiElement is null',
'Error: a._updateVisibleText is not a function', 'Error: a._updateVisibleText is not a function',
'[JavaScript Error: "Warning: unrecognized command line flag ', '[JavaScript Error: "Warning: unrecognized command line flag ',
'[JavaScript Error: "Warning: unrecognized command line flag -foreground',
'LibX:', 'LibX:',
'function skype_', 'function skype_',
'[JavaScript Error: "uncaught exception: Permission denied to call method Location.toString"]', '[JavaScript Error: "uncaught exception: Permission denied to call method Location.toString"]',
'CVE-2009-3555', 'CVE-2009-3555',
'OpenGL LayerManager', 'OpenGL LayerManager',
'trying to re-register CID' 'trying to re-register CID',
'Services.HealthReport'
]; ];
for (var i=0; i<blacklist.length; i++) { for (var i=0; i<blacklist.length; i++) {
@ -2233,6 +2238,7 @@ Zotero.Keys = new function() {
// Get the key=>command mappings from the prefs // Get the key=>command mappings from the prefs
for each(var action in actions) { for each(var action in actions) {
var action = action.substr(5); // strips 'keys.' var action = action.substr(5); // strips 'keys.'
// Remove old pref
if (action == 'overrideGlobal') { if (action == 'overrideGlobal') {
Zotero.Prefs.clear('keys.overrideGlobal'); Zotero.Prefs.clear('keys.overrideGlobal');
continue; continue;
@ -2246,26 +2252,35 @@ Zotero.Keys = new function() {
* Called by ZoteroPane.onLoad() * Called by ZoteroPane.onLoad()
*/ */
function windowInit(document) { function windowInit(document) {
var useShift = Zotero.isMac; var globalKeys = [
{
name: 'openZotero',
defaultKey: 'Z'
},
{
name: 'saveToZotero',
defaultKey: 'S'
}
];
// Zotero pane shortcut globalKeys.forEach(function (x) {
var keyElem = document.getElementById('key_openZotero'); let keyElem = document.getElementById('key_' + x.name);
if(keyElem) { if (keyElem) {
var zKey = Zotero.Prefs.get('keys.openZotero'); let prefKey = Zotero.Prefs.get('keys.' + x.name);
// Only override the default with the pref if the <key> hasn't been manually changed // Only override the default with the pref if the <key> hasn't
// and the pref has been // been manually changed and the pref has been
if (keyElem.getAttribute('key') == 'Z' && keyElem.getAttribute('modifiers') == 'accel alt' if (keyElem.getAttribute('key') == x.defaultKey
&& (zKey != 'Z' || useShift)) { && keyElem.getAttribute('modifiers') == 'accel shift'
keyElem.setAttribute('key', zKey); && prefKey != x.defaultKey) {
if (useShift) { keyElem.setAttribute('key', prefKey);
keyElem.setAttribute('modifiers', 'accel shift');
} }
} }
} });
} }
function getCommand(key) { function getCommand(key) {
key = key.toUpperCase();
return _keys[key] ? _keys[key] : false; return _keys[key] ? _keys[key] : false;
} }
} }
@ -2310,17 +2325,19 @@ Zotero.DragDrop = {
currentDataTransfer: null, currentDataTransfer: null,
getDragData: function (element, firstOnly) { getDragData: function (element, firstOnly) {
var dragData = {
dataType: '',
data: []
};
var dt = this.currentDataTransfer; var dt = this.currentDataTransfer;
if (!dt) { if (!dt) {
Zotero.debug("Drag data not available"); Zotero.debug("Drag data not available");
return false; return false;
} }
var dragData = {
dataType: '',
data: [],
dropEffect: dt.dropEffect
};
var len = firstOnly ? 1 : dt.mozItemCount; var len = firstOnly ? 1 : dt.mozItemCount;
if (dt.types.contains('zotero/collection')) { if (dt.types.contains('zotero/collection')) {

View File

@ -433,7 +433,9 @@ var ZoteroPane = new function()
return; return;
} }
Zotero.Sync.Runner.sync(true); Zotero.Sync.Runner.sync({
background: true
});
}) })
.done(); .done();
} }
@ -602,17 +604,14 @@ var ZoteroPane = new function()
} }
} }
var useShift = Zotero.isMac;
var key = String.fromCharCode(event.which); var key = String.fromCharCode(event.which);
if (!key) { if (!key) {
Zotero.debug('No key'); Zotero.debug('No key');
return; return;
} }
// Ignore modifiers other than Ctrl-Alt or Cmd-Shift // Ignore modifiers other than Ctrl-Shift/Cmd-Shift
if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) && if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) && event.shiftKey)) {
(useShift ? event.shiftKey : event.altKey))) {
return; return;
} }
@ -687,9 +686,7 @@ var ZoteroPane = new function()
} }
} }
// Use key that's not the modifier as the popup toggle // Use key that's not the modifier as the popup toggle
ZoteroPane_Local.newNote( ZoteroPane_Local.newNote(event.altKey, parent);
useShift ? event.altKey : event.shiftKey, parent
);
break; break;
case 'toggleTagSelector': case 'toggleTagSelector':
ZoteroPane_Local.toggleTagSelector(); ZoteroPane_Local.toggleTagSelector();
@ -2563,14 +2560,8 @@ var ZoteroPane = new function()
else if (tree.id == 'zotero-items-tree') { else if (tree.id == 'zotero-items-tree') {
let itemGroup = ZoteroPane_Local.getItemGroup(); let itemGroup = ZoteroPane_Local.getItemGroup();
if (itemGroup.isDuplicates()) { if (itemGroup.isDuplicates()) {
if (event.button == 0 && (event.metaKey || event.shiftKey if (event.button != 0 || event.metaKey || event.shiftKey
|| event.altKey || event.ctrlKey)) { || event.altKey || event.ctrlKey) {
return;
}
// Allow right-click on single items/attachments
var items = ZoteroPane_Local.getSelectedItems();
if (event.button != 0 && items.length == 1) {
return; return;
} }
@ -3503,6 +3494,7 @@ var ZoteroPane = new function()
this.loadURI(url, event); this.loadURI(url, event);
} }
else { else {
Zotero.Notifier.trigger('open', 'file', itemID);
Zotero.launchFile(file); Zotero.launchFile(file);
} }
} }
@ -3624,6 +3616,7 @@ var ZoteroPane = new function()
var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile); var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile);
Zotero.launchFile(parent); Zotero.launchFile(parent);
} }
Zotero.Notifier.trigger('open', 'file', attachment.id);
} }
else { else {
this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing) this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing)

View File

@ -126,16 +126,16 @@
<!ENTITY zotero.preferences.prefpane.keys "Shortcuts"> <!ENTITY zotero.preferences.prefpane.keys "Shortcuts">
<!ENTITY zotero.preferences.keys.openZotero "Open/Close Zotero Pane"> <!ENTITY zotero.preferences.keys.openZotero "Open/Close Zotero Pane">
<!ENTITY zotero.preferences.keys.saveToZotero "Save to Zotero (address bar icon)">
<!ENTITY zotero.preferences.keys.toggleFullscreen "Toggle Fullscreen Mode"> <!ENTITY zotero.preferences.keys.toggleFullscreen "Toggle Fullscreen Mode">
<!ENTITY zotero.preferences.keys.focusLibrariesPane "Focus Libraries Pane"> <!ENTITY zotero.preferences.keys.focusLibrariesPane "Focus Libraries Pane">
<!ENTITY zotero.preferences.keys.quicksearch "Quick Search"> <!ENTITY zotero.preferences.keys.quicksearch "Quick Search">
<!ENTITY zotero.preferences.keys.newItem "Create a new item"> <!ENTITY zotero.preferences.keys.newItem "Create a New Item">
<!ENTITY zotero.preferences.keys.newNote "Create a new note"> <!ENTITY zotero.preferences.keys.newNote "Create a New Note">
<!ENTITY zotero.preferences.keys.toggleTagSelector "Toggle Tag Selector"> <!ENTITY zotero.preferences.keys.toggleTagSelector "Toggle Tag Selector">
<!ENTITY zotero.preferences.keys.copySelectedItemCitationsToClipboard "Copy Selected Item Citations to Clipboard"> <!ENTITY zotero.preferences.keys.copySelectedItemCitationsToClipboard "Copy Selected Item Citations to Clipboard">
<!ENTITY zotero.preferences.keys.copySelectedItemsToClipboard "Copy Selected Items to Clipboard"> <!ENTITY zotero.preferences.keys.copySelectedItemsToClipboard "Copy Selected Items to Clipboard">
<!ENTITY zotero.preferences.keys.importFromClipboard "Import from Clipboard"> <!ENTITY zotero.preferences.keys.importFromClipboard "Import from Clipboard">
<!ENTITY zotero.preferences.keys.overrideGlobal "Try to override conflicting shortcuts">
<!ENTITY zotero.preferences.keys.changesTakeEffect "Changes take effect in new windows only"> <!ENTITY zotero.preferences.keys.changesTakeEffect "Changes take effect in new windows only">
<!ENTITY zotero.preferences.prefpane.proxies "Proxies"> <!ENTITY zotero.preferences.prefpane.proxies "Proxies">

View File

@ -934,6 +934,10 @@ locate.manageLocateEngines = Manage Lookup Engines…
standalone.corruptInstallation = Your Zotero Standalone installation appears to be corrupted due to a failed auto-update. While Zotero may continue to function, to avoid potential bugs, please download the latest version of Zotero Standalone from http://zotero.org/support/standalone as soon as possible. standalone.corruptInstallation = Your Zotero Standalone installation appears to be corrupted due to a failed auto-update. While Zotero may continue to function, to avoid potential bugs, please download the latest version of Zotero Standalone from http://zotero.org/support/standalone as soon as possible.
standalone.addonInstallationFailed.title = Add-on Installation Failed standalone.addonInstallationFailed.title = Add-on Installation Failed
standalone.addonInstallationFailed.body = The add-on "%S" could not be installed. It may be incompatible with this version of Zotero Standalone. standalone.addonInstallationFailed.body = The add-on "%S" could not be installed. It may be incompatible with this version of Zotero Standalone.
standalone.rootWarning = You appear to be running Zotero Standalone as root. This is insecure and may prevent Zotero from functioning when launched from your user account.\n\nIf you wish to install an automatic update, modify the Zotero program directory to be writeable by your user account.
standalone.rootWarning.exit = Exit
standalone.rootWarning.continue = Continue
standalone.updateMessage = A recommended update is available, but you do not have permission to install it. To update automatically, modify the Zotero program directory to be writeable by your user account.
connector.error.title = Zotero Connector Error connector.error.title = Zotero Connector Error
connector.standaloneOpen = Your database cannot be accessed because Zotero Standalone is currently open. Please view your items in Zotero Standalone. connector.standaloneOpen = Your database cannot be accessed because Zotero Standalone is currently open. Please view your items in Zotero Standalone.

View File

@ -1,10 +1,10 @@
<!ENTITY preferencesCmdMac.label "Innstillingar"> <!ENTITY preferencesCmdMac.label "Innstillingar">
<!ENTITY preferencesCmdMac.commandkey ","> <!ENTITY preferencesCmdMac.commandkey ",">
<!ENTITY servicesMenuMac.label "Services"> <!ENTITY servicesMenuMac.label "Tenestar">
<!ENTITY hideThisAppCmdMac.label "Gøym &brandShortName;"> <!ENTITY hideThisAppCmdMac.label "Gøym &brandShortName;">
<!ENTITY hideThisAppCmdMac.commandkey "H"> <!ENTITY hideThisAppCmdMac.commandkey "G">
<!ENTITY hideOtherAppsCmdMac.label "Gøym andre"> <!ENTITY hideOtherAppsCmdMac.label "Gøym andre">
<!ENTITY hideOtherAppsCmdMac.commandkey "H"> <!ENTITY hideOtherAppsCmdMac.commandkey "G">
<!ENTITY showAllAppsCmdMac.label "Vis alle"> <!ENTITY showAllAppsCmdMac.label "Vis alle">
<!ENTITY quitApplicationCmdMac.label "Avslutt Zotero"> <!ENTITY quitApplicationCmdMac.label "Avslutt Zotero">
<!ENTITY quitApplicationCmdMac.key "Q"> <!ENTITY quitApplicationCmdMac.key "Q">
@ -12,52 +12,52 @@
<!ENTITY fileMenu.label "Fil"> <!ENTITY fileMenu.label "Fil">
<!ENTITY fileMenu.accesskey "F"> <!ENTITY fileMenu.accesskey "F">
<!ENTITY saveCmd.label "Save…"> <!ENTITY saveCmd.label "Lagra …">
<!ENTITY saveCmd.key "S"> <!ENTITY saveCmd.key "S">
<!ENTITY saveCmd.accesskey "A"> <!ENTITY saveCmd.accesskey "L">
<!ENTITY pageSetupCmd.label "Page Setup…"> <!ENTITY pageSetupCmd.label "Sideoppsett …">
<!ENTITY pageSetupCmd.accesskey "U"> <!ENTITY pageSetupCmd.accesskey "D">
<!ENTITY printCmd.label "Print…"> <!ENTITY printCmd.label "Skriv ut …">
<!ENTITY printCmd.key "P"> <!ENTITY printCmd.key "S">
<!ENTITY printCmd.accesskey "P"> <!ENTITY printCmd.accesskey "S">
<!ENTITY closeCmd.label "Lukk"> <!ENTITY closeCmd.label "Lukk">
<!ENTITY closeCmd.key "W"> <!ENTITY closeCmd.key "W">
<!ENTITY closeCmd.accesskey "C"> <!ENTITY closeCmd.accesskey "L">
<!ENTITY quitApplicationCmdWin.label "Avslutt"> <!ENTITY quitApplicationCmdWin.label "Avslutt">
<!ENTITY quitApplicationCmdWin.accesskey "x"> <!ENTITY quitApplicationCmdWin.accesskey "A">
<!ENTITY quitApplicationCmd.label "Avslutt"> <!ENTITY quitApplicationCmd.label "Avslutt">
<!ENTITY quitApplicationCmd.accesskey "Q"> <!ENTITY quitApplicationCmd.accesskey "A">
<!ENTITY editMenu.label "Rediger"> <!ENTITY editMenu.label "Rediger">
<!ENTITY editMenu.accesskey "E"> <!ENTITY editMenu.accesskey "R">
<!ENTITY undoCmd.label "Angre"> <!ENTITY undoCmd.label "Angra">
<!ENTITY undoCmd.key "Z"> <!ENTITY undoCmd.key "Z">
<!ENTITY undoCmd.accesskey "U"> <!ENTITY undoCmd.accesskey "A">
<!ENTITY redoCmd.label "Gjer om"> <!ENTITY redoCmd.label "Gjer om">
<!ENTITY redoCmd.key "Y"> <!ENTITY redoCmd.key "Y">
<!ENTITY redoCmd.accesskey "R"> <!ENTITY redoCmd.accesskey "G">
<!ENTITY cutCmd.label "Klipp ut"> <!ENTITY cutCmd.label "Klipp ut">
<!ENTITY cutCmd.key "X"> <!ENTITY cutCmd.key "X">
<!ENTITY cutCmd.accesskey "t"> <!ENTITY cutCmd.accesskey "t">
<!ENTITY copyCmd.label "Kopier"> <!ENTITY copyCmd.label "Kopier">
<!ENTITY copyCmd.key "C"> <!ENTITY copyCmd.key "C">
<!ENTITY copyCmd.accesskey "C"> <!ENTITY copyCmd.accesskey "K">
<!ENTITY copyCitationCmd.label "Kopier sitering"> <!ENTITY copyCitationCmd.label "Kopier sitering">
<!ENTITY copyBibliographyCmd.label "Kopier bibliografi"> <!ENTITY copyBibliographyCmd.label "Kopier bibliografi">
<!ENTITY pasteCmd.label "Lim inn"> <!ENTITY pasteCmd.label "Lim inn">
<!ENTITY pasteCmd.key "V"> <!ENTITY pasteCmd.key "V">
<!ENTITY pasteCmd.accesskey "P"> <!ENTITY pasteCmd.accesskey "L">
<!ENTITY deleteCmd.label "Slett"> <!ENTITY deleteCmd.label "Slett">
<!ENTITY deleteCmd.key "D"> <!ENTITY deleteCmd.key "D">
<!ENTITY deleteCmd.accesskey "D"> <!ENTITY deleteCmd.accesskey "S">
<!ENTITY selectAllCmd.label "Merk alt"> <!ENTITY selectAllCmd.label "Merk alt">
<!ENTITY selectAllCmd.key "A"> <!ENTITY selectAllCmd.key "A">
<!ENTITY selectAllCmd.accesskey "A"> <!ENTITY selectAllCmd.accesskey "A">
<!ENTITY preferencesCmd.label "Preferences"> <!ENTITY preferencesCmd.label "Innstillingar …">
<!ENTITY preferencesCmd.accesskey "O"> <!ENTITY preferencesCmd.accesskey "I">
<!ENTITY preferencesCmdUnix.label "Preferences"> <!ENTITY preferencesCmdUnix.label "Innstillingar">
<!ENTITY preferencesCmdUnix.accesskey "n"> <!ENTITY preferencesCmdUnix.accesskey "I">
<!ENTITY findCmd.label "Finn"> <!ENTITY findCmd.label "Finn">
<!ENTITY findCmd.accesskey "F"> <!ENTITY findCmd.accesskey "F">
<!ENTITY findCmd.commandkey "f"> <!ENTITY findCmd.commandkey "f">
@ -69,7 +69,7 @@
<!ENTITY toolsMenu.label "Verktøy"> <!ENTITY toolsMenu.label "Verktøy">
<!ENTITY toolsMenu.accesskey "T"> <!ENTITY toolsMenu.accesskey "V">
<!ENTITY addons.label "Tillegg"> <!ENTITY addons.label "Tillegg">
@ -88,14 +88,14 @@
<!ENTITY helpMenuWin.accesskey "H"> <!ENTITY helpMenuWin.accesskey "H">
<!ENTITY helpMac.commandkey "?"> <!ENTITY helpMac.commandkey "?">
<!ENTITY aboutProduct.label "Om &brandShortName;"> <!ENTITY aboutProduct.label "Om &brandShortName;">
<!ENTITY aboutProduct.accesskey "A"> <!ENTITY aboutProduct.accesskey "O">
<!ENTITY productHelp.label "Hjelp og dokumentasjon"> <!ENTITY productHelp.label "Hjelp og dokumentasjon">
<!ENTITY productHelp.accesskey "D"> <!ENTITY productHelp.accesskey "H">
<!ENTITY helpTroubleshootingInfo.label "Feilsøking"> <!ENTITY helpTroubleshootingInfo.label "Feilsøking">
<!ENTITY helpTroubleshootingInfo.accesskey "T"> <!ENTITY helpTroubleshootingInfo.accesskey "F">
<!ENTITY helpFeedbackPage.label "Send tilbakemelding …"> <!ENTITY helpFeedbackPage.label "Send tilbakemelding …">
<!ENTITY helpFeedbackPage.accesskey "S"> <!ENTITY helpFeedbackPage.accesskey "S">
<!ENTITY helpReportErrors.label "Meld feil i Zotero …"> <!ENTITY helpReportErrors.label "Meld feil i Zotero …">
<!ENTITY helpReportErrors.accesskey "R"> <!ENTITY helpReportErrors.accesskey "M">
<!ENTITY helpCheckForUpdates.label "Sjå etter oppdateringar …"> <!ENTITY helpCheckForUpdates.label "Sjå etter oppdateringar …">
<!ENTITY helpCheckForUpdates.accesskey "U"> <!ENTITY helpCheckForUpdates.accesskey "O">

View File

@ -20,19 +20,10 @@
#reindex #reindex
{ {
margin: 0; padding-left: 5px;
padding: 0;
list-style-image: url(chrome://zotero/skin/arrow_refresh.png); list-style-image: url(chrome://zotero/skin/arrow_refresh.png);
} }
#reindex .toolbarbutton-icon
{
margin: 0 0 0 2px;
padding: 0;
width: 14px;
height: 14px;
}
#index-box > button #index-box > button
{ {
font-size: .95em; font-size: .95em;

View File

@ -65,14 +65,15 @@ pref("extensions.zotero.tagCloud", false);
// Keyboard shortcuts // Keyboard shortcuts
pref("extensions.zotero.keys.openZotero", 'Z'); pref("extensions.zotero.keys.openZotero", 'Z');
pref("extensions.zotero.keys.toggleFullscreen", 'F'); pref("extensions.zotero.keys.toggleFullscreen", 'F');
pref("extensions.zotero.keys.library", 'L'); pref("extensions.zotero.keys.saveToZotero", 'S');
pref("extensions.zotero.keys.quicksearch", 'K');
pref("extensions.zotero.keys.newItem", 'N'); pref("extensions.zotero.keys.newItem", 'N');
pref("extensions.zotero.keys.newNote", 'O'); pref("extensions.zotero.keys.newNote", 'O');
pref("extensions.zotero.keys.toggleTagSelector", 'T'); pref("extensions.zotero.keys.importFromClipboard", 'V');
pref("extensions.zotero.keys.library", 'L');
pref("extensions.zotero.keys.quicksearch", 'K');
pref("extensions.zotero.keys.copySelectedItemCitationsToClipboard", 'A'); pref("extensions.zotero.keys.copySelectedItemCitationsToClipboard", 'A');
pref("extensions.zotero.keys.copySelectedItemsToClipboard", 'C'); pref("extensions.zotero.keys.copySelectedItemsToClipboard", 'C');
pref("extensions.zotero.keys.importFromClipboard", 'V'); pref("extensions.zotero.keys.toggleTagSelector", 'T');
// Fulltext indexing // Fulltext indexing
pref("extensions.zotero.fulltext.textMaxLength", 500000); pref("extensions.zotero.fulltext.textMaxLength", 500000);

View File

@ -602,7 +602,9 @@ array_reduce(
"dispatch", "dispatch",
"when", "spread", "when", "spread",
"get", "put", "set", "del", "delete", "get", "put", "set", "del", "delete",
"post", "send", "invoke", // .send() disabled by Zotero for Mozilla Task.jsm compatibility
//"post", "send", "invoke",
"post", "invoke",
"keys", "keys",
"fapply", "fcall", "fbind", "fapply", "fcall", "fbind",
"all", "allResolved", "all", "allResolved",
@ -1146,7 +1148,8 @@ var post = Q.post = dispatcher("post");
* @param ...args array of invocation arguments * @param ...args array of invocation arguments
* @return promise for the return value * @return promise for the return value
*/ */
Q.send = send; // Disabled by Zotero for Mozilla Task.jsm compatibility
//Q.send = send;
Q.invoke = send; // synonyms Q.invoke = send; // synonyms
function send(value, name) { function send(value, name) {
var args = array_slice(arguments, 2); var args = array_slice(arguments, 2);

View File

@ -151,6 +151,7 @@
"mla-underline": "modern-language-association-underline", "mla-underline": "modern-language-association-underline",
"mla-url": "modern-language-association-with-url", "mla-url": "modern-language-association-with-url",
"mla": "modern-language-association", "mla": "modern-language-association",
"modern-language-association-note": "modern-language-association-6th-edition-note",
"molecular-biochemical-parasitology": "molecular-and-biochemical-parasitology", "molecular-biochemical-parasitology": "molecular-and-biochemical-parasitology",
"national-library-of-medicine-grant": "national-library-of-medicine-grant-proposals", "national-library-of-medicine-grant": "national-library-of-medicine-grant-proposals",
"nature-neuroscience-brief-communication": "nature-neuroscience-brief-communications", "nature-neuroscience-brief-communication": "nature-neuroscience-brief-communications",

View File

@ -1 +1 @@
2013-07-02 00:00:00 2013-07-22 02:55:00

2
styles

@ -1 +1 @@
Subproject commit b5a55500a81e470ec56a10c1757657a050e2c8fe Subproject commit c536a2c5c28ca465b511733a849ae452822fd363

@ -1 +1 @@
Subproject commit 089b8a1ae7d08ffa420b768509f2ae33cce3ab7f Subproject commit 10e7c43ed0e70cdccebf575d4cdccd81b8914ffe