app/assets/javascripts/admin/AuditLog.js
//= require ext_extensions/AjaxPanel
//= require_self
Ext.ns("Talho");
Talho.AuditLog = Ext.extend(Ext.util.Observable, {
constructor: function(config){
Talho.AuditLog.superclass.constructor.call(this, config);
this.RESULTS_PAGE_SIZE = 30;
this.resultsStore = new Ext.data.JsonStore({ // Stores the main list of versions
url: '/audits.json', method: 'GET', restful: true, root: 'versions', totalProperty: 'total_count', autoLoad: true, remoteSort: true,
fields: [ 'id', 'item_type', 'item_id', 'item_desc', 'event', 'whodunnit', 'created_at' ],
listeners: { scope: this, 'beforeload' : this.applyModelSelections }
});
this.selectionCache = new Ext.data.SimpleStore({ fields: ['id','data'] }); // Stores previously requested version data, preventing redundant ajax requests.
this.selectionStore = new Ext.data.SimpleStore({ fields: ['attribute_name', 'selected_version', 'previous_version', 'next_version', 'current_version'] }); // Holds the currently selected version data
this.modelStore = new Ext.data.JsonStore({ url: '/audits/models.json', method: 'GET', restful: true, root: 'models', autoLoad: true, fields:['model_name', 'human_name'] });
this.modelList = new Ext.list.ListView({
region: 'center', store: this.modelStore, multiSelect: true, simpleSelect:true, columnSort: false, hideHeaders: true, style: { 'background-color': 'white'},
loadingText: 'Fetching list...', emptyText: 'Error: Could not retrieve list', deferEmptyText: true,
columns: [{ dataIndex: 'human_name', cls: 'model-selector-list-item'}],
listeners: { scope: this, 'selectionchange' : function(){ this.resultsStore.load(); } }
});
this.checkboxShowCreate = new Ext.form.Checkbox({ boxLabel: 'Create', checked: true, listeners:{ scope: this, 'check': function(){ this.checkEventBoxes(); this.resultsStore.load(); } } });
this.checkboxShowUpdate = new Ext.form.Checkbox({ boxLabel: 'Update', checked: true, listeners:{ scope: this, 'check': function(){ this.checkEventBoxes(); this.resultsStore.load(); } } });
this.checkboxShowDestroy = new Ext.form.Checkbox({ boxLabel: 'Destroy', checked: true, listeners:{ scope: this, 'check': function(){ this.checkEventBoxes(); this.resultsStore.load(); } } });
this.actionSelector = new Ext.Panel({
region: 'south', height: 30, layout: 'hbox', border: false, style: {'borderTop': '1px lightgray solid'}, padding: 5,
items: [ this.checkboxShowCreate, {xtype: 'spacer', width: '6'}, this.checkboxShowUpdate, {xtype: 'spacer', width: '6'}, this.checkboxShowDestroy ] // Dirty workaround to get the margins right.
});
this.selectorPanel = new Ext.Panel({
cls: 'panel-version-selector', autoScroll: true, region: 'west', width: 200, margins: '5 5 0 5', layout: 'border',
tbar: new Ext.Toolbar({items: [
{xtype: 'tbtext', name: 'hdr', html: 'Log Filters'}, '->',
{xtype: 'button', text: 'Reset', scope: this, handler: function(){ (this.modelList.getSelectionCount() == 0) ? this.modelList.fireEvent('selectionchange') : this.modelList.clearSelections(); }}
]}),
items: [ this.modelList, this.actionSelector ]
});
this.resultsPanel = new Ext.grid.GridPanel({
cls: 'grid-version-results', loadMask: true, region: 'center', margins: '5 5 0 0', store: this.resultsStore,
colModel: new Ext.grid.ColumnModel({
columns: [
{ id: 'date', dataIndex: 'created_at', header: 'date', sortable: true, width: 130},
{ id: 'model', dataIndex: 'item_type', header: 'Type', sortable: true, width: 100},
{ id: 'id', dataIndex: 'item_id', header: 'ID', sortable: true, width: 30},
{ id: 'descriptor', dataIndex: 'item_desc', header: 'Descriptor', sortable: true, width: 330},
{ id: 'action', dataIndex: 'event', header: 'Action', sortable: true, width: 50},
{ id: 'whodunnit', dataIndex: 'whodunnit', header: 'Whodunnit', sortable: true, width: 200},
{ id: 'id', dataIndex: 'id', header: 'Event ID', sortable: true, width: 60}
]
}),
tbar: new Ext.PagingToolbar({ pageSize: this.RESULTS_PAGE_SIZE, store: this.resultsStore, displayInfo: true, displayMsg: 'Displaying Events {0} - {1} of {2}', emptyMsg: "No events" }),
listeners: { scope: this,
'cellclick': function(grid, row){
var record = grid.getStore().getAt(row);
this.getVersion({'id': record.id});
}
}
});
this.selectedVersionDisplay = new Ext.grid.GridPanel({
cls: 'grid-version-display', viewConfig: { forceFit: true }, store: this.selectionStore,
colModel: new Ext.grid.ColumnModel({
columns: [
{ id: 'attribute-name', dataIndex: 'attribute_name', header: 'Attribute', sortable: true, defaultWidth: 180 },
{ id: 'previous-version', dataIndex: 'previous_version', header: 'Previous Version', sortable: false, width: 180 },
{ id: 'selected-version', dataIndex: 'selected_version', header: 'This Version', sortable: false, width: 180},
{ id: 'next-version', dataIndex: 'next_version', header: 'Next Version', sortable: false, width: 180 },
{ id: 'current-version', dataIndex: 'current_version', header: 'Current Version', sortable: false, width: 180 }
]
})
});
this.olderButton = new Ext.Button({ text: '< Older', scope: this, disabled: true });
this.newerButton = new Ext.Button({ text: 'Newer >', scope: this, disabled: true });
this.versionButton = new Ext.Button({ text: 'Show Versions', scope: this, disabled: true, handler: function(){ this.showRecordVersions(this.selectedVersionId); } });
this.versionRefreshButton = new Ext.Button({ text: 'Force Refresh', disabled: true });
this.selectedVersionPanel = new Ext.Panel({
cls: 'panel-version-display', region: 'south', height: 250, layout: 'fit', margins: '0 5 5 5', autoScroll: true, border: false, split: true,
tbar: new Ext.Toolbar({ items: [
{xtype: 'tbtext', itemId: 'version_label', html: ''}, {xtype: 'tbtext', itemId: 'version_count', html: ''}, '->',
this.versionButton, this.versionRefreshButton, this.olderButton, this.newerButton
]}),
items: [ this.selectedVersionDisplay ],
listeners: { 'afterrender': { delay:1, scope: this, fn: function(){ this.resultsLoadMask = new Ext.LoadMask( this.selectedVersionPanel.getEl(), {msg:"Fetching version data..."} ); } } }
});
this.primary_panel = new Ext.Panel({
layout: 'border', title: config.title, itemId: config.id, closable: true,
items: [this.resultsPanel, this.selectorPanel, this.selectedVersionPanel]
});
this.getPanel = function(){ return this.primary_panel; };
},
//===== end constructor =====//
applyModelSelections : function(){
var selected_models = this.modelList.getSelectedRecords();
if ( selected_models.length > 0 ){
var models = [];
for (var i = 0; i < selected_models.length; i++){
models.push(selected_models[i].data.model_name);
}
this.resultsStore.setBaseParam('models[]', models );
} else {
this.resultsStore.setBaseParam('models[]', null );
}
var event = [];
if (this.checkboxShowCreate.getValue()){ event.push('create'); }
if (this.checkboxShowUpdate.getValue()){ event.push('update'); }
if (this.checkboxShowDestroy.getValue()){ event.push('destroy'); }
(event.length < 3) ? this.resultsStore.setBaseParam('event[]', event ) : this.resultsStore.setBaseParam('event[]', null );
return true;
},
addToCache: function(v){
var rec = new this.selectionCache.recordType(v, v.requested_version_id);
this.selectionCache.add(rec);
},
checkCacheAndPrefetch: function(vIds){ // accepts an array of versionIds and caches them if they aren't already.
for (var i = 0; i < vIds.length; i++){
if (vIds[i] && !this.selectionCache.getById(vIds[i])){
this.getVersion({'id': vIds[i], 'cache_only': true});
}
}
},
handleFetchedVersionData: function(response, cache_only){
var versionData = Ext.util.JSON.decode(response.responseText);
if (versionData['success']) {
this.selectedVersionId = versionData['versions']['requested_version'].requested_version_id;
for (var v in versionData['versions']){ this.addToCache(versionData['versions'][v]); }
if (!cache_only){
this.updateVersionDisplay(versionData['versions']['requested_version']);
this.checkCacheAndPrefetch([versionData['versions']['requested_version'].newer_id, versionData['versions']['requested_version'].older_id]);
}
}
},
handleCachedVersionData: function(versionData, cache_only){
this.selectedVersionId = versionData.requested_version_id;
if (!cache_only){
this.updateVersionDisplay(versionData);
this.checkCacheAndPrefetch([versionData.newer_id, versionData.older_id]);
}
},
getVersion: function(options){ // options: id (required), force_refresh to ignore cached version, cache_only to fetch and cache but not update display
if (!options.cache_only){ this.resultsLoadMask.show(); }
if (!options.force_refresh){ var cachedVersion = this.selectionCache.getById(options.id); }
if (cachedVersion === undefined || cachedVersion === 'loading'){
this.lastVersionRequest = Ext.Ajax.request({
url: '/audits/'+ options.id +'.json', method: 'GET', scope: this,
success: function(response){ this.handleFetchedVersionData(response, options.cache_only); },
failure: function(){ this.selectedVersionPanel.getEl().mask("Server error. Please try again."); }
});
} else {
this.handleCachedVersionData(cachedVersion['data'], options.cache_only);
}
},
checkEventBoxes: function(){ // prevents user from selecting zero checkboxes
// TODO: would a checkboxGroup clean this up?
var checked_boxes = [];
if (this.checkboxShowCreate.getValue()){ checked_boxes.push('create'); }
if (this.checkboxShowUpdate.getValue()){ checked_boxes.push('update'); }
if (this.checkboxShowDestroy.getValue()){ checked_boxes.push('destroy'); }
if (checked_boxes.length === 1){
switch(checked_boxes[0]){
case 'create': this.checkboxShowCreate.disable(); break;
case 'update': this.checkboxShowUpdate.disable(); break;
case 'destroy': this.checkboxShowDestroy.disable(); break;
}
} else {
this.checkboxShowCreate.enable();
this.checkboxShowUpdate.enable();
this.checkboxShowDestroy.enable();
}
},
updateVersionDisplay: function(versionData){
this.selectionStore.loadData(versionData.diff_list);
var version_rowid = this.resultsStore.indexOfId(this.selectedVersionId);
var sel_model = this.resultsPanel.getSelectionModel();
sel_model.clearSelections();
if ( version_rowid !== -1 ){ sel_model.selectRow(version_rowid, false); } // if the correct row is visible
this.versionButton.setText("Event " + versionData.version_index + " of " + versionData.version_count );
this.selectedVersionPanel.getTopToolbar().getComponent('version_label').update( 'Event ID '+ this.selectedVersionId + ' : ' + versionData.event + ' ' + versionData.model + ' "' + versionData.descriptor +'"');
var col_model = this.selectedVersionDisplay.getColumnModel();
if (versionData['older_id']) {
this.olderButton.purgeListeners();
this.olderButton.on('click', function(){ this.getVersion({'id': versionData['older_id']})}, this);
this.olderButton.enable();
col_model.setHidden( col_model.findColumnIndex('previous_version') , false );
} else {
this.olderButton.disable();
col_model.setHidden( col_model.findColumnIndex('previous_version') , true );
}
if (versionData['newer_id']) {
this.newerButton.purgeListeners();
this.newerButton.on('click', function(){ this.getVersion({'id': versionData['newer_id']}) }, this);
this.newerButton.enable();
col_model.setHidden( col_model.findColumnIndex('next_version') , false );
} else {
this.newerButton.disable();
col_model.setHidden( col_model.findColumnIndex('next_version') , true );
}
(versionData['deleted']) ? col_model.setHidden( col_model.findColumnIndex('current_version') , true ) : col_model.setHidden( col_model.findColumnIndex('current_version') , false );
this.versionRefreshButton.enable();
this.versionRefreshButton.purgeListeners();
this.versionRefreshButton.on('click', function(){ this.getVersion({'id': this.selectedVersionId, 'force_refresh': true})}, this);
col_model.setColumnWidth(col_model.findColumnIndex('attribute_name'), 180); // ...and I mean it.
this.versionButton.enable();
this.resultsLoadMask.hide();
},
showRecordVersions: function(version_id){
this.modelList.clearSelections(true);
this.resultsStore.load({'params': {'show_versions_for' : version_id } });
}
});
Talho.AuditLog.initialize = function(config){
var audit_log_panel = new Talho.AuditLog(config);
return audit_log_panel.getPanel();
};
Talho.ScriptManager.reg('Talho.AuditLog', Talho.AuditLog, Talho.AuditLog.initialize);