Skip to content

Commit

Permalink
Merge from upstream master
Browse files Browse the repository at this point in the history
  • Loading branch information
fbennett committed Feb 20, 2017
2 parents a1b47fc + bb0fa73 commit 3bf7571
Show file tree
Hide file tree
Showing 18 changed files with 773 additions and 51 deletions.
17 changes: 5 additions & 12 deletions chrome/content/zotero/xpcom/data/item.js
Expand Up @@ -207,7 +207,7 @@ Zotero.Item.prototype._setParentKey = function() {
//
//////////////////////////////////////////////////////////////////////////////
/*
* Retrieves (and loads from DB, if necessary) an itemData field value
* Retrieves an itemData field value
*
* @param {String|Integer} field fieldID or fieldName
* @param {Boolean} [unformatted] Skip any special processing of DB value
Expand All @@ -225,19 +225,11 @@ Zotero.Item.prototype.getField = function(field, unformatted, includeBaseMapped,

this._requireData('primaryData');

// TODO: Fix logic and add sortCreator
// TODO: Add sortCreator
if (field === 'firstCreator' && !this._id) {
// Hack to get a firstCreator for an unsaved item
var creatorsData = this.getCreators(true);
if (creators.length === 0) {
return "";
} else if (creators.length === 1) {
return creatorsData[0].lastName;
} else if (creators.length === 2) {
return creatorsData[0].lastName + " " + Zotero.getString('general.and') + " " + creatorsData[1].lastName;
} else if (creators.length > 3) {
return creatorsData[0].lastName + " " + Zotero.getString('general.etAl');
}
let creatorsData = this.getCreators(true);
return Zotero.Items.getFirstCreatorFromData(this.itemTypeID, creatorsData);
} else if (field === 'id' || this.ObjectsClass.isPrimaryField(field)) {
var privField = '_' + field;
//Zotero.debug('Returning ' + (this[privField] ? this[privField] : '') + ' (typeof ' + typeof this[privField] + ')');
Expand Down Expand Up @@ -2287,6 +2279,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
// Update child item counts and contents
if (reloadParentChildItems) {
for (let parentItemID in reloadParentChildItems) {
// Keep in sync with Zotero.Items.trash()
let parentItem = yield this.ObjectsClass.getAsync(parentItemID);
yield parentItem.reload(['primaryData', 'childItems'], true);
parentItem.clearBestAttachmentState();
Expand Down
60 changes: 58 additions & 2 deletions chrome/content/zotero/xpcom/data/items.js
Expand Up @@ -935,12 +935,17 @@ Zotero.Items = function() {
libraryIDs.add(item.libraryID);
}

var parentItemIDs = new Set();
items.forEach(item => {
item.setDeleted(true);
item.synced = false;
if (item.parentItemID) {
parentItemIDs.add(item.parentItemID);
}
});
yield Zotero.Utilities.Internal.forEachChunkAsync(ids, 250, Zotero.Promise.coroutine(function* (chunk) {
yield Zotero.DB.queryAsync(
"UPDATE items SET synced=1, clientDateModified=CURRENT_TIMESTAMP "
"UPDATE items SET synced=0, clientDateModified=CURRENT_TIMESTAMP "
+ `WHERE itemID IN (${chunk.map(id => parseInt(id)).join(", ")})`
);
yield Zotero.DB.queryAsync(
Expand All @@ -949,6 +954,11 @@ Zotero.Items = function() {
);
}.bind(this)));

// Keep in sync with Zotero.Item::saveData()
for (let parentItemID of parentItemIDs) {
let parentItem = yield Zotero.Items.getAsync(parentItemID);
yield parentItem.reload(['primaryData', 'childItems'], true);
}
Zotero.Notifier.queue('trash', 'item', ids);
Array.from(libraryIDs).forEach(libraryID => {
Zotero.Notifier.queue('refresh', 'trash', libraryID);
Expand Down Expand Up @@ -1066,8 +1076,12 @@ Zotero.Items = function() {
});



/**
* Given API JSON for an item, return the best first creator, regardless of creator order
* Given API JSON for an item, return the best single first creator, regardless of creator order
*
* Note that this is just a single creator, not the firstCreator field return from the
* Zotero.Item::firstCreator property or this.getFirstCreatorFromData()
*
* @return {Object|false} - Creator in API JSON format, or false
*/
Expand All @@ -1090,6 +1104,48 @@ Zotero.Items = function() {
};


/**
* Return a firstCreator string from internal creators data (from Zotero.Item::getCreators()).
*
* Used in Zotero.Item::getField() for unsaved items
*
* @param {Integer} itemTypeID
* @param {Object} creatorData
* @return {String}
*/
this.getFirstCreatorFromData = function (itemTypeID, creatorsData) {
if (creatorsData.length === 0) {
return "";
}

var validCreatorTypes = [
Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID),
Zotero.CreatorTypes.getID('editor'),
Zotero.CreatorTypes.getID('contributor')
];

for (let creatorTypeID of validCreatorTypes) {
let matches = creatorsData.filter(data => data.creatorTypeID == creatorTypeID)
if (!matches.length) {
continue;
}
if (matches.length === 1) {
return matches[0].lastName;
}
if (matches.length === 2) {
let a = matches[0];
let b = matches[1];
return a.lastName + " " + Zotero.getString('general.and') + " " + b.lastName;
}
if (matches.length >= 3) {
return matches[0].lastName + " " + Zotero.getString('general.etAl');
}
}

return "";
};


