From d211159d741a134d34641fbe75c1da9c6cf2dbac Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Tue, 14 Mar 2006 11:48:36 +0000 Subject: [PATCH] Initial version of data access methods -- documentation will be provided on Basecamp Surely buggy and incomplete, but a good start --- .../content/scholar/data_access.js | 1110 +++++++++++++++++ 1 file changed, 1110 insertions(+) create mode 100644 chrome/chromeFiles/content/scholar/data_access.js diff --git a/chrome/chromeFiles/content/scholar/data_access.js b/chrome/chromeFiles/content/scholar/data_access.js new file mode 100644 index 0000000000..e69452a72f --- /dev/null +++ b/chrome/chromeFiles/content/scholar/data_access.js @@ -0,0 +1,1110 @@ +/* + * Constructor for Object object + * + * Generally should be called through Scholar_Objects rather than directly + */ +function Scholar_Object(){ + this._init(); + + // Accept objectTypeID, folderID and orderIndex in constructor + if (arguments.length){ + this.setType(arguments[0]); + this.setPosition(arguments[1],arguments[2]); + } +} + +Scholar_Object.prototype._init = function(){ + // + // Public members for access by public methods -- do not access directly + // + this._data = new Array(); + this._creators = new Scholar.Hash(); + this._objectData = new Array(); + + this._creatorsLoaded = false; + this._objectDataLoaded = false; + + this._changed = new Scholar.Hash(); + this._changedCreators = new Scholar.Hash(); + this._changedObjectData = new Scholar.Hash(); +} + +/* + * Check if the specified field is a primary field from the objects table + */ +Scholar_Object.prototype.isPrimaryField = function(field){ + if (!Scholar_Object.primaryFields){ + Scholar_Object.primaryFields = Scholar_DB.getColumnHash('objects'); + } + + return !!Scholar_Object.primaryFields[field]; +} + +Scholar_Object.editableFields = { + title: true, + source: true, + rights: true +}; + + +/* + * Check if the specified primary field can be changed with setField() + */ +Scholar_Object.prototype.isEditableField = function(field){ + return !!Scholar_Object.editableFields[field]; +} + + +/* + * Build object from database + */ +Scholar_Object.prototype.loadFromID = function(id){ + var sql = 'SELECT O.*, lastName AS firstCreator FROM objects O ' + + 'LEFT JOIN objectCreators OC USING (objectID) ' + + 'LEFT JOIN creators USING (creatorID) ' + + 'WHERE objectID=' + id + ' AND OC.orderIndex=1'; + var row = Scholar_DB.rowQuery(sql); + this.loadFromRow(row); +} + + +/* + * Populate basic object data from a database row + */ +Scholar_Object.prototype.loadFromRow = function(row){ + this._init(); + for (col in row){ + if (this.isPrimaryField(col) || col=='firstCreator'){ + this._data[col] = row[col]; + } + } + return true; +} + + +/* + * Check if any data fields have changed since last save + */ +Scholar_Object.prototype.hasChanged = function(){ + return (this._changed.length || this._changedCreators.length || + this._changedObjectData.length); +} + + +Scholar_Object.prototype.getID = function(){ + return this._data['objectID'] ? this._data['objectID'] : false; +} + + +Scholar_Object.prototype.getType = function(){ + return this._data['objectTypeID'] ? this._data['objectTypeID'] : false; +} + + +/* + * Set or change the object's type + */ +Scholar_Object.prototype.setType = function(objectTypeID){ + if (objectTypeID==this.getType()){ + return true; + } + + // If existing type, clear fields from old type that aren't in new one + if (this.getType()){ + var sql = 'SELECT fieldID FROM objectTypeFields ' + + 'WHERE objectTypeID=' + this.getType() + ' AND fieldID NOT IN ' + + '(SELECT fieldID FROM objectTypeFields WHERE objectTypeID=' + + objectTypeID + ')'; + var obsoleteFields = Scholar_DB.columnQuery(sql); + + if (obsoleteFields){ + for (var i=0; i' + oldPos + ";\n"; + + sql += 'UPDATE objects SET orderIndex=orderIndex+1 WHERE folderID=' + + newFolder + ' AND orderIndex>=' + newPos + ";\n"; + } + + sql += 'UPDATE objects SET folderID=' + newFolder + ', orderIndex=' + + newPos + ' WHERE objectID=' + this.getID() + ";\n"; + + sql += 'COMMIT;'; + + Scholar_DB.query(sql); + } + + this._data['folderID'] = newFolder; + this._data['orderIndex'] = newPos; + Scholar_Objects.reloadAll(); + return true; +} + + +/* + * Save changes back to database + */ +Scholar_Object.prototype.save = function(){ + if (!this.hasChanged()){ + Scholar.debug('Object ' + this.getID() + ' has not changed', 4); + return !!this.getID(); + } + + // + // Existing object, update + // + if (this.getID()){ + Scholar.debug('Updating database with new object data', 4); + + var objectID = this.getID(); + + try { + Scholar_DB.beginTransaction(); + + // + // Primary fields + // + var sql = "UPDATE objects SET "; + var sql2; + + if (this._changed.has('objectTypeID')){ + sql += "objectTypeID='" + this.getField('objectTypeID') + "', "; + } + if (this._changed.has('title')){ + sql += "title='" + this.getField('title') + "', "; + } + if (this._changed.has('source')){ + sql += "source='" + this.getField('source') + "', "; + } + if (this._changed.has('rights')){ + sql += "rights='" + this.getField('rights') + "', "; + } + + // Always update modified time + sql += "dateModified=CURRENT_TIMESTAMP "; + sql += "WHERE objectID=" + this.getID() + ";\n"; + + + // + // Creators + // + if (this._changedCreators.length){ + for (orderIndex in this._changedCreators.items){ + Scholar.debug('Creator ' + orderIndex + ' has changed', 4); + + var creator = this.getCreator(orderIndex); + + // If empty, delete at position and shift down any above it + // + // We have to do this immediately so old entries are + // cleared before other ones are shifted down + if (!creator['firstName'] && !creator['lastName']){ + sql2 = 'DELETE FROM objectCreators ' + + ' WHERE objectID=' + this.getID() + + ' AND orderIndex=' + orderIndex; + Scholar_DB.query(sql2); + continue; + } + + // See if this is an existing creator + var creatorID = Scholar_Creators.getID( + creator['firstName'], + creator['lastName'], + creator['creatorTypeID'] + ); + + // If not, add it + if (!creatorID){ + creatorID = Scholar_Creators.add( + creator['firstName'], + creator['lastName'], + creator['creatorTypeID'] + ); + } + + + sql2 = 'SELECT COUNT(*) FROM objectCreators' + + ' WHERE objectID=' + this.getID() + + ' AND orderIndex=' + orderIndex; + + if (Scholar_DB.valueQuery(sql2)){ + sql += 'UPDATE objectCreators SET creatorID=' + + creatorID + ' WHERE objectID=' + this.getID() + + ' AND orderIndex=' + orderIndex + ";\n"; + } + else { + sql += 'INSERT INTO objectCreators VALUES (' + + creatorID + ',' + objectID + ',' + orderIndex + + ");\n"; + } + } + + // Append the SQL to delete obsolete creators + sql += Scholar_Creators.purge(true) + "\n"; + } + + + // + // ObjectData + // + if (this._changedObjectData.length){ + var del = new Array(); + for (fieldID in this._changedObjectData.items){ + if (this.getField(fieldID)){ + // Oh, for an INSERT...ON DUPLICATE KEY UPDATE + sql2 = 'SELECT COUNT(*) FROM objectData ' + + 'WHERE objectID=' + this.getID() + + ' AND fieldID=' + fieldID; + + if (Scholar_DB.valueQuery(sql2)){ + sql += "UPDATE objectData SET value="; + // Take advantage of SQLite's manifest typing + if (Scholar_ObjectFields.isInteger(fieldID)){ + sql += this.getField(fieldID); + } + else { + sql += "'" + this.getField(fieldID) + "'"; + } + sql += " WHERE objectID=" + this.getID() + + ' AND fieldID=' + fieldID + ";\n"; + } + else { + sql += 'INSERT INTO objectData VALUES (' + + this.getID() + ',' + fieldID + ','; + + if (Scholar_ObjectFields.isInteger(fieldID)){ + sql += this.getField(fieldID); + } + else { + sql += "'" + this.getField(fieldID) + "'"; + } + sql += ");\n"; + } + } + // If field changed and is empty, mark row for deletion + else { + del.push(fieldID); + } + } + + // Delete blank fields + if (del.length){ + sql += 'DELETE from objectData ' + + 'WHERE objectID=' + this.getID() + ' ' + + 'AND fieldID IN (' + del.join() + ");\n"; + } + } + + + Scholar_DB.query(sql); + Scholar_DB.commitTransaction(); + } + catch (e){ + Scholar_DB.rollbackTransaction(); + throw (e); + } + } + + // + // New object, insert and return id + // + else { + Scholar.debug('Saving data for new object to database'); + + var isNew = true; + var sqlColumns = new Array(); + var sqlValues = new Array(); + + // + // Primary fields + // + sqlColumns.push('objectTypeID'); + sqlValues.push({'int':this.getField('objectTypeID')}); + + if (this._changed.has('title')){ + sqlColumns.push('title'); + sqlValues.push({'string':this.getField('title')}); + } + if (this._changed.has('source')){ + sqlColumns.push('source'); + sqlValues.push({'string':this.getField('source')}); + } + if (this._changed.has('rights')){ + sqlColumns.push('rights'); + sqlValues.push({'string':this.getField('rights')}); + } + + sqlColumns.push('folderID'); + var newFolder = + this._changed.has('folderID') ? this.getField('folderID') : 0; + sqlValues.push({'int':newFolder}); + + try { + Scholar_DB.beginTransaction(); + + // We set the index here within the transaction so that MAX()+1 + // stays consistent through the INSERT + sqlColumns.push('orderIndex'); + if (this._changed.has('orderIndex')){ + sqlValues.push({'int':this.getField('orderIndex')}); + } + else { + var newPos = Scholar_DB.valueQuery('SELECT MAX(orderIndex)+1 ' + + 'FROM objects WHERE folderID=' + newFolder); + sqlValues.push({'int': newPos}); + } + + + // + // Creators + // + if (this._changedCreators.length){ + for (orderIndex in this._changedCreators.items){ + var creator = this.getCreator(orderIndex); + + // If empty, skip + if (typeof creator['firstName'] == 'undefined' + && typeof creator['lastName'] == 'undefined'){ + continue; + } + + // See if this is an existing creator + var creatorID = Scholar_Creators.getID( + creator['firstName'], + creator['lastName'], + creator['creatorTypeID'] + ); + + // If not, add it + if (!creatorID){ + creatorID = Scholar_Creators.add( + creator['firstName'], + creator['lastName'], + creator['creatorTypeID'] + ); + } + + sql += 'INSERT INTO objectCreators VALUES (' + + creatorID + ',' + objectID + ',' + orderIndex + + ");\n"; + } + } + + + // + // objectData fields + // + var sql = "INSERT INTO objects (" + sqlColumns.join() + ')' + + ' VALUES ('; + // Insert placeholders for bind parameters + for (var i=0; i