diff --git a/chrome/content/zotero/xpcom/data/collection.js b/chrome/content/zotero/xpcom/data/collection.js index cd40d025f8..bc5504df4d 100644 --- a/chrome/content/zotero/xpcom/data/collection.js +++ b/chrome/content/zotero/xpcom/data/collection.js @@ -30,6 +30,7 @@ Zotero.Collection.prototype._init = function () { // Public members for access by public methods -- do not access directly this._name = null; this._parent = null; + this._dateAdded = null; this._dateModified = null; this._key = null; @@ -56,6 +57,8 @@ Zotero.Collection.prototype.__defineGetter__('name', function () { return this._ Zotero.Collection.prototype.__defineSetter__('name', function (val) { this._set('name', val); }); Zotero.Collection.prototype.__defineGetter__('parent', function () { return this._get('parent'); }); Zotero.Collection.prototype.__defineSetter__('parent', function (val) { this._set('parent', val); }); +Zotero.Collection.prototype.__defineGetter__('dateAdded', function () { return this._get('dateAdded'); }); +Zotero.Collection.prototype.__defineSetter__('dateAdded', function (val) { this._set('dateAdded', val); }); Zotero.Collection.prototype.__defineGetter__('dateModified', function () { return this._get('dateModified'); }); Zotero.Collection.prototype.__defineSetter__('dateModified', function (val) { this._set('dateModified', val); }); Zotero.Collection.prototype.__defineGetter__('key', function () { return this._get('key'); }); @@ -154,6 +157,7 @@ Zotero.Collection.prototype.loadFromRow = function(row) { this._collectionID = row.collectionID; this._name = row.collectionName; this._parent = row.parentCollectionID; + this._dateAdded = row.dateAdded; this._dateModified = row.dateModified; this._key = row.key; this._hasChildCollections = !!row.hasChildCollections; @@ -321,9 +325,9 @@ Zotero.Collection.prototype.save = function () { var row = Zotero.DB.rowQuery("SELECT * FROM collections WHERE collectionID=?", oldID); // Add a new row so we can update the old rows despite FK checks // Use temp key due to UNIQUE constraint on key column - Zotero.DB.query("INSERT INTO collections VALUES (?, ?, ?, ?, ?)", + Zotero.DB.query("INSERT INTO collections VALUES (?, ?, ?, ?, ?, ?)", [this.id, row.collectionName, row.parentCollectionID, - row.dateModified, 'TEMPKEY']); + row.dateAdded, row.dateModified, 'TEMPKEY']); Zotero.DB.query("UPDATE collectionItems SET collectionID=? WHERE collectionID=?", params); Zotero.DB.query("UPDATE collections SET parentCollectionID=? WHERE parentCollectionID=?", params); @@ -354,13 +358,15 @@ Zotero.Collection.prototype.save = function () { var columns = [ 'collectionID', 'collectionName', 'parentCollectionID', - 'dateModified', 'key' + 'dateAdded', 'dateModified', 'key' ]; - var placeholders = ['?', '?', '?', '?', '?']; + var placeholders = ['?', '?', '?', '?', '?', '?']; var sqlValues = [ collectionID ? { int: collectionID } : null, { string: this.name }, this.parent ? { int: this.parent } : null, + // If date added isn't set, use current timestamp + this.dateAdded ? this.dateAdded : Zotero.DB.transactionDateTime, // If date modified hasn't changed, use current timestamp this._changed.dateModified ? this.dateModified : Zotero.DB.transactionDateTime, @@ -806,6 +812,7 @@ Zotero.Collection.prototype.serialize = function(nested) { var obj = { primary: { collectionID: this.id, + dateAdded: this.dateAdded, dateModified: this.dateModified, key: this.key }, diff --git a/chrome/content/zotero/xpcom/data/creator.js b/chrome/content/zotero/xpcom/data/creator.js index 65e5d236ad..a7db6eeb86 100644 --- a/chrome/content/zotero/xpcom/data/creator.js +++ b/chrome/content/zotero/xpcom/data/creator.js @@ -33,6 +33,7 @@ Zotero.Creator.prototype._init = function () { this._fieldMode = null; this._birthYear = null; this._key = null; + this._dateAdded = null; this._dateModified = null; this._creatorDataID = null; @@ -54,6 +55,8 @@ Zotero.Creator.prototype.__defineGetter__('fieldMode', function () { return this Zotero.Creator.prototype.__defineSetter__('fieldMode', function (val) { this._set('fieldMode', val); }); Zotero.Creator.prototype.__defineGetter__('birthYear', function () { return this._get('birthYear'); }); Zotero.Creator.prototype.__defineSetter__('birthYear', function (val) { this._set('birthYear', val); }); +Zotero.Creator.prototype.__defineGetter__('dateAdded', function () { return this._get('dateAdded'); }); +Zotero.Creator.prototype.__defineSetter__('dateAdded', function (val) { this._set('dateAdded', val); }); Zotero.Creator.prototype.__defineGetter__('dateModified', function () { return this._get('dateModified'); }); Zotero.Creator.prototype.__defineSetter__('dateModified', function (val) { this._set('dateModified', val); }); Zotero.Creator.prototype.__defineGetter__('key', function () { return this._get('key'); }); @@ -171,8 +174,8 @@ Zotero.Creator.prototype.save = function () { var row = Zotero.DB.rowQuery("SELECT * FROM creators WHERE creatorID=?", oldID); // Add a new row so we can update the old rows despite FK checks // Use temp key due to UNIQUE constraint on key column - Zotero.DB.query("INSERT INTO creators VALUES (?, ?, ?, ?)", - [this.id, row.creatorDataID, row.dateModified, 'TEMPKEY']); + Zotero.DB.query("INSERT INTO creators VALUES (?, ?, ?, ?, ?)", + [this.id, row.creatorDataID, row.dateAdded, row.dateModified, 'TEMPKEY']); Zotero.DB.query("UPDATE itemCreators SET creatorID=? WHERE creatorID=?", params); @@ -226,11 +229,13 @@ Zotero.Creator.prototype.save = function () { var creatorDataID = Zotero.Creators.getDataID(this, true); } - var columns = ['creatorID', 'creatorDataID', 'dateModified', 'key']; - var placeholders = ['?', '?', '?', '?']; + var columns = ['creatorID', 'creatorDataID', 'dateAdded', 'dateModified', 'key']; + var placeholders = ['?', '?', '?', '?', '?']; var sqlValues = [ creatorID ? { int: creatorID } : null, { int: creatorDataID }, + // If date added isn't set, use current timestamp + this.dateAdded ? this.dateAdded : Zotero.DB.transactionDateTime, // If date modified hasn't changed, use current timestamp this._changed.dateModified ? this.dateModified : Zotero.DB.transactionDateTime, @@ -337,6 +342,7 @@ Zotero.Creator.prototype.serialize = function () { obj.primary = {}; obj.primary.creatorID = this.id; + obj.primary.dateAdded = this.dateAdded; obj.primary.dateModified = this.dateModified; obj.primary.key = this.key; @@ -462,7 +468,8 @@ Zotero.Creator.prototype._checkValue = function (field, value) { this._invalidValueError(field, value); } break; - + + case 'dateAdded': case 'dateModified': if (value !== '' && !Zotero.Date.isSQLDateTime(value)) { this._invalidValueError(field, value); diff --git a/chrome/content/zotero/xpcom/data/tag.js b/chrome/content/zotero/xpcom/data/tag.js index 8cc19f63b1..fc63d0e1c3 100644 --- a/chrome/content/zotero/xpcom/data/tag.js +++ b/chrome/content/zotero/xpcom/data/tag.js @@ -30,6 +30,7 @@ Zotero.Tag.prototype._init = function () { // Public members for access by public methods -- do not access directly this._name = null; this._type = null; + this._dateAdded = null; this._dateModified = null; this._key = null; @@ -49,6 +50,8 @@ Zotero.Tag.prototype.__defineGetter__('name', function () { return this._get('na Zotero.Tag.prototype.__defineSetter__('name', function (val) { this._set('name', val); }); Zotero.Tag.prototype.__defineGetter__('type', function () { return this._get('type'); }); Zotero.Tag.prototype.__defineSetter__('type', function (val) { this._set('type', val); }); +Zotero.Tag.prototype.__defineGetter__('dateAdded', function () { return this._get('dateAdded'); }); +Zotero.Tag.prototype.__defineSetter__('dateAdded', function (val) { this._set('dateAdded', val); }); Zotero.Tag.prototype.__defineGetter__('dateModified', function () { return this._get('dateModified'); }); Zotero.Tag.prototype.__defineSetter__('dateModified', function (val) { this._set('dateModified', val); }); Zotero.Tag.prototype.__defineGetter__('key', function () { return this._get('key'); }); @@ -121,7 +124,7 @@ Zotero.Tag.prototype.load = function() { throw ("tagID not set in Zotero.Tag.load()"); } - var sql = "SELECT name, type, dateModified, key FROM tags WHERE tagID=?"; + var sql = "SELECT name, type, dateAdded, dateModified, key FROM tags WHERE tagID=?"; var row = Zotero.DB.rowQuery(sql, this.id); this.loadFromRow(row); @@ -247,8 +250,8 @@ Zotero.Tag.prototype.save = function (full) { // Add a new row so we can update the old rows despite FK checks // Use temp key due to UNIQUE constraint on key column - Zotero.DB.query("INSERT INTO tags VALUES (?, ?, ?, ?, ?)", - [this.id, row.name, row.type, row.dateModified, 'TEMPKEY']); + Zotero.DB.query("INSERT INTO tags VALUES (?, ?, ?, ?, ?, ?)", + [this.id, row.name, row.type, row.dateAdded, row.dateModified, 'TEMPKEY']); Zotero.DB.query("UPDATE itemTags SET tagID=? WHERE tagID=?", params); @@ -273,13 +276,15 @@ Zotero.Tag.prototype.save = function (full) { var key = this.key ? this.key : this._generateKey(); var columns = [ - 'tagID', 'name', 'type', 'dateModified', 'key' + 'tagID', 'name', 'type', 'dateAdded', 'dateModified', 'key' ]; - var placeholders = ['?', '?', '?', '?', '?']; + var placeholders = ['?', '?', '?', '?', '?', '?']; var sqlValues = [ tagID ? { int: tagID } : null, { string: this.name }, { int: this.type }, + // If date added isn't set, use current timestamp + this.dateAdded ? this.dateAdded : Zotero.DB.transactionDateTime, // If date modified hasn't changed, use current timestamp this._changed.dateModified ? this.dateModified : Zotero.DB.transactionDateTime, @@ -455,6 +460,7 @@ Zotero.Tag.prototype.serialize = function () { var obj = { primary: { tagID: this.id, + dateAdded: this.dateAdded, dateModified: this.dateModified, key: this.key }, diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js index f1785ec72e..c94254257d 100644 --- a/chrome/content/zotero/xpcom/schema.js +++ b/chrome/content/zotero/xpcom/schema.js @@ -2165,6 +2165,31 @@ Zotero.Schema = new function(){ if (i==48) { Zotero.DB.query("CREATE TABLE deletedItems (\n itemID INTEGER PRIMARY KEY,\n dateDeleted DEFAULT CURRENT_TIMESTAMP NOT NULL\n);"); } + + if (i==49) { + Zotero.DB.query("ALTER TABLE collections RENAME TO collectionsOld"); + Zotero.DB.query("DROP INDEX creators_creatorDataID"); + Zotero.DB.query("ALTER TABLE creators RENAME TO creatorsOld"); + Zotero.DB.query("ALTER TABLE savedSearches RENAME TO savedSearchesOld"); + Zotero.DB.query("ALTER TABLE tags RENAME TO tagsOld"); + + Zotero.DB.query("CREATE TABLE collections (\n collectionID INTEGER PRIMARY KEY,\n collectionName TEXT,\n parentCollectionID INT,\n dateAdded DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID)\n);"); + Zotero.DB.query("CREATE TABLE creators (\n creatorID INTEGER PRIMARY KEY,\n creatorDataID INT NOT NULL,\n dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n FOREIGN KEY (creatorDataID) REFERENCES creatorData(creatorDataID)\n);"); + Zotero.DB.query("CREATE TABLE savedSearches (\n savedSearchID INTEGER PRIMARY KEY,\n savedSearchName TEXT,\n dateAdded DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE\n);"); + Zotero.DB.query("CREATE TABLE tags (\n tagID INTEGER PRIMARY KEY,\n name TEXT COLLATE NOCASE,\n type INT NOT NULL,\n dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n UNIQUE (name, type)\n);"); + + Zotero.DB.query("INSERT INTO collections SELECT collectionID, collectionName, parentCollectionID, dateModified, dateModified, key FROM collectionsOld"); + Zotero.DB.query("INSERT INTO creators SELECT creatorID, creatorDataID, dateModified, dateModified, key FROM creatorsOld"); + Zotero.DB.query("INSERT INTO savedSearches SELECT savedSearchID, savedSearchName, dateModified, dateModified, key FROM savedSearchesOld"); + Zotero.DB.query("INSERT INTO tags SELECT tagID, name, type, dateModified, dateModified, key FROM tagsOld"); + + Zotero.DB.query("CREATE INDEX creators_creatorDataID ON creators(creatorDataID);"); + + Zotero.DB.query("DROP TABLE collectionsOld"); + Zotero.DB.query("DROP TABLE creatorsOld"); + Zotero.DB.query("DROP TABLE savedSearchesOld"); + Zotero.DB.query("DROP TABLE tagsOld"); + } } _updateDBVersion('userdata', toVersion); diff --git a/chrome/content/zotero/xpcom/search.js b/chrome/content/zotero/xpcom/search.js index 707644d417..fd4a29f576 100644 --- a/chrome/content/zotero/xpcom/search.js +++ b/chrome/content/zotero/xpcom/search.js @@ -29,6 +29,7 @@ Zotero.Search = function(searchID) { Zotero.Search.prototype._init = function () { // Public members for access by public methods -- do not access directly this._name = null; + this._dateAdded = null; this._dateModified = null; this._key = null; @@ -69,6 +70,8 @@ Zotero.Search.prototype.__defineSetter__('id', function (val) { this._set('id', Zotero.Search.prototype.__defineSetter__('searchID', function (val) { this._set('id', val); }); Zotero.Search.prototype.__defineGetter__('name', function () { return this._get('name'); }); Zotero.Search.prototype.__defineSetter__('name', function (val) { this._set('name', val); }); +Zotero.Search.prototype.__defineGetter__('dateAdded', function () { return this._get('dateAdded'); }); +Zotero.Search.prototype.__defineSetter__('dateAdded', function (val) { this._set('dateAdded', val); }); Zotero.Search.prototype.__defineGetter__('dateModified', function () { return this._get('dateModified'); }); Zotero.Search.prototype.__defineSetter__('dateModified', function (val) { this._set('dateModified', val); }); Zotero.Search.prototype.__defineGetter__('key', function () { return this._get('key'); }); @@ -158,6 +161,7 @@ Zotero.Search.prototype.load = function() { this._previousData = false; this._id = data.savedSearchID; this._name = data.savedSearchName; + this._dateAdded = data.dateAdded; this._dateModified = data.dateModified; this._key = data.key; this._maxSearchConditionID = data.maxID; @@ -215,8 +219,8 @@ Zotero.Search.prototype.save = function(fixGaps) { var row = Zotero.DB.rowQuery("SELECT * FROM savedSearches WHERE savedSearchID=?", oldID); // Add a new row so we can update the old rows despite FK checks // Use temp key due to UNIQUE constraint on key column - Zotero.DB.query("INSERT INTO savedSearches VALUES (?, ?, ?, ?)", - [this.id, row.savedSearchName, row.dateModified, 'TEMPKEY']); + Zotero.DB.query("INSERT INTO savedSearches VALUES (?, ?, ?, ?, ?)", + [this.id, row.savedSearchName, row.dateAdded, row.dateModified, 'TEMPKEY']); Zotero.DB.query("UPDATE savedSearchConditions SET savedSearchID=? WHERE savedSearchID=?", params); @@ -239,12 +243,14 @@ Zotero.Search.prototype.save = function(fixGaps) { var key = this.key ? this.key : this._generateKey(); var columns = [ - 'savedSearchID', 'savedSearchName', 'dateModified', 'key' + 'savedSearchID', 'savedSearchName', 'dateAdded', 'dateModified', 'key' ]; - var placeholders = ['?', '?', '?', '?']; + var placeholders = ['?', '?', '?', '?', '?']; var sqlValues = [ searchID ? { int: searchID } : null, { string: this.name }, + // If date added isn't set, use current timestamp + this.dateAdded ? this.dateAdded : Zotero.DB.transactionDateTime, // If date modified hasn't changed, use current timestamp this._changed.dateModified ? this.dateModified : Zotero.DB.transactionDateTime, @@ -815,6 +821,7 @@ Zotero.Search.prototype.serialize = function() { var obj = { primary: { id: this.id, + dateAdded: this.dateAdded, dateModified: this.dateModified, key: this.key }, diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js index 7e9aae469a..db4673b131 100644 --- a/chrome/content/zotero/xpcom/sync.js +++ b/chrome/content/zotero/xpcom/sync.js @@ -648,7 +648,7 @@ Zotero.Sync.Server = new function () { }); this.nextLocalSyncDate = false; - this.apiVersion = 3; + this.apiVersion = 4; default xml namespace = ''; @@ -2763,6 +2763,7 @@ Zotero.Sync.Server.Data = new function() { xml.@id = collection.id; xml.@name = _xmlize(collection.name); + xml.@dateAdded = collection.dateAdded; xml.@dateModified = collection.dateModified; xml.@key = collection.key; if (collection.parent) { @@ -2946,6 +2947,7 @@ Zotero.Sync.Server.Data = new function() { xml.@id = search.id; xml.@name = _xmlize(search.name); + xml.@dateAdded = search.dateAdded; xml.@dateModified = search.dateModified; xml.@key = search.key; @@ -3055,6 +3057,7 @@ Zotero.Sync.Server.Data = new function() { if (tag.type) { xml.@type = tag.type; } + xml.@dateAdded = tag.dateAdded; xml.@dateModified = tag.dateModified; xml.@key = tag.key; var linkedItems = tag.getLinkedItems(true); @@ -3095,6 +3098,7 @@ Zotero.Sync.Server.Data = new function() { tag.name = xmlTag.@name.toString(); tag.type = xmlTag.@type.toString() ? parseInt(xmlTag.@type) : 0; if (!skipPrimary) { + tag.dateAdded = xmlTag.@dateAdded.toString(); tag.dateModified = xmlTag.@dateModified.toString(); tag.key = xmlTag.@key.toString(); } diff --git a/userdata.sql b/userdata.sql index d73d9e57ed..29df458cc7 100644 --- a/userdata.sql +++ b/userdata.sql @@ -1,4 +1,4 @@ --- 48 +-- 49 -- This file creates tables containing user-specific data -- any changes made -- here must be mirrored in transition steps in schema.js::_migrateSchema() @@ -75,7 +75,8 @@ CREATE INDEX itemAttachments_syncState ON itemAttachments(syncState); CREATE TABLE tags ( tagID INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE, - type INT, + type INT NOT NULL, + dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL, key TEXT NOT NULL UNIQUE, UNIQUE (name, type) @@ -103,7 +104,8 @@ CREATE INDEX itemSeeAlso_linkedItemID ON itemSeeAlso(linkedItemID); CREATE TABLE creators ( creatorID INTEGER PRIMARY KEY, - creatorDataID INT, + creatorDataID INT NOT NULL, + dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL, key TEXT NOT NULL UNIQUE, FOREIGN KEY (creatorDataID) REFERENCES creatorData(creatorDataID) @@ -137,6 +139,7 @@ CREATE TABLE collections ( collectionID INTEGER PRIMARY KEY, collectionName TEXT, parentCollectionID INT, + dateAdded DEFAULT CURRENT_TIMESTAMP NOT NULL, dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL, key TEXT NOT NULL UNIQUE, FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID) @@ -156,6 +159,7 @@ CREATE INDEX itemID ON collectionItems(itemID); CREATE TABLE savedSearches ( savedSearchID INTEGER PRIMARY KEY, savedSearchName TEXT, + dateAdded DEFAULT CURRENT_TIMESTAMP NOT NULL, dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL, key TEXT NOT NULL UNIQUE );