/*
* Generate SQL to retrieve firstCreator field
*
Expand Down
3 changes: 3 additions & 0 deletions chrome/content/zotero/xpcom/data/search.js
Expand Up @@ -1153,6 +1153,9 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
// Old-style library-key hash
if (objKey.indexOf('_') != -1) {
[objLibraryID, objKey] = objKey.split('_');
if (objLibraryID === "0") {
objLibraryID = Zotero.Libraries.userLibraryID;
}
}
// libraryID assigned on search
else if (this.libraryID !== null) {
Expand Down
13 changes: 7 additions & 6 deletions chrome/content/zotero/xpcom/storage/zfs.js
Expand Up @@ -476,13 +476,14 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
);
throw e;
}
// This shouldn't happen, but if it does, mark item for upload and restart sync
else if (req.status == 404) {
Components.utils.reportError("Unexpected status code 404 in upload authorization "
+ "request (" + item.libraryKey + ")");

// TODO: Make an API request to fix this

throw new Error(Zotero.Sync.Storage.defaultError);
Zotero.logError(`Item ${item.libraryID}/${item.key} not found in upload authorization `
+ 'request -- marking for upload');
yield Zotero.Sync.Data.Local.markObjectAsUnsynced(item);
return new Zotero.Sync.Storage.Result({
syncRequired: true
});
}
else if (req.status == 412) {
let version = req.getResponseHeader('Last-Modified-Version');
Expand Down
45 changes: 37 additions & 8 deletions chrome/content/zotero/xpcom/sync/syncEngine.js
Expand Up @@ -1000,7 +1000,7 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
}
);
}
batch.push(o.json);
batch.push(o);
}

// No more non-failed requests
Expand All @@ -1011,8 +1011,10 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
// Remove selected and skipped objects from queue
queue.splice(0, batch.length + numSkipped);

let jsonBatch = batch.map(o => o.json);

Zotero.debug("UPLOAD BATCH:");
Zotero.debug(batch);
Zotero.debug(jsonBatch);

let results;
let numSuccessful = 0;
Expand All @@ -1022,7 +1024,7 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
"POST",
libraryVersion,
objectType,
batch
jsonBatch
));

// Mark successful and unchanged objects as synced with new version,
Expand All @@ -1038,8 +1040,8 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
let key = state == 'successful' ? current.key : current;
let changed = changedObjects.has(key);

if (key != batch[index].key) {
throw new Error("Key mismatch (" + key + " != " + batch[index].key + ")");
if (key != jsonBatch[index].key) {
throw new Error("Key mismatch (" + key + " != " + jsonBatch[index].key + ")");
}

let obj = objectsClass.getByLibraryAndKey(this.libraryID, key);
Expand Down Expand Up @@ -1074,13 +1076,14 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
// will guarantee that the item won't be redownloaded unnecessarily in the case of
// a full sync, because the version will be higher than whatever version is on the
// server.
batch[index].version = libraryVersion;
toCache.push(batch[index]);
jsonBatch[index].version = libraryVersion;
toCache.push(jsonBatch[index]);
}

numSuccessful++;
// Remove from batch to mark as successful
delete batch[index];
delete jsonBatch[index];
}
}
yield Zotero.Sync.Data.Local.saveCacheObjects(
Expand Down Expand Up @@ -1111,9 +1114,35 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
if (data) {
e.data = data;
}
Zotero.logError("Error for " + objectType + " " + batch[index].key + " in "
Zotero.logError("Error for " + objectType + " " + jsonBatch[index].key + " in "
+ this.library.name + ":\n\n" + e);

// If parent item is missing remotely and it isn't in the queue (which shouldn't happen),
// mark it as unsynced and add to queue
if (e.code == 400 && objectType == 'item' && data && data.parentItem) {
let parentItem = Zotero.Items.getByLibraryAndKey(this.libraryID, data.parentItem);
if (!parentItem) {
throw new Error(`Item ${this.libraryID}/${jsonBatch[index].key} references parent `
+ `item ${data.parentItem}, which doesn't exist`);
}

let id = parentItem.id;
// If parent item isn't already in queue, mark it as unsynced and add it
if (!queue.find(o => o.id == id) && !batch.find(o => o.id == id)) {
yield Zotero.Sync.Data.Local.markObjectAsUnsynced(parentItem);
Zotero.logError(`Adding parent item ${data.parentItem} to upload queue`);
queue.push({
id,
json: null,
tries: 0,
failed: false
});
// Pretend that we were successful so syncing continues
numSuccessful++;
continue;
}
}

// This shouldn't happen, because the upload request includes a library version and should
// prevent an outdated upload before the object version is checked. If it does, we need to
// do a full sync. This error is checked in handleUploadError().
Expand Down
10 changes: 7 additions & 3 deletions chrome/content/zotero/xpcom/utilities.js
Expand Up @@ -247,9 +247,13 @@ Zotero.Utilities = {
var lastName = author;
}
} else {
var spaceIndex = author.lastIndexOf(" ");
var lastName = author.substring(spaceIndex+1);
var firstName = author.substring(0, spaceIndex);
// Don't parse "Firstname Lastname [Country]" as "[Country], Firstname Lastname"
var spaceIndex = author.length;
do {
spaceIndex = author.lastIndexOf(" ", spaceIndex-1);
var lastName = author.substring(spaceIndex + 1);
var firstName = author.substring(0, spaceIndex);
} while (!Zotero.Utilities.XRegExp('\\pL').test(lastName[0]) && spaceIndex > 0)
}

if(firstName && allCapsRe.test(firstName) &&
Expand Down
2 changes: 1 addition & 1 deletion chrome/locale/hu-HU/zotero/csledit.dtd
@@ -1,3 +1,3 @@
<!ENTITY styles.editor "Zotero Hivatkozás Szerkesztő">

<!ENTITY styles.editor.citePosition "Cite Position:">
<!ENTITY styles.editor.citePosition "Hivatkozás helyzete:">
8 changes: 4 additions & 4 deletions chrome/locale/hu-HU/zotero/cslpreview.dtd
@@ -1,9 +1,9 @@
<!ENTITY styles.preview "Zotero Style Preview">

<!ENTITY styles.preview.citationFormat "Hivatkozási forma:">
<!ENTITY styles.preview.citationFormat.all "all">
<!ENTITY styles.preview.citationFormat.all "összes">
<!ENTITY styles.preview.citationFormat.author "szerző">
<!ENTITY styles.preview.citationFormat.authorDate "szövegközi hivatkozás (szerző-dátum)">
<!ENTITY styles.preview.citationFormat.label "label">
<!ENTITY styles.preview.citationFormat.note "note">
<!ENTITY styles.preview.citationFormat.numeric "numeric">
<!ENTITY styles.preview.citationFormat.label "címke">
<!ENTITY styles.preview.citationFormat.note "jegyzet">
<!ENTITY styles.preview.citationFormat.numeric "számozott">
4 changes: 2 additions & 2 deletions chrome/locale/nb-NO/zotero/csledit.dtd
@@ -1,3 +1,3 @@
<!ENTITY styles.editor "Zotero Style Editor">
<!ENTITY styles.editor "Zotero stilredigerer">

<!ENTITY styles.editor.citePosition "Cite Position:">
<!ENTITY styles.editor.citePosition "Sitatposisjon">
16 changes: 8 additions & 8 deletions chrome/locale/nb-NO/zotero/cslpreview.dtd
@@ -1,9 +1,9 @@
<!ENTITY styles.preview "Zotero Style Preview">
<!ENTITY styles.preview "Zotero stilforhåndsvisning">

<!ENTITY styles.preview.citationFormat "Citation Format:">
<!ENTITY styles.preview.citationFormat.all "all">
<!ENTITY styles.preview.citationFormat.author "author">
<!ENTITY styles.preview.citationFormat.authorDate "author-date">
<!ENTITY styles.preview.citationFormat.label "label">
<!ENTITY styles.preview.citationFormat.note "note">
<!ENTITY styles.preview.citationFormat.numeric "numeric">
<!ENTITY styles.preview.citationFormat "Siteringsformat:">
<!ENTITY styles.preview.citationFormat.all "alle">
<!ENTITY styles.preview.citationFormat.author "forfatter">
<!ENTITY styles.preview.citationFormat.authorDate "forfattelsesdato">
<!ENTITY styles.preview.citationFormat.label "merkelapp">
<!ENTITY styles.preview.citationFormat.note "notat">
<!ENTITY styles.preview.citationFormat.numeric "numerisk">

0 comments on commit 3bf7571

Please sign in to comment.