/* * 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