- 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:
parent
a1277ccaa9
commit
bd070f7b63
6 changed files with 396 additions and 55 deletions
|
@ -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"/>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
67
chrome/content/zotero/bindings/storagefilebox.xml
Normal file
67
chrome/content/zotero/bindings/storagefilebox.xml
Normal 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>
|
|
@ -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);
|
||||||
|
|
|
@ -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 "
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue