- Adds file conflict resolution -- not particularly attractive at the moment, and no Apply to All button, but possibly functional

- Fixes file syncing after editing a file locally
- Fixes a few storage bugs that could result in eternal spinning, invalid percentages, and other unpleasantries
- Made the attachment display box more flexible, including a filename field that we may or may not want to keep in the main view
This commit is contained in:
Dan Stillman 2009-01-15 06:58:06 +00:00
parent a1277ccaa9
commit bd070f7b63
6 changed files with 396 additions and 55 deletions

View file

@ -41,6 +41,7 @@
<field name="displayGoButtons">false</field> <field name="displayGoButtons">false</field>
<field name="clickableLink">false</field> <field name="clickableLink">false</field>
<field name="displayButton">false</field> <field name="displayButton">false</field>
<field name="displayNote">false</field>
<field name="buttonCaption"/> <field name="buttonCaption"/>
<field name="clickHandler"/> <field name="clickHandler"/>
@ -52,31 +53,52 @@
<![CDATA[ <![CDATA[
this.editable = false; this.editable = false;
this.displayGoButtons = false; this.displayGoButtons = false;
this.displayURL = false;
this.displayFileName = false;
this.clickableLink = false; this.clickableLink = false;
this.displayIndexed = false; this.displayAccessed = false;
this.displayPages = false; this.displayPages = false;
this.displayDateModified = false;
this.displayIndexed = false;
this.displayNote = false;
switch (val) { switch (val) {
case 'view': case 'view':
this.displayGoButtons = true; this.displayGoButtons = true;
this.displayURL = true;
this.displayFileName = true;
this.clickableLink = true; this.clickableLink = true;
this.displayIndexed = true; this.displayAccessed = true;
this.displayPages = true; this.displayPages = true;
this.displayIndexed = true;
this.displayNote = true;
break; break;
case 'edit': case 'edit':
this.editable = true; this.editable = true;
this.displayGoButtons = true; this.displayGoButtons = true;
this.displayURL = true;
this.displayFileName = true;
this.clickableLink = true; this.clickableLink = true;
this.displayIndexed = true; this.displayAccessed = true;
this.displayPages = true; this.displayPages = true;
this.displayIndexed = true;
this.displayNote = true;
break; break;
case 'merge': case 'merge':
this.displayURL = true;
this.displayFileName = true;
this.displayAccessed = true;
this.displayNote = true;
this.displayButton = true; this.displayButton = true;
break; break;
case 'mergeedit': case 'mergeedit':
this.displayURL = true;
this.displayFileName = true;
this.displayAccessed = true;
this.displayNote = true;
break; break;
default: default:
@ -114,9 +136,11 @@
var goButtons = this._id('go-buttons'); var goButtons = this._id('go-buttons');
var viewButton = this._id('view'); var viewButton = this._id('view');
var showButton = this._id('show'); var showButton = this._id('show');
var fileNameRow = this._id('fileNameRow');
var urlField = this._id('url'); var urlField = this._id('url');
var accessed = this._id('accessed'); var accessed = this._id('accessed');
var pagesRow = this._id('pages'); var pagesRow = this._id('pages');
var dateModifiedRow = this._id('dateModified');
var indexBox = this._id('index-box'); var indexBox = this._id('index-box');
var selectButton = this._id('select-button'); var selectButton = this._id('select-button');
@ -180,9 +204,10 @@
var str = Zotero.getString('pane.item.attachments.view.link'); var str = Zotero.getString('pane.item.attachments.view.link');
} }
showButton.setAttribute('hidden', !isImportedURL); showButton.hidden = !isImportedURL;
// URL // URL
if (this.displayURL) {
var urlSpec = this.item.getField('url'); var urlSpec = this.item.getField('url');
urlField.setAttribute('value', urlSpec); urlField.setAttribute('value', urlSpec);
urlField.setAttribute('hidden', false); urlField.setAttribute('hidden', false);
@ -195,21 +220,58 @@
else { else {
urlField.className = ''; urlField.className = '';
} }
urlField.hidden = false;
}
else {
urlField.hidden = true;
}
// Access date // Access date
accessed.setAttribute('value', if (this.displayAccessed) {
Zotero.getString('itemFields.accessDate') + ': ' accessed.value = Zotero.localeJoin([
+ Zotero.Date.sqlToDate(this.item.getField('accessDate'), true).toLocaleString()); Zotero.getString('itemFields.accessDate'),
accessed.setAttribute('hidden', false); ': ',
Zotero.Date.sqlToDate(
this.item.getField('accessDate'), true
).toLocaleString()
], '');
accessed.hidden = false;
}
else {
accessed.hidden = true;
}
} }
// Metadata for files // Metadata for files
else { else {
var str = Zotero.getString('pane.item.attachments.view.file'); var str = Zotero.getString('pane.item.attachments.view.file');
showButton.setAttribute('hidden', false); showButton.hidden = false;
urlField.setAttribute('hidden', true); urlField.hidden = true;
accessed.setAttribute('hidden', true); accessed.hidden = true;
} }
if (this.item.attachmentLinkMode
!= Zotero.Attachments.LINK_MODE_LINKED_URL
&& this.displayFileName) {
// TODO: localize
var file = this.item.getFile(false, true);
var fileName = file.leafName;
if (fileName) {
fileNameRow.value = Zotero.localeJoin([
'Filename', // TODO: localize
': ', // TODO: probably needs to be in localized string
fileName
], '');
fileNameRow.hidden = false;
}
else {
fileNameRow.hidden = true;
}
}
else {
fileNameRow.hidden = true;
}
viewButton.setAttribute('label', str); viewButton.setAttribute('label', str);
// Page count // Page count
@ -217,16 +279,35 @@
var pages = Zotero.Fulltext.getPages(this.item.id); var pages = Zotero.Fulltext.getPages(this.item.id);
var pages = pages ? pages.total : null; var pages = pages ? pages.total : null;
if (pages) { if (pages) {
var str = Zotero.getString('itemFields.pages') + ': ' + pages; var str = Zotero.localeJoin([
pagesRow.setAttribute('value', str); Zotero.getString('itemFields.pages'),
pagesRow.setAttribute('hidden', false); ': ',
pages
], '');
pagesRow.value = str;
pagesRow.hidden = false;
} }
else { else {
pagesRow.setAttribute('hidden', true); pagesRow.hidden = true;
} }
} }
else { else {
pagesRow.setAttribute('hidden', true); pagesRow.hidden = true;
}
if (this.displayDateModified) {
var str = Zotero.localeJoin([
Zotero.getString('itemFields.dateModified'),
': ',
Zotero.Date.sqlToDate(
this.item.getField('dateModified'), true
).toLocaleString()
], '');
dateModifiedRow.value = str;
dateModifiedRow.hidden = false;
}
else {
dateModifiedRow.hidden = true;
} }
// Full-text index information // Full-text index information
@ -240,6 +321,9 @@
// Note editor // Note editor
var noteEditor = this._id('note-editor'); var noteEditor = this._id('note-editor');
if (this.displayNote) {
noteEditor.hidden = false;
// Don't make note editable (at least for now) // Don't make note editable (at least for now)
if (this.mode == 'merge' || this.mode == 'mergeedit') { if (this.mode == 'merge' || this.mode == 'mergeedit') {
noteEditor.mode = 'merge'; noteEditor.mode = 'merge';
@ -250,6 +334,11 @@
} }
noteEditor.parent = null; noteEditor.parent = null;
noteEditor.item = this.item; noteEditor.item = this.item;
}
else {
noteEditor.hidden = true;
}
if (this.displayButton) { if (this.displayButton) {
selectButton.label = this.buttonCaption; selectButton.label = this.buttonCaption;
@ -412,9 +501,10 @@
<button id="show" label="&zotero.item.attachment.file.show;" flex="1"/> <button id="show" label="&zotero.item.attachment.file.show;" flex="1"/>
</hbox> </hbox>
<label id="url" crop="end"/> <label id="url" crop="end"/>
<label id="fileNameRow"/>
<label id="accessed"/> <label id="accessed"/>
<label id="pages"/> <label id="pages"/>
<label id="dateModified"/>
<hbox id="index-box"> <hbox id="index-box">
<label id="index-status"/> <label id="index-status"/>

View file

@ -134,6 +134,7 @@
if (this._leftpane.ref != 'deleted' if (this._leftpane.ref != 'deleted'
&& this._rightpane.ref != 'deleted') { && this._rightpane.ref != 'deleted') {
var dm1 = this._leftpane.ref.getField('dateModified'); var dm1 = this._leftpane.ref.getField('dateModified');
if (dm1) { if (dm1) {
dm1 = Zotero.Date.sqlToDate(dm1); dm1 = Zotero.Date.sqlToDate(dm1);
@ -318,6 +319,10 @@
elementName = 'zoteronoteeditor'; elementName = 'zoteronoteeditor';
break; break;
case 'storagefile':
elementName = 'zoterostoragefilebox';
break;
default: default:
throw ("Object type '" + this.type throw ("Object type '" + this.type
+ "' not supported in <zoteromergepane>.ref"); + "' not supported in <zoteromergepane>.ref");
@ -349,6 +354,7 @@
switch (this.type) { switch (this.type) {
case 'attachment': case 'attachment':
case 'note': case 'note':
case 'storagefile':
objbox.buttonCaption = 'Choose this version'; objbox.buttonCaption = 'Choose this version';
break; break;
} }

View file

@ -0,0 +1,67 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
-->
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="storage-file-box"
extends="chrome://zotero/content/bindings/attachmentbox.xml#attachment-box">
<implementation>
<property name="mode" onget="return this._mode;">
<setter>
<![CDATA[
this.editable = false;
this.displayGoButtons = false;
this.clickableLink = false;
this.displayIndexed = false;
this.displayPages = false;
this.displayNote = false;
switch (val) {
case 'merge':
this.displayFileName = true;
this.displayButton = true;
this.displayDateModified = true;
break;
case 'mergeedit':
this.displayFileName = true;
this.displayDateModified = true;
break;
default:
throw ("Invalid mode '" + val + "' in storagefilebox.xml");
}
this._mode = val;
document.getAnonymousNodes(this)[0].setAttribute('mode', val);
]]>
</setter>
</property>
</implementation>
</binding>
</bindings>

View file

@ -42,6 +42,7 @@ var Zotero_Merge_Window = new function () {
switch (_mergeGroup.type) { switch (_mergeGroup.type) {
case 'item': case 'item':
case 'storagefile':
break; break;
default: default:
@ -127,6 +128,8 @@ var Zotero_Merge_Window = new function () {
} }
} }
catch (e) { catch (e) {
Zotero.debug(e);
var prompt = Components.classes["@mozilla.org/network/default-prompt;1"] var prompt = Components.classes["@mozilla.org/network/default-prompt;1"]
.createInstance(Components.interfaces.nsIPrompt); .createInstance(Components.interfaces.nsIPrompt);
prompt.alert(Zotero.getString('general.error'), e); prompt.alert(Zotero.getString('general.error'), e);

View file

@ -5,6 +5,8 @@ Zotero.Sync.Storage = new function () {
this.SYNC_STATE_TO_UPLOAD = 0; this.SYNC_STATE_TO_UPLOAD = 0;
this.SYNC_STATE_TO_DOWNLOAD = 1; this.SYNC_STATE_TO_DOWNLOAD = 1;
this.SYNC_STATE_IN_SYNC = 2; this.SYNC_STATE_IN_SYNC = 2;
this.SYNC_STATE_FORCE_UPLOAD = 3;
this.SYNC_STATE_FORCE_DOWNLOAD = 4;
this.SUCCESS = 1; this.SUCCESS = 1;
this.ERROR_NO_URL = -1; this.ERROR_NO_URL = -1;
@ -22,7 +24,6 @@ Zotero.Sync.Storage = new function () {
this.ERROR_NOT_ALLOWED = -14; this.ERROR_NOT_ALLOWED = -14;
this.ERROR_UNKNOWN = -15; this.ERROR_UNKNOWN = -15;
// //
// Public properties // Public properties
// //
@ -242,7 +243,6 @@ Zotero.Sync.Storage = new function () {
} }
Zotero.debug("Beginning storage sync"); Zotero.debug("Beginning storage sync");
Zotero.Sync.Runner.setSyncIcon('animate');
_syncInProgress = true; _syncInProgress = true;
_changesMade = false; _changesMade = false;
@ -297,6 +297,8 @@ Zotero.Sync.Storage = new function () {
case this.SYNC_STATE_TO_UPLOAD: case this.SYNC_STATE_TO_UPLOAD:
case this.SYNC_STATE_TO_DOWNLOAD: case this.SYNC_STATE_TO_DOWNLOAD:
case this.SYNC_STATE_IN_SYNC: case this.SYNC_STATE_IN_SYNC:
case this.SYNC_STATE_FORCE_UPLOAD:
case this.SYNC_STATE_FORCE_DOWNLOAD:
break; break;
default: default:
@ -608,6 +610,10 @@ Zotero.Sync.Storage = new function () {
* @return {Boolean} * @return {Boolean}
*/ */
this.downloadFiles = function () { this.downloadFiles = function () {
if (!_syncInProgress) {
_syncInProgress = true;
}
// Check for active operations? // Check for active operations?
var queue = Zotero.Sync.Storage.QueueManager.get('download'); var queue = Zotero.Sync.Storage.QueueManager.get('download');
if (queue.isRunning()) { if (queue.isRunning()) {
@ -624,7 +630,9 @@ Zotero.Sync.Storage = new function () {
for each(var itemID in downloadFileIDs) { for each(var itemID in downloadFileIDs) {
var item = Zotero.Items.get(itemID); var item = Zotero.Items.get(itemID);
if (this.isFileModified(itemID)) { if (Zotero.Sync.Storage.getSyncState(itemID) !=
Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD
&& this.isFileModified(itemID)) {
Zotero.debug("File for attachment " + itemID + " has been modified"); Zotero.debug("File for attachment " + itemID + " has been modified");
this.setSyncState(itemID, this.SYNC_STATE_TO_UPLOAD); this.setSyncState(itemID, this.SYNC_STATE_TO_UPLOAD);
continue; continue;
@ -671,6 +679,24 @@ Zotero.Sync.Storage = new function () {
try { try {
var syncModTime = Zotero.Date.toUnixTimestamp(mdate); var syncModTime = Zotero.Date.toUnixTimestamp(mdate);
// Skip download if local file exists and matches mod time
var file = item.getFile();
if (file && file.exists()
&& syncModTime == Math.round(file.lastModifiedTime / 1000)) {
Zotero.debug("Stored file mod time matches remote file -- skipping download");
Zotero.DB.beginTransaction();
var syncState = Zotero.Sync.Storage.getSyncState(item.id);
var updateItem = syncState != 1;
Zotero.Sync.Storage.setSyncedModificationTime(item.id, syncModTime, true);
Zotero.Sync.Storage.setSyncState(item.id, Zotero.Sync.Storage.SYNC_STATE_IN_SYNC);
Zotero.DB.commitTransaction();
_changesMade = true;
request.finish();
return;
}
var uri = _getItemURI(item); var uri = _getItemURI(item);
var destFile = Zotero.getTempDirectory(); var destFile = Zotero.getTempDirectory();
destFile.append(item.key + '.zip.tmp'); destFile.append(item.key + '.zip.tmp');
@ -715,6 +741,10 @@ Zotero.Sync.Storage = new function () {
* @return {Boolean} * @return {Boolean}
*/ */
this.uploadFiles = function () { this.uploadFiles = function () {
if (!_syncInProgress) {
_syncInProgress = true;
}
// Check for active operations? // Check for active operations?
var queue = Zotero.Sync.Storage.QueueManager.get('upload'); var queue = Zotero.Sync.Storage.QueueManager.get('upload');
if (queue.isRunning()) { if (queue.isRunning()) {
@ -946,9 +976,24 @@ Zotero.Sync.Storage = new function () {
} }
this.resetAllSyncStates = function () { this.resetAllSyncStates = function (syncState) {
if (!syncState) {
syncState = this.SYNC_STATE_TO_UPLOAD;
}
switch (syncState) {
case this.SYNC_STATE_TO_UPLOAD:
case this.SYNC_STATE_TO_DOWNLOAD:
case this.SYNC_STATE_IN_SYNC:
break;
default:
throw ("Invalid sync state '" + syncState + "' in "
+ "Zotero.Sync.Storage.resetAllSyncStates()");
}
var sql = "UPDATE itemAttachments SET syncState=?"; var sql = "UPDATE itemAttachments SET syncState=?";
Zotero.DB.query(sql, [this.SYNC_STATE_TO_UPLOAD]); Zotero.DB.query(sql, [syncState]);
} }
@ -1217,19 +1262,29 @@ Zotero.Sync.Storage = new function () {
try { try {
// Check for conflict // Check for conflict
if (Zotero.Sync.Storage.getSyncState(item.id)
!= Zotero.Sync.Storage.SYNC_STATE_FORCE_UPLOAD) {
if (mdate) { if (mdate) {
var file = item.getFile(); var file = item.getFile();
var mtime = Zotero.Date.toUnixTimestamp(mdate); var mtime = Zotero.Date.toUnixTimestamp(mdate);
var smtime = Zotero.Sync.Storage.getSyncedModificationTime(item.id); var smtime = Zotero.Sync.Storage.getSyncedModificationTime(item.id);
if (mtime != smtime) { if (mtime != smtime) {
request.error("Conflict! Last known mod time does not match remote time!" var localData = { modTime: smtime };
var remoteData = { modTime: mtime };
Zotero.Sync.Storage.QueueManager.addConflict(
request.name, localData, remoteData
);
Zotero.debug("File conflict -- last known mod time "
+ "does not match remote time"
+ " (" + mtime + " != " + smtime + ")"); + " (" + mtime + " != " + smtime + ")");
request.finish();
return; return;
} }
} }
else { else {
Zotero.debug("Remote file not found for item " + item.id); Zotero.debug("Remote file not found for item " + item.id);
} }
}
var file = Zotero.getTempDirectory(); var file = Zotero.getTempDirectory();
file.append(item.key + '.zip'); file.append(item.key + '.zip');
@ -1364,8 +1419,13 @@ Zotero.Sync.Storage = new function () {
* @return {Number[]} Array of attachment itemIDs * @return {Number[]} Array of attachment itemIDs
*/ */
function _getFilesToDownload() { function _getFilesToDownload() {
var sql = "SELECT itemID FROM itemAttachments WHERE syncState=?"; var sql = "SELECT itemID FROM itemAttachments WHERE syncState IN (?,?)";
return Zotero.DB.columnQuery(sql, Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD); return Zotero.DB.columnQuery(sql,
[
Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD,
Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD
]
);
} }
@ -1376,11 +1436,12 @@ Zotero.Sync.Storage = new function () {
* @return {Number[]} Array of attachment itemIDs * @return {Number[]} Array of attachment itemIDs
*/ */
function _getFilesToUpload() { function _getFilesToUpload() {
var sql = "SELECT itemID FROM itemAttachments WHERE syncState=? " var sql = "SELECT itemID FROM itemAttachments WHERE syncState IN (?,?) "
+ "AND linkMode IN (?,?)"; + "AND linkMode IN (?,?)";
return Zotero.DB.columnQuery(sql, return Zotero.DB.columnQuery(sql,
[ [
Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD, Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD,
Zotero.Sync.Storage.SYNC_STATE_FORCE_UPLOAD,
Zotero.Attachments.LINK_MODE_IMPORTED_FILE, Zotero.Attachments.LINK_MODE_IMPORTED_FILE,
Zotero.Attachments.LINK_MODE_IMPORTED_URL Zotero.Attachments.LINK_MODE_IMPORTED_URL
] ]
@ -1890,6 +1951,13 @@ Zotero.Sync.Storage = new function () {
Zotero.debug("Storage sync is complete"); Zotero.debug("Storage sync is complete");
_syncInProgress = false; _syncInProgress = false;
if (!cancelled && this.resyncOnFinish) {
Zotero.debug("Force-resyncing items in conflict");
this.resyncOnFinish = false;
this.sync();
return;
}
if (cancelled || !_changesMade) { if (cancelled || !_changesMade) {
if (!_changesMade) { if (!_changesMade) {
Zotero.debug("No changes made during storage sync"); Zotero.debug("No changes made during storage sync");
@ -1991,6 +2059,7 @@ Zotero.Sync.Storage = new function () {
Zotero.Sync.Storage.QueueManager = new function () { Zotero.Sync.Storage.QueueManager = new function () {
var _queues = {}; var _queues = {};
var _conflicts = [];
/** /**
@ -2028,6 +2097,7 @@ Zotero.Sync.Storage.QueueManager = new function () {
for each(var queue in _queues) { for each(var queue in _queues) {
queue.stop(); queue.stop();
} }
_conflicts = [];
} }
@ -2035,6 +2105,14 @@ Zotero.Sync.Storage.QueueManager = new function () {
* Tell the storage system that we're finished * Tell the storage system that we're finished
*/ */
this.finish = function () { this.finish = function () {
if (_conflicts.length) {
var data = _reconcileConflicts();
if (data) {
_processMergeData(data);
}
_conflicts = [];
}
Zotero.Sync.Storage.finish(this._cancelled); Zotero.Sync.Storage.finish(this._cancelled);
this._cancelled = false; this._cancelled = false;
} }
@ -2075,8 +2153,10 @@ Zotero.Sync.Storage.QueueManager = new function () {
//Zotero.debug("Total percentage is " + percentage); //Zotero.debug("Total percentage is " + percentage);
// Remaining KB // Remaining KB
var downloadStatus = _getQueueStatus(_queues.download); var downloadStatus = _queues.download ?
var uploadStatus = _getQueueStatus(_queues.upload); _getQueueStatus(_queues.download) : 0;
var uploadStatus = _queues.upload ?
_getQueueStatus(_queues.upload) : 0;
this.updateProgressMeters( this.updateProgressMeters(
activeRequests, percentage, downloadStatus, uploadStatus activeRequests, percentage, downloadStatus, uploadStatus
@ -2124,6 +2204,15 @@ Zotero.Sync.Storage.QueueManager = new function () {
} }
this.addConflict = function (requestName, localData, remoteData) {
_conflicts.push({
name: requestName,
localData: localData,
remoteData: remoteData
});
}
/** /**
* Get a status string for a queue * Get a status string for a queue
* *
@ -2155,6 +2244,77 @@ Zotero.Sync.Storage.QueueManager = new function () {
var status = Zotero.localeJoin([kbRemaining, '(' + filesRemaining + ')']); var status = Zotero.localeJoin([kbRemaining, '(' + filesRemaining + ')']);
return status; return status;
} }
function _reconcileConflicts() {
var objectPairs = [];
for each(var conflict in _conflicts) {
var item = Zotero.Items.getByKey(conflict.name);
var item1 = item.clone();
item1.setField('dateModified',
Zotero.Date.dateToSQL(new Date(conflict.localData.modTime * 1000), true));
var item2 = item.clone();
item2.setField('dateModified',
Zotero.Date.dateToSQL(new Date(conflict.remoteData.modTime * 1000), true));
objectPairs.push([item1, item2]);
}
var io = {
dataIn: {
type: 'storagefile',
captions: [
// TODO: localize
'Local File',
'Remote File',
'Saved File'
],
objects: objectPairs
}
};
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var lastWin = wm.getMostRecentWindow("navigator:browser");
lastWin.openDialog('chrome://zotero/content/merge.xul', '', 'chrome,modal,centerscreen', io);
if (!io.dataOut) {
return false;
}
// Since we're only putting cloned items into the merge window,
// we have to manually set the ids
for (var i=0; i<_conflicts.length; i++) {
io.dataOut[i].id = Zotero.Items.getByKey(_conflicts[i].name).id;
}
return io.dataOut;
}
function _processMergeData(data) {
if (!data.length) {
return false;
}
Zotero.Sync.Storage.resyncOnFinish = true;
for each(var mergeItem in data) {
var itemID = mergeItem.id;
var dateModified = mergeItem.ref.getField('dateModified');
// Local
if (dateModified == mergeItem.left.getField('dateModified')) {
Zotero.Sync.Storage.setSyncState(
itemID, Zotero.Sync.Storage.SYNC_STATE_FORCE_UPLOAD
);
}
// Remote
else {
Zotero.Sync.Storage.setSyncState(
itemID, Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD
);
}
}
}
} }
@ -2229,6 +2389,10 @@ Zotero.Sync.Storage.Queue = function (name) {
return remaining; return remaining;
}); });
this.__defineGetter__('percentage', function () { this.__defineGetter__('percentage', function () {
if (this.totalRequests == 0) {
return 0;
}
var completedRequests = 0; var completedRequests = 0;
for each(var request in this._requests) { for each(var request in this._requests) {
completedRequests += request.percentage / 100; completedRequests += request.percentage / 100;
@ -2374,8 +2538,14 @@ Zotero.Sync.Storage.Queue.prototype.stop = function () {
Zotero.debug(this.Name + " queue is already finished"); Zotero.debug(this.Name + " queue is already finished");
return; return;
} }
this._stopping = true;
// If no requests, finish manually
if (this.activeRequests == 0) {
this._finishedRequests = this._finishedRequests;
return;
}
this._stopping = true;
for each(var request in this._requests) { for each(var request in this._requests) {
if (!request.isFinished()) { if (!request.isFinished()) {
request.stop(); request.stop();
@ -2461,6 +2631,7 @@ Zotero.Sync.Storage.Request.prototype.__defineGetter__('percentage', function ()
if (this.progressMax == 0) { if (this.progressMax == 0) {
return 0; return 0;
} }
var percentage = Math.round((this.progress / this.progressMax) * 100); var percentage = Math.round((this.progress / this.progressMax) * 100);
if (percentage < this._percentage) { if (percentage < this._percentage) {
Zotero.debug(percentage + " is less than last percentage of " Zotero.debug(percentage + " is less than last percentage of "

View file

@ -73,6 +73,10 @@ zoteroattachmentbox
-moz-binding: url('chrome://zotero/content/bindings/attachmentbox.xml#attachment-box'); -moz-binding: url('chrome://zotero/content/bindings/attachmentbox.xml#attachment-box');
} }
zoterostoragefilebox
{
-moz-binding: url('chrome://zotero/content/bindings/storagefilebox.xml#storage-file-box');
}
zoteronoteeditor zoteronoteeditor
{ {