using Disco.Data.Repository; using Disco.Data.Repository.Monitor; using Disco.Models.Repository; using Disco.Services.Extensions; using Disco.Services.Tasks; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Reactive.Linq; namespace Disco.Services.Users.UserFlags { public static class UserFlagService { private static Cache _cache; internal static Lazy> UserFlagAssignmentRepositoryEvents; static UserFlagService() { // Statically defined (lazy) Assignment Repository Definition // Used by UserFlagAssignedUsersManagedGroup & UserFlagAssignedUserDevicesManagedGroup UserFlagAssignmentRepositoryEvents = new Lazy>(() => RepositoryMonitor.StreamAfterCommit.Where(e => e.EntityType == typeof(UserFlagAssignment) && (e.EventType != RepositoryMonitorEventType.Modified || e.ModifiedProperties.Contains("RemovedDate")) ) ); } public static void Initialize(DiscoDataContext Database) { _cache = new Cache(Database); // Initialize Managed Groups (if configured) _cache.GetUserFlags().ForEach(uf => { UserFlagUsersManagedGroup.Initialize(uf); UserFlagUserDevicesManagedGroup.Initialize(uf); }); } public static List GetUserFlags() { return _cache.GetUserFlags(); } public static UserFlag GetUserFlag(int UserFlagId) { return _cache.GetUserFlag(UserFlagId); } #region User Flag Maintenance public static UserFlag CreateUserFlag(DiscoDataContext Database, UserFlag UserFlag) { // Verify if (string.IsNullOrWhiteSpace(UserFlag.Name)) throw new ArgumentException("The User Flag Name is required"); // Name Unique if (_cache.GetUserFlags().Any(f => f.Name == UserFlag.Name)) throw new ArgumentException("Another User Flag already exists with that name", "UserFlag"); // Clone to break reference var flag = new UserFlag() { Name = UserFlag.Name, Description = UserFlag.Description, Icon = UserFlag.Icon, IconColour = UserFlag.IconColour, UsersLinkedGroup = UserFlag.UsersLinkedGroup, UserDevicesLinkedGroup = UserFlag.UserDevicesLinkedGroup }; Database.UserFlags.Add(flag); Database.SaveChanges(); _cache.AddOrUpdate(flag); return flag; } public static UserFlag Update(DiscoDataContext Database, UserFlag UserFlag) { // Verify if (string.IsNullOrWhiteSpace(UserFlag.Name)) throw new ArgumentException("The User Flag Name is required"); // Name Unique if (_cache.GetUserFlags().Any(f => f.Id != UserFlag.Id && f.Name == UserFlag.Name)) throw new ArgumentException("Another User Flag already exists with that name", "UserFlag"); Database.SaveChanges(); _cache.AddOrUpdate(UserFlag); UserFlagUsersManagedGroup.Initialize(UserFlag); UserFlagUserDevicesManagedGroup.Initialize(UserFlag); return UserFlag; } public static void DeleteUserFlag(DiscoDataContext Database, int UserFlagId, IScheduledTaskStatus Status) { UserFlag flag = Database.UserFlags.Find(UserFlagId); // Dispose of AD Managed Groups Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(UserFlagUserDevicesManagedGroup.GetKey(flag)); Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(UserFlagUsersManagedGroup.GetKey(flag)); // Delete Assignments Status.UpdateStatus(0, string.Format("Removing '{0}' [{1}] User Flag", flag.Name, flag.Id), "Starting"); List flagAssignments = Database.UserFlagAssignments.Where(fa => fa.UserFlagId == flag.Id).ToList(); if (flagAssignments.Count > 0) { Status.UpdateStatus(20, "Removing flag from users"); flagAssignments.ForEach(flagAssignment => Database.UserFlagAssignments.Remove(flagAssignment)); Database.SaveChanges(); } // Delete Flag Status.UpdateStatus(90, "Deleting User Flag"); Database.UserFlags.Remove(flag); Database.SaveChanges(); // Remove from Cache _cache.Remove(UserFlagId); Status.Finished(string.Format("Successfully Deleted User Flag: '{0}' [{1}]", flag.Name, flag.Id)); } #endregion #region Bulk Assignment public static IEnumerable BulkAssignAddUsers(DiscoDataContext Database, UserFlag UserFlag, User Technician, string Comments, List Users, IScheduledTaskStatus Status) { if (Users.Count > 0) { double progressInterval; const int databaseChunkSize = 100; string comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim(); var addUsers = Users.Where(u => !u.UserFlagAssignments.Any(a => a.UserFlagId == UserFlag.Id && !a.RemovedDate.HasValue)).ToList(); progressInterval = (double)100 / addUsers.Count; var addedUserAssignments = addUsers.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) => { var chunkIndexOffset = databaseChunkSize * chunkIndex; var chunkResults = chunk.Select((user, index) => { Status.UpdateStatus((chunkIndexOffset + index) * progressInterval, string.Format("Assigning Flag: {0}", user.ToString())); var fa = new UserFlagAssignment() { UserFlagId = UserFlag.Id, UserId = user.UserId, AddedDate = DateTime.Now, AddedUserId = Technician.UserId, Comments = comments }; Database.UserFlagAssignments.Add(fa); return fa; }).ToList(); // Save Chunk Items to Database Database.SaveChanges(); return chunkResults; }).Where(fa => fa != null).ToList(); Status.SetFinishedMessage(string.Format("{0} Users/s Added; {1} User/s Skipped", addUsers.Count, (Users.Count - addUsers.Count))); return addedUserAssignments; } else { Status.SetFinishedMessage("No changes found"); return Enumerable.Empty(); } } public static IEnumerable BulkAssignOverrideUsers(DiscoDataContext Database, UserFlag UserFlag, User Technician, string Comments, List Users, IScheduledTaskStatus Status) { double progressInterval; const int databaseChunkSize = 100; string comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim(); Status.UpdateStatus(0, "Calculating assignment changes"); var currentAssignments = Database.UserFlagAssignments.Include("User").Where(a => a.UserFlagId == UserFlag.Id && !a.RemovedDate.HasValue).ToList(); var removeAssignments = currentAssignments.Where(ca => !Users.Any(u => u.UserId.Equals(ca.UserId, StringComparison.OrdinalIgnoreCase))).ToList(); var addUsers = Users.Where(u => !currentAssignments.Any(ca => ca.UserId.Equals(u.UserId, StringComparison.OrdinalIgnoreCase))).ToList(); if (removeAssignments.Count > 0 || addUsers.Count > 0) { progressInterval = (double)100 / (removeAssignments.Count + addUsers.Count); var removedDateTime = DateTime.Now; // Remove Assignments removeAssignments.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) => { var chunkIndexOffset = (chunkIndex * databaseChunkSize) + removeAssignments.Count; var chunkResults = chunk.Select((flagAssignment, index) => { Status.UpdateStatus((chunkIndexOffset + index) * progressInterval, string.Format("Removing Flag: {0}", flagAssignment.User.ToString())); flagAssignment.RemovedDate = removedDateTime; flagAssignment.RemovedUserId = Technician.UserId; return flagAssignment; }).ToList(); // Save Chunk Items to Database Database.SaveChanges(); return chunkResults; }).ToList(); // Add Assignments var addedUserAssignments = addUsers.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) => { var chunkIndexOffset = (chunkIndex * databaseChunkSize) + removeAssignments.Count; var chunkResults = chunk.Select((user, index) => { Status.UpdateStatus((chunkIndexOffset + index) * progressInterval, string.Format("Assigning Flag: {0}", user.ToString())); var fa = new UserFlagAssignment() { UserFlagId = UserFlag.Id, UserId = user.UserId, AddedDate = DateTime.Now, AddedUserId = Technician.UserId, Comments = comments }; Database.UserFlagAssignments.Add(fa); return fa; }).ToList(); // Save Chunk Items to Database Database.SaveChanges(); return chunkResults; }).ToList(); Status.SetFinishedMessage(string.Format("{0} Users/s Added; {1} User/s Removed; {2} User/s Skipped", addUsers.Count, removeAssignments.Count, (Users.Count - addUsers.Count))); return addedUserAssignments; } else { Status.SetFinishedMessage("No changes found"); return Enumerable.Empty(); } } #endregion public static string RandomUnusedIcon() { return UIHelpers.RandomIcon(_cache.GetUserFlags().Select(f => f.Icon)); } public static string RandomUnusedThemeColour() { return UIHelpers.RandomThemeColour(_cache.GetUserFlags().Select(f => f.IconColour)); } } }