/////////////////////////////////////////////////////////// // NotificationList.cs // Implementation of Class NotificationList // CSLA type: Read only collection // Created on: 13-Oct-2005 // Object design: John // Coded: John 13-Oct-2005 /////////////////////////////////////////////////////////// using System; using System.Data; using GZTW.Data; using CSLA.Data; using CSLA; using System.Threading; using CSLA.Security; using System.Collections.Generic; using System.Text.RegularExpressions; namespace GZTW.AyaNova.BLL { #pragma warning disable 1591 /// /// List of notifications formatted for delivery /// and screened to be deliverable /// /// /// /// Loops through all NotifyEvenRecords /// For pending type events, checks to see if within users selected notification window /// foreach NotifyEventRecord it adds a NotificationListInfo /// for each open delivery window for each subscriber to that event /// As it Processes each deliverable notification it formats it to users /// locale and preferences for size etc and keeps track of the address and /// delivery type. /// /// /// End result is a list ready to deliver. /// As each one is delivered Ok it should be deleted from the NotifyEvent table by the /// delivery Process that uses this list. /// /// [Serializable] public class NotificationList : ReadOnlyCollectionBase { #region Caching //Cache notificaiton messages so we can send them out the door faster without the //expensive lookup process if they are identical //this happens often in a large office, let's say there is a notification about a workorder past it's due date //there could be 20 people subscribed to that notification all of whom will get the same message //rather than go through the time consuming process of building up the message for each user //we can go to the cache and get it after the first time it's built. private List listMessageCache = new List(); //cache subscriptions while processing deliveries private Dictionary listNotifySubscriptionCache = new Dictionary(); #endregion #region Data structure /// /// /// [Serializable] public struct NotificationListInfo { //Event identifying info internal Guid _RootObjectID; internal RootObjectTypes _RootObjectType; internal int _EventType; internal Guid _AppliesToUserID; internal Guid _NotifyEventID; //internal SmartDate _EventDate; internal Guid _GuidValue; //Message info internal string _Subject; internal string _Message; internal Guid _DeliverToUserID; //Physical delivery method info internal NotifyDeliveryMethods _DeliveryMethod; internal string _Address; //Public properties public Guid NotifyEventID {get{return _NotifyEventID;}} public Guid RootObjectID {get{return _RootObjectID;}} public RootObjectTypes RootObjectType {get{return _RootObjectType;}} public int EventType {get{return _EventType;}} public Guid AppliesToUserID {get{return _AppliesToUserID;}} //public SmartDate EventDate {get{return _EventDate;}} public Guid GuidValue {get{return _GuidValue;}} public NotifyDeliveryMethods DeliveryMethod {get{return _DeliveryMethod;}} public string Address {get{return _Address;}} public string Message {get{return _Message;}} public string Subject {get{return _Subject;}} public Guid DeliverToUserID {get{return _DeliverToUserID;}} }//end NotificationListInfo #endregion #region Constructor private NotifyDeliverySettings _DeliverySettings; protected NotificationList() { //fetch all users delivery settings so it's //cached for use below _DeliverySettings=NotifyDeliverySettings.GetItems(Guid.Empty); } #endregion #region Business properties and methods /// /// Get item by index /// /// public NotificationListInfo this[int Item] { get { return (NotificationListInfo) List[Item]; } } #endregion #region Static methods /// /// Get all available notifications for delivery /// /// public static NotificationList GetList() { if(AyaBizUtils.IsGenerator) return (NotificationList) DataPortal.Fetch(new Criteria()); else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToChange"), LocalizedTextTable.GetLocalizedTextDirect("O.Notification"))); } #endregion #region DAL DATA ACCESS //removed these as part of case 1386, //probably were used for testing but no longer in use // int nHitCache = 0; // int nMissedCache = 0; protected override void DataPortal_Fetch(object Criteria) { listMessageCache.Clear(); listNotifySubscriptionCache.Clear(); //Loop through all the events in the table SafeDataReader dr = null; try { DBCommandWrapper cm = DBUtil.DB.GetSqlStringCommandWrapper( //************************************************************ "SELECT aNotifyEvent.*, aNotifySubscription.aID " + "AS aSUBSCRIPTIONID FROM aNotifyEvent INNER " + "JOIN aNotifySubscription ON aNotifyEvent.AAPPLIESTOUSERID " + "= aNotifySubscription.aUserID " + "AND aNotifyEvent.aRootObjectType = aNotifySubscription.aRootObjectType " + "AND aNotifyEvent.aEventType " + "= aNotifySubscription.aEventType " + " AND ( " + " (ANOTIFYEVENT.AGUIDVALUE = ANOTIFYSUBSCRIPTION.AGUIDVALUE) " + " or ((ANOTIFYEVENT.AGUIDVALUE IS NULL AND ANOTIFYSUBSCRIPTION.AGUIDVALUE IS NULL )) " + " ) " //************************************************************ ); dr=new SafeDataReader(DBUtil.DB.ExecuteReader(cm)); while(dr.Read()) { ProcessSubscription(dr); } if(dr!=null) dr.Close(); //Now loop through all NON user specific events //Outer joining the subscription so that we will get none to many //subscribers to a single non-user-specific event DBCommandWrapper cm2 = DBUtil.DB.GetSqlStringCommandWrapper( //************************************************************ "SELECT aNotifyEvent.*, aNotifySubscription.aID " + "AS aSUBSCRIPTIONID FROM aNotifyEvent LEFT " + "OUTER JOIN aNotifySubscription ON aNotifyEvent.aRootObjectType " + "= aNotifySubscription.aRootObjectType " + "AND aNotifyEvent.aEventType = aNotifySubscription.aEventType " + "AND aNotifyEvent.aGuidValue " + "= aNotifySubscription.aGuidValue " + "WHERE (aNotifyEvent.AAPPLIESTOUSERID = @GUIDEMPTY) " //************************************************************ ); cm2.AddInParameter("@GUIDEMPTY",DbType.Guid,Guid.Empty); dr=new SafeDataReader(DBUtil.DB.ExecuteReader(cm2)); while(dr.Read()) { #if (DEBUG) throw new ApplicationException("Non user specific event found in notification list Processor."); #else ProcessSubscription(dr); #endif } } finally { if(dr!=null) dr.Close(); } } #region Process Subscription Record (add to list) /// /// Process an event/subscription record /// and insert into list /// /// Gets all the info necessary for delivery as well /// as for removing the notify event after sucessful delivery /// /// private void ProcessSubscription(SafeDataReader dr) { //Skip records with no matching subscription //(this shouldn't happen should it? TODO:Log this) if(dr.GetGuid("aSUBSCRIPTIONID")==Guid.Empty) return; //Fetch Subscription //TODO: CACHE THIS Guid subscriptionID=dr.GetGuid("aSUBSCRIPTIONID"); //if not in cache then add it if (!listNotifySubscriptionCache.ContainsKey(subscriptionID)) { //nMissedCache++; listNotifySubscriptionCache[subscriptionID] = NotifySubscription.GetItem(subscriptionID); } //else // nHitCache++; //set from cache NotifySubscription ns = listNotifySubscriptionCache[subscriptionID]; //Check if a dated notification is now deliverable SmartDate EventDate=DBUtil.ToLocal(dr.GetSmartDate("aEventDate")); if(!EventDate.IsEmpty)//it's a dated event... { //Ok, we have an event date, let's see if it's deliverable //This method called will account for the two types of dated events //ones where the user can set an advance warning date range //and the ones where they can't and it's just delivered on that date if(!ns.EventDateIsWithinDeliverableRange(EventDate.Date)) return; } foreach(NotifySubscriptionDelivery nsd in ns.Deliveries) { NotifyDeliverySetting ds=this._DeliverySettings[nsd.NotifyDeliveryMethodID]; if (ds.EventWindows.InEventWindow(DBUtil.CurrentWorkingDateTime))//case 1163 { //******************************************* //Add to list NotificationListInfo info=new NotificationListInfo(); info._NotifyEventID=dr.GetGuid("aID"); info._Address=ds.Address; info._DeliverToUserID=ds.UserID; info._AppliesToUserID=dr.GetGuid("AAPPLIESTOUSERID"); info._DeliveryMethod=ds.DeliveryMethod; info._EventType=dr.GetInt16("aEventType"); info._GuidValue=dr.GetGuid("aGuidValue"); info._RootObjectID=dr.GetGuid("aRootObjectID"); info._RootObjectType=(RootObjectTypes)dr.GetInt16("aRootObjectType"); //don't allow deliver notifications about new memos via memo //or it will cause a chain reaction if(info._RootObjectType==RootObjectTypes.Memo && info.EventType == (int)MemoEvent.Created && ds.DeliveryMethod==NotifyDeliveryMethods.Memo) continue;//skip out of the loop string Language = User.GetUserLanguage(info.DeliverToUserID); //case 1415 double TimeZoneOffset = User.GetUserTimeZoneOffset(info.DeliverToUserID); //see if there is a message in the cache we can use instead NotifyMessage nm = null; NotifyMessageRequestData d = new NotifyMessageRequestData( info.RootObjectType, info.EventType, info.RootObjectID, Language, ds.MaxCharacters, ds.MessageFormat, info._GuidValue, TimeZoneOffset ); //case 812 if (d.RootObject == RootObjectTypes.User && d.EventType == 1) { //case 1375 add ability for subject without schema change string sSavedMessage=dr.GetString("ASAVEDMESSAGE"); if (sSavedMessage.StartsWith("")) { Match m = AyaBizUtils.rxQuickNotifySubject.Match(sSavedMessage); if (m.Success) { string sSubject=m.Groups["subject"].Value; string sBody=m.Groups["body"].Value; if (string.IsNullOrEmpty(sSubject)) sSubject = "QuickNotification"; if (string.IsNullOrEmpty(sBody)) sBody = ""; nm = new NotifyMessage(sSubject, sBody); } else nm = new NotifyMessage("QuickNotification", sSavedMessage); } else//no subject or old style nm = new NotifyMessage("QuickNotification", sSavedMessage); } else { //Get it from the cache if present foreach (NotifyMessageRequestData n in listMessageCache) { if (n.Equals(d)) { //nHitCache++; nm = n.Message; break; } } //If not retrieved from cache then //retrive and add to cache if (nm == null) { //nMissedCache++; try { nm = NotifyEventUIHelper.GetNotificationMessage(d); d.Message = new NotifyMessage(nm.Subject, nm.Message); listMessageCache.Add(d); } catch(Exception ex) { //case 1386 //crack exception if necessary while (ex.InnerException != null) ex = ex.InnerException; //log and then continue NotifyDeliveryLog.LogDelivery(d.RootObject, d.RootObjectID, d.EventType, ds.UserID, false, d.GuidValue, ds.DeliveryMethod, "Error in NotificationList building new notification message. Full error is:\r\n" + ex.Message,DateTime.Now); continue; } } } info._Message = nm.Message; info._Subject = nm.Subject; InnerList.Add(info); //******************************************* } } } #endregion Process subscription record #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { //public Guid UserID; public Criteria(/*Guid _UserID*/) { //UserID=_UserID; } } #endregion }//end NotificationList #pragma warning restore 1591 }//end namespace GZTW.AyaNova.BLL