diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js index 615c67fcf8..674ae2a4a2 100644 --- a/chrome/content/zotero/xpcom/data/item.js +++ b/chrome/content/zotero/xpcom/data/item.js @@ -1440,12 +1440,22 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) { // Make sure parent is a regular item if (parentItemID) { let parentItem = yield Zotero.Items.getAsync(parentItemID); - if (!parentItem.isRegularItem() - // Allow embedded-image attachments under notes - && !(this.isEmbeddedImageAttachment() && parentItem.isNote()) - // Allow annotations under attachments - && !(this.isAnnotation() && parentItem.isFileAttachment())) { - throw new Error(`Parent item ${parentItem.libraryKey} must be a regular item`); + if (!parentItem.isRegularItem()) { + // Allow embedded-image attachments under notes + if (this.isEmbeddedImageAttachment()) { + if (!parentItem.isNote()) { + throw new Error(`Parent item ${parentItem.libraryKey} must a note`); + } + } + // Allow annotations under attachments + else if (this.isAnnotation()) { + if (!parentItem.isFileAttachment()) { + throw new Error(`Parent item ${parentItem.libraryKey} must be a file attachment`); + } + } + else { + throw new Error(`Parent item ${parentItem.libraryKey} must be a regular item`); + } } } @@ -4653,13 +4663,25 @@ Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) { } } - // Zotero.Sync.EventListeners.ChangeListener needs to know if this was a storage file - env.notifierData[this.id].storageDeleteLog = this.isStoredFileAttachment(); - if (this.isFileAttachment()) { + // Delete child annotations + let sql = "SELECT itemID FROM itemAnnotations WHERE parentItemID=?"; + let toDelete = yield Zotero.DB.columnQueryAsync(sql, [this.id]); + for (let i = 0; i < toDelete.length; i++) { + let obj = yield this.ObjectsClass.getAsync(toDelete[i]); + yield obj.erase({ + skipParentRefresh: true, + skipEditCheck: env.options.skipEditCheck + }); + } + + // Delete last page index let id = this._getLastPageIndexSettingKey(); yield Zotero.SyncedSettings.clear(Zotero.Libraries.userLibraryID, id); } + + // Zotero.Sync.EventListeners.ChangeListener needs to know if this was a storage file + env.notifierData[this.id].storageDeleteLog = this.isStoredFileAttachment(); } // Delete cached file for image annotation else if (this.isAnnotation()) { diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js index 3f609991d8..e6d02ef805 100644 --- a/chrome/content/zotero/xpcom/schema.js +++ b/chrome/content/zotero/xpcom/schema.js @@ -41,7 +41,7 @@ Zotero.Schema = new function(){ // If updating from this userdata version or later, don't show "Upgrading database…" and don't make // DB backup first. This should be set to false when breaking compatibility or making major changes. - const minorUpdateFrom = false; + const minorUpdateFrom = 112; var _dbVersions = []; var _schemaVersions = []; @@ -3250,6 +3250,24 @@ Zotero.Schema = new function(){ yield Zotero.DB.queryAsync("CREATE INDEX itemAttachments_lastProcessedModificationTime ON itemAttachments(lastProcessedModificationTime)"); } + else if (i == 113) { + yield Zotero.DB.queryAsync("ALTER TABLE itemAnnotations RENAME TO itemAnnotationsOld"); + 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)\n)"); + yield Zotero.DB.queryAsync("INSERT INTO itemAnnotations SELECT * FROM itemAnnotationsOld"); + yield Zotero.DB.queryAsync("DROP TABLE itemAnnotationsOld"); + yield Zotero.DB.queryAsync("CREATE INDEX itemAnnotations_parentItemID ON itemAnnotations(parentItemID)"); + + let annotationID = parseInt((yield Zotero.DB.valueQueryAsync( + "SELECT itemTypeID FROM itemTypes WHERE typeName='annotation'" + )) || -1); + let syncObjectTypeID = yield Zotero.DB.valueQueryAsync("SELECT syncObjectTypeID FROM syncObjectTypes WHERE name='item'"); + let rows = yield Zotero.DB.queryAsync("SELECT libraryID, key FROM items WHERE itemTypeID=? AND itemID NOT IN (SELECT itemID FROM itemAnnotations)", annotationID); + for (let row of rows) { + yield Zotero.DB.queryAsync("REPLACE INTO syncDeleteLog (syncObjectTypeID, libraryID, key) VALUES (?, ?, ?)", [syncObjectTypeID, row.libraryID, row.key]); + } + yield Zotero.DB.queryAsync("DELETE FROM items WHERE itemTypeID=? AND itemID NOT IN (SELECT itemID FROM itemAnnotations)", annotationID); + } + // If breaking compatibility or doing anything dangerous, clear minorUpdateFrom } diff --git a/resource/schema/userdata.sql b/resource/schema/userdata.sql index 620525b1de..ab0b4e3288 100644 --- a/resource/schema/userdata.sql +++ b/resource/schema/userdata.sql @@ -1,4 +1,4 @@ --- 112 +-- 113 -- Copyright (c) 2009 Center for History and New Media -- George Mason University, Fairfax, Virginia, USA @@ -127,7 +127,7 @@ CREATE TABLE itemAnnotations ( 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 + FOREIGN KEY (parentItemID) REFERENCES itemAttachments(itemID) ); CREATE INDEX itemAnnotations_parentItemID ON itemAnnotations(parentItemID);