Files
ayanova7/source/bizobjects/AyaLib/GZTW.AyaNova.BLL/NotifyEvent.cs
2018-06-29 19:47:36 +00:00

646 lines
26 KiB
C#

///////////////////////////////////////////////////////////
// NotifyEvent.cs
// Implementation of Class NotifyEvent
// CSLA type: Editable Root
// Created on: 13-Oct-2005
// Object design: John
// Coded: John 13-Oct-2005
///////////////////////////////////////////////////////////
using System;
using System.Data;
using CSLA.Data;
using GZTW.Data;
using CSLA;
using System.Threading;
using CSLA.Security;
using System.Collections;
namespace GZTW.AyaNova.BLL
{
/// <summary>
/// AyaNova NotifyEvent used to insert new
/// notification events in the database.
///
/// Event's are Processed appropriately here according
/// to their type and purpose.
///
/// This class ensures that only events subscribed to
/// are inserted and tags them with the correct user ID
/// for user specific subscriptions.
/// </summary>
[Serializable]
public class NotifyEvent : BusinessBase
{
#region Attributes
private Guid _RootObjectID;
private RootObjectTypes _RootObjectType;
private int _EventType;
private Guid _AppliesToUserID;
private SmartDate _EventDate;
private Guid _GuidValue;
//Sometimes an event is no longer required due to deletion etc
//so this flag will remove it
private bool _Remove;
private bool _RemoveIsBecauseDelivered;
private Guid _NotifyEventID;
private bool _RemoveIsBecauseUnSubscribed;
//case 812
private string _SavedMessage;
#endregion
#region Constructor
/// <summary>
/// Private constructor to prevent direct instantiation
/// used by all shared Add/Update methods but not by any of the remove methods
/// </summary>
private NotifyEvent(RootObjectTypes RootObjectType,Guid RootObjectID,
int EventType,Guid AppliesToUserID,SmartDate EventDate,Guid GuidValue, string SavedMessage)//case 812
{
_RootObjectType=RootObjectType;
_RootObjectID=RootObjectID;
_EventType=EventType;
_AppliesToUserID=AppliesToUserID;
_EventDate=EventDate;
_Remove=false;
_GuidValue=GuidValue;
_RemoveIsBecauseDelivered=false;
_RemoveIsBecauseUnSubscribed=false;
_SavedMessage = SavedMessage;
}
/// <summary>
/// Constructor for remove all / specific event method
/// </summary>
/// <param name="RootObjectID">Object's events to be removed regardless of status</param>
/// <param name="EventType">0=all events, non-zero means specific event only</param>
private NotifyEvent(Guid RootObjectID,int EventType)
{
_RootObjectID=RootObjectID;
_Remove=true;
_RemoveIsBecauseDelivered=false;
_EventType=EventType;
_RemoveIsBecauseUnSubscribed=false;
}
/// <summary>
/// Constructor for RemoveDeliveredEvent method
/// </summary>
/// <param name="NotifyEventID">NotifyEvent ID to be removed</param>
private NotifyEvent(Guid NotifyEventID)
{
_NotifyEventID=NotifyEventID;
_Remove=true;
_RemoveIsBecauseDelivered=true;
_RemoveIsBecauseUnSubscribed=false;
}
/// <summary>
/// Constructor for subscription related remove events methods
/// </summary>
/// <param name="RootObjectType"></param>
/// <param name="EventType"></param>
/// <param name="AppliesToUserID"></param>
/// <param name="GuidValue"></param>
private NotifyEvent(RootObjectTypes RootObjectType,
int EventType,Guid AppliesToUserID, Guid GuidValue)
{
_RootObjectType=RootObjectType;
_EventType=EventType;
_AppliesToUserID=AppliesToUserID;
_GuidValue=GuidValue;
_Remove=true;
_RemoveIsBecauseDelivered=false;
_RemoveIsBecauseUnSubscribed=true;
}
#endregion
#region Static methods
/// <summary>
/// Add a new event if no matching one already exists or
/// replaces a matching existing event.
///
///
/// </summary>
/// <param name="RootObjectType"></param>
/// <param name="RootObjectID"></param>
/// <param name="EventType"></param>
/// <param name="AppliesToUserID"></param>
/// <param name="EventDate"></param>
/// <param name="GuidValue"></param>
public static void AddOrUpdateEvent(RootObjectTypes RootObjectType,Guid RootObjectID,
int EventType,Guid AppliesToUserID,SmartDate EventDate, Guid GuidValue)
{
DataPortal.Update(new NotifyEvent( RootObjectType, RootObjectID,
EventType, AppliesToUserID, EventDate, GuidValue,""));
}
/// <summary>
/// Add a new event if no matching one already exists or
/// replaces a matching existing event.
/// </summary>
/// <param name="RootObjectType"></param>
/// <param name="RootObjectID"></param>
/// <param name="EventType"></param>
public static void AddOrUpdateEvent(RootObjectTypes RootObjectType, Guid RootObjectID,
int EventType)
{
DataPortal.Update(new NotifyEvent(RootObjectType, RootObjectID,
EventType, Guid.Empty, new SmartDate(), Guid.Empty,""));
}
//case 812 for QuickNotification
/// <summary>
/// Add a new event with saved message
/// </summary>
/// <param name="RootObjectType"></param>
/// <param name="RootObjectID"></param>
/// <param name="EventType"></param>
/// <param name="AppliesToUserID"></param>
/// <param name="sMessage"></param>
public static void AddOrUpdateEvent(RootObjectTypes RootObjectType, Guid RootObjectID,
int EventType, Guid AppliesToUserID, string sMessage)
{
//check for license if quick notification
if (RootObjectType == RootObjectTypes.User && EventType == (int)UserEvent.QuickNotification)
{
//case 2094
if(!AyaBizUtils.PluginSubscriptionExists("QuickNotification"))
throw new System.NotSupportedException("A license for QuickNotification plugin is required to use this method");
}
DataPortal.Update(new NotifyEvent(RootObjectType, RootObjectID,
EventType, AppliesToUserID, new SmartDate(), Guid.Empty,sMessage));
}
//
/// <summary>
/// Add a new pending event if no matching one already exists or
/// replaces an unProcessed matching existing event.
///
/// If the pending until date is empty then will simply remove any
/// existing matching pending event, otherwise will add/update it
///
/// Any pre-existing matching event found is replaced.
/// </summary>
/// <param name="RootObjectType"></param>
/// <param name="RootObjectID"></param>
/// <param name="EventType"></param>
/// <param name="AppliesToUserID"></param>
/// <param name="EventDate"></param>
/// <param name="GuidValue"></param>
public static void AddOrUpdatePendingEvent(RootObjectTypes RootObjectType,Guid RootObjectID,
int EventType,Guid AppliesToUserID,SmartDate EventDate, Guid GuidValue)
{
DataPortal.Update(new NotifyEvent( RootObjectType, RootObjectID,
EventType, AppliesToUserID, EventDate, GuidValue,""));
}
#region Object related and initiated removal
/// <summary>
/// Removes all events for a given object ID
/// This is used within objects when they are deleted to
/// ensure no events remain related to them.
///
/// It is also used for updating objects like schedule markers where
/// a slight change, i.e. scheduleableusergroup change could
/// render a lot of the user specific events meaningless since
/// the entire group of users could change with a small change in the
/// schedulemarker. In a case like that, this method is called to
/// wipe out the entire event list for that object and a new one
/// is created
/// </summary>
/// <param name="RootObjectID">ID of deleted object</param>
public static void RemoveAllEventsForObject(Guid RootObjectID)
{
DataPortal.Update(new NotifyEvent( RootObjectID,0));
}
/// <summary>
/// Removes a specific event for a given object ID
/// This is used within objects when they are changed in some way that
/// renders the event meaningless, i.e. if a client's contractexpires is set to empty
/// </summary>
/// <param name="RootObjectID"></param>
/// <param name="EventType"></param>
public static void RemoveSpecificEventForObject(Guid RootObjectID,int EventType)
{
DataPortal.Update(new NotifyEvent( RootObjectID, EventType));
}
#endregion
#region Subscription related and initiated removal
/// <summary>
/// Removes a specific event for a specific user
/// This is used when a user unsubscribes from a specfic event to ensure
/// that there are no event's for that event and subscriber left floating
/// </summary>
/// <param name="RootObjectType"></param>
/// <param name="EventType"></param>
/// <param name="AppliesToUserID"></param>
/// <param name="GuidValue"></param>
public static void RemoveSpecificEventForUser(RootObjectTypes RootObjectType,
int EventType,Guid AppliesToUserID, Guid GuidValue)
{
DataPortal.Update(new NotifyEvent(RootObjectType,EventType,AppliesToUserID, GuidValue));
}
#endregion
/// <summary>
/// Removes an event that has been delivered
///
/// Used by notification delivery Processor to remove events
/// that have been delivered but not touch ones that are fresh since
/// delivery
/// </summary>
/// <param name="NotifyEventID"></param>
public static void RemoveDeliveredEvent(Guid NotifyEventID)
{
DataPortal.Update(new NotifyEvent( NotifyEventID));
}
#endregion
#region DAL DATA ACCESS
/// <summary>
/// Case 58
/// Used to ensure that a user is within the region
/// of the object before inserting notification
/// </summary>
/// <param name="userID"></param>
/// <param name="tid"></param>
/// <returns></returns>
private static bool InUserRegion(Guid userID, TypeAndID tid)
{
Guid UserRegionID = UserRegionIDFetcher.UserRegion(userID);
//Short circuit if user is in default region, no need to check further
//they get everything
if (UserRegionID == Region.DefaultRegionID) return true;
//case 1152
//fix to stop lockup when the "longer way round" code below fires and
//objectregionidfetcher attempts to lookup the region of a workorder via the
//grandchild object that triggered the notification. Problem being that the
//grandchild object has been saved but is in a transaction that hasn't been committed yet
//so it seems to cause some sort of deadlock
//Fix is to ignore the region for these objects since in most cases they can't be
//triggered outside their region anyway from the UI level
if (tid.RootObjectType == RootObjectTypes.WorkorderItemScheduledUser) return true;
if (tid.RootObjectType == RootObjectTypes.WorkorderItemOutsideService) return true;
if (tid.RootObjectType == RootObjectTypes.WorkorderItemPartRequest) return true;
//longer way around
if(AyaBizUtils.InYourRegion(UserRegionID,ObjectRegionIDFetcher.ObjectRegion(tid)))
return true;
return false;
}
/// <summary>
/// Called by DataPortal to delete/add/update data into the database
/// </summary>
protected override void DataPortal_Update()
{
#region Delete event
//Explicit removal of event(s)
if(_Remove)
{
if(_RemoveIsBecauseDelivered)
{//REmove a particular event record that has been delivered
DBCommandWrapper cmDeleteOnly=DBUtil.GetCommandFromSQL(
"DELETE FROM aNotifyEvent WHERE aID=@ID"
);
cmDeleteOnly.AddInParameter("@ID",DbType.Guid,this._NotifyEventID);
DBUtil.DB.ExecuteNonQuery(cmDeleteOnly);
return;
}
if(_RemoveIsBecauseUnSubscribed)
{
DBCommandWrapper cmDeleteUnsubscriber = null;
cmDeleteUnsubscriber=DBUtil.GetCommandFromSQL(
"DELETE FROM aNotifyEvent WHERE " +
"aRootObjectType=@RootObjectType AND " +
"aEventType=@EventType AND AAPPLIESTOUSERID=@AppliesToUserID " +
(_GuidValue==Guid.Empty?"":"AND (aGuidValue = @GuidValue)")
);
cmDeleteUnsubscriber.AddInParameter("@RootObjectType",DbType.Int16,(int)this._RootObjectType);
cmDeleteUnsubscriber.AddInParameter("@EventType",DbType.Int16,this._EventType);
cmDeleteUnsubscriber.AddInParameter("@AppliesToUserID",DbType.Guid,this._AppliesToUserID);
if(_GuidValue!=Guid.Empty)
cmDeleteUnsubscriber.AddInParameter("@GuidValue",DbType.Guid,_GuidValue);
DBUtil.DB.ExecuteNonQuery(cmDeleteUnsubscriber);
return;
}
//Only reason left is object initiated removal for change or delete:
{//this is here to local scope the contents so I don't have to rename the datacommand.
//Remove all or a specific event for the object id in question because it was deleted or changed
//Also remove any lingering popup notifications in the queue that are now stale
DBCommandWrapper cmDeleteOnly=null;
if(_EventType==0)
{
cmDeleteOnly=DBUtil.GetCommandFromSQL(
"DELETE FROM aNotifyEvent WHERE " +
"aRootObjectID=@RootObjectID "
);
//Remove any popup notifications about it as well
DBCommandWrapper cmDeletePop=DBUtil.GetCommandFromSQL(
"DELETE FROM aNotifyPopUp WHERE " +
"aRootObjectID=@RootObjectID "
);
cmDeletePop.AddInParameter("@RootObjectID",DbType.Guid,this._RootObjectID);
DBUtil.DB.ExecuteNonQuery(cmDeletePop);
}
else
{//specific event for this object only
cmDeleteOnly=DBUtil.GetCommandFromSQL(
"DELETE FROM aNotifyEvent " +
"WHERE (aRootObjectID=@RootObjectID) AND (aEventType = @EventType)"
);
cmDeleteOnly.AddInParameter("@EventType",DbType.Int16,this._EventType);
}
cmDeleteOnly.AddInParameter("@RootObjectID",DbType.Guid,this._RootObjectID);
DBUtil.DB.ExecuteNonQuery(cmDeleteOnly);
}
return;
}
#endregion delete event
#region add/update event
//Delete any current matching event record
//This is done ahead of checking to see if there are subscribers
//because it's possible there are no longer any subscribers but there is an existing
//event record from before the last subscriber unsubscribed
DBCommandWrapper cmDelete = null;
//WARNING: this is going to remove any old workorder status events
//when status changes because it doesn't include the GUID value
//is this a bad thing or not? I'm leaving it to remove it because
//I can't see where a use want's to know what a workorder status *was*
//only what it is now.
//case 812
bool bIsQuickNotification=(_RootObjectType == RootObjectTypes.User && _EventType == (int)UserEvent.QuickNotification);
//BUGBUG: 30-may-2006 Shouldn't this be deleting all events regardless of applies to user id
//or at least if it's an event without an applies to user id then it should delete all
//that match otherwise? (i.e. workorder close by date passed, every time a workorder is
//resaved it keeps adding more notifyevent records
if (_AppliesToUserID != Guid.Empty)
{
cmDelete = DBUtil.GetCommandFromSQL(
"DELETE FROM aNotifyEvent WHERE " +
"aRootObjectType=@RootObjectType AND aRootObjectID=@RootObjectID AND " +
"aEventType=@EventType AND AAPPLIESTOUSERID=@AppliesToUserID"
);
cmDelete.AddInParameter("@RootObjectType", DbType.Int16, (int)this._RootObjectType);
cmDelete.AddInParameter("@RootObjectID", DbType.Guid, this._RootObjectID);
cmDelete.AddInParameter("@EventType", DbType.Int16, this._EventType);
cmDelete.AddInParameter("@AppliesToUserID", DbType.Guid, this._AppliesToUserID);
}
else
{
//Changed: 31-May-2006 added the following to delete events that are not user specific
//problem was that if deliveries were stuck for some reason it would just keep adding
//the same events over and over until there were thousands.
cmDelete = DBUtil.GetCommandFromSQL(
"DELETE FROM aNotifyEvent WHERE " +
"aRootObjectType=@RootObjectType AND aRootObjectID=@RootObjectID AND " +
"aEventType=@EventType"
);
cmDelete.AddInParameter("@RootObjectType", DbType.Int16, (int)this._RootObjectType);
cmDelete.AddInParameter("@RootObjectID", DbType.Guid, this._RootObjectID);
cmDelete.AddInParameter("@EventType", DbType.Int16, this._EventType);
}
//case 812, don't delete quick notifications
if (!bIsQuickNotification)
DBUtil.DB.ExecuteNonQuery(cmDelete);
//DOES ANYONE SUBSCRIBE TO THIS EVENT?
//If notify event of interest subscriber count = zero then can
//bail out early
if(!NotifyEventOfInterestFetcher.IsEventInteresting(_RootObjectType,_EventType, _GuidValue)) return;
//If AppliesToUserID is non-null then see if this particular event is subscribed to by
//that particular user, if not then it doesn't need to be Processed and we can bail early
//(this is a design decision to distribute the load amongst the users rather than put it all
//at the event Processor)
//The reason for this is, for example, let's say there is a event of interest in
//memo.created. Every time a memo is created this code will be executed, but
//not all memo's are created for users that are subscribed to know about it.
//DOES THE INDICATED USER SUBSCRIBE TO THIS EVENT?
if(_AppliesToUserID!=Guid.Empty)
{
//let's see if the user in question is subscribed to the event in question
DBCommandWrapper cmSub = DBUtil.DB.GetSqlStringCommandWrapper(
"SELECT aID FROM aNotifySubscription " +
"WHERE (aRootObjectType = @RootObjectType) AND (aEventType = @EventType) " +
"AND (aUserID=@AppliesToUserID) " +
(_GuidValue==Guid.Empty?"":"AND (aGuidValue = @GuidValue)")
);
if(_GuidValue!=Guid.Empty)
cmSub.AddInParameter("@GuidValue",DbType.Guid,this._GuidValue);
cmSub.AddInParameter("@RootObjectType",DbType.Int16,(int)this._RootObjectType);
cmSub.AddInParameter("@EventType",DbType.Int16,this._EventType);
cmSub.AddInParameter("@AppliesToUserID",DbType.Guid,this._AppliesToUserID);
if(DBUtil.ToGuid(DBUtil.DB.ExecuteScalar(cmSub))==Guid.Empty)
return;//BAIL OUT, No user is not currently subscribed to this event
}
//Yes, at least one user is currently subscribed to this event
//Is it a general event that doesn't apply to a single user only?
if(_AppliesToUserID==Guid.Empty)
{
//then we need to create
//a notifyevent record for each user that is subscribed
//as they each need a record so that the event can be assured notification delivery
//and deletion upon delivery
//For example if two users subscribe to a contract.expires event and one gets the delivery
//during a notification Process, but the other doesnt' because they don't have an open time
//window to deliver at that moment the event needs to be kept for them but deleted for the
//person that could receive the delivery. The initial version of this code didn't take that into
//account so I want to re-iterate it here in case of future changes that might potentially
//affect this behaviour
//Iterate through subscriptions looking for people subscribed to this event
//create an event record for each one
DBCommandWrapper cmSubscribers = DBUtil.DB.GetSqlStringCommandWrapper(
//************************************************************
"SELECT aUserID FROM aNotifySubscription WHERE " +
"(aRootObjectType = @RootObjectType) AND (aEventType = @EventType) " +
(_GuidValue==Guid.Empty?"":"AND (aGuidValue = @GuidValue)")
//************************************************************
);
cmSubscribers.AddInParameter("@RootObjectType",DbType.Int16,(int)this._RootObjectType);
cmSubscribers.AddInParameter("@EventType",DbType.Int16,this._EventType);
if(_GuidValue!=Guid.Empty)
cmSubscribers.AddInParameter("@GuidValue",DbType.Guid,this._GuidValue);
SafeDataReader dr = new SafeDataReader(DBUtil.DB.ExecuteReader(cmSubscribers));
ArrayList al=new ArrayList();
while(dr.Read())
{
al.Add(dr.GetGuid("aUserID"));
}
foreach(object o in al)
{
//Case 58
if (InUserRegion((Guid)o, new TypeAndID(this._RootObjectType, this._RootObjectID)))
{
DBCommandWrapper cm = DBUtil.GetCommandFromSQL(
"INSERT INTO aNotifyEvent (aID, aRootObjectType, aRootObjectID, aEventType,aGuidValue, AAPPLIESTOUSERID, aEventDate, ACREATED) " +
"VALUES (@ID,@RootObjectType,@RootObjectID,@EventType,@GuidValue,@AppliesToUserID,@EventDate, @CREATED)"
);
cm.AddInParameter("@ID", DbType.Guid, Guid.NewGuid());
cm.AddInParameter("@RootObjectID", DbType.Guid, this._RootObjectID);
cm.AddInParameter("@GuidValue", DbType.Guid, this._GuidValue);
cm.AddInParameter("@RootObjectType", DbType.Int16, (int)this._RootObjectType);
cm.AddInParameter("@EventType", DbType.Int16, this._EventType);
cm.AddInParameter("@AppliesToUserID", DbType.Guid, (Guid)o);
cm.AddInParameter("@EventDate", DbType.DateTime, DBUtil.ToUTC(this._EventDate).DBValue);
cm.AddInParameter("@CREATED", DbType.DateTime, DBUtil.ToUTC(DBUtil.CurrentWorkingDateTime));
DBUtil.DB.ExecuteNonQuery(cm);
}
}
}
else
{
//It's *not* an event that applies to more than one user
//so we can just stick it in the table as is
//because only one user will ever be notified about it
//add the record for the event to the table
//case 812
if (!bIsQuickNotification)
{
if (InUserRegion(_AppliesToUserID, new TypeAndID(this._RootObjectType, this._RootObjectID)))
{
DBCommandWrapper cm = DBUtil.GetCommandFromSQL(
"INSERT INTO aNotifyEvent (aID, aRootObjectType, aRootObjectID, aEventType,aGuidValue, AAPPLIESTOUSERID, aEventDate, ACREATED) " +
"VALUES (@ID,@RootObjectType,@RootObjectID,@EventType,@GuidValue,@AppliesToUserID,@EventDate, @CREATED)"
);
cm.AddInParameter("@ID", DbType.Guid, Guid.NewGuid());
cm.AddInParameter("@RootObjectID", DbType.Guid, this._RootObjectID);
cm.AddInParameter("@GuidValue", DbType.Guid, this._GuidValue);
cm.AddInParameter("@RootObjectType", DbType.Int16, (int)this._RootObjectType);
cm.AddInParameter("@EventType", DbType.Int16, this._EventType);
cm.AddInParameter("@AppliesToUserID", DbType.Guid, this._AppliesToUserID);
cm.AddInParameter("@EventDate", DbType.DateTime, DBUtil.ToUTC(this._EventDate).DBValue);
cm.AddInParameter("@CREATED", DbType.DateTime, DBUtil.ToUTC(DBUtil.CurrentWorkingDateTime));
DBUtil.DB.ExecuteNonQuery(cm);
}
}
else
{
//case 812
DBCommandWrapper cm = DBUtil.GetCommandFromSQL(
"INSERT INTO aNotifyEvent (aID, aRootObjectType, aRootObjectID, aEventType,aGuidValue, AAPPLIESTOUSERID, aEventDate, ASAVEDMESSAGE, ACREATED) " +
"VALUES (@ID,@RootObjectType,@RootObjectID,@EventType,@GuidValue,@AppliesToUserID,@EventDate,@SAVEDMESSAGE, @CREATED)"
);
cm.AddInParameter("@ID", DbType.Guid, Guid.NewGuid());
cm.AddInParameter("@RootObjectID", DbType.Guid, this._RootObjectID);
cm.AddInParameter("@GuidValue", DbType.Guid, this._GuidValue);
cm.AddInParameter("@RootObjectType", DbType.Int16, (int)this._RootObjectType);
cm.AddInParameter("@EventType", DbType.Int16, this._EventType);
cm.AddInParameter("@AppliesToUserID", DbType.Guid, this._AppliesToUserID);
cm.AddInParameter("@EventDate", DbType.DateTime, DBUtil.ToUTC(this._EventDate).DBValue);
cm.AddInParameter("@SAVEDMESSAGE", DbType.String, _SavedMessage);
cm.AddInParameter("@CREATED", DbType.DateTime, DBUtil.ToUTC(DBUtil.CurrentWorkingDateTime));
DBUtil.DB.ExecuteNonQuery(cm);
}
}
#endregion add/update event
}
#endregion
}//end NotifyEvent
}//end namespace GZTW.AyaNova.BLL