using System; using System.Data; using System.Data.OleDb; using System.Xml; using System.Messaging; using System.Runtime.Serialization; namespace CallCenter { public class Admin : CallCenter.Utilities { public XmlDocument XmlListConfig = new XmlDocument(); public Admin() { // Load up the admin systems Xml config file. We'll use // this to determine which lists are open/closed. try { XmlListConfig.Load(this.ListCfgPath + @"\listconfig\calllists.xml"); } catch (Exception e) { this.RW(e.Message + "<br>"); } } public void StartList(string sID) { // Activate a specific list and save the updated node back to calllists.xml // We redirect back to the same page to avoid page a refresh processing // a list start/stop/recycle when it shouldn't. try { XmlNode oNode = this.XmlListConfig.SelectSingleNode("//LIST[@id='" + sID + "']"); oNode.Attributes.GetNamedItem("openforcalling").Value = "1"; } catch (Exception e) { this.RW(e.Message + "<br>"); } this.XmlListConfigSave(); this.SiteRedirect("default.aspx"); } public void StopList(string sID) { string sFile=""; string sList=""; string sQueue=""; // Stop a list. Process any remaining records in the disposition queue. // Even though a list is stopped, as long as it is not "hidden", future loads // of the admin page will finalize any records still left over from agents still // on the call when the list was stopped. // Also clears all unretrieved messages from the calling queue. This does // not make records flagged as in the queue immediately callable again. // The list must be recycled for this. try { XmlNode oNode = this.XmlListConfig.SelectSingleNode("//LIST[@id='" + sID + "']"); sFile = oNode.Attributes.GetNamedItem("filename").Value; oNode.Attributes.GetNamedItem("openforcalling").Value = "0"; sList = this.CallList + sID; sQueue = this.QueuePath + sList; this.ProcessFinalizedRecords(sID,sFile); ClearQueue(sQueue); } catch (Exception e) { this.RW(e.Message + "<br>"); } this.XmlListConfigSave(); this.SiteRedirect("default.aspx"); } public void RecycleList(string sID) { string sFile=""; string sList=""; string sSQL =""; string sQueue=""; // Reset all records that had been previously dialed that were dispositioned // to be called again in the future. Processes any finalized records before // initiating the reset. try { XmlNode oNode = this.XmlListConfig.SelectSingleNode("//LIST[@id='" + sID + "']"); sFile = oNode.Attributes.GetNamedItem("filename").Value; sFile = this.ListCfgPath + @"\lists\" + sFile; sList = this.CallList + sID; sQueue = this.QueuePath + sList; this.ProcessFinalizedRecords(sID,sFile); ClearQueue(sQueue); sSQL = "update [" + sList + "$] set queue='0' where fdisp in (" + this.FdispCallable + ")"; this.DBExecute(sSQL,sFile); } catch (Exception e) { this.RW(e.Message + "<br>"); } this.XmlListConfigSave(); this.SiteRedirect("default.aspx"); } public void XmlListConfigSave() { try { XmlListConfig.Save(this.ListCfgPath + @"\listconfig\calllists.xml"); } catch (Exception e) { this.RW(e.Message + "<br>"); } } public bool FlagRecordInQueue(string sRecordID,string sFile,string sList) { bool fRet=false; string sSQL =""; // Set the queue column in the spreadsheet so the record won't be // pulled up again in queue load. try { sSQL = "update [" + sList + "$] set queue='1',callcnt = callcnt + 1 "; sSQL += " where recordid in (" + sRecordID + ")"; this.DBExecute(sSQL,sFile); fRet=true; } catch (Exception e) { this.RW("Flag In Queue: " + e.Message + "<br>"); } return fRet; } public bool SaveRecordToExcel(string sXML,string sID,string sFile,string sList) { string sRecordID=""; string sSQL=""; string sField=""; bool fRet=false; XmlDocument oXML = new XmlDocument(); XmlNodeList oList; // Typically called from ProcessFinalizedRecords(). Loads the Xml // document deserialized from the message queue. Builds a SQL update // string and saves the record back to Excel. try { oXML.LoadXml(sXML); sRecordID = oXML.SelectSingleNode("//DEF[@fieldname='recordid']").InnerText.ToString(); oList = oXML.SelectNodes("//DEF"); foreach(XmlNode oNode in oList) { sField=oNode.Attributes.GetNamedItem("fieldname").Value.ToString(); switch(sField) { case "recordid": break; case "queue": sSQL += "queue='2',"; break; default: sSQL += this.CreateSqlPair(oNode); break; } } sSQL = "update [" + sList + "$] set " + sSQL.Substring(0,sSQL.Length -1); sSQL += " where recordid in (" + sRecordID + ")"; this.DBExecute(sSQL,sFile); fRet=true; } catch (Exception e) {this.RW(e.Message + "<br>"); } return fRet; } public void SetRecordCounts(string sID,string sFile) { int lTotal=0; int lCallable=0; string sList=""; string sSQL=""; DataSet oDS; // Update the admin display to reflect the various records counts for // the call list. try { sFile = this.ListCfgPath + @"\lists\" + sFile; sList = this.CallList + sID; oDS = this.GetDataSetFromExcel(sFile,"select * from [" + sList + "$]"); lTotal = oDS.Tables[0].Rows.Count; sSQL = "select * from [" + sList + "$] where fdisp in (" + this.FdispCallable + ")"; oDS = this.GetDataSetFromExcel(sFile,sSQL); lCallable = oDS.Tables[0].Rows.Count; XmlNode oNode = this.XmlListConfig.SelectSingleNode("//LIST[@id='" + sID + "']"); oNode.Attributes.GetNamedItem("totalrecords").Value = lTotal.ToString(); oNode.Attributes.GetNamedItem("callablerecords").Value = lCallable.ToString(); this.XmlListConfigSave(); } catch (Exception e) { this.RW(e.Message + "<br>"); } } public void LoadCallingQueue(string sID,string sFile) { string sQueue=""; string sList=""; string sCol=""; string sRecordID=""; string sSQL=""; int lRecords=0; int lMessageCount=0; DataSet oDS; XmlDocument oRecord; XmlNodeList oList; try { sList = this.CallList + sID; sQueue = this.QueuePath + sList; sFile = this.ListCfgPath + @"\lists\" + sFile; // Subtract the number of messages in the queue // from our MaxRecordsInQueue variable. This // allows to keep the desired number of callable records // at all times. lMessageCount = this.GetQueueMessageCount(sQueue); if (this.MaxRecordsInQueue >= lMessageCount) { lRecords = this.MaxRecordsInQueue - lMessageCount; } else { lRecords = 0; } if (lRecords<1) { return; } // Query the top x records based on lowest number of call attempts. // Notice the ORDER BY clause. It appears that either Excel or ADO.NET // has a small bug when you use ORDER BY with Excel. If the column values are not // unique enough, it causes the query to bring back improper results. Very // strange. I've added the unique id "recordid" to guarantee uniqueness. // Since "callcnt" is really the column we want to order by, this keeps the // problem from occurring. sSQL = "select TOP " + lRecords.ToString() + " * from [" + sList + "$] "; sSQL += " where queue in (0) and fdisp in (" + this.FdispCallable + ") "; sSQL += " order by callcnt,recordid ASC "; oDS = this.GetDataSetFromExcel(sFile,sSQL); if (oDS==null) { return; } // Load the call list defintion blank shell document with column settings oRecord = new XmlDocument(); oRecord.Load(this.ListCfgPath + @"\listconfig\" + this.CallListDef + sID + ".xml"); foreach(DataRow oRow in oDS.Tables[0].Rows) { oList = oRecord.SelectNodes("//DEF"); // Populate the shell XmlDocument with actual data from // the spreadsheet using the "fieldname" attribute to determine // which column data each node should get. foreach(XmlNode oNode in oList ) { sCol = oNode.Attributes.GetNamedItem("fieldname").Value.ToString(); if (sCol=="recordid") { sRecordID = oRow[sCol].ToString(); } oNode.InnerText = oRow[sCol].ToString(); } // Flag this record in the spreadsheet so future queue loads won't // add this record. if (this.FlagRecordInQueue(sRecordID,sFile,sList)==true) { // Serialize the XmlDocument and add it to the message queue if (this.SendRecordToQueue(sQueue,oRecord.InnerXml)==false) { return; } } } } catch (Exception e) { this.RW(e.Message + "<br>"); } } public void ProcessFinalizedRecords(string sID,string sFile) { string sQueue=""; string sList=""; string sXML=""; int lCount=0; int lMessageCount=0; Message oMessage; // Go through the disposition queue for this call list // and save the updated record back to the Excel sheet. try { sList = this.CallList + sID; sQueue = this.QueuePath + this.CallListDisp + sID; sFile = this.ListCfgPath + @"\lists\" + sFile; lMessageCount = GetQueueMessageCount(sQueue); for (lCount=0;lCount<lMessageCount;lCount++) { oMessage = this.GetRecordFromQueue(sQueue); if (oMessage!=null) { sXML = (string) oMessage.Body; this.SaveRecordToExcel(sXML,sID,sFile,sList); } } } catch (Exception e) { this.RW(e.Message + "<br>"); } } public void WriteMainMenu() { XmlNodeList oList; string sID=""; string sDesc=""; string sFile=""; string sOpen=""; string sHide=""; string sTotal=""; string sCallable=""; string sQueue=""; string sList=""; int lCallableQueue=0; try { oList = this.XmlListConfig.SelectNodes("//LIST"); foreach(XmlNode oNode in oList ) { sID = oNode.Attributes.GetNamedItem("id").InnerText.ToString(); sFile = oNode.Attributes.GetNamedItem("filename").InnerText.ToString(); sDesc = oNode.Attributes.GetNamedItem("desc").InnerText.ToString(); sOpen = oNode.Attributes.GetNamedItem("openforcalling").InnerText.ToString(); sHide = oNode.Attributes.GetNamedItem("hide").InnerText.ToString(); sList = this.CallList + sID; sQueue = this.QueuePath + sList; if (sHide!="1") { this.ProcessFinalizedRecords(sID,sFile); if (sOpen=="1") { this.LoadCallingQueue(sID,sFile); } this.SetRecordCounts(sID,sFile); sTotal = oNode.Attributes.GetNamedItem("totalrecords").InnerText.ToString(); sCallable = oNode.Attributes.GetNamedItem("callablerecords").InnerText.ToString(); lCallableQueue=this.GetQueueMessageCount(sQueue); this.RW("<tr>"); this.RW("<td align=left width=200 class=BodyText8 >" + sDesc + "</td>"); if (sOpen=="1") { this.RW("<td align=center nowrap><a href=default.aspx?action=stop&id=" + sID); this.RW(" class=BodyLink8>Stop List</a></td>"); this.RW("<td align=center> </td>"); this.RW("<td align=center><a href=default.aspx class=BodyLink8>Process</a></td>"); } else { this.RW("<td align=center nowrap><a href=default.aspx?action=start&id=" + sID); this.RW(" class=BodyLink8>Start List</a></td>"); this.RW("<td align=center nowrap><a href=default.aspx?action=recycle&id=" + sID); this.RW(" class=BodyLink8>Recycle List</a></td>"); this.RW("<td align=center><a href=default.aspx class=BodyLink8>Process</a></td>"); } this.RW("<td align=right class=BodyText8 nowrap>" + lCallableQueue.ToString() + "</td>"); this.RW("<td align=right class=BodyText8 nowrap>" + sTotal + "</td>"); this.RW("<td align=right class=BodyText8 nowrap>" + sCallable + "</td>"); this.RW("</tr>"); } } } catch (Exception e) { this.RW("<tr><td align=left class=BodyText10>" + e.Message + "</td></tr>"); } } } }
using System; using System.Data; using System.Data.OleDb; using System.Xml; using System.Messaging; using System.Runtime.Serialization; namespace CallCenter { public class Utilities { public string CallList = "calllist_"; public string CallListDef = "calllistdef_"; public string CallListDisp = "calllistdisp_"; public string QueuePath = ".\\PRIVATE$\\"; public string ListCfgPath=System.Web.HttpContext.Current.Server.MapPath("/admin"); public string FdispCallable = "0,30,31,32,33,34,35,36,37,38,39,40"; public string ExcelCon = @"Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;"; public int MaxRecordsInQueue=20; public Utilities() { System.Web.HttpContext.Current.Response.Buffer = true; } public string RequestObject(string sName) { string sRet=""; try { sRet = System.Web.HttpContext.Current.Request[sName].ToString().Trim(); } catch (Exception) { sRet = "";} return sRet; } public void SiteRedirect(string sURL) { System.Web.HttpContext.Current.Response.Redirect(sURL); } public string HTMLName(string sFieldName) { return " name='" + sFieldName + "' id='" + sFieldName + "'"; } public void RW(string sVal) { try { if (System.Web.HttpContext.Current !=null) { System.Web.HttpContext.Current.Response.Write(sVal +'\n'); } } catch(Exception) {} } public int GetQueueMessageCount(string sQueue) { int nRet=0; MessageQueue oQueue; Message[] oMessages; try { if (!MessageQueue.Exists(sQueue)) { MessageQueue.Create(sQueue); } oQueue = new MessageQueue(sQueue); oMessages = oQueue.GetAllMessages(); nRet = oMessages.GetLength(0); oQueue.Close(); } catch (Exception) {} return nRet; } public Message GetRecordFromQueue(string sQueue) { MessageQueue oQueue; Message oMessage; try { if (!MessageQueue.Exists(sQueue)) { MessageQueue.Create(sQueue); } oQueue = new MessageQueue(sQueue); // Utilize our custom message formatter to deserialize the XmlDocument stored // in the message queue. oQueue.Formatter = new CallCenter.CustomMessageFormatter(); oMessage = oQueue.Receive(new TimeSpan(0,0,5)); try {oQueue.Close(); } catch (Exception) { } oQueue.Close(); return oMessage; } catch (Exception) { } return null; } public bool SendRecordToQueue(string sQueue,object oRecord) { bool fRet=false; MessageQueue oQueue; try { if (!MessageQueue.Exists(sQueue)) { MessageQueue.Create(sQueue); } oQueue = new MessageQueue(sQueue); // Utilize our custom message formatter to serialize oRecord which is // itself an XmlDocument. oQueue.Formatter = new CallCenter.CustomMessageFormatter(); oQueue.Send(oRecord); fRet = true; oQueue.Close(); } catch (Exception e) { this.RW("Send Queue Error:" + e.Message + "<br>"); } return fRet; } public bool ClearQueue(string sQueue) { bool fRet=false; MessageQueue oQueue; try { if (!MessageQueue.Exists(sQueue)) { MessageQueue.Create(sQueue); } oQueue = new MessageQueue(sQueue); oQueue.Purge(); fRet = true; oQueue.Close(); } catch (Exception e) { this.RW("Clear Queue Error:" + e.Message + "<br>"); } return fRet; } public bool DBExecute(string sSQL,string sFile) { bool fRet=false; OleDbConnection oConn = new OleDbConnection(); try { oConn.ConnectionString = this.ExcelCon + "Data Source=" + sFile + ";"; oConn.Open(); OleDbCommand oCmd = new OleDbCommand(sSQL,oConn); oCmd.ExecuteNonQuery(); oConn.Close(); fRet=true; } catch (Exception e) { this.RW("DBExecute: " + e.Message + "<br>"); } finally { if (oConn.State == ConnectionState.Open) { oConn.Close(); }} return fRet; } public string CreateSqlPair(XmlNode oNode) { string sCol=""; string sColVal=""; sCol=oNode.Attributes.GetNamedItem("fieldname").Value.ToString(); sColVal = oNode.InnerText.ToString(); if (sColVal.Length <1) { return ""; } sColVal = sColVal.Replace("'",""); return sCol + "='" + sColVal + "',"; } public DataSet GetDataSetFromExcel(string sFile,string sSQL) { DataSet oDS = new DataSet(); OleDbConnection oConn = new OleDbConnection(); try { oConn.ConnectionString = this.ExcelCon + "Data Source=" + sFile + ";"; oConn.Open(); OleDbDataAdapter oCmd = new OleDbDataAdapter(sSQL,oConn); oCmd.Fill(oDS); } catch (Exception) { } finally { if (oConn.State == ConnectionState.Open) { oConn.Close(); } } return oDS; } } }
using System; using System.IO; using System.Text; using System.Messaging; namespace CallCenter { public class CustomMessageFormatter : IMessageFormatter { public CustomMessageFormatter() { } public object Clone() { return new CustomMessageFormatter(); } public bool CanRead (System.Messaging.Message oMessage){ return true;} public void Write (System.Messaging.Message oMessage, object oMessageObject) { try { byte[] buff; buff = Encoding.UTF8.GetBytes (oMessageObject.ToString()); Stream oStream = new MemoryStream(buff); oMessage.BodyStream = oStream; } catch (Exception e){System.Web.HttpContext.Current.Response.Write(e.Message + "<br>");} } public object Read (System.Messaging.Message oMessage) { try { Stream oStream = oMessage.BodyStream; StreamReader oReader = new StreamReader (oStream); return oReader.ReadToEnd(); } catch (Exception e){System.Web.HttpContext.Current.Response.Write(e.Message + "<br>");} return null; } } }
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Xml" %> <%@ Import Namespace="System.Messaging" %> <%@ Import Namespace="CallCenter" %> <script Language="C#" runat="server"> protected CallCenter.Utilities oSite = new CallCenter.Utilities(); protected XmlDocument oRecord = new XmlDocument(); protected string sCallListID="3"; protected string sRecordID=""; protected bool ShowRecord=false; protected void Page_Load(object sender, EventArgs e) { ProcessPage(); } public void ProcessPage() { string sXML=""; string sRecordQueue=""; string sRecordDispositionQueue=""; Message oMessage; sRecordQueue=oSite.QueuePath + "calllist_" + sCallListID; sRecordDispositionQueue=oSite.QueuePath + "calllistdisp_" + sCallListID; try { if (oSite.RequestObject("action")=="post") { SaveCustomerRecord(sRecordDispositionQueue); } oMessage = oSite.GetRecordFromQueue(sRecordQueue); sXML = (string) oMessage.Body; if (sXML.Length >1) { oRecord.LoadXml(sXML); sRecordID = oRecord.SelectSingleNode("//DEF[@fieldname='recordid']").InnerText.ToString(); ShowRecord=true; } } catch (Exception) { } } public void SaveCustomerRecord(string sQueue) { XmlDocument oXML = new XmlDocument(); oXML.Load(oSite.ListCfgPath + @"\listconfig\" + oSite.CallListDef + sCallListID + ".xml"); oXML.SelectSingleNode("//DEF[@fieldname='fdisp']").InnerText = oSite.RequestObject("FDISP"); oXML.SelectSingleNode("//DEF[@fieldname='recordid']").InnerText = oSite.RequestObject("RECORDID"); oXML.SelectSingleNode("//DEF[@fieldname='phone']").InnerText = oSite.RequestObject("PHONE"); oXML.SelectSingleNode("//DEF[@fieldname='fname']").InnerText = oSite.RequestObject("FNAME"); oXML.SelectSingleNode("//DEF[@fieldname='lname']").InnerText = oSite.RequestObject("LNAME"); oXML.SelectSingleNode("//DEF[@fieldname='addr1']").InnerText = oSite.RequestObject("ADDR1"); oXML.SelectSingleNode("//DEF[@fieldname='addr2']").InnerText = oSite.RequestObject("ADDR2"); oXML.SelectSingleNode("//DEF[@fieldname='city']").InnerText = oSite.RequestObject("CITY"); oXML.SelectSingleNode("//DEF[@fieldname='state']").InnerText = oSite.RequestObject("STATE"); oXML.SelectSingleNode("//DEF[@fieldname='zip']").InnerText = oSite.RequestObject("ZIP"); oSite.SendRecordToQueue(sQueue,oXML.InnerXml); } public string GetDispositionList() { string sH=""; sH = "<select id=FDISP name=FDISP>"; sH += "<option value='0'>Not Dialed</option>"; sH += "<option value='30' selected>Unavailable</option>"; sH += "<option value='20'>Refused</option>"; sH += "<option value='01'>Sale</option>"; sH +="</select>"; return sH; } public void DisplayRecord() { string sFieldName=""; string sFieldValue=""; string sFieldDesc=""; string sName=""; try { oSite.RW("<table border=0 align=left cellspacing=2 cellpadding=2>"); if (ShowRecord==true) { XmlNodeList oList = oRecord.SelectNodes("//DEF[@showinscript='true']"); foreach(XmlNode oNode in oList ) { sFieldName=oNode.Attributes.GetNamedItem("fieldname").InnerText.ToString(); sFieldDesc=oNode.Attributes.GetNamedItem("desc").InnerText.ToString(); sFieldValue=oNode.InnerText.ToString(); sName = oSite.HTMLName(sFieldName); oSite.RW("<tr><td align=left class=BodyText8><b>" + sFieldDesc + "</b></td>"); oSite.RW("<td align=left class=BodyText8>"); switch(sFieldName) { case "fdisp": oSite.RW(GetDispositionList() + "---" + sFieldValue); break; case "phone": oSite.RW(sFieldValue); break; case "recordid": oSite.RW(sFieldValue); break; case "callcnt": oSite.RW(sFieldValue); break; case "fname": oSite.RW("<input " + sName + " type=text size=40 maxlength=40 value='" + sFieldValue + "'>"); break; case "lname": oSite.RW("<input " + sName + " type=text size=40 maxlength=40 value='" + sFieldValue + "'>"); break; case "addr1": oSite.RW("<input " + sName + " type=text size=40 maxlength=40 value='" + sFieldValue + "'>"); break; case "addr2": oSite.RW("<input " + sName + " type=text size=40 maxlength=40 value='" + sFieldValue + "'>"); break; case "city": oSite.RW("<input " + sName + " type=text size=40 maxlength=40 value='" + sFieldValue + "'>"); break; case "state": oSite.RW("<input " + sName + " type=text size=2 maxlength=2 value='" + sFieldValue + "'>"); break; case "zip": oSite.RW("<input " + sName + " type=text size=5 maxlength=5 value='" + sFieldValue + "'>"); break; } oSite.RW("</td></tr>"); } oSite.RW("<tr><td align=left> </td><td align=left class=BodyText8>"); oSite.RW("<input type=button name=Submit value='Submit' onclick=SubmitForm();></td></tr>"); } else { oSite.RW("<tr><td align=left colspan=2 class=BodyText8>"); oSite.RW("There are no records in call list: " + sCallListID + ". "); oSite.RW("<a href=javascript:ReloadPage(); class=BodyLink8 target=_self>Retry record query</a></td></tr>"); } oSite.RW("</table>"); } catch (Exception e) { Response.Write("Display Record Error: " + e.Message + "<br>"); } } </script> <HTML><TITLE>Call Center Agent Demo</TITLE> <HEAD> <LINK href=/home/global.css rel=Stylesheet> <SCRIPT language=JavaScript> var fSubmitted=false; function ReloadPage() { document.frmSubmit.Action.value='reload'; document.frmSubmit.submit(); } function SubmitForm() { if (fSubmitted==false) { document.frmSubmit.Action.value='post'; document.frmSubmit.submit(); fSubmitted=true; } } </SCRIPT> </HEAD> <BODY bottomMargin=0 bgColor=#ffffff leftMargin=10 topMargin=0 rightMargin=0 marginwidth=0 marginheight=0 > <FORM method=post action=calllist_<%=sCallListID%>.aspx id=frmSubmit name=frmSubmit> <table valign=top align=left class=ColorTable border=0 cellPadding=2 cellSpacing=2 width='90%' > <tr><td align=left class=BodyText8><br></td></tr> <tr><td align=left class=ColorText10><b>Call List <%=sCallListID%></b></td></tr> <tr><td><hr size=1 color=#CCCCCC noshade></td></tr> <tr><td align=left class=BodyText9>Hello, my name is _________ and I'm calling for the Happy Gardener Magazine!</td></tr> <tr><td align=left class=BodyText8><br></td></tr> <tr><td align=left class=BodyText9>We'd like to offer you a full one year subscription for $1. Would you like to buy a subscription?</td></tr> <tr><td align=left class=BodyText8><br></td></tr> <tr><td align=left><% DisplayRecord(); %></td></tr> </table> <input type=hidden name=Action id=Action value='0'> <input type=hidden name=RECORDID id=RECORDID value='<%=sRecordID%>'> </form></body></html>