/////////////////////////////////////////////////////////// // NotifySubscription.cs // Implementation of Class NotifySubscription // CSLA type: Switchable // Created on: 04-Oct-2005 // Object design: John // Coded: John 04-Oct-2005 /////////////////////////////////////////////////////////// using System; using System.Data; using CSLA.Data; using GZTW.Data; using CSLA; using System.Threading; using CSLA.Security; namespace GZTW.AyaNova.BLL { #pragma warning disable 1591 /// /// Switchable (child or root), As child it is part of collection under NotifySubscriber /// User's selection of events they want to subscribe to being notified about /// [Serializable] public class NotifySubscription : BusinessBase { #region Attributes private bool bReadOnly; private Guid mID; private Guid mCreator; private Guid mModifier; private SmartDate mCreated; private SmartDate mModified; /// /// Guid ID of root object (UserID from Subscriber parent object) /// private Guid mUserID; private RootObjectTypes mRootObjectType; private int mEventType; private int mPendingSpan; private AyaUnitsOfTime mPendingSpanUnit; private NotifySubscriptionDeliveries mDeliveries; //used by events that require user specify an int value //(Like WorkorderEvent.Status for example) private Guid mGuidValue=Guid.Empty; #endregion #region Constructor private NotifySubscription() { //Set to read / write initially so that properties //can be set bReadOnly=false; //New ID mID = Guid.NewGuid(); //Set record history to defaults mCreated = new SmartDate(DBUtil.CurrentWorkingDateTime); mModified=new SmartDate(); mCreator=Guid.Empty; mModifier=Guid.Empty; //break the event to indicate unsettedness EventType=0; mPendingSpanUnit=AyaUnitsOfTime.Days; mPendingSpan=10; mDeliveries = NotifySubscriptionDeliveries.NewItems(); mGuidValue=Guid.Empty; } #endregion #region Business Properties //---Common properties /// /// Initial created date of this object /// public string Created { get { return mCreated.ToString(); } } /// /// Initial created date of this object /// as SmartDate (used by display list) /// public SmartDate CreatedSD { get { return mCreated; } } /// /// User ID of who initially created this object /// public Guid Creator { get { return mCreator; } } /// /// Last modified date of this object /// public string Modified { get { return mModified.ToString(); } } /// /// User ID of who last modified this object /// public Guid Modifier { get { return mModifier; } } /// /// Unique ID of this object /// public Guid ID { get { return mID; } } /// /// UserID (parent SubscriberID) of this object /// public Guid UserID { get { return mUserID; } } //-------- /// /// Get /set event publisher root object type /// Read only once saved /// public RootObjectTypes RootObjectType { get { return mRootObjectType; } set { if(bReadOnly || !IsNew)//Only pending minutes can be changed on an existing record ThrowSetError(); else { if(mRootObjectType!=value) { mRootObjectType = value; MarkDirty(); } } } } /// /// Get /set event publisher's particulare event type subscribed to /// Read only once saved /// public int EventType { get { return mEventType; } set { if(bReadOnly || !IsNew) ThrowSetError(); else { if(mEventType!=value) { //TODO: Bizrule required here for being invalid //if set to zero mEventType = value; MarkDirty(); } } } } /// /// Get /set time span units of SpanRange for calculating /// event date with pending type events /// /// Can be changed after saving /// public int PendingSpan { get { return mPendingSpan; } set { if(bReadOnly) ThrowSetError(); else { if(mPendingSpan!=value) { mPendingSpan = value; MarkDirty(); } } } } /// /// Get /set time span unit of time for calculating /// event date with pending type events /// /// Can be changed after saving /// public AyaUnitsOfTime PendingSpanUnit { get { return mPendingSpanUnit; } set { if(bReadOnly) ThrowSetError(); else { if(mPendingSpanUnit!=value) { mPendingSpanUnit = value; MarkDirty(); } } } } /// /// TAkes the date of an event and returns true /// if it's within the range the user set in this subscription /// Used by the NotificationList object for dated type /// events when generating /// notifications to send out to users /// /// /// public bool EventDateIsWithinDeliverableRange(System.DateTime EventDate) { //Firstly, if it's a user selectable timeframe advance //notice type event then account for the fact that //we may not yet have reached the actual event date //but we may be deliverable anyway due to user set //to warn in advance of the actual event date if(IsUserTimeFrameSelectableEvent && mPendingSpan!=0) { //Use the PendingSpanUnit and PendingSpan value to subtract from the event date //then compare with current date to see if it's within range switch(PendingSpanUnit) { case AyaUnitsOfTime.Minutes: EventDate=EventDate.AddMinutes(-mPendingSpan); break; case AyaUnitsOfTime.Hours: EventDate=EventDate.AddHours(-mPendingSpan); break; case AyaUnitsOfTime.Days: EventDate=EventDate.AddDays(-mPendingSpan); break; case AyaUnitsOfTime.Months: EventDate=EventDate.AddMonths(-mPendingSpan); break; case AyaUnitsOfTime.Years: EventDate=EventDate.AddYears(-mPendingSpan); break; default: throw(new ApplicationException("INTERNAL ERROR: [" +PendingSpanUnit.ToString() + "] Is an invalid unit of time in: NotifySubscription.EventDateIsWithinPendingDateRange ")); //hours, days months years anything else is invalid and should barf } } if (DBUtil.CurrentWorkingDateTime >= EventDate) return true;//case 1163 return false; } public NotifySubscriptionDeliveries Deliveries { get { return mDeliveries; } } /// /// Returns the localized text key describing the /// currently set notification event /// public string EventDescriptionLocalizedTextKey { get { if( this.mEventType>0)//zero indicates it isn't valid yet return NotifyEventUIHelper.GetEventLocalizedTextKey(this.mRootObjectType,this.EventType); else return ""; } } /// /// If the event associated with this subscription /// is a type of event that the user can select a time /// frame for (most pending type events, but not all) /// /// /// public bool IsUserTimeFrameSelectableEvent { get { //(User selectable time frame events all start at internal value 256 or higher) if(mEventType > 255) return true; return false; } } /// /// If the event associated with this subscription /// uses the GuidValue property then returns true. /// /// /// public bool IsWorkorderStatusEvent { get { if(this.mRootObjectType==RootObjectTypes.Workorder && mEventType==(int)WorkorderEvent.Status) return true; return false; } } /// /// Used by some events where an additional Int value /// needs to be specified in the subscription /// /// can *not* be changed after saving /// public Guid GuidValue { get { return this.mGuidValue ; } set { if(bReadOnly || !IsNew) ThrowSetError(); else { if(mGuidValue!=value) { mGuidValue = value; MarkDirty(); } } } } //-------------- /// /// Throw an error when a read only user /// tries to set a property /// (this should normally never be called unless someone is using the developer api since the UI /// should prevent it from happening initially) /// private void ThrowSetError() { throw new System.Security.SecurityException ( string.Format ( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToChange"), LocalizedTextTable.GetLocalizedTextDirect("O.Notification") ) ); } #endregion #region System.object overrides /// /// /// /// public override string ToString() { return "NotifySubscription" + mID.ToString(); } /// /// /// /// /// public override bool Equals(Object obj) { if ( obj == null || GetType ( ) != obj.GetType ( ) ) return false; NotifySubscription c=(NotifySubscription)obj; return mID==c.mID; } /// /// /// /// public override int GetHashCode() { return ("NotifySubscription" + mID).GetHashCode(); } /// /// Used by UI code to weed out already selected /// delivery methods in delivery method selector forms /// /// /// public bool ContainsDelivery(Guid DeliverySettingID) { return this.mDeliveries.Contains(this.ID,DeliverySettingID); } #endregion #region Static methods #region Editable root versions /// /// Create item standalone editable root version /// /// Subscriber ID /// New Item public static NotifySubscription NewItem(Guid UserID) { if(AyaBizUtils.Right("Object.Notification")>(int)SecurityLevelTypes.ReadOnly) { NotifySubscription child=new NotifySubscription(); child.mUserID=UserID; return child; } else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToCreate"), LocalizedTextTable.GetLocalizedTextDirect("O.Notification"))); } /// /// Retrieve item - Editable root version /// /// /// public static NotifySubscription GetItem(Guid NotifySubscriptionID) { if(AyaBizUtils.Right("Object.Notification")>(int)SecurityLevelTypes.NoAccess || AyaBizUtils.IsGenerator) { return (NotifySubscription)DataPortal.Fetch(new Criteria(NotifySubscriptionID)); } else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToRetrieve"), LocalizedTextTable.GetLocalizedTextDirect("O.Notification"))); } /// /// Delete NotifySubscription Item /// /// public static void DeleteItem(Guid NotifySubscriptionID) { if(AyaBizUtils.Right("Object.Notification")>(int)SecurityLevelTypes.ReadWrite) DataPortal.Delete(new Criteria(NotifySubscriptionID)); else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToDelete"), LocalizedTextTable.GetLocalizedTextDirect("O.Notification"))); } #endregion #region Child versions /// /// Create item Child version /// /// Parent subscriber object /// New Item internal static NotifySubscription NewItemChild(NotifySubscriber obj) { if(AyaBizUtils.Right("Object.Notification")>(int)SecurityLevelTypes.ReadOnly) { NotifySubscription child=new NotifySubscription(); child.MarkAsChild(); child.mUserID=obj.UserID; return child; } else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToCreate"), LocalizedTextTable.GetLocalizedTextDirect("O.Notification"))); } /// /// Retrieve item /// /// Data reader /// item from database internal static NotifySubscription GetItemChild(SafeDataReader dr) { if(AyaBizUtils.Right("Object.Notification")>(int)SecurityLevelTypes.NoAccess) { NotifySubscription child = new NotifySubscription(); child.Fetch(dr); return child; } else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToRetrieve"), LocalizedTextTable.GetLocalizedTextDirect("O.Notification"))); } #endregion #endregion #region DAL DATA ACCESS #region Child versions #region Fetch /// /// Fetch from db /// /// private void Fetch(SafeDataReader dr) { MarkAsChild(); //Standard items mCreated=DBUtil.ToLocal(dr.GetSmartDate("aCreated")); mCreator=dr.GetGuid("aCreator"); mModified=DBUtil.ToLocal(dr.GetSmartDate("aModified")); mModifier=dr.GetGuid("aModifier"); //NotifySubscription specific parameters mID=dr.GetGuid("aID"); mUserID=dr.GetGuid("aUserID"); mRootObjectType=(RootObjectTypes)dr.GetInt16("aRootObjectType"); mEventType=dr.GetInt16("aEventType"); mPendingSpan=dr.GetInt32("aPendingSpan"); mPendingSpanUnit=(AyaUnitsOfTime)dr.GetInt16("aPendingSpanUnit"); this.mGuidValue=dr.GetGuid("aGuidValue"); //Deliveries mDeliveries=NotifySubscriptionDeliveries.GetItems(this); //Get access rights level bReadOnly=AyaBizUtils.Right("Object.Notification")<(int)SecurityLevelTypes.ReadWrite; MarkOld(); } #endregion child fetch #region Update child /// /// Persist object to database /// /// Parent object /// Parents transaction object internal void Update(NotifySubscriber obj,IDbTransaction tr) { //No need to update if there is nothing changed if(!this.IsDirty) return; //get modification time temporarily, if update succeeds then //set to this time System.DateTime dtModified = DBUtil.CurrentWorkingDateTime; // If not a new record, check if record was modified //by another user since original retrieval: if(!IsNew) DBUtil.CheckSafeToUpdate(this.mModified.Date,this.mID,"aNotifySubscription"); #region Delete if(IsDeleted) { if(!IsNew) { //call static shared deleteItem method below DeleteItem(this.mID,this.mRootObjectType,this.mEventType,this.mGuidValue,this.mUserID, tr); } MarkNew(); return; } #endregion #region Add / Update DBCommandWrapper cm = null; if(IsNew)//Add or update? cm=DBUtil.GetCommandFromSQL( "INSERT INTO aNotifySubscription (aID, aUserID, aRootObjectType, aEventType, aPendingSpan, aPendingSpanUnit, " + "aGuidValue, aCreated, aModified, aCreator,aModifier) " + "VALUES (@ID, @UserID,@RootObjectType,@EventType,@PendingSpan, @PendingSpanUnit, " + "@GuidValue, @Created, @Modified, @CurrentUserID,@CurrentUserID)" ); else //Only pending minutes can be changed on a non new record cm=DBUtil.GetCommandFromSQL( "UPDATE aNotifySubscription SET aID=@ID, aUserID=@UserID, aPendingSpan=@PendingSpan, aPendingSpanUnit=@PendingSpanUnit, " + "aGuidValue=@GuidValue, aModifier=@CurrentUserID, " + "aModified=@Modified WHERE aID=@ID" ); //NotifySubscription fields cm.AddInParameter("@ID",DbType.Guid,mID); cm.AddInParameter("@UserID",DbType.Guid,mUserID); cm.AddInParameter("@RootObjectType",DbType.Int16,(int)mRootObjectType); cm.AddInParameter("@EventType",DbType.Int16,mEventType); cm.AddInParameter("@PendingSpan",DbType.Int32,mPendingSpan); cm.AddInParameter("@PendingSpanUnit",DbType.Int16,(int)mPendingSpanUnit); cm.AddInParameter("@GuidValue",DbType.Guid,mGuidValue); //Standard fields cm.AddInParameter("@CurrentUserID",DbType.Guid, CurrentUserID); cm.AddInParameter("@Created",DbType.DateTime, DBUtil.ToUTC(mCreated.Date)); cm.AddInParameter("@Modified",DbType.DateTime, DBUtil.ToUTC(dtModified)); DBUtil.DB.ExecuteNonQuery(cm, tr); if(IsNew) { //Increment subscriber count to this event NotifyEventOfInterestFetcher.Subscribe(this.RootObjectType,this.EventType,this.mGuidValue, true); } //Deliveries mDeliveries.Update(tr); MarkOld();//db is now synched with object //Successful update so //change modification time to match this.mModified.Date=dtModified; #endregion } #endregion update child version #endregion #region Editable root versions #region Fetch - Editable root style /// /// protected override void DataPortal_Fetch(object Criteria) { Criteria crit = (Criteria)Criteria; SafeDataReader dr = null; try { DBCommandWrapper dbCommandWrapper = DBUtil.DB.GetSqlStringCommandWrapper( "SELECT * FROM aNotifySubscription WHERE aID=@ID" ); dbCommandWrapper.AddInParameter("@ID",DbType.Guid,crit.ID); dr = new SafeDataReader(DBUtil.DB.ExecuteReader(dbCommandWrapper)); if(!dr.Read()) DBUtil.ThrowFetchError("NotifySubscription: " + crit.ID.ToString()); //Standard items mCreated=DBUtil.ToLocal(dr.GetSmartDate("aCreated")); mCreator=dr.GetGuid("aCreator"); mModified=DBUtil.ToLocal(dr.GetSmartDate("aModified")); mModifier=dr.GetGuid("aModifier"); //NotifySubscription specific parameters mID=dr.GetGuid("aID"); mUserID=dr.GetGuid("aUserID"); mRootObjectType=(RootObjectTypes)dr.GetInt16("aRootObjectType"); mEventType=dr.GetInt16("aEventType"); mPendingSpan=dr.GetInt32("aPendingSpan"); mPendingSpanUnit=(AyaUnitsOfTime)dr.GetInt16("aPendingSpanUnit"); this.mGuidValue=dr.GetGuid("aGuidValue"); //Deliveries mDeliveries=NotifySubscriptionDeliveries.GetItems(this); } finally { if(dr!=null) dr.Close(); } MarkOld(); //Get access rights level bReadOnly=AyaBizUtils.Right("Object.Notification")<(int)SecurityLevelTypes.ReadWrite; } #endregion fetch editable root style #region Update - Editable Root Version /// /// Called by DataPortal to delete/add/update data into the database /// Editable root version /// protected override void DataPortal_Update() { // If not a new record, check if record was modified //by another user since original retrieval: if(!IsNew) DBUtil.CheckSafeToUpdate(this.mModified.Date,this.mID,"aNotifySubscription"); #region Delete if(IsDeleted) { if(!IsNew) { //Delete object using (IDbConnection connection = DBUtil.DB.GetConnection()) { connection.Open(); IDbTransaction transaction = connection.BeginTransaction(); try { //call static shared deleteItem method below DeleteItem(this.mID,this.mRootObjectType,this.mEventType,this.mGuidValue,this.mUserID, transaction); // Commit the transaction transaction.Commit(); } catch { // Rollback transaction transaction.Rollback(); throw; } finally { connection.Close(); } } //----------------------------- } MarkNew(); return; } #endregion #region Add / Update //get modification time temporarily, if update succeeds then //set to this time System.DateTime dtModified = DBUtil.CurrentWorkingDateTime; DBCommandWrapper cm = null; if(IsNew)//Add or update? cm=DBUtil.GetCommandFromSQL( "INSERT INTO aNotifySubscription (aID, aUserID, aRootObjectType, aEventType, aPendingSpan, aPendingSpanUnit, " + "aGuidValue, aCreated, aModified, aCreator,aModifier) " + "VALUES (@ID, @UserID,@RootObjectType,@EventType,@PendingSpan, @PendingSpanUnit, " + "@Created, @Modified, @CurrentUserID,@CurrentUserID)" ); else //Only pending minutes can be changed on a non new record cm=DBUtil.GetCommandFromSQL( "UPDATE aNotifySubscription SET aID=@ID, aUserID=@UserID, aPendingSpan=@PendingSpan, aPendingSpanUnit=@PendingSpanUnit, " + "aGuidValue=@GuidValue, aModifier=@CurrentUserID, " + "aModified=@Modified WHERE aID=@ID" ); //NotifySubscription fields cm.AddInParameter("@ID",DbType.Guid,mID); cm.AddInParameter("@UserID",DbType.Guid,mUserID); cm.AddInParameter("@RootObjectType",DbType.Int16,(int)mRootObjectType); cm.AddInParameter("@EventType",DbType.Int16,mEventType); cm.AddInParameter("@PendingSpan",DbType.Int32,mPendingSpan); cm.AddInParameter("@PendingSpanUnit",DbType.Int16,(int)mPendingSpanUnit); cm.AddInParameter("@GuidValue",DbType.Guid,mGuidValue); //Standard fields cm.AddInParameter("@CurrentUserID",DbType.Guid, CurrentUserID); cm.AddInParameter("@Created",DbType.DateTime, DBUtil.ToUTC(mCreated.Date)); cm.AddInParameter("@Modified",DbType.DateTime, DBUtil.ToUTC(dtModified)); using (IDbConnection connection = DBUtil.DB.GetConnection()) { connection.Open(); IDbTransaction transaction = connection.BeginTransaction(); try { DBUtil.DB.ExecuteNonQuery(cm, transaction); if(IsNew) { //Increment subscriber count to this event NotifyEventOfInterestFetcher.Subscribe(this.RootObjectType,this.EventType,this.GuidValue, true); } //Deliveries mDeliveries.Update(transaction); MarkOld();//db is now synched with object // Commit the transaction transaction.Commit(); } catch { // Rollback transaction transaction.Rollback(); throw; } finally { connection.Close(); } //Successful update so //change modification time to match this.mModified.Date=dtModified; } #endregion } #endregion Update editable root version #region Delete - editable root version /// /// Remove a record from the database /// /// protected override void DataPortal_Delete(object Criteria) { Criteria crit = (Criteria)Criteria; NotifySubscription ns=NotifySubscription.GetItem(crit.ID); using (IDbConnection connection = DBUtil.DB.GetConnection()) { connection.Open(); IDbTransaction transaction = connection.BeginTransaction(); try { //call static shared deleteItem method below DeleteItem(crit.ID,ns.RootObjectType,ns.EventType, ns.GuidValue,ns.UserID, transaction); // Commit the transaction transaction.Commit(); } catch { // Rollback transaction transaction.Rollback(); throw; } finally { connection.Close(); } } } #endregion Delete editable root style #endregion #endregion #region DeleteItem /// /// This handles deleting a subscription record and updating notifyeventofinterest accordingly /// It's called internally here and also by the parent collection /// /// It also removes any events that might be present in the NotifyEvent queue /// /// /// /// /// /// /// internal static void DeleteItem(Guid NotifySubscriptionID, RootObjectTypes RootObjectType, int EventType, Guid GuidValue, Guid UserID, IDbTransaction transaction) { //Delete subscription and update notifyeventofinterest //Delete deliveries NotifySubscriptionDeliveries.DeleteItems(NotifySubscriptionID,transaction); //Delete events that may be in queue for this particular subscription and user NotifyEvent.RemoveSpecificEventForUser(RootObjectType,EventType,UserID,GuidValue); //Delete subscription DBCommandWrapper cmDelete = DBUtil.GetCommandFromSQL("DELETE FROM aNotifySubscription WHERE aID = @ID;"); cmDelete.AddInParameter("@ID",DbType.Guid,NotifySubscriptionID); DBUtil.DB.ExecuteNonQuery(cmDelete, transaction); //decrement subscriber count to this event NotifyEventOfInterestFetcher.Subscribe(RootObjectType,EventType,GuidValue, false); } #endregion #region Override IsValid / IsDirty //Override base class version if there are child objects public override bool IsValid { get { return base.IsValid && mDeliveries.IsValid; } } public override bool IsDirty { get { return base.IsDirty || mDeliveries.IsDirty; } } #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { public Guid ID; public Criteria( Guid _ID) { ID=_ID; } } #endregion }//end NotifySubscription #pragma warning restore 1591 }//end namespace GZTW.AyaNova.BLL