diff --git a/webgui/ajax.php b/webgui/ajax.php
index fb31692cb568ff1ccb687d138698fdbdc68bf040..7fb563be3d97ee5a2e5b8de9ce1bdfb67e46c495 100644
--- a/webgui/ajax.php
+++ b/webgui/ajax.php
@@ -15,9 +15,10 @@
 	include_once("include/ajax/functions/AdminRealms.php");
 	include_once("include/ajax/functions/AdminRealmAttributes.php");
 
+	include_once("include/ajax/functions/WiSPUsers.php");
 	include_once("include/ajax/functions/WiSPLocations.php");
 	include_once("include/ajax/functions/WiSPLocationMembers.php");
-	include_once("include/ajax/functions/WiSPUsers.php");
+	include_once("include/ajax/functions/WiSPUserLogs.php");
 
 	include_once("include/radiuscodes.php");
 
@@ -503,6 +504,35 @@
 			echo json_encode($res->export());
 			break;
 
+		# WiSPUserLogs.js functions
+		case "getWiSPUserLogs":
+
+			$res = getWiSPUserLogs($soapParams);
+			$rawData = $res[0]; $numResults = $res[1];
+
+			$res = new json_response;
+			$res->setID('ID');
+			$res->addField('ID','int');
+			$res->addField('EventTimestamp','string');
+			$res->addField('AcctStatusType','int');
+			$res->addField('ServiceType','int');
+			$res->addField('FramedProtocol','int');
+			$res->addField('NASPortType','int');
+			$res->addField('NASPortID','string');
+			$res->addField('CallingStationID','string');
+			$res->addField('CalledStationID','string');
+			$res->addField('AcctSessionID','string');
+			$res->addField('FramedIPAddress','string');
+			$res->addField('AcctInputMbyte','int');
+			$res->addField('AcctOutputMbyte','int');
+			$res->addField('ConnectTermReason','string');
+			$res->parseArray($rawData);
+			$res->setDatasetSize($numResults);
+
+			echo json_encode($res->export());
+
+			break;
+	
 		# WiSPLocationMembers.js functions
 		case "getWiSPLocationMembers":
 
