/* * Constructor for Item object * * Generally should be called through Scholar.Items rather than directly */ Scholar.Item = function(){ this._init(); // Accept itemTypeID, folderID and orderIndex in constructor if (arguments.length){ this.setType(arguments[0]); if (arguments.length>1){ this.setPosition(arguments[1], arguments[2] ? arguments[2] : false); } } } Scholar.Item.prototype._init = function(){ // // Public members for access by public methods -- do not access directly // this._data = new Array(); this._creators = new Scholar.Hash(); this._itemData = new Array(); this._creatorsLoaded = false; this._itemDataLoaded = false; this._changed = new Scholar.Hash(); this._changedCreators = new Scholar.Hash(); this._changedItemData = new Scholar.Hash(); } ////////////////////////////////////////////////////////////////////////////// // // Public Scholar.Item methods // ////////////////////////////////////////////////////////////////////////////// /* * Check if the specified field is a primary field from the items table */ Scholar.Item.prototype.isPrimaryField = function(field){ if (!Scholar.Item.primaryFields){ Scholar.Item.primaryFields = Scholar.DB.getColumnHash('items'); Scholar.Item.primaryFields['firstCreator'] = true; Scholar.Item.primaryFields['parentFolderID'] = true; Scholar.Item.primaryFields['orderIndex'] = true; } return !!Scholar.Item.primaryFields[field]; } Scholar.Item.editableFields = { title: true, source: true, rights: true }; /* * Check if the specified primary field can be changed with setField() */ Scholar.Item.prototype.isEditableField = function(field){ return !!Scholar.Item.editableFields[field]; } /* * Build object from database */ Scholar.Item.prototype.loadFromID = function(id){ var sql = 'SELECT I.*, lastName AS firstCreator, TS.parentFolderID, ' + 'TS.orderIndex ' + 'FROM items I ' + 'LEFT JOIN treeStructure TS ON (I.itemID=TS.id AND isFolder=0) ' + 'LEFT JOIN itemCreators IC ON (I.itemID=IC.itemID) ' + 'LEFT JOIN creators C ON (IC.creatorID=C.creatorID) ' + 'WHERE itemID=' + id + ' AND (IC.orderIndex=0 OR IC.orderIndex IS NULL)'; var row = Scholar.DB.rowQuery(sql); this.loadFromRow(row); } /* * Populate basic item data from a database row */ Scholar.Item.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.Item.prototype.hasChanged = function(){ return (this._changed.length || this._changedCreators.length || this._changedItemData.length); } Scholar.Item.prototype.getID = function(){ return this._data['itemID'] ? this._data['itemID'] : false; } Scholar.Item.prototype.getType = function(){ return this._data['itemTypeID'] ? this._data['itemTypeID'] : false; } Scholar.Item.prototype.getParent = function(){ return this._data['parentFolderID'] ? this._data['parentFolderID'] : false; } /* * Set or change the item's type */ Scholar.Item.prototype.setType = function(itemTypeID){ if (itemTypeID==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 itemTypeFields ' + 'WHERE itemTypeID=' + this.getType() + ' AND fieldID NOT IN ' + '(SELECT fieldID FROM itemTypeFields WHERE itemTypeID=' + itemTypeID + ')'; var obsoleteFields = Scholar.DB.columnQuery(sql); if (obsoleteFields){ for (var i=0; i' + oldPos + ";\n"; sql += 'UPDATE treeStructure SET orderIndex=orderIndex+1 ' + 'WHERE parentFolderID=' + newFolder + ' AND orderIndex>=' + newPos + ";\n"; Scholar.DB.query(sql); } // If a new item, insert if (isNew){ var sql = 'INSERT INTO treeStructure ' + '(id, isFolder, orderIndex, parentFolderID) VALUES (' + this.getID() + ', 0, ' + newPos + ', ' + newFolder + ')'; } // Otherwise update else { var sql = 'UPDATE treeStructure SET parentFolderID=' + newFolder + ', orderIndex=' + newPos + ' WHERE id=' + this.getID() + " AND isFolder=0;\n"; } Scholar.DB.query(sql); if (!isNew){ Scholar.DB.commitTransaction(); } } this._data['parentFolderID'] = newFolder; this._data['orderIndex'] = newPos; if (this.getID() && !isNew){ Scholar.Items.reloadAll(); Scholar.Folders.reloadAll(); // needed to recheck isEmpty } return true; } /* * Save changes back to database */ Scholar.Item.prototype.save = function(){ if (!this.hasChanged()){ Scholar.debug('Item ' + this.getID() + ' has not changed', 4); return !!this.getID(); } // // Existing item, update // if (this.getID()){ Scholar.debug('Updating database with new item data', 4); var itemID = this.getID(); try { Scholar.DB.beginTransaction(); // // Primary fields // var sql = "UPDATE items SET "; var sql2; if (this._changed.has('itemTypeID')){ sql += "itemTypeID='" + this.getField('itemTypeID') + "', "; } 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 itemID=" + 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 if (!creator['firstName'] && !creator['lastName']){ sql2 = 'DELETE FROM itemCreators ' + ' WHERE itemID=' + 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'] ); // If not, add it if (!creatorID){ creatorID = Scholar.Creators.add( creator['firstName'], creator['lastName'] ); } // If there's a creator at this position, update // with new creator data sql2 = 'SELECT COUNT(*) FROM itemCreators' + ' WHERE itemID=' + this.getID() + ' AND orderIndex=' + orderIndex; if (Scholar.DB.valueQuery(sql2)){ sql += 'UPDATE itemCreators SET creatorID=' + creatorID + ', creatorTypeID=' + creator['creatorTypeID'] + ', ' + 'WHERE itemID=' + this.getID() + ' AND orderIndex=' + orderIndex + ";\n"; } // Otherwise insert else { sql += 'INSERT INTO itemCreators VALUES (' + itemID + ', ' + creatorID + ', ' + creator['creatorTypeID'] + ', ' + orderIndex + ");\n"; } } // Append the SQL to delete obsolete creators // // TODO: fix this so it actually purges the internal memory sql += Scholar.Creators.purge(true) + "\n"; } // // ItemData // if (this._changedItemData.length){ var del = new Array(); for (fieldID in this._changedItemData.items){ if (this.getField(fieldID)){ // Oh, for an INSERT...ON DUPLICATE KEY UPDATE sql2 = 'SELECT COUNT(*) FROM itemData ' + 'WHERE itemID=' + this.getID() + ' AND fieldID=' + fieldID; if (Scholar.DB.valueQuery(sql2)){ sql += "UPDATE itemData SET value="; // Take advantage of SQLite's manifest typing if (Scholar.ItemFields.isInteger(fieldID)){ sql += this.getField(fieldID); } else { sql += "'" + this.getField(fieldID) + "'"; } sql += " WHERE itemID=" + this.getID() + ' AND fieldID=' + fieldID + ";\n"; } else { sql += 'INSERT INTO itemData VALUES (' + this.getID() + ',' + fieldID + ','; if (Scholar.ItemFields.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 itemData ' + 'WHERE itemID=' + this.getID() + ' ' + 'AND fieldID IN (' + del.join() + ");\n"; } } Scholar.DB.query(sql); Scholar.DB.commitTransaction(); } catch (e){ Scholar.DB.rollbackTransaction(); throw(e); } } // // New item, insert and return id // else { Scholar.debug('Saving data for new item to database'); var isNew = true; var sqlColumns = new Array(); var sqlValues = new Array(); // // Primary fields // sqlColumns.push('itemTypeID'); sqlValues.push({'int':this.getField('itemTypeID')}); 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')}); } try { Scholar.DB.beginTransaction(); // // Creators // if (this._changedCreators.length){ for (orderIndex in this._changedCreators.items){ var creator = this.getCreator(orderIndex); // If empty, skip if (!creator['firstName'] && !creator['lastName']){ continue; } // See if this is an existing creator var creatorID = Scholar.Creators.getID( creator['firstName'], creator['lastName'] ); // If not, add it if (!creatorID){ creatorID = Scholar.Creators.add( creator['firstName'], creator['lastName'] ); } sql += 'INSERT INTO itemCreators VALUES (' + itemID + ',' + creatorID + ',' + creator['creatorTypeID'] + ', ' + orderIndex + ");\n"; } } // // itemData fields // var sql = "INSERT INTO items (" + sqlColumns.join() + ')' + ' VALUES ('; // Insert placeholders for bind parameters for (var i=0; i' + orderIndex + ";\n\n"; sql += 'DELETE FROM itemCreators WHERE itemID=' + this.getID() + ";\n"; sql += 'DELETE FROM itemKeywords WHERE itemID=' + this.getID() + ";\n"; sql += 'DELETE FROM itemData WHERE itemID=' + this.getID() + ";\n"; sql += 'DELETE FROM items WHERE itemID=' + this.getID() + ";\n"; Scholar.DB.query(sql); Scholar.Creators.purge(); try { Scholar.DB.commitTransaction(); } catch (e){ Scholar.DB.rollbackTransaction(); throw (e); } Scholar.Items.unload(this.getID()); // TODO: trigger reloading of treeview } Scholar.Item.prototype.toString = function(){ return this.getTitle(); } Scholar.Item.prototype.isFolder = function(){ return false; } ////////////////////////////////////////////////////////////////////////////// // // Private Scholar.Item methods // ////////////////////////////////////////////////////////////////////////////// /* * Load in the creators from the database */ Scholar.Item.prototype._loadCreators = function(){ if (!this.getID()){ throw ('ItemID not set for item before attempting to load creators'); } var sql = 'SELECT C.creatorID, C.*, creatorTypeID, orderIndex ' + 'FROM itemCreators IC ' + 'LEFT JOIN creators C USING (creatorID) ' + 'WHERE itemID=' + this.getID() + ' ORDER BY orderIndex'; var creators = Scholar.DB.query(sql); this._creatorsLoaded = true; if (!creators){ return true; } this._creators = new Scholar.Hash(); for (var i=0; i0"; // skip 'root' folder var result = Scholar.DB.query(sql); if (!result){ throw ('No folders exist'); } for (var i=0; i