#138 device flag services

This commit is contained in:
Gary Sharp
2024-01-14 17:54:06 +11:00
parent 53baf4eb78
commit 15e2806731
10 changed files with 1292 additions and 0 deletions
@@ -0,0 +1,61 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services.Devices.DeviceFlags
{
internal class Cache
{
private ConcurrentDictionary<int, DeviceFlag> _Cache;
public Cache(DiscoDataContext Database)
{
Initialize(Database);
}
public void ReInitialize(DiscoDataContext Database)
{
Initialize(Database);
}
private void Initialize(DiscoDataContext Database)
{
// Queues from Database
var flags = Database.DeviceFlags.ToList();
// Add Queues to In-Memory Cache
_Cache = new ConcurrentDictionary<int, DeviceFlag>(flags.Select(f => new KeyValuePair<int, DeviceFlag>(f.Id, f)));
}
public DeviceFlag GetDeviceFlag(int deviceFlagId)
{
if (_Cache.TryGetValue(deviceFlagId, out var item))
return item;
else
return null;
}
public List<DeviceFlag> GetDeviceFlags()
{
return _Cache.Values.ToList();
}
public void AddOrUpdate(DeviceFlag flag)
{
_Cache.AddOrUpdate(flag.Id, flag, (key, existingItem) => flag);
}
public DeviceFlag Remove(int deviceFlagId)
{
if (_Cache.TryRemove(deviceFlagId, out var item))
return item;
else
return null;
}
public DeviceFlag Remove(DeviceFlag deviceFlag)
{
return Remove(deviceFlag.Id);
}
}
}
@@ -0,0 +1,184 @@
using Disco.Data.Repository;
using Disco.Data.Repository.Monitor;
using Disco.Models.Repository;
using Disco.Models.Services.Interop.ActiveDirectory;
using Disco.Services.Interop.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace Disco.Services.Devices.DeviceFlags
{
public class DeviceFlagDeviceAssignedUsersManagedGroup : ADManagedGroup
{
private const string KeyFormat = "DeviceFlag_{0}_DeviceAssignedUsers";
private const string DescriptionFormat = "User associated with devices which have the {0} Flag will be added to this Active Directory group.";
private const string CategoryDescriptionFormat = "Device Assigned Users Linked Group";
private const string GroupDescriptionFormat = "{0} [Device Flag Device Assigned Users]";
private IDisposable repositorySubscription;
private int deviceFlagId;
private string deviceFlagName;
public override string Description { get { return string.Format(DescriptionFormat, deviceFlagName); } }
public override string CategoryDescription { get { return CategoryDescriptionFormat; } }
public override string GroupDescription { get { return string.Format(GroupDescriptionFormat, deviceFlagName); } }
public override bool IncludeFilterBeginDate { get { return true; } }
private DeviceFlagDeviceAssignedUsersManagedGroup(string Key, ADManagedGroupConfiguration Configuration, DeviceFlag deviceFlag)
: base(Key, Configuration)
{
deviceFlagId = deviceFlag.Id;
deviceFlagName = deviceFlag.Name;
}
public override void Initialize()
{
// Subscribe to changes
repositorySubscription = DeviceFlagService.DeviceFlagAssignmentRepositoryEvents.Value
.Where(e =>
(((DeviceFlagAssignment)e.Entity).DeviceFlagId == deviceFlagId))
.Subscribe(ProcessRepositoryEvent);
}
public static string GetKey(DeviceFlag deviceFlag)
{
return string.Format(KeyFormat, deviceFlag.Id);
}
public static string GetDescription(DeviceFlag deviceFlag)
{
return string.Format(DescriptionFormat, deviceFlag.Name);
}
public static string GetCategoryDescription(DeviceFlag deviceFlag)
{
return CategoryDescriptionFormat;
}
public static bool TryGetManagedGroup(DeviceFlag deviceFlag, out DeviceFlagDeviceAssignedUsersManagedGroup managedGroup)
{
return ActiveDirectory.Context.ManagedGroups.TryGetValue(GetKey(deviceFlag), out managedGroup);
}
public static DeviceFlagDeviceAssignedUsersManagedGroup Initialize(DeviceFlag deviceFlag)
{
if (deviceFlag.Id > 0)
{
var key = GetKey(deviceFlag);
if (!string.IsNullOrEmpty(deviceFlag.DeviceUsersLinkedGroup))
{
var config = ConfigurationFromJson(deviceFlag.DeviceUsersLinkedGroup);
if (config != null && !string.IsNullOrWhiteSpace(config.GroupId))
{
var group = new DeviceFlagDeviceAssignedUsersManagedGroup(
key,
config,
deviceFlag);
// Add to AD Context
ActiveDirectory.Context.ManagedGroups.AddOrUpdate(group);
return group;
}
}
// Remove from AD Context
ActiveDirectory.Context.ManagedGroups.Remove(key);
}
return null;
}
public override IEnumerable<string> DetermineMembers(DiscoDataContext Database)
{
var query = (IQueryable<User>)Database.Users;
if (Configuration.FilterBeginDate.HasValue)
{
query = query
.Where(u => u.DeviceUserAssignments.Any(a =>
a.UnassignedDate == null &&
a.Device.DeviceFlagAssignments.Any(fa =>
fa.DeviceFlagId == deviceFlagId &&
!fa.RemovedDate.HasValue &&
fa.AddedDate >= Configuration.FilterBeginDate)));
}
else
{
query = query
.Where(u => u.DeviceUserAssignments.Any(a =>
a.UnassignedDate == null &&
a.Device.DeviceFlagAssignments.Any(fa =>
fa.DeviceFlagId == deviceFlagId &&
!fa.RemovedDate.HasValue)));
}
return query.Select(u => u.UserId)
.Distinct()
.ToList()
.Where(ActiveDirectory.IsValidDomainAccountId)
.ToList();
}
private void ProcessRepositoryEvent(RepositoryMonitorEvent Event)
{
var assignment = (DeviceFlagAssignment)Event.Entity;
string userId = assignment.Device?.AssignedUserId;
if (!ActiveDirectory.IsValidDomainAccountId(userId))
return;
switch (Event.EventType)
{
case RepositoryMonitorEventType.Added:
if (Configuration.FilterBeginDate.HasValue)
{
if (!assignment.RemovedDate.HasValue && assignment.AddedDate >= Configuration.FilterBeginDate)
{
AddMember(userId);
}
}
else
{
if (!assignment.RemovedDate.HasValue)
{
AddMember(userId);
}
}
break;
case RepositoryMonitorEventType.Modified:
if (!Configuration.FilterBeginDate.HasValue || assignment.AddedDate >= Configuration.FilterBeginDate)
{
if (assignment.RemovedDate.HasValue)
RemoveMember(userId, (database) =>
{
if (database.Users.Any(u => u.DeviceUserAssignments.Any(a =>
a.UnassignedDate == null &&
a.Device.DeviceFlagAssignments.Any(fa =>
fa.DeviceFlagId == deviceFlagId &&
!fa.RemovedDate.HasValue))))
{
return null;
}
else
{
return new[] { userId };
}
}
);
else
AddMember(userId);
}
break;
}
}
public override void Dispose()
{
if (repositorySubscription != null)
repositorySubscription.Dispose();
}
}
}
@@ -0,0 +1,202 @@
using Disco.Data.Repository;
using Disco.Data.Repository.Monitor;
using Disco.Models.Repository;
using Disco.Models.Services.Interop.ActiveDirectory;
using Disco.Services.Interop.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace Disco.Services.Devices.DeviceFlags
{
public class DeviceFlagDevicesManagedGroup : ADManagedGroup
{
private const string KeyFormat = "DeviceFlag_{0}_Devices";
private const string DescriptionFormat = "Devices associated with the {0} Flag will be added to this Active Directory group.";
private const string CategoryDescriptionFormat = "Assigned Devices Linked Group";
private const string GroupDescriptionFormat = "{0} [Device Flag Devices]";
private IDisposable repositorySubscription;
private int deviceFlagId;
private string deviceFlagName;
public override string Description { get { return string.Format(DescriptionFormat, deviceFlagName); } }
public override string CategoryDescription { get { return CategoryDescriptionFormat; } }
public override string GroupDescription { get { return string.Format(GroupDescriptionFormat, deviceFlagName); } }
public override bool IncludeFilterBeginDate { get { return true; } }
private DeviceFlagDevicesManagedGroup(string Key, ADManagedGroupConfiguration Configuration, DeviceFlag DeviceFlag)
: base(Key, Configuration)
{
deviceFlagId = DeviceFlag.Id;
deviceFlagName = DeviceFlag.Name;
}
public override void Initialize()
{
// Subscribe to changes
repositorySubscription = DeviceFlagService.DeviceFlagAssignmentRepositoryEvents.Value
.Where(e =>
((DeviceFlagAssignment)e.Entity).DeviceFlagId == deviceFlagId)
.Subscribe(ProcessRepositoryEvent);
}
public static string GetKey(DeviceFlag deviceFlag)
{
return string.Format(KeyFormat, deviceFlag.Id);
}
public static string GetDescription(DeviceFlag deviceFlag)
{
return string.Format(DescriptionFormat, deviceFlag.Name);
}
public static string GetCategoryDescription(DeviceFlag deviceFlag)
{
return CategoryDescriptionFormat;
}
public static bool TryGetManagedGroup(DeviceFlag deviceFlag, out DeviceFlagDevicesManagedGroup managedGroup)
{
return ActiveDirectory.Context.ManagedGroups.TryGetValue(GetKey(deviceFlag), out managedGroup);
}
public static DeviceFlagDevicesManagedGroup Initialize(DeviceFlag deviceFlag)
{
if (deviceFlag.Id > 0)
{
var key = GetKey(deviceFlag);
if (!string.IsNullOrEmpty(deviceFlag.DevicesLinkedGroup))
{
var config = ConfigurationFromJson(deviceFlag.DevicesLinkedGroup);
if (config != null && !string.IsNullOrWhiteSpace(config.GroupId))
{
var group = new DeviceFlagDevicesManagedGroup(
key,
config,
deviceFlag);
// Add to AD Context
ActiveDirectory.Context.ManagedGroups.AddOrUpdate(group);
return group;
}
}
// Remove from AD Context
ActiveDirectory.Context.ManagedGroups.Remove(key);
}
return null;
}
public override IEnumerable<string> DetermineMembers(DiscoDataContext Database)
{
var query = Database.DeviceFlagAssignments
.Where(a => a.DeviceFlagId == deviceFlagId && !a.RemovedDate.HasValue && a.Device.DeviceDomainId != null);
if (Configuration.FilterBeginDate.HasValue)
query = query.Where(a => a.AddedDate >= Configuration.FilterBeginDate);
return query
.Select(a => a.Device.DeviceDomainId)
.ToList()
.Where(ActiveDirectory.IsValidDomainAccountId)
.Select(id => id + "$");
}
private void ProcessRepositoryEvent(RepositoryMonitorEvent Event)
{
var assignment = (DeviceFlagAssignment)Event.Entity;
var domainId = assignment.Device?.DeviceDomainId;
if (!ActiveDirectory.IsValidDomainAccountId(domainId))
return;
domainId += "$";
switch (Event.EventType)
{
case RepositoryMonitorEventType.Added:
if (Configuration.FilterBeginDate.HasValue)
{
if (!assignment.RemovedDate.HasValue && assignment.AddedDate >= Configuration.FilterBeginDate)
{
AddMember(domainId);
}
}
else
{
if (!assignment.RemovedDate.HasValue)
{
AddMember(domainId);
}
}
break;
case RepositoryMonitorEventType.Modified:
if (Configuration.FilterBeginDate.HasValue)
{
if (assignment.AddedDate >= Configuration.FilterBeginDate)
{
if (assignment.RemovedDate.HasValue)
{
RemoveMember(domainId);
}
else
{
AddMember(domainId);
}
}
}
else
{
if (assignment.RemovedDate.HasValue)
{
RemoveMember(domainId);
}
else
{
AddMember(domainId);
}
}
break;
case RepositoryMonitorEventType.Deleted:
// Remove the device if no other (non-removed) assignments exist.
var serialNumber = assignment.DeviceSerialNumber;
RemoveMember(domainId, (database) =>
{
if (Configuration.FilterBeginDate.HasValue)
{
if (database.DeviceFlagAssignments.Any(a => a.DeviceFlagId == deviceFlagId && a.DeviceSerialNumber == serialNumber && !a.RemovedDate.HasValue && a.AddedDate >= Configuration.FilterBeginDate))
{
return null;
}
else
{
return new string[] { domainId };
}
}
else
{
if (database.DeviceFlagAssignments.Any(a => a.DeviceFlagId == deviceFlagId && a.DeviceSerialNumber == serialNumber && !a.RemovedDate.HasValue))
{
return null;
}
else
{
return new string[] { domainId };
}
}
});
break;
}
}
public override void Dispose()
{
if (repositorySubscription != null)
repositorySubscription.Dispose();
}
}
}
@@ -0,0 +1,229 @@
using Disco.Data.Repository;
using Disco.Models.Exporting;
using Disco.Models.Services.Devices.DeviceFlag;
using Disco.Models.Services.Exporting;
using Disco.Services.Plugins.Features.DetailsProvider;
using Disco.Services.Tasks;
using Disco.Services.Users;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
namespace Disco.Services.Devices.DeviceFlags
{
using Metadata = ExportFieldMetadata<DeviceFlagExportRecord>;
public class DeviceFlagExport
{
private readonly DiscoDataContext database;
private readonly DeviceFlagExportOptions options;
public DeviceFlagExport(DiscoDataContext database, DeviceFlagExportOptions options)
{
this.database = database;
this.options = options;
}
public ExportResult Generate(IScheduledTaskStatus status)
{
var records = BuildRecords(status);
var metadata = BuildMetadata(records, status);
if (metadata.Count == 0)
throw new ArgumentException("At least one export field must be specified", nameof(options));
status.UpdateStatus(90, $"Formatting {records.Count} records for export");
return ExportHelpers.WriteExport(options, status, metadata, records);
}
private List<DeviceFlagExportRecord> BuildRecords(IScheduledTaskStatus status)
{
var query = database.DeviceFlagAssignments
.Include(a => a.DeviceFlag);
if (options.HasDeviceOptions())
query = query.Include(a => a.Device);
if (options.HasDeviceModelOptions())
query = query.Include(a => a.Device.DeviceModel);
if (options.HasDeviceBatchOptions())
query = query.Include(a => a.Device.DeviceBatch);
if (options.HasDeviceProfileOptions())
query = query.Include(a => a.Device.DeviceProfile);
if (options.HasAssignedUserOptions())
query = query.Include(a => a.Device.AssignedUser);
if (options.AssignedUserDetailCustom)
query = query.Include(a => a.Device.AssignedUser.UserDetails);
query = query.Where(a => options.DeviceFlagIds.Contains(a.DeviceFlagId));
if (options.CurrentOnly)
{
query = query.Where(a => !a.RemovedDate.HasValue);
}
// Update Users
if (options.HasAssignedUserOptions())
{
status.UpdateStatus(5, "Refreshing user details from Active Directory");
var userIds = query.Where(d => d.Device.AssignedUserId != null).Select(d => d.Device.AssignedUserId).Distinct().ToList();
foreach (var userId in userIds)
{
try
{
UserService.GetUser(userId, database);
}
catch (Exception) { } // Ignore Errors
}
}
status.UpdateStatus(15, "Extracting records from the database");
var records = query.Select(a => new DeviceFlagExportRecord()
{
Assignment = a
}).ToList();
if (options.AssignedUserDetailCustom)
{
status.UpdateStatus(50, "Extracting custom user detail records");
var detailsService = new DetailsProviderService(database);
var cache = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal);
foreach (var record in records)
{
var userId = record.Assignment.Device.AssignedUserId;
if (userId == null)
continue;
if (!cache.TryGetValue(userId, out var details))
details = detailsService.GetDetails(record.Assignment.Device.AssignedUser);
record.AssignedUserCustomDetails = details;
}
}
return records;
}
private List<Metadata> BuildMetadata(List<DeviceFlagExportRecord> records, IScheduledTaskStatus status)
{
status.UpdateStatus(80, "Building metadata");
IEnumerable<string> userDetailCustomKeys = null;
if (options.AssignedUserDetailCustom)
userDetailCustomKeys = records.Where(r => r.AssignedUserCustomDetails != null).SelectMany(r => r.AssignedUserCustomDetails.Keys).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
var accessors = BuildAccessors(userDetailCustomKeys);
return typeof(DeviceFlagExportOptions).GetProperties()
.Where(p => p.PropertyType == typeof(bool))
.Select(p => new
{
property = p,
details = (DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault()
})
.Where(p => p.details != null && p.property.Name != nameof(options.CurrentOnly) && (bool)p.property.GetValue(options))
.SelectMany(p =>
{
var fieldMetadata = accessors[p.property.Name];
fieldMetadata.ForEach(f =>
{
if (f.ColumnName == null)
f.ColumnName = (p.details.ShortName == "Device Flag") ? p.details.Name : $"{p.details.ShortName} {p.details.Name}";
});
return fieldMetadata;
}).ToList();
}
private static Dictionary<string, List<Metadata>> BuildAccessors(IEnumerable<string> userDetailsCustomKeys)
{
const string DateFormat = "yyyy-MM-dd";
const string DateTimeFormat = DateFormat + " HH:mm:ss";
Func<object, string> csvStringEncoded = (o) => o == null ? null : $"\"{((string)o).Replace("\"", "\"\"")}\"";
Func<object, string> csvToStringEncoded = (o) => o == null ? null : o.ToString();
Func<object, string> csvCurrencyEncoded = (o) => ((decimal?)o).HasValue ? ((decimal?)o).Value.ToString("C") : null;
Func<object, string> csvDateEncoded = (o) => ((DateTime)o).ToString(DateFormat);
Func<object, string> csvDateTimeEncoded = (o) => ((DateTime)o).ToString(DateTimeFormat);
Func<object, string> csvNullableDateEncoded = (o) => ((DateTime?)o).HasValue ? csvDateEncoded(o) : null;
Func<object, string> csvNullableDateTimeEncoded = (o) => ((DateTime?)o).HasValue ? csvDateTimeEncoded(o) : null;
var metadata = new Dictionary<string, List<Metadata>>();
// Device Flag
metadata.Add(nameof(DeviceFlagExportOptions.Id), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Id), typeof(string), r => r.Assignment.DeviceFlagId, csvToStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.Name), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Name), typeof(string), r => r.Assignment.DeviceFlag.Name, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.Description), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Description), typeof(string), r => r.Assignment.DeviceFlag.Description, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.Icon), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Icon), typeof(string), r => r.Assignment.DeviceFlag.Icon, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.IconColour), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.IconColour), typeof(string), r => r.Assignment.DeviceFlag.IconColour, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AssignmentId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignmentId), typeof(string), r => r.Assignment.Id, csvToStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AddedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AddedDate), typeof(string), r => r.Assignment.AddedDate, csvDateTimeEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AddedUserId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AddedUserId), typeof(string), r => r.Assignment.AddedUserId, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.RemovedUserId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.RemovedUserId), typeof(string), r => r.Assignment.RemovedUserId, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.RemovedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.RemovedDate), typeof(string), r => r.Assignment.RemovedDate, csvNullableDateTimeEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.Comments), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Comments), typeof(string), r => r.Assignment.Comments, csvStringEncoded) });
// Device
metadata.Add(nameof(DeviceFlagExportOptions.DeviceSerialNumber), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceSerialNumber), typeof(string), r => r.Assignment.Device.SerialNumber, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceAssetNumber), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceAssetNumber), typeof(string), r => r.Assignment.Device.AssetNumber, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceLocation), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceLocation), typeof(string), r => r.Assignment.Device.Location, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceComputerName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceComputerName), typeof(string), r => r.Assignment.Device.DeviceDomainId, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceLastNetworkLogon), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceLastNetworkLogon), typeof(DateTime), r => r.Assignment.Device.LastNetworkLogonDate, csvNullableDateTimeEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceCreatedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceCreatedDate), typeof(DateTime), r => r.Assignment.Device.CreatedDate, csvDateTimeEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceFirstEnrolledDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceFirstEnrolledDate), typeof(DateTime), r => r.Assignment.Device.EnrolledDate, csvNullableDateTimeEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceLastEnrolledDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceLastEnrolledDate), typeof(DateTime), r => r.Assignment.Device.LastEnrolDate, csvNullableDateTimeEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceAllowUnauthenticatedEnrol), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceAllowUnauthenticatedEnrol), typeof(bool), r => r.Assignment.Device.AllowUnauthenticatedEnrol, csvToStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceDecommissionedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceDecommissionedDate), typeof(DateTime), r => r.Assignment.Device.DecommissionedDate, csvNullableDateTimeEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.DeviceDecommissionedReason), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceDecommissionedReason), typeof(string), r => r.Assignment.Device.DecommissionReason, csvToStringEncoded) });
// Model
metadata.Add(nameof(DeviceFlagExportOptions.ModelId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelId), typeof(int), r => r.Assignment.Device.DeviceModel.Id, csvToStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.ModelDescription), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelDescription), typeof(string), r => r.Assignment.Device.DeviceModel.Description, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.ModelManufacturer), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelManufacturer), typeof(string), r => r.Assignment.Device.DeviceModel.Manufacturer, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.ModelModel), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelModel), typeof(string), r => r.Assignment.Device.DeviceModel.Model, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.ModelType), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelType), typeof(string), r => r.Assignment.Device.DeviceModel.ModelType, csvStringEncoded) });
// Batch
metadata.Add(nameof(DeviceFlagExportOptions.BatchId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchId), typeof(int), r => r.Assignment.Device.DeviceBatch?.Id, csvToStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchName), typeof(string), r => r.Assignment.Device.DeviceBatch?.Name, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchPurchaseDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchPurchaseDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.PurchaseDate, csvNullableDateEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchSupplier), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchSupplier), typeof(string), r => r.Assignment.Device.DeviceBatch?.Supplier, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchUnitCost), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchUnitCost), typeof(decimal), r => r.Assignment.Device.DeviceBatch?.UnitCost, csvCurrencyEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchWarrantyValidUntilDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchWarrantyValidUntilDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.WarrantyValidUntil, csvNullableDateEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchInsuredDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchInsuredDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.InsuredDate, csvNullableDateEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchInsuranceSupplier), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchInsuranceSupplier), typeof(string), r => r.Assignment.Device.DeviceBatch?.InsuranceSupplier, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.BatchInsuredUntilDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchInsuredUntilDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.InsuredUntil, csvNullableDateEncoded) });
// Profile
metadata.Add(nameof(DeviceFlagExportOptions.ProfileId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ProfileId), typeof(int), r => r.Assignment.Device.DeviceProfile?.Id, csvToStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.ProfileName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ProfileName), typeof(string), r => r.Assignment.Device.DeviceProfile?.Name, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.ProfileShortName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ProfileShortName), typeof(string), r => r.Assignment.Device.DeviceProfile?.ShortName, csvStringEncoded) });
// User
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserId), typeof(string), r => r.Assignment.Device?.AssignedUser?.UserId, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserDisplayName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserDisplayName), typeof(string), r => r.Assignment.Device?.AssignedUser?.DisplayName, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserSurname), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserSurname), typeof(string), r => r.Assignment.Device?.AssignedUser?.Surname, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserGivenName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserGivenName), typeof(string), r => r.Assignment.Device?.AssignedUser?.GivenName, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserPhoneNumber), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserPhoneNumber), typeof(string), r => r.Assignment.Device?.AssignedUser?.PhoneNumber, csvStringEncoded) });
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserEmailAddress), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserEmailAddress), typeof(string), r => r.Assignment.Device?.AssignedUser?.EmailAddress, csvStringEncoded) });
if (userDetailsCustomKeys != null)
{
var userDetailCustomFields = new List<Metadata>();
foreach (var detailKey in userDetailsCustomKeys.OrderBy(k => k, StringComparer.OrdinalIgnoreCase))
{
var key = detailKey;
userDetailCustomFields.Add(new Metadata(detailKey, detailKey, typeof(string), r => r.AssignedUserCustomDetails != null && r.AssignedUserCustomDetails.TryGetValue(key, out var value) ? value : null, csvStringEncoded));
}
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserDetailCustom), userDetailCustomFields);
}
return metadata;
}
}
}
@@ -0,0 +1,46 @@
using Disco.Data.Repository;
using Disco.Models.Services.Devices.DeviceFlag;
using Disco.Services.Exporting;
using Disco.Services.Tasks;
using Quartz;
namespace Disco.Services.Devices.DeviceFlags
{
public class DeviceFlagExportTask : ScheduledTask
{
private const string JobDataMapContext = "Context";
public override string TaskName { get; } = "Export Device Flags";
public override bool SingleInstanceTask { get { return false; } }
public override bool CancelInitiallySupported { get { return false; } }
public static ExportTaskContext<DeviceFlagExportOptions> ScheduleNow(DeviceFlagExportOptions options)
{
// Build Context
var context = new ExportTaskContext<DeviceFlagExportOptions>(options);
// Build Data Map
var task = new DeviceFlagExportTask();
JobDataMap taskData = new JobDataMap() { { JobDataMapContext, context } };
// Schedule Task
context.TaskStatus = task.ScheduleTask(taskData);
return context;
}
protected override void ExecuteTask()
{
var context = (ExportTaskContext<DeviceFlagExportOptions>)ExecutionContext.JobDetail.JobDataMap[JobDataMapContext];
Status.UpdateStatus(10, "Exporting Device Flag Records", "Starting...");
using (DiscoDataContext Database = new DiscoDataContext())
{
var export = new DeviceFlagExport(Database, context.Options);
context.Result = export.Generate(Status);
}
}
}
}
@@ -0,0 +1,196 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Expressions;
using Disco.Services.Logging;
using Disco.Services.Users;
using System;
using System.Collections;
using System.Data.Entity;
using System.Linq;
namespace Disco.Services
{
public static class DeviceFlagExtensions
{
#region Edit Comments
public static bool CanEditComments(this DeviceFlagAssignment fa)
{
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.EditFlags);
}
public static void OnEditComments(this DeviceFlagAssignment fa, string Comments)
{
if (!fa.CanEditComments())
throw new InvalidOperationException("Editing comments for device flags is denied");
fa.Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim();
}
#endregion
#region Remove
public static bool CanRemove(this DeviceFlagAssignment fa)
{
if (fa.RemovedDate.HasValue)
return false;
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.RemoveFlags);
}
public static void OnRemove(this DeviceFlagAssignment fa, DiscoDataContext Database, User RemovingUser)
{
if (!fa.CanRemove())
throw new InvalidOperationException("Removing device flags is denied");
fa.OnRemoveUnsafe(Database, RemovingUser);
}
public static void OnRemoveUnsafe(this DeviceFlagAssignment fa, DiscoDataContext Database, User RemovingUser)
{
fa = Database.DeviceFlagAssignments
.Include(a => a.DeviceFlag)
.First(a => a.Id == fa.Id);
RemovingUser = Database.Users.First(u => u.UserId == RemovingUser.UserId);
fa.RemovedDate = DateTime.Now;
fa.RemovedUserId = RemovingUser.UserId;
if (!string.IsNullOrWhiteSpace(fa.DeviceFlag.OnUnassignmentExpression))
{
try
{
Database.SaveChanges();
var expressionResult = fa.EvaluateOnUnassignmentExpression(Database, RemovingUser, fa.AddedDate);
if (!string.IsNullOrWhiteSpace(expressionResult))
{
fa.OnUnassignmentExpressionResult = expressionResult;
Database.SaveChanges();
}
}
catch (Exception ex)
{
SystemLog.LogException("Device Flag Expression - OnUnassignmentExpression", ex);
}
}
}
#endregion
#region Add
public static bool CanAddDeviceFlags(this Device d)
{
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.AddFlags);
}
public static bool CanAddDeviceFlag(this Device d, DeviceFlag flag)
{
// Shortcut
if (!d.CanAddDeviceFlags())
return false;
// Already has Device Flag?
if (d.DeviceFlagAssignments.Any(fa => !fa.RemovedDate.HasValue && fa.DeviceFlagId == flag.Id))
return false;
return true;
}
public static DeviceFlagAssignment OnAddDeviceFlag(this Device d, DiscoDataContext Database, DeviceFlag flag, User AddingUser, string Comments)
{
if (!d.CanAddDeviceFlag(flag))
throw new InvalidOperationException("Adding device flag is denied");
return d.OnAddDeviceFlagUnsafe(Database, flag, AddingUser, Comments);
}
public static DeviceFlagAssignment OnAddDeviceFlagUnsafe(this Device d, DiscoDataContext Database, DeviceFlag flag, User AddingUser, string Comments)
{
flag = Database.DeviceFlags.First(f => f.Id == flag.Id);
d = Database.Devices.First(de => de.SerialNumber == d.SerialNumber);
AddingUser = Database.Users.First(user => user.UserId == AddingUser.UserId);
var fa = new DeviceFlagAssignment()
{
DeviceFlag = flag,
Device = d,
AddedDate = DateTime.Now,
AddedUser = AddingUser,
AddedUserId = AddingUser.UserId,
Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim()
};
Database.DeviceFlagAssignments.Add(fa);
if (!string.IsNullOrWhiteSpace(flag.OnAssignmentExpression))
{
try
{
Database.SaveChanges();
var expressionResult = fa.EvaluateOnAssignmentExpression(Database, AddingUser, fa.AddedDate);
if (!string.IsNullOrWhiteSpace(expressionResult))
{
fa.OnAssignmentExpressionResult = expressionResult;
Database.SaveChanges();
}
}
catch (Exception ex)
{
SystemLog.LogException("Device Flag Expression - OnAssignmentExpression", ex);
}
}
return fa;
}
#endregion
#region Expressions
public static Expression OnAssignmentExpressionFromCache(this DeviceFlag uf)
{
return ExpressionCache.GetOrCreateSingleExpressions($"DeviceFlag_OnAssignmentExpression_{uf.Id}", () => Expression.TokenizeSingleDynamic(null, uf.OnAssignmentExpression, 0));
}
public static void OnAssignmentExpressionInvalidateCache(this DeviceFlag uf)
{
ExpressionCache.InvalidateSingleCache($"DeviceFlag_OnAssignmentExpression_{uf.Id}");
}
public static string EvaluateOnAssignmentExpression(this DeviceFlagAssignment dfa, DiscoDataContext Database, User AddingUser, DateTime TimeStamp)
{
if (!string.IsNullOrEmpty(dfa.DeviceFlag.OnAssignmentExpression))
{
Expression compiledExpression = dfa.DeviceFlag.OnAssignmentExpressionFromCache();
IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, AddingUser, TimeStamp, null, dfa.Device);
object result = compiledExpression.EvaluateFirst<object>(dfa, evaluatorVariables);
if (result == null)
return null;
else
return result.ToString();
}
return null;
}
public static Expression OnUnassignmentExpressionFromCache(this DeviceFlag df)
{
return ExpressionCache.GetOrCreateSingleExpressions($"DeviceFlag_OnUnassignmentExpression_{df.Id}", () => Expression.TokenizeSingleDynamic(null, df.OnUnassignmentExpression, 0));
}
public static void OnUnassignmentExpressionInvalidateCache(this DeviceFlag df)
{
ExpressionCache.InvalidateSingleCache($"DeviceFlag_OnUnassignmentExpression_{df.Id}");
}
public static string EvaluateOnUnassignmentExpression(this DeviceFlagAssignment dfa, DiscoDataContext Database, User RemovingUser, DateTime TimeStamp)
{
if (!string.IsNullOrEmpty(dfa.DeviceFlag.OnUnassignmentExpression))
{
Expression compiledExpression = dfa.DeviceFlag.OnUnassignmentExpressionFromCache();
IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, RemovingUser, TimeStamp, null, dfa.Device);
object result = compiledExpression.EvaluateFirst<object>(dfa, evaluatorVariables);
if (result == null)
return null;
else
return result.ToString();
}
return null;
}
#endregion
}
}
@@ -0,0 +1,241 @@
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.Generic;
using System.Data.Entity;
using System.Linq;
using System.Reactive.Linq;
namespace Disco.Services.Devices.DeviceFlags
{
public static class DeviceFlagService
{
private static Cache _cache;
internal static Lazy<IObservable<RepositoryMonitorEvent>> DeviceFlagAssignmentRepositoryEvents;
static DeviceFlagService()
{
// Statically defined (lazy) Assignment Repository Definition
DeviceFlagAssignmentRepositoryEvents =
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
RepositoryMonitor.StreamAfterCommit.Where(e =>
e.EntityType == typeof(DeviceFlagAssignment) &&
(e.EventType != RepositoryMonitorEventType.Modified ||
e.ModifiedProperties.Contains(nameof(DeviceFlagAssignment.RemovedDate)))
)
);
}
public static void Initialize(DiscoDataContext database)
{
_cache = new Cache(database);
// Initialize Managed Groups (if configured)
_cache.GetDeviceFlags().ForEach(uf =>
{
DeviceFlagDevicesManagedGroup.Initialize(uf);
DeviceFlagDeviceAssignedUsersManagedGroup.Initialize(uf);
});
}
public static List<DeviceFlag> GetDeviceFlags() { return _cache.GetDeviceFlags(); }
public static DeviceFlag GetDeviceFlag(int deviceFlagId) { return _cache.GetDeviceFlag(deviceFlagId); }
#region Device Flag Maintenance
public static DeviceFlag CreateDeviceFlag(DiscoDataContext database, DeviceFlag deviceFlag)
{
// Verify
if (string.IsNullOrWhiteSpace(deviceFlag.Name))
throw new ArgumentException("The Device Flag Name is required", nameof(deviceFlag));
// Name Unique
if (_cache.GetDeviceFlags().Any(f => f.Name == deviceFlag.Name))
throw new ArgumentException("Another Device Flag already exists with that name", nameof(deviceFlag));
// Clone to break reference
var flag = new DeviceFlag()
{
Name = deviceFlag.Name,
Description = deviceFlag.Description,
Icon = deviceFlag.Icon,
IconColour = deviceFlag.IconColour,
DevicesLinkedGroup = deviceFlag.DevicesLinkedGroup,
DeviceUsersLinkedGroup = deviceFlag.DeviceUsersLinkedGroup,
};
database.DeviceFlags.Add(flag);
database.SaveChanges();
_cache.AddOrUpdate(flag);
return flag;
}
public static DeviceFlag Update(DiscoDataContext database, DeviceFlag deviceFlag)
{
// Verify
if (string.IsNullOrWhiteSpace(deviceFlag.Name))
throw new ArgumentException("The Device Flag Name is required", nameof(deviceFlag));
// Name Unique
if (_cache.GetDeviceFlags().Any(f => f.Id != deviceFlag.Id && f.Name == deviceFlag.Name))
throw new ArgumentException("Another Device Flag already exists with that name", nameof(deviceFlag));
database.SaveChanges();
_cache.AddOrUpdate(deviceFlag);
DeviceFlagDevicesManagedGroup.Initialize(deviceFlag);
DeviceFlagDeviceAssignedUsersManagedGroup.Initialize(deviceFlag);
return deviceFlag;
}
public static void DeleteDeviceFlag(DiscoDataContext database, int deviceFlagId, IScheduledTaskStatus status)
{
var flag = database.DeviceFlags.Find(deviceFlagId);
// Dispose of AD Managed Groups
Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(DeviceFlagDeviceAssignedUsersManagedGroup.GetKey(flag));
Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(DeviceFlagDevicesManagedGroup.GetKey(flag));
// Delete Assignments
status.UpdateStatus(0, $"Removing '{flag.Name}' [{flag.Id}] Device Flag", "Starting");
var flagAssignments = database.DeviceFlagAssignments.Where(fa => fa.DeviceFlagId == flag.Id).ToList();
if (flagAssignments.Count > 0)
{
status.UpdateStatus(20, "Removing flag from devices");
flagAssignments.ForEach(flagAssignment => database.DeviceFlagAssignments.Remove(flagAssignment));
database.SaveChanges();
}
// Delete Flag
status.UpdateStatus(90, "Deleting Device Flag");
database.DeviceFlags.Remove(flag);
database.SaveChanges();
// Remove from Cache
_cache.Remove(deviceFlagId);
status.Finished($"Successfully Deleted Device Flag: '{flag.Name}' [{flag.Id}]");
}
#endregion
#region Bulk Assignment
public static IEnumerable<DeviceFlagAssignment> BulkAssignAddDevices(DiscoDataContext database, DeviceFlag deviceFlag, User technician, string comments, List<Device> devices, IScheduledTaskStatus status)
{
if (devices.Count > 0)
{
double progressInterval;
const int databaseChunkSize = 100;
comments = string.IsNullOrWhiteSpace(comments) ? null : comments.Trim();
var addDevices = devices.Where(d => !d.DeviceFlagAssignments.Any(a => a.DeviceFlagId == deviceFlag.Id && !a.RemovedDate.HasValue)).ToList();
progressInterval = (double)100 / addDevices.Count;
var addedDeviceAssignments = addDevices.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) =>
{
var chunkIndexOffset = databaseChunkSize * chunkIndex;
var chunkResults = chunk.Select((device, index) =>
{
status.UpdateStatus((chunkIndexOffset + index) * progressInterval, $"Assigning Flag: {device}");
return device.OnAddDeviceFlag(database, deviceFlag, technician, comments);
}).ToList();
// Save Chunk Items to Database
database.SaveChanges();
return chunkResults;
}).Where(fa => fa != null).ToList();
status.SetFinishedMessage($"{addDevices.Count} Devices/s Added; {(devices.Count - addDevices.Count)} Devices/s Skipped");
return addedDeviceAssignments;
}
else
{
status.SetFinishedMessage("No changes found");
return Enumerable.Empty<DeviceFlagAssignment>();
}
}
public static IEnumerable<DeviceFlagAssignment> BulkAssignOverrideDevices(DiscoDataContext database, DeviceFlag deviceFlag, User technician, string comments, List<Device> devices, IScheduledTaskStatus status)
{
double progressInterval;
const int databaseChunkSize = 100;
comments = string.IsNullOrWhiteSpace(comments) ? null : comments.Trim();
status.UpdateStatus(0, "Calculating assignment changes");
var currentAssignments = database.DeviceFlagAssignments.Include(fa => fa.Device).Where(a => a.DeviceFlagId == deviceFlag.Id && !a.RemovedDate.HasValue).ToList();
var removeAssignments = currentAssignments.Where(ca => !devices.Any(d => d.SerialNumber.Equals(ca.DeviceSerialNumber, StringComparison.OrdinalIgnoreCase))).ToList();
var addAssignments = devices.Where(d => !currentAssignments.Any(ca => ca.DeviceSerialNumber.Equals(d.SerialNumber, StringComparison.OrdinalIgnoreCase))).ToList();
if (removeAssignments.Count > 0 || addAssignments.Count > 0)
{
progressInterval = (double)100 / (removeAssignments.Count + addAssignments.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, $"Removing Flag: {flagAssignment.Device}");
flagAssignment.OnRemoveUnsafe(database, technician);
return flagAssignment;
}).ToList();
// Save Chunk Items to Database
database.SaveChanges();
return chunkResults;
}).ToList();
// Add Assignments
var addedAssignments = addAssignments.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) =>
{
var chunkIndexOffset = (chunkIndex * databaseChunkSize) + removeAssignments.Count;
var chunkResults = chunk.Select((device, index) =>
{
status.UpdateStatus((chunkIndexOffset + index) * progressInterval, $"Assigning Flag: {device}");
return device.OnAddDeviceFlag(database, deviceFlag, technician, comments);
}).ToList();
// Save Chunk Items to Database
database.SaveChanges();
return chunkResults;
}).ToList();
status.SetFinishedMessage($"{addAssignments.Count} Devices/s Added; {removeAssignments.Count} Devices/s Removed; {(devices.Count - addAssignments.Count)} Devices/s Skipped");
return addedAssignments;
}
else
{
status.SetFinishedMessage("No changes found");
return Enumerable.Empty<DeviceFlagAssignment>();
}
}
#endregion
public static string RandomUnusedIcon()
{
return UIHelpers.RandomIcon(_cache.GetDeviceFlags().Select(f => f.Icon));
}
public static string RandomUnusedThemeColour()
{
return UIHelpers.RandomThemeColour(_cache.GetDeviceFlags().Select(f => f.IconColour));
}
}
}
@@ -0,0 +1,84 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Tasks;
using Quartz;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace Disco.Services.Devices.DeviceFlags
{
public class DeviceFlagBulkAssignTask : ScheduledTask
{
public override string TaskName { get { return "Device Flags - Bulk Assign Devices"; } }
public override bool SingleInstanceTask { get { return false; } }
public override bool CancelInitiallySupported { get { return false; } }
public override bool LogExceptionsOnly { get { return true; } }
protected override void ExecuteTask()
{
int deviceFlagId = (int)ExecutionContext.JobDetail.JobDataMap["DeviceFlagId"];
string technicianUserId = (string)ExecutionContext.JobDetail.JobDataMap["TechnicianUserId"];
string comments = (string)ExecutionContext.JobDetail.JobDataMap["Comments"];
List<string> deviceSerialNumbers = (List<string>)ExecutionContext.JobDetail.JobDataMap["DeviceSerialNumbers"];
bool @override = (bool)ExecutionContext.JobDetail.JobDataMap["Override"];
using (DiscoDataContext Database = new DiscoDataContext())
{
// Load Flag
var flag = Database.DeviceFlags.FirstOrDefault(uf => uf.Id == deviceFlagId);
if (flag == null)
throw new Exception("Invalid Device Flag Id");
Status.UpdateStatus(0, string.Format("Bulk Assigning Devices to Device Flag: {0}", flag.Name), "Preparing to start");
// Load Technician
var technician = Database.Users.FirstOrDefault(user => user.UserId == technicianUserId);
if (technician == null)
throw new Exception("Invalid Technician User Id");
// Parse Devices
Status.UpdateStatus(10, "Loading devices from the database");
var devices = Database.Devices
.Include(d => d.DeviceFlagAssignments)
.Where(d => deviceSerialNumbers.Contains(d.SerialNumber)).ToList();
var missingDevices = deviceSerialNumbers.Where(sn => !devices.Any(u => string.Equals(sn, u.SerialNumber, StringComparison.OrdinalIgnoreCase))).ToList();
if (missingDevices.Count > 0)
{
throw new InvalidOperationException(string.Format("Bulk assignment aborted, invalid Serial Numbers: {0}", string.Join(", ", missingDevices)));
}
devices = devices.OrderBy(d => d.SerialNumber).ToList();
Status.ProgressOffset = 50;
Status.ProgressMultiplier = 0.5;
if (@override)
{
DeviceFlagService.BulkAssignOverrideDevices(Database, flag, technician, comments, devices, Status);
}
else
{
DeviceFlagService.BulkAssignAddDevices(Database, flag, technician, comments, devices, Status);
}
}
}
public static ScheduledTaskStatus ScheduleBulkAssignDevices(DeviceFlag deviceFlag, User technician, string comments, List<string> deviceSerialNumbers, bool @override)
{
JobDataMap taskData = new JobDataMap() {
{"DeviceFlagId", deviceFlag.Id },
{"TechnicianUserId", technician.UserId },
{"Comments", comments },
{"DeviceSerialNumbers", deviceSerialNumbers },
{"Override", @override }
};
var instance = new DeviceFlagBulkAssignTask();
return instance.ScheduleTask(taskData);
}
}
}
@@ -0,0 +1,34 @@
using Disco.Data.Repository;
using Disco.Services.Tasks;
using Quartz;
namespace Disco.Services.Devices.DeviceFlags
{
public class DeviceFlagDeleteTask : ScheduledTask
{
public override string TaskName { get { return "Device Flags - Delete Flag"; } }
public override bool SingleInstanceTask { get { return false; }}
public override bool CancelInitiallySupported { get { return false; } }
public override bool LogExceptionsOnly { get { return true; } }
protected override void ExecuteTask()
{
int deviceFlagId = (int)ExecutionContext.JobDetail.JobDataMap["DeviceFlagId"];
using (DiscoDataContext Database = new DiscoDataContext())
{
DeviceFlagService.DeleteDeviceFlag(Database, deviceFlagId, Status);
}
}
public static ScheduledTaskStatus ScheduleNow(int deviceFlagId)
{
var taskData = new JobDataMap() { { "DeviceFlagId", deviceFlagId } };
var instance = new DeviceFlagDeleteTask();
return instance.ScheduleTask(taskData);
}
}
}
@@ -61,10 +61,25 @@ namespace Disco.Services.Interop.ActiveDirectory
return false;
}
public bool TryGetValue(string Key, out ADManagedGroup ManagedGroup)
{
return managedGroups.TryGetValue(Key, out ManagedGroup);
}
public bool TryGetValue<T>(string key, out T managedGroup) where T : ADManagedGroup
{
if (managedGroups.TryGetValue(key, out var item) && item is T typedItem)
{
managedGroup = typedItem;
return true;
}
else
{
managedGroup = null;
return false;
}
}
public List<ADManagedGroup> Values
{
get