diff --git a/webgui/include/ajax/functions/WiSPUserLogs.php b/webgui/include/ajax/functions/WiSPUserLogs.php
new file mode 100644
index 0000000000000000000000000000000000000000..8cc5ce293057e7e4e1cd13ec772e823d0266a682
--- /dev/null
+++ b/webgui/include/ajax/functions/WiSPUserLogs.php
@@ -0,0 +1,112 @@
+<?php
+
+include_once("include/db.php");
+
+
+# Return list of users
+function getWiSPUserLogs($params) {
+	global $db;
+
+	# Filters and sorts are the same here
+	$filtersorts = array(
+		'ID' => 'accounting.ID',
+		'EventTimestamp' => 'accounting.EventTimestamp',
+		'AcctStatusType' => 'accounting.AcctStatusType',
+		'ServiceType' => 'accounting.ServiceType',
+		'FramedProtocol' => 'accounting.FramedProtocol',
+		'NASPortType' => 'accounting.NASPortType',
+		'NASPortID' => 'accounting.NASPortID',
+		'CallingStationID' => 'accounting.CallingStationID',
+		'CalledStationID' => 'accounting.CalledStationID',
+		'AcctSessionID' => 'accounting.AcctSessionID',
+		'FramedIPAddress' => 'accounting.FramedIPAddress',
+	);
+
+	$res = DBSelectSearch("
+				SELECT 
+					accounting.ID,
+					accounting.EventTimestamp, 
+					accounting.AcctStatusType, 
+					accounting.ServiceType,
+					accounting.FramedProtocol,
+					accounting.NASPortType,
+					accounting.NASPortID,
+					accounting.CallingStationID,
+					accounting.CalledStationID,
+					accounting.AcctSessionID,
+					accounting.FramedIPAddress,
+					accounting.AcctInputOctets,
+					accounting.AcctInputGigawords,
+					accounting.AcctOutputOctets,
+					accounting.AcctOutputGigawords,
+					accounting.AcctTerminateCause
+				FROM 
+					accounting, users
+				WHERE 
+					users.Username = accounting.Username
+				AND
+					users.ID = ".DBQuote($params[0])."
+					",$params[1],$filtersorts,$filtersorts);
+
+	$sth = $res[0]; $numResults = $res[1];
+	# If STH is blank, return the error back to whoever requested the data
+	if (!isset($sth)) {
+		return $res;
+	}
+
+	$resultArray = array();
+
+	# loop through rows
+	while ($row = $sth->fetchObject()) {
+
+		# Input
+		$acctInputMbyte = 0;
+
+		if (isset($row->acctinputoctets) && $row->acctinputoctets > 0) {
+			$acctInputMbyte += ($row->acctinputoctets / 1024) / 1024;
+		}
+		if (isset($row->acctinputgigawords) && $row->acctinputgigawords > 0) {
+			$acctInputMbyte += ($row->acctinputgigawords * 4096);
+		}
+
+
+		# Output
+		$acctOutputMbyte = 0;
+
+		if (isset($row->acctoutputoctets) && $row->acctoutputoctets > 0) {
+			$acctOutputMbyte += ($row->acctoutputoctets / 1024) / 1024;
+		}
+		if (isset($row->acctoutputgigawords) && $row->acctoutputgigawords > 0) {
+			$acctOutputMbyte += ($row->acctoutputgigawords * 4096);
+		}
+
+		$item = array();
+
+		$item['ID'] = $row->id;
+
+		# Convert to ISO format	
+		$date = new DateTime($row->eventtimestamp);
+		$value = $date->format("Y-m-d H:i:s");
+		$item['EventTimestamp'] = $value;
+
+		$item['AcctStatusType'] = $row->acctstatustype;
+		$item['ServiceType'] = $row->servicetype;
+		$item['FramedProtocol'] = $row->framedprotocol;
+		$item['NASPortType'] = $row->nasporttype;
+		$item['NASPortID'] = $row->nasportid;
+		$item['CallingStationID'] = $row->callingstationid;
+		$item['CalledStationID'] = $row->calledstationid;
+		$item['AcctSessionID'] = $row->acctsessionid;
+		$item['FramedIPAddress'] = $row->framedipaddress;
+		$item['AcctInputMbyte'] = $acctInputMbyte;
+		$item['AcctOutputMbyte'] = $acctOutputMbyte;
+		$item['ConnectTermReason'] = strRadiusTermCode($row->servicetype);
+
+		# push this row onto array
+		array_push($resultArray,$item);
+	}
+
+	return array($resultArray,$numResults);
+}
+
+# vim: ts=4
diff --git a/webgui/js/app/windows/WiSPUserLogs.js b/webgui/js/app/windows/WiSPUserLogs.js
index 40a2264a2cd16bdb0458acfaea5a63851a968c86..3b27c85d23d4eb2c21fe4e6eadde1af7a7067cb7 100644
--- a/webgui/js/app/windows/WiSPUserLogs.js
+++ b/webgui/js/app/windows/WiSPUserLogs.js
@@ -1,5 +1,250 @@
 
 
 function showWiSPUserLogsWindow(id) {
-	showAdminUserLogsWindow(id);
+	// Calculate dates we going to need
+	var today = new Date();
+	var firstOfMonth = today.getFirstDateOfMonth();
+	var firstOfNext = today.getLastDateOfMonth().add(Date.DAY, 1);
+
+	var wispUserLogsWindow = new Ext.ux.GenericGridWindow(
+		// Window config
+		{
+			title: 'Logs',
+			layout:'border',
+			height: 480,
+			width: 700,
+			minHeight: 480,
+			minWidth: 700,
+			closable: true,
+			plain: true,
+			uxItems: [
+				{
+					xtype: 'form',
+					id: 'search-form',
+					title: 'Search',
+					region: 'west',
+					border: true,
+					frame: true,
+					defaultType: 'datefield',
+					height: 180,
+					width: 320,
+					labelWidth: 100,
+					items: [
+						{
+							id: 'after',
+							name: 'after',
+							width: 180,
+							fieldLabel: 'From',
+							vtype: 'daterange',
+							format: 'Y-m-d',
+							value: firstOfMonth,
+							endDateField: 'before'
+						},
+						{
+							id: 'before',
+							name: 'before',
+							width: 180,
+							fieldLabel: 'To',
+							vtype: 'daterange',
+							format: 'Y-m-d',
+							value: firstOfNext,
+							startDateField: 'after'
+						}
+					],
+					buttons: [
+						{
+							text: 'Search',
+							id: 'formbtn',
+							handler: function() {
+								// Pull in window, grid & form	
+								var mainWindow = this.ownerCt.ownerCt;
+								var grid = mainWindow.getComponent('gridpanel');
+								var form = mainWindow.getComponent('search-form');
+
+								// Grab store
+								var store = grid.getStore();
+
+								// Grab timestamp filter
+								var gridFilters = grid.filters;
+								var timestampFilter = gridFilters.getFilter('EventTimestamp');
+
+								// Grab	form fields
+								var afterField = form.getForm().findField('after');
+								var beforeField = form.getForm().findField('before');
+
+								// Set filter values from form
+								timestampFilter.setValue({
+									after: afterField.getValue(),
+									before: beforeField.getValue()
+								});
+
+								// Trigger store reload
+								store.reload();
+							}
+						}
+					],
+					buttonAlign: 'center'
+				},
+				{
+					xtype: 'form',
+					id: 'summary-form',
+					region: 'center',
+					split: true,
+					border: true,
+					autoScroll: true,
+					defaultType: 'textarea',
+					height: 180,
+					width: 400,
+					labelWidth: 80,
+					items: [
+						{
+							id: 'summaryTotal',
+							name: 'summaryTotal',
+							readOnly: true,
+							height: 139,
+							width: 275,
+							fieldLabel: 'Summary',
+							fieldClass: 'font-family: monospace; font-size: 10px;',
+							value: ''
+						}
+					]					
+				}
+			]
+		},
+		// Grid config
+		{
+			region: 'south',
+			width: 700,
+			border: true,
+			// Column model
+			colModel: new Ext.grid.ColumnModel([
+				{
+					header: "ID",
+					hidden: true,
+					dataIndex: 'ID'
+				},
+				{
+					header: "Timestamp",
+					sortable: true,
+					dataIndex: 'EventTimestamp'
+				},
+				{
+					header: "Status",
+					sortable: true,
+					hidden: true,
+					dataIndex: 'AcctStatusType'
+				},
+				{
+					header: "Service Type",
+					sortable: true,
+					dataIndex: 'ServiceType'
+				},
+				{
+					header: "Framed Protocol",
+					sortable: true,
+					dataIndex: 'FramedProtocol'
+				},
+				{
+					header: "NAS Port Type",
+					hidden: true,
+					dataIndex: 'NASPortType'
+				},
+				{
+					header: "Calling Station",
+					sortable: true,
+					dataIndex: 'CallingStationID'
+				},
+				{
+					header: "Called Station",
+					hidden: true,
+					dataIndex: 'CalledStationID'
+				},
+				{
+					header: "Session ID",
+					hidden: true,
+					dataIndex: 'AcctSessionID'
+				},
+				{
+					header: "IP Address",
+					hidden: true,
+					dataIndex: 'FramedIPAddress'
+				},
+				{
+					header: "Input Mbyte",
+					dataIndex: 'AcctInputMbyte'
+				},
+				{
+					header: "Output Mbyte",
+					dataIndex: 'AcctOutputMbyte'
+				},
+				{
+					header: "Term. Reason",
+					dataIndex: 'ConnectTermReason'
+				}
+			])
+		},
+		// Store config
+		{
+			baseParams: {
+				ID: id,
+				SOAPUsername: globalConfig.soap.username,
+				SOAPPassword: globalConfig.soap.password,
+				SOAPAuthType: globalConfig.soap.authtype,
+				SOAPModule: 'WiSPUserLogs',
+				SOAPFunction: 'getWiSPUserLogs',
+				SOAPParams: 'ID,__search'
+			}
+		},
+		// Filter config
+		{
+			filters: [
+				{type: 'numeric', dataIndex: 'ID'},
+				{
+					type: 'date',  
+					format: 'Y-m-d H:i:s',
+					dataIndex: 'EventTimestamp', 
+					value: {
+						after: firstOfMonth,
+						before: firstOfNext
+					}
+				},
+				{type: 'numeric',  dataIndex: 'AcctStatusType'},
+				{type: 'numeric',  dataIndex: 'ServiceType'},
+				{type: 'numeric',  dataIndex: 'FramedProtocol'},
+				{type: 'numeric',  dataIndex: 'NASPortType'},
+				{type: 'string',  dataIndex: 'NASPortID'},
+				{type: 'string',  dataIndex: 'CallingStationID'},
+				{type: 'string',  dataIndex: 'CalledStationID'},
+				{type: 'string',  dataIndex: 'AcctSessionID'},
+				{type: 'string',  dataIndex: 'FramedIPAddress'},
+				{type: 'numeric',  dataIndex: 'AcctInputMbyte'},
+				{type: 'numeric',  dataIndex: 'AcctOutputMbyte'},
+				{type: 'string',  dataIndex: 'ConnectTermReason'}
+			]
+		}
+	);
+	// Grab store
+	var store = wispUserLogsWindow.getComponent('gridpanel').getStore();
+
+	store.on('load',function() {
+		var inputTotal = store.sum('AcctInputMbyte');
+		var outputTotal = store.sum('AcctOutputMbyte');
+
+		var userCap = 3000;
+		var userTopups = 1000;
+		
+		// Total up into this ... 
+		
+		var userTotalAllowed = userCap + userTopups;
+		var userUsage = inputTotal + outputTotal;
+		var userLeft = userTotalAllowed - userUsage;
+
+		var form = wispUserLogsWindow.getComponent('summary-form');
+		var summaryTotal = form.getForm().findField('summaryTotal');
+
+		summaryTotal.setValue(
+				sprintf('Cap Total: %6d\nTopups   : %6d\n-----------------\n           %6d\n-----------------\nUsage    : %6d\n=================\nAvailable: %6d',userCap,userTopups,userTotalAllowed,userUsage,userLeft)
+		);
+	});
+	wispUserLogsWindow.show();				
 }