Begin to work undo functionality into data layer -- currently just for Item.save()

History.undo()/redo() now reload the item, though changes won't show in open metadata pane due to #71

refs #67
This commit is contained in:
Dan Stillman 2006-06-25 04:11:19 +00:00
parent f897564f0e
commit b8ad832e74
2 changed files with 154 additions and 12 deletions

View file

@ -366,9 +366,14 @@ Scholar.Item.prototype.save = function(){
try {
Scholar.DB.beginTransaction();
// Begin history transaction
Scholar.History.begin('modify-item', this.getID());
//
// Primary fields
//
Scholar.History.modify('items', 'itemID', this.getID());
var sql = "UPDATE items SET ";
var sql2;
var sqlValues = [];
@ -408,6 +413,9 @@ Scholar.Item.prototype.save = function(){
var creator = this.getCreator(orderIndex);
// Delete at position
Scholar.History.remove('itemCreators', 'itemID-orderIndex',
[this.getID(), orderIndex]);
sql2 = 'DELETE FROM itemCreators'
+ ' WHERE itemID=' + this.getID()
+ ' AND orderIndex=' + orderIndex;
@ -430,15 +438,21 @@ Scholar.Item.prototype.save = function(){
creator['firstName'],
creator['lastName']
);
Scholar.History.add('creators', 'creatorID', creatorID);
}
// If this creator and creatorType exists elsewhere, move it
sql2 = 'SELECT COUNT(*) FROM itemCreators'
+ ' WHERE itemID=' + this.getID()
+ ' AND creatorID=' + creatorID
+ ' AND creatorTypeID=' + creator['creatorTypeID'];
// If this creator and creatorType exists elsewhere, move it
if (Scholar.DB.valueQuery(sql2)){
Scholar.History.modify('itemCreators',
'itemID-creatorID-creatorTypeID',
[this.getID(), creatorID, creator['creatorTypeID']]);
sql = 'UPDATE itemCreators SET orderIndex=? '
+ "WHERE itemID=? AND creatorID=? AND "
+ "creatorTypeID=?";
@ -452,6 +466,7 @@ Scholar.Item.prototype.save = function(){
Scholar.DB.query(sql, sqlValues);
}
// Otherwise insert
else {
sql = "INSERT INTO itemCreators VALUES (?,?,?,?)";
@ -464,11 +479,21 @@ Scholar.Item.prototype.save = function(){
];
Scholar.DB.query(sql, sqlValues);
Scholar.History.add('itemCreators',
'itemID-creatorID-creatorTypeID',
[this.getID(), creatorID, creator['creatorTypeID']]);
}
}
// Delete obsolete creators
Scholar.Creators.purge();
var deleted;
if (deleted = Scholar.Creators.purge()){
for (var i in deleted){
// Add purged creators to history
Scholar.History.remove('creators', 'creatorID', i);
}
}
}
@ -484,9 +509,13 @@ Scholar.Item.prototype.save = function(){
+ 'WHERE itemID=' + this.getID()
+ ' AND fieldID=' + fieldID;
// Update
if (Scholar.DB.valueQuery(sql2)){
sqlValues = [];
Scholar.History.modify('itemData', 'itemID-fieldID',
[this.getID(), fieldID]);
sql = "UPDATE itemData SET value=?";
// Take advantage of SQLite's manifest typing
if (Scholar.ItemFields.isInteger(fieldID)){
@ -504,7 +533,12 @@ Scholar.Item.prototype.save = function(){
Scholar.DB.query(sql, sqlValues);
}
// Insert
else {
Scholar.History.add('itemData', 'itemID-fieldID',
[this.getID(), fieldID]);
sql = "INSERT INTO itemData VALUES (?,?,?)";
sqlValues = [
@ -522,6 +556,7 @@ Scholar.Item.prototype.save = function(){
Scholar.DB.query(sql, sqlValues);
}
}
// If field changed and is empty, mark row for deletion
else {
del.push(fieldID);
@ -530,6 +565,12 @@ Scholar.Item.prototype.save = function(){
// Delete blank fields
if (del.length){
// Add to history
for (var i in del){
Scholar.History.remove('itemData', 'itemID-fieldID',
[this.getID(), del[i]]);
}
sql = 'DELETE from itemData '
+ 'WHERE itemID=' + this.getID() + ' '
+ 'AND fieldID IN (' + del.join() + ")";
@ -537,9 +578,11 @@ Scholar.Item.prototype.save = function(){
}
}
Scholar.History.commit();
Scholar.DB.commitTransaction();
}
catch (e){
Scholar.History.cancel();
Scholar.DB.rollbackTransaction();
throw(e);
}
@ -569,8 +612,12 @@ Scholar.Item.prototype.save = function(){
try {
Scholar.DB.beginTransaction();
// Begin history transaction
// No associated id yet, so we use false
Scholar.History.begin('add-item', false);
//
// itemData fields
// Primary fields
//
var sql = "INSERT INTO items (" + sqlColumns.join() + ')'
+ ' VALUES (';
@ -584,7 +631,12 @@ Scholar.Item.prototype.save = function(){
var itemID = Scholar.DB.query(sql,sqlValues);
this._data['itemID'] = itemID;
// Set itemData
Scholar.History.setAssociatedID(itemID);
Scholar.History.add('items', 'itemID', itemID);
//
// ItemData
//
if (this._changedItemData.length){
for (fieldID in this._changedItemData.items){
if (!this.getField(fieldID)){
@ -609,6 +661,9 @@ Scholar.Item.prototype.save = function(){
}
Scholar.DB.query(sql, sqlValues);
Scholar.History.add('itemData', 'itemID-fieldID',
this.getField(fieldID));
}
}
@ -636,6 +691,7 @@ Scholar.Item.prototype.save = function(){
creator['firstName'],
creator['lastName']
);
Scholar.History.add('creators', 'creatorID', creatorID);
}
sql = 'INSERT INTO itemCreators VALUES ('
@ -643,9 +699,14 @@ Scholar.Item.prototype.save = function(){
+ creator['creatorTypeID'] + ', ' + orderIndex
+ ")";
Scholar.DB.query(sql);
Scholar.History.add('itemCreators',
'itemID-creatorID-creatorTypeID',
[this.getID(), creatorID, creator['creatorTypeID']]);
}
}
Scholar.History.commit();
Scholar.DB.commitTransaction();
// Reload collection to update isEmpty,
@ -653,6 +714,7 @@ Scholar.Item.prototype.save = function(){
Scholar.Collections.reloadAll();
}
catch (e){
Scholar.History.cancel();
Scholar.DB.rollbackTransaction();
throw(e);
}

View file

@ -1,5 +1,6 @@
Scholar.History = new function(){
this.begin = begin;
this.setAssociatedID = setAssociatedID;
this.add = add;
this.modify = modify;
this.remove = remove;
@ -18,12 +19,15 @@ Scholar.History = new function(){
var _activeEvent;
var _maxID = 0;
// event: ('item-add', 'item-delete', 'item-modify', 'collection-add', 'collection-modify', 'collection-delete')
// context: (itemCreators.itemID-creatorID.1-1)
// action: ('add', 'delete', 'modify')
/**
* Begin a transaction set
*
* event: 'item-add', 'item-delete', 'item-modify', 'collection-add',
* 'collection-modify', 'collection-delete'...
*
* id: An id or array of ids that will be passed to
* Scholar.Notifier.trigger() on an undo or redo
**/
function begin(event, id){
if (_activeID){
@ -40,8 +44,16 @@ Scholar.History = new function(){
Scholar.debug('Beginning history transaction set ' + event);
var sql = "INSERT INTO transactionSets (event, id) VALUES "
+ "('" + event + "', ";
// If integer, insert natively; if array, insert as string
sql += (typeof id=='object') ? "'" + id.join('-') + "'" : id;
if (!id){
sql += '0';
}
// If array, insert hyphen-delimited string
else if (typeof id=='object'){
sql += "'" + id.join('-') + "'"
}
else {
sql += id;
}
sql += ")";
Scholar.DB.beginTransaction();
@ -50,8 +62,44 @@ Scholar.History = new function(){
}
/**
* Associate an id or array of ids with the transaction set --
* for use if the ids weren't available at when begin() was called
*
* id: An id or array of ids that will be passed to
* Scholar.Notifier.trigger() on an undo or redo
**/
function setAssociatedID(id){
if (!_activeID){
throw('Cannot call setAssociatedID() with no history transaction set in progress');
}
var sql = "UPDATE transactionSets SET id=";
if (!id){
sql += '0';
}
// If array, insert hyphen-delimited string
else if (typeof id=='object'){
sql += "'" + id.join('-') + "'"
}
else {
sql += id;
}
sql += " WHERE transactionSetID=" + _activeID;
Scholar.DB.query(sql);
}
/**
* Add an add transaction to the current set
*
* Can be called before or after an INSERT statement
*
* key is a hyphen-delimited list of columns identifying the row
* e.g. 'itemID-creatorID'
*
* keyValues is a hyphen-delimited list of values matching the key parts
* e.g. '1-1'
**/
function add(table, key, keyValues){
return _addTransaction('add', table, key, keyValues);
@ -61,6 +109,14 @@ Scholar.History = new function(){
/**
* Add a modify transaction to the current set
*
* Must be called before an UPDATE statement
*
* key is a hyphen-delimited list of columns identifying the row
* e.g. 'itemID-creatorID'
*
* keyValues is a hyphen-delimited list of values matching the key parts
* e.g. '1-1'
*
* _field_ is optional -- otherwise all fields are saved
**/
function modify(table, key, keyValues, field){
@ -70,6 +126,14 @@ Scholar.History = new function(){
/**
* Add a remove transaction to the current set
*
* Must be called before a DELETE statement
*
* key is a hyphen-delimited list of columns identifying the row
* e.g. 'itemID-creatorID'
*
* keyValues is a hyphen-delimited list of values matching the key parts
* e.g. '1-1'
**/
function remove(table, key, keyValues){
return _addTransaction('remove', table, key, keyValues);
@ -139,7 +203,7 @@ Scholar.History = new function(){
var undone = _do('undo');
_currentID--;
Scholar.DB.commitTransaction();
_notifyEvent(id);
_reloadAndNotify(id);
return true;
}
@ -154,7 +218,7 @@ Scholar.History = new function(){
var redone = _do('redo');
_currentID++;
Scholar.DB.commitTransaction();
_notifyEvent(id);
_reloadAndNotify(id, true);
return redone;
}
@ -421,9 +485,25 @@ Scholar.History = new function(){
}
function _notifyEvent(transactionSetID){
function _reloadAndNotify(transactionSetID, redo){
var data = _getSetData(transactionSetID);
var eventParts = data['event'].split('-'); // e.g. modify-item
if (redo){
switch (eventParts[0]){
case 'add':
eventParts[0] = 'remove';
break;
case 'remove':
eventParts[0] = 'add';
break;
}
}
switch (eventParts[1]){
case 'item':
Scholar.Items.reload(data['id']);
break;
}
Scholar.Notifier.trigger(eventParts[0], eventParts[1], data['id']);
}
}