using EnVisage.Code; using EnVisage.Properties; using System; using System.Collections.Generic; using System.Linq; using System.Timers; using System.Web; namespace EnVisage.App_Start { public class LockerResponse { public enum ResponseCode { OK = 0, ObjectLocked = 1, ObjectExpired = 2, } public ResponseCode Code { get; set; } public bool Status { get; set; } public string LockedBy { get; set; } public string EntityTitle { get; set; } public LockerResponse() { Code = ResponseCode.OK; } } public static class ContentLocker { static readonly Dictionary EntityTitles = new Dictionary { {"SystemSettings", "System Settings"}, {"Clients", "Client"}, {"CreditDepartment", "Credit Department"}, {"ExpenditureCategory", "Expenditure Category"}, {"GLAccount", "GL Account"}, {"Trainings", "Training"}, {"Vacations", "Vacation"}, {"ScenarioGroup", "Scenario Group"}, {"ExpenditureCategoryRate", "Rate"}, {"Project Statuses", "Project Status"}, {"Types", "Type"}, {"UnitOfMeasure", "Unit Of Measure"}, {"TrainingTypes", "Training Type"}, {"PeopleResource", "People Resource"} }; static Timer timer; static Dictionary> lockersList; static ContentLocker() { lockersList = new Dictionary>(); timer = new Timer(Settings.Default.CheckInterval); timer.Elapsed += checkDeadLocks; if (!timer.Enabled) timer.Start(); } static void checkDeadLocks(object sender, ElapsedEventArgs e) { foreach (var lockedElement in lockersList) { unlockDead(lockedElement.Value); } } static void unlockDead(Dictionary lockedList) { List unlockItems = new List(); foreach (var lockedElement in lockedList) { if (lockedElement.Value.GetLockInterval() >= Settings.Default.UnlockInterval) { unlockItems.Add(lockedElement.Key); } } foreach (var unlockId in unlockItems) { lockedList.Remove(unlockId); } } /// /// Adds an object lock to the dictionary (if possible) and returns result of this operation. /// /// Unique identifier of the database table in which we store the locked object. /// Unique identifier of the locked object. /// Username of the owner of the lock. /// A timestamp of the object to check that system works with actual data. /// An instance of with info about operation result. public static LockerResponse AddLock(String tableId, String fieldId, string owner, string timestamp) { // add a table to the main dictionary if there is no such table yet if (!lockersList.ContainsKey(tableId)) { lockersList.Add(tableId, new Dictionary()); } // if there is a lock on the specified object but made by another user then return failed result with info about locked object if (lockersList[tableId].ContainsKey(fieldId) && lockersList[tableId][fieldId].Owner != owner) { return new LockerResponse { Status = false, Code=LockerResponse.ResponseCode.ObjectLocked, LockedBy = lockersList[tableId][fieldId].Owner, EntityTitle = GetLockedEntityTitle(tableId) }; } // if there is a lock on the specified object made by current user then do not add lock and return successfull result if (lockersList[tableId].ContainsKey(fieldId) && lockersList[tableId][fieldId].Owner == owner) { return new LockerResponse { Status = true, EntityTitle = GetLockedEntityTitle(tableId) }; } // if there is no lock on the specified object but it has been updated since last access then return failed result with info about modified object if (!IsObjectValid(tableId, fieldId, timestamp)) { return new LockerResponse { Status = false, Code = LockerResponse.ResponseCode.ObjectExpired, EntityTitle = GetLockedEntityTitle(tableId) }; } // add a lock to the specified object by current user lockersList[tableId].Add(fieldId, new LockedElement(owner)); // return successfull result return new LockerResponse { Status = true, EntityTitle = GetLockedEntityTitle(tableId) }; } public static bool RemoveLock(String tableId, String fieldId, string owner) { if (!lockersList.ContainsKey(tableId)) { return false; } if (!lockersList[tableId].ContainsKey(fieldId)) { return false; } if (lockersList[tableId][fieldId].Owner == owner) { lockersList[tableId].Remove(fieldId); return true; } return false; } public static bool UpdateLock(String tableId, String fieldId, string owner) { if (!lockersList.ContainsKey(tableId)) { return false; } if (!lockersList[tableId].ContainsKey(fieldId)) { return false; } if (lockersList[tableId][fieldId].Owner == owner) { lockersList[tableId][fieldId].UpdateLock(); return true; } return false; } public static string[] getLockList(String tableId) { if (lockersList.ContainsKey(tableId)) { List lockItems = new List(); foreach (var lockedElement in lockersList) { lockItems.Add(lockedElement.Key); } return lockItems.ToArray(); } return null; } public static LockerResponse IsLocked(String tableId, String fieldId, string owner) { if (lockersList.ContainsKey(tableId) && lockersList[tableId].ContainsKey(fieldId) && lockersList[tableId][fieldId].Owner != owner) { return new LockerResponse { Status = true, Code = LockerResponse.ResponseCode.ObjectLocked, LockedBy = lockersList[tableId][fieldId].Owner, EntityTitle = GetLockedEntityTitle(tableId) }; } return new LockerResponse { Status = false, EntityTitle = GetLockedEntityTitle(tableId) }; } public static bool IsLock(String tableId, String fieldId, string owner) { return IsLocked(tableId, fieldId, owner).Status; } public static string GetLockedEntityTitle(string tableId) { if (EntityTitles.ContainsKey(tableId)) return EntityTitles[tableId]; return tableId; } private static bool IsObjectValid(string tableId, string fieldId, string timestamp) { var projectValid = true; if (string.IsNullOrWhiteSpace(timestamp)) return projectValid; using (var dbContext = new EnVisageEntities()) { switch(tableId) { case "Project": var stamp = timestamp.StringToByteArray(); var id = Guid.Parse(fieldId); projectValid = dbContext.Projects.Count(t => t.Id == id && t.DateEdited == stamp) > 0; break; default: break; } } return projectValid; } } }