Add annotationIsExternal
property to annotations
This commit is contained in:
parent
5266734ac9
commit
ebc0ca2462
9 changed files with 117 additions and 7 deletions
|
@ -75,6 +75,7 @@ Zotero.Item = function(itemTypeOrID) {
|
|||
this._annotationPageLabel = null;
|
||||
this._annotationSortIndex = null;
|
||||
this._annotationPosition = null;
|
||||
this._annotationIsExternal = null;
|
||||
|
||||
this._tags = [];
|
||||
this._collections = [];
|
||||
|
@ -1795,10 +1796,11 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
let pageLabel = this._getLatestField('annotationPageLabel');
|
||||
let sortIndex = this._getLatestField('annotationSortIndex');
|
||||
let position = this._getLatestField('annotationPosition');
|
||||
let isExternal = this._getLatestField('annotationIsExternal');
|
||||
|
||||
let sql = "REPLACE INTO itemAnnotations "
|
||||
+ "(itemID, parentItemID, type, text, comment, color, pageLabel, sortIndex, position) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
+ "(itemID, parentItemID, type, text, comment, color, pageLabel, sortIndex, position, isExternal) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
[
|
||||
|
@ -1810,7 +1812,8 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
color || null,
|
||||
pageLabel || null,
|
||||
sortIndex,
|
||||
position
|
||||
position,
|
||||
isExternal ? 1 : 0
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -3547,7 +3550,7 @@ Zotero.Item.prototype.clearBestAttachmentState = function () {
|
|||
////////////////////////////////////////////////////////
|
||||
|
||||
// Main annotation properties (required for items list display)
|
||||
for (let name of ['type', 'text', 'comment', 'color', 'pageLabel', 'sortIndex']) {
|
||||
for (let name of ['type', 'text', 'comment', 'color', 'pageLabel', 'sortIndex', 'isExternal']) {
|
||||
let field = 'annotation' + name[0].toUpperCase() + name.substr(1);
|
||||
Zotero.defineProperty(Zotero.Item.prototype, field, {
|
||||
get: function () {
|
||||
|
@ -3587,6 +3590,16 @@ for (let name of ['type', 'text', 'comment', 'color', 'pageLabel', 'sortIndex'])
|
|||
throw new Error(`Invalid sortIndex '${value}`);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'isExternal':
|
||||
if (typeof value != 'boolean') {
|
||||
throw new Error('annotationIsExternal must be a boolean');
|
||||
}
|
||||
let currentValue = this._getLatestField('annotationIsExternal');
|
||||
if (currentValue !== null && currentValue !== value) {
|
||||
throw new Error("Cannot change annotationIsExternal");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this._markFieldChange(field, value);
|
||||
|
|
|
@ -495,7 +495,8 @@ Zotero.Items = function() {
|
|||
|
||||
|
||||
this._loadAnnotations = async function (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT itemID, IA.parentItemID, IA.type, IA.text, IA.comment, IA.color, IA.sortIndex "
|
||||
var sql = "SELECT itemID, IA.parentItemID, IA.type, IA.text, IA.comment, IA.color, "
|
||||
+ "IA.sortIndex, IA.isExternal "
|
||||
+ "FROM items JOIN itemAnnotations IA USING (itemID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
@ -536,6 +537,7 @@ Zotero.Items = function() {
|
|||
item._annotationComment = row.getResultByIndex(4);
|
||||
item._annotationColor = row.getResultByIndex(5);
|
||||
item._annotationSortIndex = row.getResultByIndex(6);
|
||||
item._annotationIsExternal = !!row.getResultByIndex(7);
|
||||
|
||||
item._loaded.annotation = true;
|
||||
item._clearChanged('annotation');
|
||||
|
|
|
@ -3236,7 +3236,7 @@ Zotero.Schema = new function(){
|
|||
yield Zotero.DB.queryAsync("DROP TABLE IF EXISTS users");
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE users (\n userID INTEGER PRIMARY KEY,\n name TEXT NOT NULL\n)");
|
||||
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE itemAnnotations (\n itemID INTEGER PRIMARY KEY,\n parentItemID INT NOT NULL,\n type INTEGER NOT NULL,\n text TEXT,\n comment TEXT,\n color TEXT,\n pageLabel TEXT,\n sortIndex TEXT NOT NULL,\n position TEXT NOT NULL,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (parentItemID) REFERENCES itemAttachments(itemID) ON DELETE CASCADE\n)");
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE itemAnnotations (\n itemID INTEGER PRIMARY KEY,\n parentItemID INT NOT NULL,\n type INTEGER NOT NULL,\n text TEXT,\n comment TEXT,\n color TEXT,\n pageLabel TEXT,\n sortIndex TEXT NOT NULL,\n position TEXT NOT NULL,\n isExternal INT NOT NULL,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,\n FOREIGN KEY (parentItemID) REFERENCES itemAttachments(itemID) ON DELETE CASCADE\n)");
|
||||
yield Zotero.DB.queryAsync("CREATE INDEX itemAnnotations_parentItemID ON itemAnnotations(parentItemID)");
|
||||
|
||||
yield Zotero.DB.queryAsync("ALTER TABLE itemAttachments ADD COLUMN lastProcessedModificationTime INT");
|
||||
|
|
|
@ -543,9 +543,13 @@ Zotero.Sync.Data.Local = {
|
|||
var sql = "SELECT O." + objectsClass.idColumn + " FROM " + objectsClass.table + " O";
|
||||
if (objectType == 'item') {
|
||||
sql += " LEFT JOIN itemAttachments IA USING (itemID) "
|
||||
+ "LEFT JOIN itemNotes INo ON (O.itemID=INo.itemID) ";
|
||||
+ "LEFT JOIN itemNotes INo ON (O.itemID=INo.itemID) "
|
||||
+ "LEFT JOIN itemAnnotations IAn ON (O.itemID=IAn.itemID)";
|
||||
}
|
||||
sql += " WHERE libraryID=? AND synced=0";
|
||||
if (objectType == 'item') {
|
||||
sql += " AND (IAn.isExternal IS NULL OR IAN.isExternal=0)";
|
||||
}
|
||||
|
||||
var ids = yield Zotero.DB.columnQueryAsync(sql, [libraryID]);
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@ CREATE TABLE itemAnnotations (
|
|||
pageLabel TEXT,
|
||||
sortIndex TEXT NOT NULL,
|
||||
position TEXT NOT NULL,
|
||||
isExternal INT NOT NULL,
|
||||
FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE,
|
||||
FOREIGN KEY (parentItemID) REFERENCES itemAttachments(itemID) ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
@ -952,6 +952,9 @@ async function createAnnotation(type, parentItem, options = {}) {
|
|||
[314.4, 412.8, 556.2, 609.6]
|
||||
]
|
||||
});
|
||||
if (options.isExternal) {
|
||||
annotation.annotationIsExternal = options.isExternal;
|
||||
}
|
||||
if (options.tags) {
|
||||
annotation.setTags(options.tags);
|
||||
}
|
||||
|
|
|
@ -1834,6 +1834,26 @@ describe("Zotero.Item", function () {
|
|||
assert.deepEqual(json.annotationPosition, item.annotationPosition);
|
||||
assert.notProperty(json, 'collections');
|
||||
assert.notProperty(json, 'relations');
|
||||
assert.notProperty(json, 'annotationIsExternal');
|
||||
});
|
||||
|
||||
describe("#annotationIsExternal", function () {
|
||||
it("should be false if not set", async function () {
|
||||
var item = await createAnnotation('highlight', attachment);
|
||||
assert.isFalse(item.annotationIsExternal);
|
||||
});
|
||||
|
||||
it("should be true if set", async function () {
|
||||
var item = await createAnnotation('highlight', attachment, { isExternal: true });
|
||||
assert.isTrue(item.annotationIsExternal);
|
||||
});
|
||||
|
||||
it("should prevent changing of annotationIsExternal on existing item", async function () {
|
||||
var item = await createAnnotation('highlight', attachment);
|
||||
assert.throws(() => {
|
||||
item.annotationIsExternal = true;
|
||||
}, "Cannot change annotationIsExternal");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1386,6 +1386,62 @@ describe("Zotero.Sync.Data.Engine", function () {
|
|||
assert.equal(json.data.md5, md5);
|
||||
})
|
||||
|
||||
// See also: "shouldn't include external annotations" in syncLocalTest.js
|
||||
it("shouldn't upload external annotations", async function () {
|
||||
({ engine, client, caller } = await setup());
|
||||
|
||||
var library = Zotero.Libraries.userLibrary;
|
||||
var libraryID = library.id;
|
||||
var lastLibraryVersion = 5;
|
||||
library.libraryVersion = lastLibraryVersion;
|
||||
await library.saveTx();
|
||||
var nextLibraryVersion = lastLibraryVersion + 1;
|
||||
|
||||
var attachment = await importFileAttachment('test.pdf');
|
||||
var annotation1 = await createAnnotation('highlight', attachment);
|
||||
var annotation2 = await createAnnotation('highlight', attachment, { isExternal: true });
|
||||
|
||||
var item1ResponseJSON = attachment.toResponseJSON();
|
||||
item1ResponseJSON.version = item1ResponseJSON.data.version = nextLibraryVersion;
|
||||
var item2ResponseJSON = annotation1.toResponseJSON();
|
||||
item2ResponseJSON.version = item2ResponseJSON.data.version = nextLibraryVersion;
|
||||
|
||||
server.respond(function (req) {
|
||||
if (req.method == "POST") {
|
||||
assert.equal(
|
||||
req.requestHeaders["If-Unmodified-Since-Version"], lastLibraryVersion
|
||||
);
|
||||
|
||||
if (req.url == baseURL + "users/1/items") {
|
||||
let json = JSON.parse(req.requestBody);
|
||||
assert.lengthOf(json, 2);
|
||||
let keys = [json[0].key, json[1].key];
|
||||
assert.include(keys, attachment.key);
|
||||
assert.include(keys, annotation1.key);
|
||||
|
||||
req.respond(
|
||||
200,
|
||||
{
|
||||
"Content-Type": "application/json",
|
||||
"Last-Modified-Version": nextLibraryVersion
|
||||
},
|
||||
JSON.stringify({
|
||||
successful: {
|
||||
"0": item1ResponseJSON,
|
||||
"1": item2ResponseJSON
|
||||
},
|
||||
unchanged: {},
|
||||
failed: {}
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
await engine.start();
|
||||
});
|
||||
|
||||
it("should update local objects with remotely saved version after uploading if necessary", function* () {
|
||||
({ engine, client, caller } = yield setup());
|
||||
|
||||
|
|
|
@ -468,6 +468,17 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
|
||||
|
||||
describe("#getUnsynced()", function () {
|
||||
// See also: "shouldn't upload external annotations" in syncEngineTest.js
|
||||
it("shouldn't include external annotations", async function () {
|
||||
var attachment = await importFileAttachment('test.pdf');
|
||||
var annotation1 = await createAnnotation('highlight', attachment);
|
||||
var annotation2 = await createAnnotation('highlight', attachment, { isExternal: true });
|
||||
|
||||
var ids = await Zotero.Sync.Data.Local.getUnsynced('item', Zotero.Libraries.userLibraryID);
|
||||
assert.include(ids, attachment.id);
|
||||
assert.include(ids, annotation1.id);
|
||||
});
|
||||
|
||||
it("should correct incorrectly nested collections", async function () {
|
||||
var c1 = await createDataObject('collection');
|
||||
var c2 = await createDataObject('collection');
|
||||
|
|
Loading…
Reference in a new issue