initial source commit
@@ -0,0 +1,96 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using iTextSharp.text.pdf;
|
||||||
|
|
||||||
|
namespace Disco.BI.AttachmentBI
|
||||||
|
{
|
||||||
|
public static class Utilities
|
||||||
|
{
|
||||||
|
|
||||||
|
public static bool GenerateThumbnail(Stream Source, string SourceMimeType, Stream OutStream)
|
||||||
|
{
|
||||||
|
if (Source != null)
|
||||||
|
{
|
||||||
|
// GDI+ (jpg, png, gif, bmp)
|
||||||
|
if (SourceMimeType.Equals("image/jpeg", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("jpg") ||
|
||||||
|
SourceMimeType.Equals("image/png", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("png") ||
|
||||||
|
SourceMimeType.Equals("image/gif", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("gif") ||
|
||||||
|
SourceMimeType.Equals("image/bmp", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("bmp"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (Image sourceImage = Image.FromStream(Source))
|
||||||
|
{
|
||||||
|
using (Image thumbImage = sourceImage.ResizeImage(48, 48))
|
||||||
|
{
|
||||||
|
using (Image mimeTypeIcon = Disco.Properties.Resources.MimeType_img16)
|
||||||
|
thumbImage.EmbedIconOverlay(mimeTypeIcon);
|
||||||
|
thumbImage.SaveJpg(90, OutStream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Ignore Thumbnail Generation exceptions for images
|
||||||
|
//throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF
|
||||||
|
if (SourceMimeType.Equals("application/pdf", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("pdf"))
|
||||||
|
{
|
||||||
|
PdfReader pdfReader = new PdfReader(Source);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (DisposableImageCollection pdfPageImages = pdfReader.PdfPageImages(1))
|
||||||
|
{
|
||||||
|
if (pdfPageImages.Count() > 0)
|
||||||
|
{
|
||||||
|
// Find Biggest Image on Page
|
||||||
|
Image biggestImage = pdfPageImages.OrderByDescending(i => i.Height * i.Width).First();
|
||||||
|
using (Image thumbImage = biggestImage.ResizeImage(48, 48, Brushes.White))
|
||||||
|
{
|
||||||
|
using (Image mimeTypeIcon = Disco.Properties.Resources.MimeType_pdf16)
|
||||||
|
thumbImage.EmbedIconOverlay(mimeTypeIcon);
|
||||||
|
thumbImage.SaveJpg(90, OutStream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (pdfReader != null)
|
||||||
|
pdfReader.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool GenerateThumbnail(string SourceFilename, string SourceMimeType, string DestinationFilename)
|
||||||
|
{
|
||||||
|
using (FileStream sourceStream = new FileStream(SourceFilename, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
return GenerateThumbnail(sourceStream, SourceMimeType, DestinationFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static bool GenerateThumbnail(Stream Source, string SourceMimeType, string DestinationFilename)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
using (FileStream destinationStream = new FileStream(DestinationFilename, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
result = GenerateThumbnail(Source, SourceMimeType, destinationStream);
|
||||||
|
}
|
||||||
|
if (!result && File.Exists(DestinationFilename))
|
||||||
|
File.Delete(DestinationFilename);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Data.Configuration;
|
||||||
|
|
||||||
|
namespace Disco.BI
|
||||||
|
{
|
||||||
|
public static class DataStore
|
||||||
|
{
|
||||||
|
|
||||||
|
public static string CreateLocation(DiscoDataContext dbContext, string SubLocation, DateTime? SubSubLocationTimestamp = null)
|
||||||
|
{
|
||||||
|
return CreateLocation(dbContext.DiscoConfiguration, SubLocation, SubSubLocationTimestamp);
|
||||||
|
}
|
||||||
|
public static string CreateLocation(ConfigurationContext DiscoConfiguration, string SubLocation, DateTime? SubSubLocationTimestamp = null)
|
||||||
|
{
|
||||||
|
string SubSubLocation = string.Empty;
|
||||||
|
if (SubSubLocationTimestamp.HasValue)
|
||||||
|
SubSubLocation = SubSubLocationTimestamp.Value.ToString(@"yyyy\\MM");
|
||||||
|
|
||||||
|
string storeDirectory = System.IO.Path.Combine(DiscoConfiguration.DataStoreLocation, SubLocation, SubSubLocation);
|
||||||
|
if (!System.IO.Directory.Exists(storeDirectory))
|
||||||
|
System.IO.Directory.CreateDirectory(storeDirectory);
|
||||||
|
|
||||||
|
return storeDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.DeviceBI
|
||||||
|
{
|
||||||
|
public static class BatchUtilities
|
||||||
|
{
|
||||||
|
public static DeviceBatch DefaultNewDeviceBatch(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return new DeviceBatch()
|
||||||
|
{
|
||||||
|
PurchaseDate = DateTime.Today
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,621 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Disco.BI.Interop.ActiveDirectory;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using Disco.Data.Configuration.Modules;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.ClientServices;
|
||||||
|
using Disco.Models.Interop.ActiveDirectory;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Tamir.SharpSsh;
|
||||||
|
using Disco.Services.Plugins;
|
||||||
|
using Disco.Services.Plugins.Features.CertificateProvider;
|
||||||
|
|
||||||
|
namespace Disco.BI.DeviceBI
|
||||||
|
{
|
||||||
|
public class DeviceEnrol
|
||||||
|
{
|
||||||
|
public enum EnrolmentTypes
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Mac = 5,
|
||||||
|
MacSecure
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Regex SshPromptRegEx = new Regex("[\\$,\\#]", RegexOptions.Multiline);
|
||||||
|
public static MacSecureEnrolResponse MacSecureEnrol(DiscoDataContext dbContext, string Host)
|
||||||
|
{
|
||||||
|
MacEnrol trustedRequest = new MacEnrol();
|
||||||
|
string sessionId = System.Guid.NewGuid().ToString("B");
|
||||||
|
MacSecureEnrolResponse MacSecureEnrol;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionStarting(sessionId, Host, EnrolmentTypes.MacSecure);
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 0, string.Format("Connecting to '{0}' as '{1}'", Host, dbContext.DiscoConfiguration.Bootstrapper.MacSshUsername));
|
||||||
|
SshShell shell = new SshShell(Host, dbContext.DiscoConfiguration.Bootstrapper.MacSshUsername, dbContext.DiscoConfiguration.Bootstrapper.MacSshPassword);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shell.ExpectPattern = "#";
|
||||||
|
shell.Connect();
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 10, "Connected, Authenticating");
|
||||||
|
var output = shell.Expect(SshPromptRegEx);
|
||||||
|
bool sessionElevated = false;
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
||||||
|
if (!output.TrimEnd(new char[0]).EndsWith("#"))
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 22, "Connected, Elevating Credentials");
|
||||||
|
shell.WriteLine("sudo -k");
|
||||||
|
System.Threading.Thread.Sleep(250);
|
||||||
|
output = shell.Expect(SshPromptRegEx);
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 25, "Connected, Elevating Credentials");
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
||||||
|
shell.WriteLine("sudo -s -S");
|
||||||
|
System.Threading.Thread.Sleep(250);
|
||||||
|
output = shell.Expect(":");
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 27, "Connected, Elevating Credentials");
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
||||||
|
shell.WriteLine(dbContext.DiscoConfiguration.Bootstrapper.MacSshPassword);
|
||||||
|
System.Threading.Thread.Sleep(250);
|
||||||
|
output = shell.Expect(SshPromptRegEx);
|
||||||
|
sessionElevated = true;
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
||||||
|
}
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 20, "Retrieving Serial Number");
|
||||||
|
trustedRequest.DeviceSerialNumber = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Serial Number\" | cut -d \":\" -f 2-", sessionId);
|
||||||
|
EnrolmentLog.LogSessionDevice(sessionId, trustedRequest.DeviceSerialNumber, null);
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 30, "Retrieving Hardware UUID");
|
||||||
|
trustedRequest.DeviceUUID = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Hardware UUID:\" | cut -d \":\" -f 2-", sessionId);
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 40, "Retrieving Computer Name");
|
||||||
|
trustedRequest.DeviceComputerName = ParseMacShellCommand(shell, "scutil --get ComputerName", sessionId);
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 50, "Retrieving Ethernet MAC Address");
|
||||||
|
string lanNicId = ParseMacShellCommand(shell, "system_profiler SPEthernetDataType | egrep -o \"en0|en1|en2|en3|en4|en5|en6\"", sessionId);
|
||||||
|
if (!string.IsNullOrWhiteSpace(lanNicId))
|
||||||
|
{
|
||||||
|
trustedRequest.DeviceLanMacAddress = ParseMacShellCommand(shell, string.Format("ifconfig {0} | grep ether | cut -d \" \" -f 2-", lanNicId), sessionId);
|
||||||
|
}
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 65, "Retrieving Wireless MAC Address");
|
||||||
|
string wlanNicId = ParseMacShellCommand(shell, "system_profiler SPAirPortDataType | egrep -o \"en0|en1|en2|en3|en4|en5|en6\"", sessionId);
|
||||||
|
if (!string.IsNullOrWhiteSpace(wlanNicId))
|
||||||
|
{
|
||||||
|
trustedRequest.DeviceWlanMacAddress = ParseMacShellCommand(shell, string.Format("ifconfig {0} | grep ether | cut -d \" \" -f 2-", wlanNicId), sessionId);
|
||||||
|
}
|
||||||
|
trustedRequest.DeviceManufacturer = "Apple Inc.";
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Model");
|
||||||
|
trustedRequest.DeviceModel = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Model Identifier:\" | cut -d \":\" -f 2-", sessionId);
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 90, "Retrieving Model Type");
|
||||||
|
trustedRequest.DeviceModelType = ParseMacModelType(ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Model Name:\" | cut -d \":\" -f 2-", sessionId));
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 99, "Disconnecting");
|
||||||
|
output = ParseMacModelType(ParseMacShellCommand(shell, "exit", sessionId));
|
||||||
|
if (sessionElevated)
|
||||||
|
{
|
||||||
|
output = ParseMacModelType(ParseMacShellCommand(shell, "exit", sessionId));
|
||||||
|
}
|
||||||
|
if (shell.Connected)
|
||||||
|
{
|
||||||
|
shell.Close();
|
||||||
|
}
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 100, "Disconnected, Starting Disco Enrolment");
|
||||||
|
MacSecureEnrolResponse response = MacSecureEnrolResponse.FromMacEnrolResponse(MacEnrol(dbContext, trustedRequest, true, sessionId));
|
||||||
|
EnrolmentLog.LogSessionFinished(sessionId);
|
||||||
|
MacSecureEnrol = response;
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (shell != null)
|
||||||
|
{
|
||||||
|
bool connected = shell.Connected;
|
||||||
|
if (connected)
|
||||||
|
{
|
||||||
|
shell.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionError(sessionId, ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
return MacSecureEnrol;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region "Mac Enrol Helpers"
|
||||||
|
|
||||||
|
private static string ParseMacModelType(string ModelName)
|
||||||
|
{
|
||||||
|
string ParseMacModelType;
|
||||||
|
if (!string.IsNullOrWhiteSpace(ModelName))
|
||||||
|
{
|
||||||
|
string mn = ModelName.ToLower();
|
||||||
|
if (mn.Contains("imac") || mn.Contains("mini"))
|
||||||
|
{
|
||||||
|
ParseMacModelType = "Desktop";
|
||||||
|
return ParseMacModelType;
|
||||||
|
}
|
||||||
|
if (mn.Contains("macbook"))
|
||||||
|
{
|
||||||
|
ParseMacModelType = "Mobile";
|
||||||
|
return ParseMacModelType;
|
||||||
|
}
|
||||||
|
if (mn.Contains("xserve"))
|
||||||
|
{
|
||||||
|
ParseMacModelType = "Server";
|
||||||
|
return ParseMacModelType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseMacModelType = "Unknown";
|
||||||
|
return ParseMacModelType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ParseMacShellCommand(SshShell Shell, string Command, string LogSessionId)
|
||||||
|
{
|
||||||
|
Shell.WriteLine(Command);
|
||||||
|
System.Threading.Thread.Sleep(250);
|
||||||
|
string Response = Shell.Expect(SshPromptRegEx);
|
||||||
|
Response = Response.Replace("\r", string.Empty);
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(LogSessionId, Response);
|
||||||
|
bool flag = Response.Contains("\n");
|
||||||
|
string ParseMacShellCommand;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
string[] ResponseLines = Response.Split(new char[]
|
||||||
|
{
|
||||||
|
'\n'
|
||||||
|
});
|
||||||
|
switch (ResponseLines.Length)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
ParseMacShellCommand = string.Empty;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
ParseMacShellCommand = ResponseLines[1].Trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder ResponseBuilder = new System.Text.StringBuilder();
|
||||||
|
int num = ResponseLines.Length - 2;
|
||||||
|
int lineIndex = 1;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int arg_111_0 = lineIndex;
|
||||||
|
int num2 = num;
|
||||||
|
if (arg_111_0 > num2)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ResponseBuilder.AppendLine(ResponseLines[lineIndex]);
|
||||||
|
lineIndex++;
|
||||||
|
}
|
||||||
|
ParseMacShellCommand = ResponseBuilder.ToString().Trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParseMacShellCommand = Response;
|
||||||
|
}
|
||||||
|
return ParseMacShellCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static MacEnrolResponse MacEnrol(DiscoDataContext dbContext, MacEnrol Request, bool Trusted, string OpenSessionId = null)
|
||||||
|
{
|
||||||
|
string sessionId;
|
||||||
|
if (OpenSessionId == null)
|
||||||
|
{
|
||||||
|
sessionId = System.Guid.NewGuid().ToString("B");
|
||||||
|
EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Mac);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sessionId = OpenSessionId;
|
||||||
|
}
|
||||||
|
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
|
||||||
|
MacEnrolResponse response = new MacEnrolResponse();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 10, "Querying Database");
|
||||||
|
Device RepoDevice = dbContext.Devices.Include("AssignedUser").Include("DeviceProfile").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault();
|
||||||
|
if (!Trusted)
|
||||||
|
{
|
||||||
|
if (RepoDevice == null)
|
||||||
|
throw new EnrolSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber));
|
||||||
|
if (!RepoDevice.AllowUnauthenticatedEnrol)
|
||||||
|
throw new EnrolSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber));
|
||||||
|
}
|
||||||
|
if (RepoDevice == null)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 50, "New Device, Building Disco Instance");
|
||||||
|
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.DeviceSerialNumber);
|
||||||
|
DeviceProfile deviceProfile = dbContext.DeviceProfiles.Find(dbContext.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
|
||||||
|
DeviceModel deviceModel = dbContext.DeviceModels.Where(dm => dm.Manufacturer == Request.DeviceManufacturer.Trim() && dm.Model == Request.DeviceModel.Trim()).FirstOrDefault();
|
||||||
|
if (deviceModel == null)
|
||||||
|
{
|
||||||
|
deviceModel = new DeviceModel
|
||||||
|
{
|
||||||
|
Manufacturer = Request.DeviceManufacturer.Trim(),
|
||||||
|
Model = Request.DeviceModel.Trim(),
|
||||||
|
ModelType = Request.DeviceModelType.Trim(),
|
||||||
|
Description = string.Format("{0} {1}", Request.DeviceManufacturer.Trim(), Request.DeviceModel)
|
||||||
|
};
|
||||||
|
dbContext.DeviceModels.Add(deviceModel);
|
||||||
|
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
|
||||||
|
}
|
||||||
|
RepoDevice = new Device
|
||||||
|
{
|
||||||
|
SerialNumber = Request.DeviceSerialNumber,
|
||||||
|
ComputerName = Request.DeviceComputerName,
|
||||||
|
DeviceProfile = deviceProfile,
|
||||||
|
DeviceModel = deviceModel,
|
||||||
|
AllowUnauthenticatedEnrol = false,
|
||||||
|
Active = true,
|
||||||
|
CreatedDate = DateTime.Now,
|
||||||
|
EnrolledDate = DateTime.Now
|
||||||
|
};
|
||||||
|
dbContext.Devices.Add(RepoDevice);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 50, "Existing Device, Updating Disco Instance");
|
||||||
|
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.DeviceSerialNumber);
|
||||||
|
if (!RepoDevice.DeviceModelId.HasValue || RepoDevice.DeviceModelId.Value == 1)
|
||||||
|
{
|
||||||
|
DeviceModel deviceModel = dbContext.DeviceModels.Where(dm => dm.Manufacturer == Request.DeviceManufacturer.Trim() && dm.Model == Request.DeviceModel.Trim()).FirstOrDefault();
|
||||||
|
if (deviceModel == null)
|
||||||
|
{
|
||||||
|
deviceModel = new DeviceModel
|
||||||
|
{
|
||||||
|
Manufacturer = Request.DeviceManufacturer.Trim(),
|
||||||
|
Model = Request.DeviceModel.Trim(),
|
||||||
|
ModelType = Request.DeviceModelType.Trim(),
|
||||||
|
Description = string.Format("{0} {1}", Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim())
|
||||||
|
};
|
||||||
|
dbContext.DeviceModels.Add(deviceModel);
|
||||||
|
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
|
||||||
|
}
|
||||||
|
RepoDevice.DeviceModel = deviceModel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, RepoDevice.DeviceModelId);
|
||||||
|
}
|
||||||
|
RepoDevice.ComputerName = Request.DeviceComputerName;
|
||||||
|
if (!RepoDevice.EnrolledDate.HasValue)
|
||||||
|
{
|
||||||
|
RepoDevice.EnrolledDate = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RepoDevice.LastEnrolDate = DateTime.Now;
|
||||||
|
RepoDevice.AllowUnauthenticatedEnrol = false;
|
||||||
|
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
|
||||||
|
//DeviceProfileConfiguration RepoDeviceProfileContext = RepoDevice.DeviceProfile.Configuration(Context);
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 90, "Building Response");
|
||||||
|
//if (RepoDeviceProfileContext.DistributionType == DeviceProfileConfiguration.DeviceProfileDistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
|
||||||
|
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
|
||||||
|
{
|
||||||
|
ActiveDirectoryUserAccount AssignedUserInfo = ActiveDirectory.GetUserAccount(RepoDevice.AssignedUser.Id);
|
||||||
|
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.sAMAccountName, AssignedUserInfo.Domain, AssignedUserInfo.ObjectSid);
|
||||||
|
response.DeviceAssignedUserUsername = AssignedUserInfo.sAMAccountName;
|
||||||
|
response.DeviceAssignedUserDomain = AssignedUserInfo.Domain;
|
||||||
|
response.DeviceAssignedUserName = AssignedUserInfo.DisplayName;
|
||||||
|
response.DeviceAssignedUserSID = AssignedUserInfo.ObjectSid;
|
||||||
|
}
|
||||||
|
response.DeviceComputerName = RepoDevice.ComputerName;
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
|
||||||
|
}
|
||||||
|
catch (EnrolSafeException ex)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionError(sessionId, ex);
|
||||||
|
return new MacEnrolResponse { ErrorMessage = ex.Message };
|
||||||
|
}
|
||||||
|
catch (System.Exception ex2)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionError(sessionId, ex2);
|
||||||
|
throw ex2;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (OpenSessionId == null)
|
||||||
|
EnrolmentLog.LogSessionFinished(sessionId);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
public static EnrolResponse Enrol(DiscoDataContext dbContext, string Username, Models.ClientServices.Enrol Request)
|
||||||
|
{
|
||||||
|
ActiveDirectoryMachineAccount MachineInfo = null;
|
||||||
|
EnrolResponse response = new EnrolResponse();
|
||||||
|
User authenticatedUser = null;
|
||||||
|
bool isAuthenticated = false;
|
||||||
|
string sessionId = System.Guid.NewGuid().ToString("B");
|
||||||
|
response.SessionId = sessionId;
|
||||||
|
EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Normal);
|
||||||
|
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 10, "Loading User Data");
|
||||||
|
if (!string.IsNullOrWhiteSpace(Username))
|
||||||
|
{
|
||||||
|
authenticatedUser = UserBI.UserCache.GetUser(Username, dbContext);
|
||||||
|
isAuthenticated = (authenticatedUser != null);
|
||||||
|
}
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 13, "Loading Device Data");
|
||||||
|
|
||||||
|
Device RepoDevice = dbContext.Devices.Include("AssignedUser").Include("DeviceModel").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault();
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 15, "Discovering User/Device Disco Permissions");
|
||||||
|
if (isAuthenticated)
|
||||||
|
{
|
||||||
|
if (authenticatedUser.Type != "Admin")
|
||||||
|
{
|
||||||
|
if (authenticatedUser.Type != "Computer")
|
||||||
|
throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1}; User Type: {2})", Request.DeviceSerialNumber, authenticatedUser.Id, authenticatedUser.Type));
|
||||||
|
if (!authenticatedUser.Id.Equals(string.Format("{0}$", Request.DeviceComputerName), System.StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1}; User Type: {2})", Request.DeviceSerialNumber, authenticatedUser.Id, authenticatedUser.Type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (RepoDevice == null)
|
||||||
|
throw new EnrolSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber));
|
||||||
|
if (!RepoDevice.AllowUnauthenticatedEnrol)
|
||||||
|
throw new EnrolSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber));
|
||||||
|
}
|
||||||
|
if (Request.DeviceIsPartOfDomain && !string.IsNullOrWhiteSpace(Request.DeviceComputerName))
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 20, "Loading Active Directory Computer Account");
|
||||||
|
System.Guid? uuidGuid = null;
|
||||||
|
System.Guid? macAddressGuid = null;
|
||||||
|
if (!string.IsNullOrEmpty(Request.DeviceUUID))
|
||||||
|
uuidGuid = ActiveDirectoryMachineAccountExtensions.NetbootGUIDFromUUID(Request.DeviceUUID);
|
||||||
|
if (!string.IsNullOrEmpty(Request.DeviceLanMacAddress))
|
||||||
|
macAddressGuid = ActiveDirectoryMachineAccountExtensions.NetbootGUIDFromMACAddress(Request.DeviceLanMacAddress);
|
||||||
|
MachineInfo = ActiveDirectory.GetMachineAccount(Request.DeviceComputerName, uuidGuid, macAddressGuid);
|
||||||
|
}
|
||||||
|
if (RepoDevice == null)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 30, "New Device, Creating Disco Instance");
|
||||||
|
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.DeviceSerialNumber);
|
||||||
|
DeviceProfile deviceProfile = dbContext.DeviceProfiles.Find(dbContext.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
|
||||||
|
DeviceModel deviceModel = dbContext.DeviceModels.Where(dm => dm.Manufacturer == Request.DeviceManufacturer.Trim() && dm.Model == Request.DeviceModel.Trim()).FirstOrDefault();
|
||||||
|
if (deviceModel == null)
|
||||||
|
{
|
||||||
|
deviceModel = new DeviceModel
|
||||||
|
{
|
||||||
|
Manufacturer = Request.DeviceManufacturer.Trim(),
|
||||||
|
Model = Request.DeviceModel.Trim(),
|
||||||
|
ModelType = Request.DeviceModelType.Trim(),
|
||||||
|
Description = string.Format("{0} {1}", Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim())
|
||||||
|
};
|
||||||
|
dbContext.DeviceModels.Add(deviceModel);
|
||||||
|
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
|
||||||
|
}
|
||||||
|
RepoDevice = new Device
|
||||||
|
{
|
||||||
|
SerialNumber = Request.DeviceSerialNumber,
|
||||||
|
ComputerName = Request.DeviceComputerName,
|
||||||
|
DeviceProfile = deviceProfile,
|
||||||
|
DeviceModel = deviceModel,
|
||||||
|
AllowUnauthenticatedEnrol = false,
|
||||||
|
Active = true,
|
||||||
|
CreatedDate = DateTime.Now,
|
||||||
|
EnrolledDate = DateTime.Now,
|
||||||
|
LastEnrolDate = DateTime.Now
|
||||||
|
};
|
||||||
|
dbContext.Devices.Add(RepoDevice);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 30, "Existing Device, Updating Disco Instance");
|
||||||
|
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.DeviceSerialNumber);
|
||||||
|
|
||||||
|
DeviceModel deviceModel = dbContext.DeviceModels.Where(dm => dm.Manufacturer == Request.DeviceManufacturer.Trim() && dm.Model == Request.DeviceModel.Trim()).FirstOrDefault();
|
||||||
|
if (deviceModel == null)
|
||||||
|
{
|
||||||
|
deviceModel = new DeviceModel
|
||||||
|
{
|
||||||
|
Manufacturer = Request.DeviceManufacturer.Trim(),
|
||||||
|
Model = Request.DeviceModel.Trim(),
|
||||||
|
ModelType = Request.DeviceModelType.Trim(),
|
||||||
|
Description = string.Format("{0} {1}", Request.DeviceManufacturer.Trim(), Request.DeviceModel)
|
||||||
|
};
|
||||||
|
dbContext.DeviceModels.Add(deviceModel);
|
||||||
|
RepoDevice.DeviceModel = deviceModel;
|
||||||
|
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!RepoDevice.DeviceModelId.HasValue || RepoDevice.DeviceModelId.Value != deviceModel.Id)
|
||||||
|
{
|
||||||
|
RepoDevice.DeviceModel = deviceModel;
|
||||||
|
}
|
||||||
|
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, RepoDevice.DeviceModelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RepoDevice.EnrolledDate.HasValue)
|
||||||
|
RepoDevice.EnrolledDate = DateTime.Now;
|
||||||
|
RepoDevice.LastEnrolDate = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MachineInfo == null)
|
||||||
|
{
|
||||||
|
if (isAuthenticated || RepoDevice.AllowUnauthenticatedEnrol)
|
||||||
|
{
|
||||||
|
if (RepoDevice.DeviceProfile.ProvisionADAccount)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 50, "Provisioning an Active Directory Computer Account");
|
||||||
|
if (string.IsNullOrEmpty(RepoDevice.ComputerName) || RepoDevice.DeviceProfile.EnforceComputerNameConvention)
|
||||||
|
RepoDevice.ComputerName = RepoDevice.ComputerNameRender(dbContext);
|
||||||
|
EnrolmentLog.LogSessionTaskProvisioningADAccount(sessionId, RepoDevice.SerialNumber, RepoDevice.ComputerName);
|
||||||
|
MachineInfo = ActiveDirectory.GetMachineAccount(RepoDevice.ComputerName);
|
||||||
|
response.OfflineDomainJoin = ActiveDirectory.OfflineDomainJoinProvision(ref MachineInfo, RepoDevice.ComputerName, RepoDevice.DeviceProfile.OrganisationalUnit, sessionId);
|
||||||
|
response.RequireReboot = true;
|
||||||
|
}
|
||||||
|
if (MachineInfo != null)
|
||||||
|
{
|
||||||
|
response.DeviceComputerName = MachineInfo.Name;
|
||||||
|
response.DeviceDomainName = MachineInfo.Domain;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.DeviceComputerName = RepoDevice.ComputerName;
|
||||||
|
response.DeviceDomainName = RepoDevice.ComputerName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RepoDevice.ComputerName = Request.DeviceComputerName;
|
||||||
|
response.DeviceComputerName = Request.DeviceComputerName;
|
||||||
|
response.DeviceDomainName = RepoDevice.ComputerName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RepoDevice.ComputerName = MachineInfo.Name;
|
||||||
|
response.DeviceComputerName = MachineInfo.Name;
|
||||||
|
response.DeviceDomainName = MachineInfo.Domain;
|
||||||
|
|
||||||
|
// Enforce Computer Name Convention
|
||||||
|
if (RepoDevice.DeviceProfile.EnforceComputerNameConvention)
|
||||||
|
{
|
||||||
|
var calculatedComputerName = RepoDevice.ComputerNameRender(dbContext);
|
||||||
|
if (!Request.DeviceComputerName.Equals(calculatedComputerName, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 50, string.Format("Renaming Device: {0} -> {1}", Request.DeviceComputerName, calculatedComputerName));
|
||||||
|
EnrolmentLog.LogSessionTaskRenamingDevice(sessionId, Request.DeviceComputerName, calculatedComputerName);
|
||||||
|
|
||||||
|
RepoDevice.ComputerName = calculatedComputerName;
|
||||||
|
response.DeviceComputerName = calculatedComputerName;
|
||||||
|
|
||||||
|
// Create New Account
|
||||||
|
response.OfflineDomainJoin = ActiveDirectory.OfflineDomainJoinProvision(ref MachineInfo, RepoDevice.ComputerName, RepoDevice.DeviceProfile.OrganisationalUnit, sessionId);
|
||||||
|
response.RequireReboot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce Organisation Unit
|
||||||
|
if (response.OfflineDomainJoin == null && RepoDevice.DeviceProfile.EnforceOrganisationalUnit)
|
||||||
|
{
|
||||||
|
if ((RepoDevice.DeviceProfile.OrganisationalUnit == null && MachineInfo.ParentDistinguishedName.Equals("CN=Computers", StringComparison.InvariantCultureIgnoreCase)) // Null (Default) OU
|
||||||
|
|| !MachineInfo.ParentDistinguishedName.Equals(RepoDevice.DeviceProfile.OrganisationalUnit, StringComparison.InvariantCultureIgnoreCase)) // Custom OU
|
||||||
|
{
|
||||||
|
string newOU = RepoDevice.DeviceProfile.OrganisationalUnit ?? "CN=Computers";
|
||||||
|
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 65, string.Format("Moving Device Organisation Unit: {0} -> {1}", MachineInfo.ParentDistinguishedName, newOU));
|
||||||
|
EnrolmentLog.LogSessionTaskMovingDeviceOrganisationUnit(sessionId, MachineInfo.ParentDistinguishedName, newOU);
|
||||||
|
MachineInfo.MoveOrganisationUnit(RepoDevice.DeviceProfile.OrganisationalUnit);
|
||||||
|
MachineInfo = ActiveDirectory.GetMachineAccount(MachineInfo.sAMAccountName);
|
||||||
|
response.RequireReboot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (MachineInfo != null)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 75, "Updating Active Directory Computer Account Properties");
|
||||||
|
MachineInfo.UpdateNetbootGUID(Request.DeviceUUID, Request.DeviceLanMacAddress);
|
||||||
|
if (RepoDevice.AssignedUser != null)
|
||||||
|
MachineInfo.SetDescription(RepoDevice);
|
||||||
|
}
|
||||||
|
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne)
|
||||||
|
{
|
||||||
|
if (RepoDevice.AssignedUser == null)
|
||||||
|
{
|
||||||
|
response.AllowBootstrapperUninstall = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Active Directory Assigned User Account");
|
||||||
|
ActiveDirectoryUserAccount AssignedUserInfo = ActiveDirectory.GetUserAccount(RepoDevice.AssignedUser.Id);
|
||||||
|
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.sAMAccountName, AssignedUserInfo.Domain, AssignedUserInfo.ObjectSid);
|
||||||
|
response.AllowBootstrapperUninstall = true;
|
||||||
|
response.DeviceAssignedUserUsername = AssignedUserInfo.sAMAccountName;
|
||||||
|
response.DeviceAssignedUserDomain = AssignedUserInfo.Domain;
|
||||||
|
response.DeviceAssignedUserName = AssignedUserInfo.DisplayName;
|
||||||
|
response.DeviceAssignedUserSID = AssignedUserInfo.ObjectSid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.AllowBootstrapperUninstall = true;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(Request.DeviceWlanMacAddress) && !string.IsNullOrEmpty(RepoDevice.DeviceProfile.CertificateProviderId))
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 90, "Provisioning a Wireless Certificate");
|
||||||
|
|
||||||
|
var allocationResult = RepoDevice.AllocateCertificate(dbContext);
|
||||||
|
var deviceCertificate = allocationResult.Item1;
|
||||||
|
if (deviceCertificate != null)
|
||||||
|
{
|
||||||
|
bool certAlreadyInstalled = false;
|
||||||
|
if (Request.DeviceCertificates != null && Request.DeviceCertificates.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (string existingCertName in Request.DeviceCertificates)
|
||||||
|
{
|
||||||
|
if (existingCertName.Contains(deviceCertificate.Name))
|
||||||
|
{
|
||||||
|
certAlreadyInstalled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!certAlreadyInstalled)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionTaskProvisioningWirelessCertificate(sessionId, RepoDevice.SerialNumber, deviceCertificate.Name);
|
||||||
|
response.DeviceCertificate = System.Convert.ToBase64String(deviceCertificate.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.DeviceCertificateRemoveExisting = allocationResult.Item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset 'AllowUnauthenticatedEnrol'
|
||||||
|
if (RepoDevice.AllowUnauthenticatedEnrol)
|
||||||
|
RepoDevice.AllowUnauthenticatedEnrol = false;
|
||||||
|
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
|
||||||
|
}
|
||||||
|
catch (EnrolSafeException ex)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionError(sessionId, ex);
|
||||||
|
return new EnrolResponse
|
||||||
|
{
|
||||||
|
SessionId = sessionId,
|
||||||
|
ErrorMessage = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (System.Exception ex2)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionError(sessionId, ex2);
|
||||||
|
throw ex2;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionFinished(sessionId);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
namespace Disco.BI
|
||||||
|
{
|
||||||
|
public class EnrolSafeException : System.Exception
|
||||||
|
{
|
||||||
|
public EnrolSafeException(string Message) : base(Message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,482 @@
|
|||||||
|
using Disco.Services.Logging;
|
||||||
|
using Disco.Services.Logging.Models;
|
||||||
|
using Disco.Models.ClientServices;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
namespace Disco.BI.DeviceBI
|
||||||
|
{
|
||||||
|
public class EnrolmentLog : LogBase
|
||||||
|
{
|
||||||
|
public enum EventTypeIds
|
||||||
|
{
|
||||||
|
SessionStarting = 10,
|
||||||
|
SessionProgress,
|
||||||
|
SessionDevice,
|
||||||
|
SessionDeviceInfo,
|
||||||
|
SessionFinished = 20,
|
||||||
|
SessionDiagnosticInformation,
|
||||||
|
SessionWarning,
|
||||||
|
SessionError,
|
||||||
|
SessionErrorWithInner,
|
||||||
|
SessionClientError,
|
||||||
|
SessionTaskAddedDevice = 50,
|
||||||
|
SessionTaskUpdatingDevice,
|
||||||
|
SessionTaskCreatedDeviceModel = 56,
|
||||||
|
SessionTaskProvisioningADAccount = 58,
|
||||||
|
SessionTaskAssigningUser = 60,
|
||||||
|
SessionTaskProvisioningWirelessCertificate = 62,
|
||||||
|
SessionTaskRenamingDevice = 64,
|
||||||
|
SessionTaskMovingDeviceOrganisationUnit = 66,
|
||||||
|
ClientError = 400
|
||||||
|
}
|
||||||
|
private const int _ModuleId = 50;
|
||||||
|
public static EnrolmentLog Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (EnrolmentLog)LogContext.LogModules[50];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override string ModuleDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "Device Enrolment";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override int ModuleId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override string ModuleName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "DeviceEnrolment";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public EnrolmentLog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
private static void Log(EnrolmentLog.EventTypeIds EventTypeId, params object[] Args)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Current.Log((int)EventTypeId, Args);
|
||||||
|
}
|
||||||
|
public static void LogSessionStarting(string SessionId, string HostId, DeviceEnrol.EnrolmentTypes EnrolmentType)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionStarting, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
HostId,
|
||||||
|
System.Enum.GetName(EnrolmentType.GetType(), EnrolmentType)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionDevice(string SessionId, string DeviceSerialNumber, int? DeviceModelId)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionDevice, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DeviceSerialNumber,
|
||||||
|
DeviceModelId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionDeviceInfo(string SessionId, string SerialNumber, string UUID, string ComputerName, string LanMacAddress, string WlanMacAddress, string Manufacturer, string Model, string ModelType)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionDeviceInfo, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
SerialNumber,
|
||||||
|
UUID,
|
||||||
|
ComputerName,
|
||||||
|
LanMacAddress,
|
||||||
|
WlanMacAddress,
|
||||||
|
Manufacturer,
|
||||||
|
Model,
|
||||||
|
ModelType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionDeviceInfo(string SessionId, MacEnrol Request)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, Request.DeviceLanMacAddress, Request.DeviceWlanMacAddress, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType);
|
||||||
|
}
|
||||||
|
public static void LogSessionDeviceInfo(string SessionId, Models.ClientServices.Enrol Request)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, Request.DeviceLanMacAddress, Request.DeviceWlanMacAddress, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType);
|
||||||
|
}
|
||||||
|
public static void LogSessionProgress(string SessionId, int Progress, string Status)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionProgress, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Progress,
|
||||||
|
Status
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionFinished(string SessionId)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionFinished, new object[]
|
||||||
|
{
|
||||||
|
SessionId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionDiagnosticInformation(string SessionId, string Message)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionDiagnosticInformation, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionWarning(string SessionId, string Message)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionWarning, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionError(string SessionId, System.Exception Ex)
|
||||||
|
{
|
||||||
|
if (Ex.InnerException == null)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionError, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Ex.GetType().Name,
|
||||||
|
Ex.Message,
|
||||||
|
Ex.StackTrace
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionErrorWithInner, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Ex.GetType().Name,
|
||||||
|
Ex.Message,
|
||||||
|
Ex.InnerException.GetType().Name,
|
||||||
|
Ex.InnerException.Message,
|
||||||
|
Ex.StackTrace,
|
||||||
|
Ex.InnerException.StackTrace
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void LogSessionClientError(string SessionId, string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionClientError, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
ClientIP,
|
||||||
|
ClientIdentifier,
|
||||||
|
ClientVersion,
|
||||||
|
Error,
|
||||||
|
RawError
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskAddedDevice(string SessionId, string DeviceSerialNumber)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskAddedDevice, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DeviceSerialNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskUpdatingDevice(string SessionId, string DeviceSerialNumber)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskUpdatingDevice, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DeviceSerialNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskCreatedDeviceModel(string SessionId, string DeviceSerialNumber, string Manufacturer, string Model)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskCreatedDeviceModel, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DeviceSerialNumber,
|
||||||
|
Manufacturer,
|
||||||
|
Model
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskProvisioningADAccount(string SessionId, string DeviceSerialNumber, string ADAccountName)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskProvisioningADAccount, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DeviceSerialNumber,
|
||||||
|
ADAccountName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskAssigningUser(string SessionId, string DeviceSerialNumber, string UserDisplayName, string UserUsername, string UserDomain, string UserSID)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskAssigningUser, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DeviceSerialNumber,
|
||||||
|
UserDisplayName,
|
||||||
|
UserUsername,
|
||||||
|
UserDomain,
|
||||||
|
UserSID
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskProvisioningWirelessCertificate(string SessionId, string DeviceSerialNumber, string CertificateName)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskProvisioningWirelessCertificate, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DeviceSerialNumber,
|
||||||
|
CertificateName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskRenamingDevice(string SessionId, string OldComputerName, string NewComputerName)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskRenamingDevice, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
OldComputerName,
|
||||||
|
NewComputerName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogSessionTaskMovingDeviceOrganisationUnit(string SessionId, string OldOrganisationUnit, string NewOrganisationUnit)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskMovingDeviceOrganisationUnit, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
OldOrganisationUnit,
|
||||||
|
NewOrganisationUnit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogClientError(string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
|
||||||
|
{
|
||||||
|
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.ClientError, new object[]
|
||||||
|
{
|
||||||
|
ClientIP,
|
||||||
|
ClientIdentifier,
|
||||||
|
ClientVersion,
|
||||||
|
Error,
|
||||||
|
RawError
|
||||||
|
});
|
||||||
|
}
|
||||||
|
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
|
||||||
|
{
|
||||||
|
return new System.Collections.Generic.List<LogEventType>
|
||||||
|
{
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 10,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Starting",
|
||||||
|
Format = "Starting '{2}' Enrolment for {1} (Session# {0})",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 12,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Device",
|
||||||
|
Format = null,
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = false
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 11,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Progress",
|
||||||
|
Format = "Processing Session# {0}; {1}% Complete; Status: {2}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = false,
|
||||||
|
UseDisplay = false
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 13,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Device Info",
|
||||||
|
Format = null,
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 20,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Finished",
|
||||||
|
Format = "Finished Session# {0}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 21,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Diagnostic Information",
|
||||||
|
Format = null,
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = false,
|
||||||
|
UseDisplay = false
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 22,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Warning",
|
||||||
|
Format = null,
|
||||||
|
Severity = 1,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 23,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Error",
|
||||||
|
Format = "An Error Occurred: [{1}] {2}",
|
||||||
|
Severity = 2,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 24,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Session Error with Internal",
|
||||||
|
Format = "An Error Occurred: [{1}] {2}; Internal Error: [{3}] {4}",
|
||||||
|
Severity = 2,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = (int)EventTypeIds.SessionClientError,
|
||||||
|
ModuleId = _ModuleId,
|
||||||
|
Name = "Client Error",
|
||||||
|
Format = "IP: {1}; Device ID: {2}; Version: {3} Error: {4}; Session# {0}",
|
||||||
|
Severity = (int)LogEventType.Severities.Error,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 50,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Added Device",
|
||||||
|
Format = "Creating Disco Device {1}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 51,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Updating Device",
|
||||||
|
Format = "Updating Disco Device {1}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 56,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Creating Device Model",
|
||||||
|
Format = "Creating Device Model '{2} {3}' for Device {1}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 58,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Provisioning Active Directory Account",
|
||||||
|
Format = "Provisioning Active Directory Account '{2}' for Device {1}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 60,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Assigning User",
|
||||||
|
Format = "Assigning User '{2}' ({4}\\{3} {{{5}}}) for Device {1}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 62,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Provisioning Wireless Certificate",
|
||||||
|
Format = "Provisioning Wireless Certificate '{2}' for Device {1}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 64,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Renaming Device",
|
||||||
|
Format = "Renaming Device '{1}' to '{2}'",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 66,
|
||||||
|
ModuleId = 50,
|
||||||
|
Name = "Task - Moving Device Organisation Unit",
|
||||||
|
Format = "Moving Device Organisation Unit '{1}' to '{2}'",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = (int)EventTypeIds.ClientError,
|
||||||
|
ModuleId = _ModuleId,
|
||||||
|
Name = "Client Error",
|
||||||
|
Format = "IP: {0}; Device ID: {1}; Version: {2} Error: {3}",
|
||||||
|
Severity = (int)LogEventType.Severities.Error,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.BI.Search;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.DeviceBI
|
||||||
|
{
|
||||||
|
public static class Searching
|
||||||
|
{
|
||||||
|
private static List<DeviceSearchResultItem> Search_SelectDeviceSearchResultItem(IQueryable<Device> Query, int? LimitCount = null){
|
||||||
|
if (LimitCount.HasValue)
|
||||||
|
Query = Query.Take(LimitCount.Value);
|
||||||
|
|
||||||
|
return Query.Select(d => new DeviceSearchResultItem()
|
||||||
|
{
|
||||||
|
SerialNumber = d.SerialNumber,
|
||||||
|
AssetNumber = d.AssetNumber,
|
||||||
|
ComputerName = d.ComputerName,
|
||||||
|
DeviceModelDescription = d.DeviceModel.Description,
|
||||||
|
DeviceProfileDescription = d.DeviceProfile.Description,
|
||||||
|
DecommissionedDate = d.DecommissionedDate,
|
||||||
|
AssignedUserId = d.AssignedUserId,
|
||||||
|
AssignedUserDisplayName = d.AssignedUser.DisplayName,
|
||||||
|
JobCount = d.Jobs.Count()
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DeviceSearchResultItem> Search(DiscoDataContext dbContext, string Term, int? LimitCount = null)
|
||||||
|
{
|
||||||
|
return Search_SelectDeviceSearchResultItem(dbContext.Devices.Where(d =>
|
||||||
|
d.AssetNumber.Contains(Term) ||
|
||||||
|
d.ComputerName.Contains(Term) ||
|
||||||
|
d.SerialNumber.Contains(Term) ||
|
||||||
|
d.Location.Contains(Term) ||
|
||||||
|
Term.Contains(d.SerialNumber)
|
||||||
|
), LimitCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DeviceSearchResultItem> SearchDeviceModel(DiscoDataContext dbContext, int DeviceModelId, int? LimitCount = null)
|
||||||
|
{
|
||||||
|
return Search_SelectDeviceSearchResultItem(dbContext.Devices.Where(d => d.DeviceModelId == DeviceModelId), LimitCount);
|
||||||
|
}
|
||||||
|
public static List<DeviceSearchResultItem> SearchDeviceProfile(DiscoDataContext dbContext, int DeviceProfileId, int? LimitCount = null)
|
||||||
|
{
|
||||||
|
return Search_SelectDeviceSearchResultItem(dbContext.Devices.Where(d => d.DeviceProfileId == DeviceProfileId), LimitCount);
|
||||||
|
}
|
||||||
|
public static List<DeviceSearchResultItem> SearchDeviceBatch(DiscoDataContext dbContext, int DeviceBatchId, int? LimitCount = null)
|
||||||
|
{
|
||||||
|
return Search_SelectDeviceSearchResultItem(dbContext.Devices.Where(d => d.DeviceBatchId == DeviceBatchId), LimitCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Disco.BI
|
||||||
|
{
|
||||||
|
public class DisposableImageCollection : List<Bitmap>, IDisposable
|
||||||
|
{
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (Image i in this)
|
||||||
|
{
|
||||||
|
if (i != null)
|
||||||
|
i.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using System.Web;
|
||||||
|
using System.Drawing;
|
||||||
|
using iTextSharp.text.pdf;
|
||||||
|
|
||||||
|
namespace Disco.BI.DocumentTemplateBI
|
||||||
|
{
|
||||||
|
class DocumentTemplateQRCodeLocationCache
|
||||||
|
{
|
||||||
|
private static ConcurrentDictionary<string, List<RectangleF>> _Cache = new ConcurrentDictionary<string, List<RectangleF>>();
|
||||||
|
|
||||||
|
public static List<RectangleF> GetLocations(DocumentTemplate dt, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Check Cache
|
||||||
|
List<RectangleF> locations;
|
||||||
|
if (_Cache.TryGetValue(dt.Id, out locations))
|
||||||
|
{
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
// Generate Cache
|
||||||
|
return GenerateLocations(dt, dbContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool InvalidateLocations(DocumentTemplate dt)
|
||||||
|
{
|
||||||
|
List<RectangleF> locations;
|
||||||
|
return _Cache.TryRemove(dt.Id, out locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool SetValue(string DocumentTemplateId, List<RectangleF> Locations)
|
||||||
|
{
|
||||||
|
if (_Cache.ContainsKey(DocumentTemplateId))
|
||||||
|
{
|
||||||
|
List<RectangleF> oldLocations;
|
||||||
|
if (_Cache.TryGetValue(DocumentTemplateId, out oldLocations))
|
||||||
|
{
|
||||||
|
return _Cache.TryUpdate(DocumentTemplateId, Locations, oldLocations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _Cache.TryAdd(DocumentTemplateId, Locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<RectangleF> GenerateLocations(DocumentTemplate dt, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
string templateFilename = dt.RepositoryFilename(dbContext);
|
||||||
|
PdfReader pdfReader = new PdfReader(templateFilename);
|
||||||
|
List<RectangleF> locations = new List<RectangleF>();
|
||||||
|
|
||||||
|
if (pdfReader.AcroFields.Fields.ContainsKey("DiscoAttachmentId"))
|
||||||
|
{
|
||||||
|
foreach (var pdfFieldPosition in pdfReader.AcroFields.GetFieldPositions("DiscoAttachmentId"))
|
||||||
|
{
|
||||||
|
var pdfPageSize = pdfReader.GetPageSize(pdfFieldPosition.page);
|
||||||
|
locations.Add(new RectangleF((float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(pdfFieldPosition.position.Left / pdfPageSize.Width) - 0.1)), (float)System.Math.Min(1.0, System.Math.Max(0.0, (double)((pdfPageSize.Height - pdfFieldPosition.position.Top) / pdfPageSize.Height) - 0.1)), (float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(pdfFieldPosition.position.Width / pdfPageSize.Width) + 0.2)), (float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(pdfFieldPosition.position.Height / pdfPageSize.Height) + 0.2))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pdfReader.Close();
|
||||||
|
|
||||||
|
// Update Cache
|
||||||
|
SetValue(dt.Id, locations);
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System;
|
||||||
|
namespace Disco.BI.DocumentTemplateBI
|
||||||
|
{
|
||||||
|
public class DocumentUniqueIdentifier
|
||||||
|
{
|
||||||
|
private bool? _loadedComponentsOk;
|
||||||
|
private DocumentTemplate _documentTemplate;
|
||||||
|
private object _data;
|
||||||
|
private string _dataDescription;
|
||||||
|
public string TemplateTypeId { get; private set; }
|
||||||
|
public string DataId { get; private set; }
|
||||||
|
public string DocumentUniqueId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return string.Format("{0}|{1}", this.TemplateTypeId, this.DataId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string CreatorId { get; private set; }
|
||||||
|
public System.DateTime TimeStamp { get; private set; }
|
||||||
|
public int Page { get; private set; }
|
||||||
|
public string Tag { get; private set; }
|
||||||
|
public DocumentTemplate DocumentTemplate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
bool flag = this._loadedComponentsOk.HasValue && this._loadedComponentsOk.Value;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
return this._documentTemplate;
|
||||||
|
}
|
||||||
|
throw new System.Exception("Document Unique Identifier Components not loaded or invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public object Data
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
bool flag = this._loadedComponentsOk.HasValue && this._loadedComponentsOk.Value;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
return this._data;
|
||||||
|
}
|
||||||
|
throw new System.Exception("Document Unique Identifier Components not loaded or invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string DataDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
bool flag = this._loadedComponentsOk.HasValue && this._loadedComponentsOk.Value;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
return this._dataDescription;
|
||||||
|
}
|
||||||
|
throw new System.Exception("Document Unique Identifier Components not loaded or invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string DataScope { get; private set; }
|
||||||
|
public static bool IsDocumentUniqueIdentifier(string UniqueIdentifier)
|
||||||
|
{
|
||||||
|
return UniqueIdentifier.StartsWith("Disco|", System.StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
public DocumentUniqueIdentifier(string TemplateTypeId, string DataId, string CreatorId, DateTime TimeStamp, int? Page = null, string Tag = null)
|
||||||
|
{
|
||||||
|
this.Tag = Tag;
|
||||||
|
this.TemplateTypeId = TemplateTypeId;
|
||||||
|
this.DataId = DataId;
|
||||||
|
this.CreatorId = CreatorId;
|
||||||
|
this.TimeStamp = TimeStamp;
|
||||||
|
this.Page = Page ?? 0;
|
||||||
|
}
|
||||||
|
public DocumentUniqueIdentifier(string UniqueIdentifier, string Tag)
|
||||||
|
{
|
||||||
|
if (!DocumentUniqueIdentifier.IsDocumentUniqueIdentifier(UniqueIdentifier))
|
||||||
|
{
|
||||||
|
throw new System.ArgumentException("Invalid Document Unique Identifier", "UniqueIdentifier");
|
||||||
|
}
|
||||||
|
this.Tag = Tag;
|
||||||
|
string[] s = UniqueIdentifier.Split(new char[] { '|' });
|
||||||
|
string left = s[1].ToUpper();
|
||||||
|
if (left == "AT" || left == "1")
|
||||||
|
{
|
||||||
|
if (s.Length >= 3)
|
||||||
|
{
|
||||||
|
this.TemplateTypeId = s[2];
|
||||||
|
}
|
||||||
|
if (s.Length >= 4)
|
||||||
|
{
|
||||||
|
this.DataId = s[3];
|
||||||
|
}
|
||||||
|
if (s.Length >= 5)
|
||||||
|
{
|
||||||
|
this.CreatorId = s[4];
|
||||||
|
}
|
||||||
|
if (s.Length >= 6)
|
||||||
|
{
|
||||||
|
System.DateTime timeStamp;
|
||||||
|
if (System.DateTime.TryParse(s[5], out timeStamp))
|
||||||
|
{
|
||||||
|
this.TimeStamp = timeStamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s.Length >= 7)
|
||||||
|
{
|
||||||
|
int page = 0;
|
||||||
|
if (int.TryParse(s[6], out page))
|
||||||
|
{
|
||||||
|
this.Page = page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new System.ArgumentException(string.Format("Invalid Document Unique Identifier Version ({0})", s[1]), "UniqueIdentifier");
|
||||||
|
}
|
||||||
|
public bool LoadComponents(DiscoDataContext Context)
|
||||||
|
{
|
||||||
|
bool LoadComponents;
|
||||||
|
if (!this._loadedComponentsOk.HasValue)
|
||||||
|
{
|
||||||
|
string scopeType;
|
||||||
|
if (this.TemplateTypeId.StartsWith("--"))
|
||||||
|
{
|
||||||
|
string templateTypeId = this.TemplateTypeId;
|
||||||
|
switch (this.TemplateTypeId)
|
||||||
|
{
|
||||||
|
case "--DEVICE":
|
||||||
|
scopeType = DocumentTemplate.DocumentTemplateScopes.Device;
|
||||||
|
break;
|
||||||
|
case "--JOB":
|
||||||
|
scopeType = DocumentTemplate.DocumentTemplateScopes.Job;
|
||||||
|
break;
|
||||||
|
case "--USER":
|
||||||
|
scopeType = DocumentTemplate.DocumentTemplateScopes.User;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
scopeType = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._documentTemplate = Context.DocumentTemplates.Find(this.TemplateTypeId);
|
||||||
|
if (this._documentTemplate != null)
|
||||||
|
{
|
||||||
|
scopeType = this._documentTemplate.Scope;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scopeType = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scopeType != null)
|
||||||
|
{
|
||||||
|
this.DataScope = scopeType;
|
||||||
|
switch (scopeType)
|
||||||
|
{
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||||
|
Device d = Context.Devices.Find(this.DataId);
|
||||||
|
if (d != null)
|
||||||
|
{
|
||||||
|
this._data = d;
|
||||||
|
this._dataDescription = d.SerialNumber;
|
||||||
|
this._loadedComponentsOk = true;
|
||||||
|
LoadComponents = true;
|
||||||
|
return LoadComponents;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||||
|
Job i = Context.Jobs.Find(int.Parse(this.DataId));
|
||||||
|
if (i != null)
|
||||||
|
{
|
||||||
|
this._data = i;
|
||||||
|
this._dataDescription = i.Id.ToString();
|
||||||
|
this._loadedComponentsOk = true;
|
||||||
|
LoadComponents = true;
|
||||||
|
return LoadComponents;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||||
|
User u = Context.Users.Find(this.DataId);
|
||||||
|
if (u != null)
|
||||||
|
{
|
||||||
|
this._data = u;
|
||||||
|
this._dataDescription = u.DisplayName;
|
||||||
|
this._loadedComponentsOk = true;
|
||||||
|
LoadComponents = true;
|
||||||
|
return LoadComponents;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._loadedComponentsOk = false;
|
||||||
|
}
|
||||||
|
LoadComponents = this._loadedComponentsOk.Value;
|
||||||
|
return LoadComponents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Web.Caching;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Quartz;
|
||||||
|
using Quartz.Impl;
|
||||||
|
using Quartz.Impl.Triggers;
|
||||||
|
|
||||||
|
namespace Disco.BI.DocumentTemplateBI.Importer
|
||||||
|
{
|
||||||
|
public class DocumentDropBoxMonitor : System.IDisposable
|
||||||
|
{
|
||||||
|
private IScheduler _scheduler;
|
||||||
|
private FileSystemWatcher _fsw;
|
||||||
|
private Cache _httpCache;
|
||||||
|
|
||||||
|
public const string WatcherFilter = "*.pdf";
|
||||||
|
public string DropBoxLocation { get; private set; }
|
||||||
|
|
||||||
|
public DocumentDropBoxMonitor(DiscoDataContext Context, ISchedulerFactory SchedulerFactory, Cache HttpCache)
|
||||||
|
{
|
||||||
|
if (Context == null)
|
||||||
|
throw new System.ArgumentNullException("Context");
|
||||||
|
|
||||||
|
this._httpCache = HttpCache;
|
||||||
|
|
||||||
|
var location = DataStore.CreateLocation(Context, "DocumentDropBox");
|
||||||
|
this.DropBoxLocation = location.EndsWith(@"\") ? location : string.Concat(location, @"\");
|
||||||
|
|
||||||
|
this._scheduler = SchedulerFactory.GetScheduler();
|
||||||
|
this._scheduler.Start();
|
||||||
|
}
|
||||||
|
public void ScheduleCurrentFiles(int Delay)
|
||||||
|
{
|
||||||
|
foreach (var filename in System.IO.Directory.GetFiles(this.DropBoxLocation, "*.pdf"))
|
||||||
|
{
|
||||||
|
this.ScheduleFile(filename, Delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void StartWatching()
|
||||||
|
{
|
||||||
|
if (this._fsw == null)
|
||||||
|
{
|
||||||
|
this._fsw = new FileSystemWatcher(this.DropBoxLocation, "*.pdf");
|
||||||
|
this._fsw.Created += new FileSystemEventHandler(this.FSW_Created);
|
||||||
|
}
|
||||||
|
this._fsw.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
public void StopWatching()
|
||||||
|
{
|
||||||
|
if (this._fsw != null)
|
||||||
|
{
|
||||||
|
this._fsw.EnableRaisingEvents = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void ScheduleFile(string Filename, int Delay)
|
||||||
|
{
|
||||||
|
System.Guid guid = System.Guid.NewGuid();
|
||||||
|
JobDetailImpl jd = new JobDetailImpl(guid.ToString(), typeof(DocumentImporterJob));
|
||||||
|
jd.JobDataMap.Add("Filename", Filename);
|
||||||
|
jd.JobDataMap.Add("RetryCount", 0);
|
||||||
|
jd.JobDataMap.Add("HttpCache", this._httpCache);
|
||||||
|
guid = System.Guid.NewGuid();
|
||||||
|
|
||||||
|
System.DateTimeOffset startTimeUtc = new System.DateTimeOffset(DateTime.Now.AddSeconds((double)Delay));
|
||||||
|
SimpleTriggerImpl trig = new SimpleTriggerImpl(guid.ToString(), startTimeUtc);
|
||||||
|
|
||||||
|
this._scheduler.ScheduleJob(jd, trig);
|
||||||
|
}
|
||||||
|
private void FSW_Created(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if ((e.ChangeType & WatcherChangeTypes.Deleted) != WatcherChangeTypes.Deleted)
|
||||||
|
this.ScheduleFile(e.FullPath, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.StopWatching();
|
||||||
|
if (this._fsw != null)
|
||||||
|
this._fsw.Dispose();
|
||||||
|
if (this._scheduler != null)
|
||||||
|
this._scheduler.Shutdown(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Services.Logging;
|
||||||
|
using Quartz;
|
||||||
|
using Quartz.Impl;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
|
||||||
|
namespace Disco.BI.DocumentTemplateBI.Importer
|
||||||
|
{
|
||||||
|
public class DocumentImporterCleanCacheJob : ScheduledTask
|
||||||
|
{
|
||||||
|
public override string TaskName { get { return "Document Importer - Clean Cache Task"; } }
|
||||||
|
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Trigger Daily @ 12:30am
|
||||||
|
TriggerBuilder triggerBuilder = TriggerBuilder.Create().WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(0, 30));
|
||||||
|
|
||||||
|
this.ScheduleTask(triggerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
string dataStoreLocation;
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
dataStoreLocation = DataStore.CreateLocation(dbContext, "Cache\\DocumentDropBox_SessionPages");
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleteCount = 0;
|
||||||
|
int errorCount = 0;
|
||||||
|
|
||||||
|
System.IO.DirectoryInfo dataStoreInfo = new System.IO.DirectoryInfo(dataStoreLocation);
|
||||||
|
|
||||||
|
System.DateTime today = System.DateTime.Today;
|
||||||
|
|
||||||
|
foreach (System.IO.FileInfo file in dataStoreInfo.GetFiles())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (file.CreationTime < today)
|
||||||
|
{
|
||||||
|
file.Delete();
|
||||||
|
deleteCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemLog.LogInformation(
|
||||||
|
string.Format("Cleared DocumentDropBox_SessionPages Cache, Deleted {0} File/s, with {1} Error/s", deleteCount, errorCount),
|
||||||
|
deleteCount,
|
||||||
|
errorCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Web.Caching;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Quartz;
|
||||||
|
using Quartz.Impl.Triggers;
|
||||||
|
|
||||||
|
namespace Disco.BI.DocumentTemplateBI.Importer
|
||||||
|
{
|
||||||
|
[PersistJobDataAfterExecution]
|
||||||
|
public class DocumentImporterJob : IJob
|
||||||
|
{
|
||||||
|
|
||||||
|
void IJob.Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
string sessionId = context.JobDetail.JobDataMap["SessionId"] as string;
|
||||||
|
if (string.IsNullOrEmpty(sessionId))
|
||||||
|
{
|
||||||
|
sessionId = Guid.NewGuid().ToString();
|
||||||
|
context.JobDetail.JobDataMap["SessionId"] = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
string filename = context.JobDetail.JobDataMap["Filename"] as string;
|
||||||
|
int retryCount = (int)context.JobDetail.JobDataMap["RetryCount"];
|
||||||
|
Cache httpCache = context.JobDetail.JobDataMap["HttpCache"] as Cache;
|
||||||
|
|
||||||
|
var friendlyFilename = filename;
|
||||||
|
if (!string.IsNullOrEmpty(friendlyFilename))
|
||||||
|
friendlyFilename = System.IO.Path.GetFileName(friendlyFilename);
|
||||||
|
|
||||||
|
DocumentImporterLog.LogImportStarting(sessionId, friendlyFilename);
|
||||||
|
|
||||||
|
if (!File.Exists(filename))
|
||||||
|
{
|
||||||
|
DocumentImporterLog.LogImportWarning(sessionId, string.Format("File not found: {0}", filename));
|
||||||
|
DocumentImporterLog.LogImportFinished(sessionId);
|
||||||
|
context.Scheduler.DeleteJob(context.JobDetail.Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
if (retryCount < 18)
|
||||||
|
{
|
||||||
|
context.JobDetail.JobDataMap["RetryCount"] = (++retryCount);
|
||||||
|
bool processResult = Interop.Pdf.PdfImporter.ProcessPdfAttachment(filename, dbContext, sessionId, httpCache);
|
||||||
|
|
||||||
|
if (processResult)
|
||||||
|
{
|
||||||
|
// Import Successful - Delete
|
||||||
|
if (File.Exists(filename))
|
||||||
|
File.Delete(filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Import Failed - Move to Errors Folder
|
||||||
|
if (File.Exists(filename))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string folderError = DataStore.CreateLocation(dbContext, "DocumentDropBox_Errors");
|
||||||
|
string filenameError = Path.Combine(folderError, Path.GetFileName(filename));
|
||||||
|
int filenameErrorCount = 0;
|
||||||
|
while (File.Exists(filenameError))
|
||||||
|
{
|
||||||
|
filenameError = Path.Combine(folderError, string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(filename), ++filenameErrorCount, Path.GetExtension(filename)));
|
||||||
|
}
|
||||||
|
File.Move(filename, filenameError);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore Errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// To Many Errors
|
||||||
|
DocumentImporterLog.LogImportError(sessionId, string.Format("To many errors occurred trying to import '{1}' (SessionId: {0})", sessionId, friendlyFilename));
|
||||||
|
// Move to Errors Folder
|
||||||
|
if (File.Exists(filename))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string folderError = DataStore.CreateLocation(dbContext, "DocumentDropBox_Errors");
|
||||||
|
string filenameError = Path.Combine(folderError, Path.GetFileName(filename));
|
||||||
|
int filenameErrorCount = 0;
|
||||||
|
while (File.Exists(filenameError))
|
||||||
|
{
|
||||||
|
filenameError = Path.Combine(folderError, string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(filename), ++filenameErrorCount, Path.GetExtension(filename)));
|
||||||
|
}
|
||||||
|
File.Move(filename, filenameError);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore Errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DocumentImporterLog.LogImportFinished(sessionId);
|
||||||
|
|
||||||
|
// All Done
|
||||||
|
context.Scheduler.DeleteJob(context.JobDetail.Key);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.LogImportWarning(sessionId, string.Format("{0}; Will try again in 10 Seconds", ex.Message));
|
||||||
|
// Reschedule Job for 10 seconds
|
||||||
|
SimpleTriggerImpl trig = new SimpleTriggerImpl(Guid.NewGuid().ToString(), new DateTimeOffset(DateTime.Now.AddSeconds(10)));
|
||||||
|
context.Scheduler.RescheduleJob(context.Trigger.Key, trig);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
using Disco.Services.Logging;
|
||||||
|
using Disco.Services.Logging.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
namespace Disco.BI.DocumentTemplateBI.Importer
|
||||||
|
{
|
||||||
|
public class DocumentImporterLog : LogBase
|
||||||
|
{
|
||||||
|
public enum EventTypeIds
|
||||||
|
{
|
||||||
|
ImportStarting = 10,
|
||||||
|
ImportProgress,
|
||||||
|
ImportFinished,
|
||||||
|
ImportWarning = 15,
|
||||||
|
ImportError,
|
||||||
|
ImportPageStarting = 100,
|
||||||
|
ImportPageImageUpdate = 104,
|
||||||
|
ImportPageProgress,
|
||||||
|
ImportPageDetected = 110,
|
||||||
|
ImportPageUndetected = 115,
|
||||||
|
ImportPageError = 120,
|
||||||
|
ImportPageUndetectedStored = 150
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int _ModuleId = 40;
|
||||||
|
|
||||||
|
public static DocumentImporterLog Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (DocumentImporterLog)LogContext.LogModules[_ModuleId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ModuleDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "Document Importer";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override int ModuleId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _ModuleId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override string ModuleName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "DocumentImporter";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void Log(DocumentImporterLog.EventTypeIds EventTypeId, params object[] Args)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Current.Log((int)EventTypeId, Args);
|
||||||
|
}
|
||||||
|
public static void LogImportStarting(string SessionId, string DocumentName)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportStarting, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
DocumentName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportProgress(string SessionId, int? Progress, string Status)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportProgress, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Progress,
|
||||||
|
Status
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportFinished(string SessionId)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportFinished, new object[]
|
||||||
|
{
|
||||||
|
SessionId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportWarning(string SessionId, string Message)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportWarning, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportError(string SessionId, string Message)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportError, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportPageStarting(string SessionId, int PageNumber)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportPageStarting, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
PageNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportPageImageUpdate(string SessionId, int PageNumber)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportPageImageUpdate, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
PageNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportPageProgress(string SessionId, int PageNumber, int? Progress, string Status)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportPageProgress, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
PageNumber,
|
||||||
|
Progress,
|
||||||
|
Status
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportPageDetected(string SessionId, int PageNumber, string DocumentTypeId, string DocumentTypeName, string TargetType, string AssignedId, string AssignedName)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportPageDetected, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
PageNumber,
|
||||||
|
DocumentTypeId,
|
||||||
|
DocumentTypeName,
|
||||||
|
TargetType,
|
||||||
|
AssignedId,
|
||||||
|
AssignedName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportPageUndetected(string SessionId, int PageNumber)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportPageUndetected, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
PageNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportPageError(string SessionId, int PageNumber, string Message)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportPageError, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
PageNumber,
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogImportPageUndetectedStored(string SessionId, int PageNumber)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.Log(DocumentImporterLog.EventTypeIds.ImportPageUndetectedStored, new object[]
|
||||||
|
{
|
||||||
|
SessionId,
|
||||||
|
PageNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
|
||||||
|
{
|
||||||
|
return new System.Collections.Generic.List<LogEventType>
|
||||||
|
{
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 10,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Starting",
|
||||||
|
Format = "Starting import of document: {1} (SessionId: {0})",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 11,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Progress",
|
||||||
|
Format = "Processing: {1}% Complete; Status: {2}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = false,
|
||||||
|
UseDisplay = false
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 12,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Finished",
|
||||||
|
Format = "Import of document complete (SessionId: {0})",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 15,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Warning",
|
||||||
|
Format = "Import Warning: {1} (SessionId: {0})",
|
||||||
|
Severity = 1,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 16,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Error",
|
||||||
|
Format = "Import Error: {1} (SessionId: {0})",
|
||||||
|
Severity = 2,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 100,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Page Starting",
|
||||||
|
Format = "Starting import of page: {1} (SessionId: {0})",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 104,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Page Image Update",
|
||||||
|
Format = null,
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = false,
|
||||||
|
UseDisplay = false
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 105,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Page Progress",
|
||||||
|
Format = "Processing: Page {1}; {2}% Complete; Status: {3}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = false,
|
||||||
|
UseDisplay = false
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 110,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Page Assigned",
|
||||||
|
Format = "Page {1} of type '{3}' assigned to {4}: '{6}'",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 115,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Page Undetected",
|
||||||
|
Format = "Page {1} not detected",
|
||||||
|
Severity = 1,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 120,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Page Error",
|
||||||
|
Format = "Page {1}, Import Error: {2}",
|
||||||
|
Severity = 2,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 150,
|
||||||
|
ModuleId = 40,
|
||||||
|
Name = "Import Page Undetected Stored",
|
||||||
|
Format = null,
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = false,
|
||||||
|
UseDisplay = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using iTextSharp.text;
|
||||||
|
using iTextSharp.text.pdf;
|
||||||
|
|
||||||
|
namespace Disco.BI.DocumentTemplateBI
|
||||||
|
{
|
||||||
|
public static class Utilities
|
||||||
|
{
|
||||||
|
public static System.IO.Stream JoinPdfs(params System.IO.Stream[] Pdfs)
|
||||||
|
{
|
||||||
|
if (Pdfs.Length == 0)
|
||||||
|
throw new System.ArgumentNullException("Pdfs");
|
||||||
|
|
||||||
|
// Only One PDF - Possible Reference Bug v's Memory/Speed (Returning Param Memory Stream)
|
||||||
|
if (Pdfs.Length == 1)
|
||||||
|
return Pdfs[0];
|
||||||
|
|
||||||
|
// Join Pdfs
|
||||||
|
System.IO.MemoryStream msBuilder = new System.IO.MemoryStream();
|
||||||
|
|
||||||
|
Document pdfDoc = new Document();
|
||||||
|
PdfCopy pdfCopy = new PdfCopy(pdfDoc, msBuilder);
|
||||||
|
pdfDoc.Open();
|
||||||
|
pdfCopy.CloseStream = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < Pdfs.Length; i++)
|
||||||
|
{
|
||||||
|
System.IO.Stream pdf = Pdfs[i];
|
||||||
|
PdfReader pdfReader = new PdfReader(pdf);
|
||||||
|
|
||||||
|
for (int indexPage = 1; indexPage <= pdfReader.NumberOfPages; indexPage++)
|
||||||
|
{
|
||||||
|
iTextSharp.text.Rectangle pageSize = pdfReader.GetPageSizeWithRotation(indexPage);
|
||||||
|
PdfImportedPage page = pdfCopy.GetImportedPage(pdfReader, indexPage);
|
||||||
|
pdfDoc.SetPageSize(pageSize);
|
||||||
|
pdfDoc.NewPage();
|
||||||
|
pdfCopy.AddPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfReader.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfDoc.Close();
|
||||||
|
pdfCopy.Close();
|
||||||
|
msBuilder.Position = 0;
|
||||||
|
|
||||||
|
return msBuilder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Spring.Expressions.Parser.antlr;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public class EvaluateExpressionParseException
|
||||||
|
{
|
||||||
|
public string Expression { get; set; }
|
||||||
|
public int PositionRow { get; set; }
|
||||||
|
public int PositionColumn { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
internal static EvaluateExpressionParseException FromRecognitionException(RecognitionException e, string Expression)
|
||||||
|
{
|
||||||
|
return new EvaluateExpressionParseException()
|
||||||
|
{
|
||||||
|
Expression = Expression,
|
||||||
|
Message = e.Message,
|
||||||
|
PositionRow = e.getLine(),
|
||||||
|
PositionColumn = e.getColumn()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Spring.Expressions.Parser.antlr;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public class EvaluateExpressionPart : IExpressionPart
|
||||||
|
{
|
||||||
|
private Spring.Expressions.IExpression _Expression;
|
||||||
|
private RecognitionException _ExpressionParseException;
|
||||||
|
private EvaluateExpressionParseException _EvaluateParseException;
|
||||||
|
|
||||||
|
public string RawSource { get; set; }
|
||||||
|
public string Source { get; set; }
|
||||||
|
public bool ErrorsAllowed { get; set; }
|
||||||
|
public bool IsDynamic { get { return true; } set { return; } }
|
||||||
|
|
||||||
|
public EvaluateExpressionParseException ParseException
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_ExpressionParseException == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
if (_EvaluateParseException == null)
|
||||||
|
_EvaluateParseException = EvaluateExpressionParseException.FromRecognitionException(_ExpressionParseException, this.Source);
|
||||||
|
return _EvaluateParseException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ParseError
|
||||||
|
{
|
||||||
|
get { return (_ExpressionParseException != null); }
|
||||||
|
}
|
||||||
|
public string ParseErrorMessage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (ParseError)
|
||||||
|
return ParseException.Message;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EvaluateExpressionPart(string Source)
|
||||||
|
{
|
||||||
|
this.RawSource = Source;
|
||||||
|
|
||||||
|
if (Source.StartsWith("{") && Source.EndsWith("}"))
|
||||||
|
Source = Source.Substring(1, Source.Length - 2);
|
||||||
|
|
||||||
|
if (Source[0] == '~')
|
||||||
|
{
|
||||||
|
this.ErrorsAllowed = true;
|
||||||
|
this.Source = Source.Substring(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.ErrorsAllowed = false;
|
||||||
|
this.Source = Source;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this._Expression = Spring.Expressions.Expression.Parse(this.Source);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (RecognitionException ex)
|
||||||
|
{
|
||||||
|
this._ExpressionParseException = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object IExpressionPart.Evaluate(object ExpressionContext, System.Collections.IDictionary Variables)
|
||||||
|
{
|
||||||
|
if (this._ExpressionParseException == null)
|
||||||
|
{
|
||||||
|
return this._Expression.GetValue(ExpressionContext, Variables);
|
||||||
|
}
|
||||||
|
throw this._ExpressionParseException;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.DocumentTemplates;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.BI.Expressions;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public sealed class Expression : System.Collections.Generic.List<IExpressionPart>
|
||||||
|
{
|
||||||
|
public string Name { get; private set; }
|
||||||
|
public string Source { get; private set; }
|
||||||
|
public bool IsDynamic { get; private set; }
|
||||||
|
public int Ordinal { get; private set; }
|
||||||
|
|
||||||
|
private Expression(string Name, string Source, int Ordinal)
|
||||||
|
{
|
||||||
|
this.Name = Name;
|
||||||
|
this.Source = Source;
|
||||||
|
this.Ordinal = Ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeExpressions()
|
||||||
|
{
|
||||||
|
Spring.Core.TypeResolution.TypeRegistry.RegisterType("DataExt", typeof(Extensions.DataExt));
|
||||||
|
Spring.Core.TypeResolution.TypeRegistry.RegisterType("UserExt", typeof(Extensions.UserExt));
|
||||||
|
Spring.Core.TypeResolution.TypeRegistry.RegisterType("DeviceExt", typeof(Extensions.DeviceExt));
|
||||||
|
Spring.Core.TypeResolution.TypeRegistry.RegisterType("ImageExt", typeof(Extensions.ImageExt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public T EvaluateFirst<T>(object ExpressionContext, System.Collections.IDictionary Variables)
|
||||||
|
{
|
||||||
|
T result = default(T);
|
||||||
|
if (this.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object expressionResult = this[0].Evaluate(ExpressionContext, Variables);
|
||||||
|
if (expressionResult != null)
|
||||||
|
{
|
||||||
|
if (expressionResult is T)
|
||||||
|
{
|
||||||
|
result = (T)expressionResult;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Expression returned an invalid type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Expression evaluation resulted in an error", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tuple<string, bool, object> Evaluate(object ExpressionContext, System.Collections.IDictionary Variables)
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder resultValue = new System.Text.StringBuilder();
|
||||||
|
object resultObject = null;
|
||||||
|
bool resultError = false;
|
||||||
|
foreach (var expressionPart in this)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object partValue = expressionPart.Evaluate(ExpressionContext, Variables);
|
||||||
|
if (partValue != null)
|
||||||
|
{
|
||||||
|
// Check for Result Objects
|
||||||
|
if (partValue is IImageExpressionResult)
|
||||||
|
resultObject = partValue;
|
||||||
|
else
|
||||||
|
resultValue.Append(partValue.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
if (!expressionPart.ErrorsAllowed)
|
||||||
|
{
|
||||||
|
resultValue.Append("## ERROR # ");
|
||||||
|
resultValue.Append(ex.Message);
|
||||||
|
resultValue.Append(" ##");
|
||||||
|
resultError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Tuple<string, bool, object>(resultValue.ToString(), resultError, resultObject);
|
||||||
|
}
|
||||||
|
public static Expression TokenizeSingleDynamic(string Name, string ExpressionSource, int Ordinal)
|
||||||
|
{
|
||||||
|
Expression e = new Expression(Name, ExpressionSource, Ordinal);
|
||||||
|
if (ExpressionSource != null && !string.IsNullOrWhiteSpace(ExpressionSource))
|
||||||
|
e.Add(new EvaluateExpressionPart(ExpressionSource));
|
||||||
|
e.IsDynamic = true;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
public static Expression Tokenize(string Name, string ExpressionSource, int Ordinal)
|
||||||
|
{
|
||||||
|
Expression e = new Expression(Name, ExpressionSource, Ordinal);
|
||||||
|
if (!ExpressionSource.Contains("{") || !ExpressionSource.Contains("}"))
|
||||||
|
{
|
||||||
|
e.Add(new TextExpressionPart(ExpressionSource));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder token = new System.Text.StringBuilder();
|
||||||
|
bool tokenEval = false;
|
||||||
|
int tokenEvalDepth = 0;
|
||||||
|
foreach (char c in ExpressionSource)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '{':
|
||||||
|
{
|
||||||
|
if (!tokenEval)
|
||||||
|
{
|
||||||
|
if (token.Length > 0)
|
||||||
|
{
|
||||||
|
e.Add(new TextExpressionPart(token.ToString()));
|
||||||
|
token = new System.Text.StringBuilder();
|
||||||
|
}
|
||||||
|
tokenEval = true;
|
||||||
|
tokenEvalDepth = 0;
|
||||||
|
}
|
||||||
|
tokenEvalDepth++;
|
||||||
|
token.Append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '}':
|
||||||
|
{
|
||||||
|
token.Append(c);
|
||||||
|
if (tokenEval)
|
||||||
|
{
|
||||||
|
tokenEvalDepth--;
|
||||||
|
if (tokenEvalDepth <= 0)
|
||||||
|
{
|
||||||
|
if (token.Length != 2 && (token.Length != 3 || token[1] != '@'))
|
||||||
|
{
|
||||||
|
e.Add(new EvaluateExpressionPart(token.ToString()));
|
||||||
|
e.IsDynamic = true;
|
||||||
|
token = new System.Text.StringBuilder();
|
||||||
|
}
|
||||||
|
tokenEval = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
token.Append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (token.Length > 0)
|
||||||
|
{
|
||||||
|
e.Add(new TextExpressionPart(token.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDictionary StandardVariables(DocumentTemplate AttachmentType, DiscoDataContext DataContext, User User, System.DateTime TimeStamp, DocumentState DocumentState)
|
||||||
|
{
|
||||||
|
return new Hashtable
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
"DataContext",
|
||||||
|
DataContext
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"User",
|
||||||
|
User
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"TimeStamp",
|
||||||
|
TimeStamp
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"AttachmentType",
|
||||||
|
AttachmentType
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"State",
|
||||||
|
DocumentState
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public static Dictionary<string, string> StandardVariableTypes()
|
||||||
|
{
|
||||||
|
return new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
"#DataContext",
|
||||||
|
typeof(DiscoDataContext).AssemblyQualifiedName
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"#User",
|
||||||
|
typeof(User).AssemblyQualifiedName
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"#TimeStamp",
|
||||||
|
typeof(System.DateTime).AssemblyQualifiedName
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"#AttachmentType",
|
||||||
|
typeof(DocumentTemplate).AssemblyQualifiedName
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"#State",
|
||||||
|
typeof(DocumentState).AssemblyQualifiedName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public static Dictionary<string, string> ExtensionLibraryTypes()
|
||||||
|
{
|
||||||
|
return new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"DataExt",
|
||||||
|
typeof(Extensions.DataExt).AssemblyQualifiedName
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"DeviceExt",
|
||||||
|
typeof(Extensions.DeviceExt).AssemblyQualifiedName
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"ImageExt",
|
||||||
|
typeof(Extensions.ImageExt).AssemblyQualifiedName
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"UserExt",
|
||||||
|
typeof(Extensions.UserExt).AssemblyQualifiedName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public static class ExpressionCache
|
||||||
|
{
|
||||||
|
private static ConcurrentDictionary<string, ConcurrentDictionary<string, Expression>> _Cache = new ConcurrentDictionary<string, ConcurrentDictionary<string, Expression>>();
|
||||||
|
|
||||||
|
public delegate Expression CreateValueDelegate();
|
||||||
|
|
||||||
|
public static ConcurrentDictionary<string, Expression> GetModule(string Module, bool Create = false)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<string, Expression> moduleCache;
|
||||||
|
if (_Cache.TryGetValue(Module, out moduleCache))
|
||||||
|
return moduleCache;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Create)
|
||||||
|
{
|
||||||
|
moduleCache = new ConcurrentDictionary<string, Expression>();
|
||||||
|
_Cache.TryAdd(Module, moduleCache);
|
||||||
|
return moduleCache;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static Expression GetModuleValue(string Module, string Key, CreateValueDelegate CreateValue)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, (CreateValue != null));
|
||||||
|
if (moduleCache != null)
|
||||||
|
{
|
||||||
|
Expression expression;
|
||||||
|
if (moduleCache.TryGetValue(Key, out expression))
|
||||||
|
{
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
if (CreateValue != null)
|
||||||
|
{
|
||||||
|
expression = CreateValue();
|
||||||
|
Expression oldExpression;
|
||||||
|
if (moduleCache.TryGetValue(Key, out oldExpression))
|
||||||
|
moduleCache.TryUpdate(Key, expression, oldExpression);
|
||||||
|
else
|
||||||
|
moduleCache.TryAdd(Key, expression);
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression GetValue(string Module, string Key, CreateValueDelegate CreateValue)
|
||||||
|
{
|
||||||
|
return GetModuleValue(Module, Key, CreateValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression GetValue(string Module, string Key)
|
||||||
|
{
|
||||||
|
return GetModuleValue(Module, Key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool InvalidModule(string Module)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<string, Expression> moduleCache;
|
||||||
|
return _Cache.TryRemove(Module, out moduleCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool InvalidateKey(string Module, string Key)
|
||||||
|
{
|
||||||
|
Expression expression;
|
||||||
|
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, false);
|
||||||
|
if (moduleCache != null)
|
||||||
|
{
|
||||||
|
bool removeResult = moduleCache.TryRemove(Key, out expression);
|
||||||
|
if (moduleCache.Count == 0)
|
||||||
|
InvalidModule(Module);
|
||||||
|
return removeResult;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SetValue(string Module, string Key, Expression Expression)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, true);
|
||||||
|
|
||||||
|
if (moduleCache.ContainsKey(Key))
|
||||||
|
{
|
||||||
|
Expression oldExpression;
|
||||||
|
if (moduleCache.TryGetValue(Key, out oldExpression))
|
||||||
|
{
|
||||||
|
return moduleCache.TryUpdate(Key, Expression, oldExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moduleCache.TryAdd(Key, Expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Quartz;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public class ExpressionCachePreloadTask : ScheduledTask
|
||||||
|
{
|
||||||
|
|
||||||
|
public override string TaskName { get { return "Expression Cache - Preload Task"; } }
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Run in Background 1 Second after Scheduled (on App Startup)
|
||||||
|
TriggerBuilder triggerBuilder = TriggerBuilder.Create().StartAt(new DateTimeOffset(DateTime.Now).AddSeconds(5));
|
||||||
|
|
||||||
|
this.ScheduleTask(triggerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
// Cache Document Template Filter Expressions
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
foreach (var documentTemplate in dbContext.DocumentTemplates.Where(dt => dt.FilterExpression != null && dt.FilterExpression != string.Empty))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(documentTemplate.FilterExpression))
|
||||||
|
documentTemplate.FilterExpressionFromCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public class ExpressionTypeDescriptor
|
||||||
|
{
|
||||||
|
public string ExpressionType { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public List<ExpressionTypeMemberDescriptor> Members { get; set; }
|
||||||
|
|
||||||
|
public static ExpressionTypeDescriptor Build(System.Type t, bool StaticDeclaredMembersOnly = true)
|
||||||
|
{
|
||||||
|
ExpressionTypeDescriptor i = new ExpressionTypeDescriptor
|
||||||
|
{
|
||||||
|
ExpressionType = t.AssemblyQualifiedName,
|
||||||
|
Name = t.Name
|
||||||
|
};
|
||||||
|
i.Members = new System.Collections.Generic.List<ExpressionTypeMemberDescriptor>();
|
||||||
|
|
||||||
|
System.Reflection.MemberInfo[] members;
|
||||||
|
if (StaticDeclaredMembersOnly)
|
||||||
|
members = t.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||||
|
else
|
||||||
|
members = t.GetMembers(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
|
||||||
|
for (int j = 0; j < members.Length; j++)
|
||||||
|
{
|
||||||
|
System.Reflection.MemberInfo member = members[j];
|
||||||
|
if (member is System.Reflection.PropertyInfo)
|
||||||
|
{
|
||||||
|
System.Reflection.PropertyInfo pi = (System.Reflection.PropertyInfo)member;
|
||||||
|
if (!pi.IsSpecialName && pi.CanRead)
|
||||||
|
{
|
||||||
|
i.Members.Add(ExpressionTypeMemberDescriptor.Build(pi));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (member is System.Reflection.MethodInfo)
|
||||||
|
{
|
||||||
|
System.Reflection.MethodInfo mi2 = (System.Reflection.MethodInfo)member;
|
||||||
|
if (!mi2.IsSpecialName)
|
||||||
|
{
|
||||||
|
i.Members.Add(ExpressionTypeMemberDescriptor.Build(mi2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.Members = (
|
||||||
|
from mi in i.Members
|
||||||
|
orderby mi.Name
|
||||||
|
select mi).ToList();
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public class ExpressionTypeMemberDescriptor
|
||||||
|
{
|
||||||
|
public const string FunctionKind = "function";
|
||||||
|
public const string PropertyKind = "property";
|
||||||
|
public const string ParameterKind = "parameter";
|
||||||
|
|
||||||
|
public string Kind {get;set;}
|
||||||
|
public string Name {get;set;}
|
||||||
|
public string ReturnType {get;set;}
|
||||||
|
public string ReturnExpressionType{get;set;}
|
||||||
|
public List<ExpressionTypeMemberDescriptor> Parameters{get;set;}
|
||||||
|
|
||||||
|
public static ExpressionTypeMemberDescriptor Build(System.Reflection.MethodInfo m)
|
||||||
|
{
|
||||||
|
ExpressionTypeMemberDescriptor md = new ExpressionTypeMemberDescriptor
|
||||||
|
{
|
||||||
|
Kind = "function",
|
||||||
|
Name = m.Name,
|
||||||
|
ReturnType = m.ReturnType.Name,
|
||||||
|
ReturnExpressionType = m.ReturnType.AssemblyQualifiedName
|
||||||
|
};
|
||||||
|
md.Parameters = (
|
||||||
|
from mdp in m.GetParameters()
|
||||||
|
select ExpressionTypeMemberDescriptor.Build(mdp)).ToList<ExpressionTypeMemberDescriptor>();
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
public static ExpressionTypeMemberDescriptor Build(System.Reflection.PropertyInfo p)
|
||||||
|
{
|
||||||
|
ExpressionTypeMemberDescriptor md = new ExpressionTypeMemberDescriptor
|
||||||
|
{
|
||||||
|
Kind = "property",
|
||||||
|
Name = p.Name,
|
||||||
|
ReturnType = p.PropertyType.Name,
|
||||||
|
ReturnExpressionType = p.PropertyType.AssemblyQualifiedName
|
||||||
|
};
|
||||||
|
md.Parameters = (
|
||||||
|
from mdp in p.GetIndexParameters()
|
||||||
|
select ExpressionTypeMemberDescriptor.Build(mdp)).ToList<ExpressionTypeMemberDescriptor>();
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
public static ExpressionTypeMemberDescriptor Build(System.Reflection.ParameterInfo pi)
|
||||||
|
{
|
||||||
|
return new ExpressionTypeMemberDescriptor
|
||||||
|
{
|
||||||
|
Kind = "parameter",
|
||||||
|
Name = pi.Name,
|
||||||
|
ReturnType = pi.ParameterType.Name,
|
||||||
|
ReturnExpressionType = pi.ParameterType.AssemblyQualifiedName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Data.Odbc;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions
|
||||||
|
{
|
||||||
|
public static class DataExt
|
||||||
|
{
|
||||||
|
#region SqlClient
|
||||||
|
|
||||||
|
private static SqlConnection BuildSqlConnection(string Server, string Database, string Username, string Password)
|
||||||
|
{
|
||||||
|
SqlConnectionStringBuilder dbConnectionStringBuilder = new SqlConnectionStringBuilder();
|
||||||
|
dbConnectionStringBuilder.ApplicationName = "Disco";
|
||||||
|
dbConnectionStringBuilder.DataSource = Server;
|
||||||
|
dbConnectionStringBuilder.InitialCatalog = Database;
|
||||||
|
dbConnectionStringBuilder.MultipleActiveResultSets = true;
|
||||||
|
dbConnectionStringBuilder.PersistSecurityInfo = true;
|
||||||
|
if (Username == null || Password == null)
|
||||||
|
dbConnectionStringBuilder.IntegratedSecurity = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dbConnectionStringBuilder.UserID = Username;
|
||||||
|
dbConnectionStringBuilder.Password = Password;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SqlConnection(dbConnectionStringBuilder.ConnectionString);
|
||||||
|
}
|
||||||
|
private static void BuildSqlParameters(SqlCommand dbCommand, Hashtable SqlParameters)
|
||||||
|
{
|
||||||
|
if (SqlParameters != null)
|
||||||
|
{
|
||||||
|
foreach (var sqlParameterKey in SqlParameters.Keys)
|
||||||
|
{
|
||||||
|
string key = sqlParameterKey.ToString();
|
||||||
|
if (!key.StartsWith("@"))
|
||||||
|
key = string.Concat("@", key);
|
||||||
|
dbCommand.Parameters.AddWithValue(key, SqlParameters[sqlParameterKey]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DataTable QuerySqlDatabase(string Server, string Database, string Username, string Password, string SqlQuery, Hashtable SqlParameters)
|
||||||
|
{
|
||||||
|
using (SqlConnection dbConnection = BuildSqlConnection(Server, Database, Username, Password))
|
||||||
|
{
|
||||||
|
using (SqlCommand dbCommand = new SqlCommand(SqlQuery, dbConnection))
|
||||||
|
{
|
||||||
|
BuildSqlParameters(dbCommand, SqlParameters);
|
||||||
|
using (SqlDataAdapter dbAdapter = new SqlDataAdapter(dbCommand))
|
||||||
|
{
|
||||||
|
var dbTable = new DataTable();
|
||||||
|
dbAdapter.Fill(dbTable);
|
||||||
|
return dbTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static DataTable QuerySqlDatabase(string Server, string Database, string SqlQuery, Hashtable SqlParameters)
|
||||||
|
{
|
||||||
|
return QuerySqlDatabase(Server, Database, null, null, SqlQuery, SqlParameters);
|
||||||
|
}
|
||||||
|
public static DataTable QuerySqlDatabase(string Server, string Database, string SqlQuery)
|
||||||
|
{
|
||||||
|
return QuerySqlDatabase(Server, Database, null, null, SqlQuery, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object QuerySqlDatabaseScalar(string Server, string Database, string Username, string Password, string SqlQuery, Hashtable SqlParameters)
|
||||||
|
{
|
||||||
|
using (SqlConnection dbConnection = BuildSqlConnection(Server, Database, Username, Password))
|
||||||
|
{
|
||||||
|
using (SqlCommand dbCommand = new SqlCommand(SqlQuery, dbConnection))
|
||||||
|
{
|
||||||
|
BuildSqlParameters(dbCommand, SqlParameters);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
return dbCommand.ExecuteScalar();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
dbConnection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static object QuerySqlDatabaseScalar(string Server, string Database, string SqlQuery, Hashtable SqlParameters)
|
||||||
|
{
|
||||||
|
return QuerySqlDatabaseScalar(Server, Database, null, null, SqlQuery, SqlParameters);
|
||||||
|
}
|
||||||
|
public static object QuerySqlDatabaseScalar(string Server, string Database, string SqlQuery)
|
||||||
|
{
|
||||||
|
return QuerySqlDatabaseScalar(Server, Database, null, null, SqlQuery, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ODBC
|
||||||
|
|
||||||
|
private static OdbcConnection BuildOdbcConnection(string ConnectionString)
|
||||||
|
{
|
||||||
|
return new OdbcConnection(ConnectionString);
|
||||||
|
}
|
||||||
|
private static void BuildOdbcParameters(OdbcCommand dbCommand, Hashtable OdbcParameters)
|
||||||
|
{
|
||||||
|
if (OdbcParameters != null)
|
||||||
|
{
|
||||||
|
foreach (var odbcParameterKey in OdbcParameters.Keys)
|
||||||
|
{
|
||||||
|
string key = odbcParameterKey.ToString();
|
||||||
|
dbCommand.Parameters.AddWithValue(key, OdbcParameters[odbcParameterKey]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DataTable QueryOdbcDatabase(string ConnectionString, string OdbcQuery, Hashtable OdbcParameters)
|
||||||
|
{
|
||||||
|
using (OdbcConnection dbConnection = BuildOdbcConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
using (OdbcCommand dbCommand = new OdbcCommand(OdbcQuery, dbConnection))
|
||||||
|
{
|
||||||
|
BuildOdbcParameters(dbCommand, OdbcParameters);
|
||||||
|
using (OdbcDataAdapter dbAdapter = new OdbcDataAdapter(dbCommand))
|
||||||
|
{
|
||||||
|
var dbTable = new DataTable();
|
||||||
|
dbAdapter.Fill(dbTable);
|
||||||
|
return dbTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static DataTable QueryOdbcDatabase(string ConnectionString, string OdbcQuery)
|
||||||
|
{
|
||||||
|
return QueryOdbcDatabase(ConnectionString, OdbcQuery, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object QueryOdbcDatabaseScalar(string ConnectionString, string OdbcQuery, Hashtable OdbcParameters)
|
||||||
|
{
|
||||||
|
using (OdbcConnection dbConnection = BuildOdbcConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
using (OdbcCommand dbCommand = new OdbcCommand(OdbcQuery, dbConnection))
|
||||||
|
{
|
||||||
|
BuildOdbcParameters(dbCommand, OdbcParameters);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
return dbCommand.ExecuteScalar();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
dbConnection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static object QueryOdbcDatabaseScalar(string ConnectionString, string OdbcQuery)
|
||||||
|
{
|
||||||
|
return QueryOdbcDatabaseScalar(ConnectionString, OdbcQuery, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using Disco.BI.Interop.ActiveDirectory;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions
|
||||||
|
{
|
||||||
|
public static class DeviceExt
|
||||||
|
{
|
||||||
|
public static object GetActiveDirectoryObjectValue(Device Device, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
var adMachineAccount = Device.ActiveDirectoryAccount(PropertyName);
|
||||||
|
if (adMachineAccount != null)
|
||||||
|
return adMachineAccount.GetPropertyValue(PropertyName, Index);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetActiveDirectoryStringValue(Device Device, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
var objectValue = GetActiveDirectoryObjectValue(Device, PropertyName, Index);
|
||||||
|
string stringValue = objectValue as string;
|
||||||
|
if (stringValue == null && objectValue != null)
|
||||||
|
stringValue = objectValue.ToString();
|
||||||
|
return stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetActiveDirectoryIntegerValue(Device Device, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
var objectValue = GetActiveDirectoryObjectValue(Device, PropertyName, Index);
|
||||||
|
if (objectValue == null)
|
||||||
|
return default(int);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int intValue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
intValue = (int)Convert.ChangeType(objectValue, typeof(int));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return intValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.BI.Expressions;
|
||||||
|
using Disco.BI.Expressions.Extensions.ImageResultImplementations;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using System.Collections;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions
|
||||||
|
{
|
||||||
|
public static class ImageExt
|
||||||
|
{
|
||||||
|
public static FileImageExpressionResult ImageFromFile(string AbsoluteFilePath)
|
||||||
|
{
|
||||||
|
return new FileImageExpressionResult(AbsoluteFilePath);
|
||||||
|
}
|
||||||
|
public static FileImageExpressionResult ImageFromDataStoreFile(string RelativeFilePath)
|
||||||
|
{
|
||||||
|
var configCache = new Disco.Data.Configuration.ConfigurationContext(null);
|
||||||
|
string DataStoreLocation = configCache.DataStoreLocation;
|
||||||
|
string AbsoluteFilePath = System.IO.Path.Combine(DataStoreLocation, RelativeFilePath);
|
||||||
|
return new FileImageExpressionResult(AbsoluteFilePath);
|
||||||
|
}
|
||||||
|
public static FileImageExpressionResult JobAttachmentFirstImage(Job Job, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
var attachment = Job.JobAttachments.FirstOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
if (attachment != null)
|
||||||
|
{
|
||||||
|
var filename = attachment.RepositoryFilename(dbContext);
|
||||||
|
return new FileImageExpressionResult(filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public static FileImageExpressionResult JobAttachmentLastImage(Job Job, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
var attachment = Job.JobAttachments.LastOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
if (attachment != null)
|
||||||
|
{
|
||||||
|
var filename = attachment.RepositoryFilename(dbContext);
|
||||||
|
return new FileImageExpressionResult(filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public static FileImageExpressionResult JobAttachmentImage(JobAttachment JobAttachment, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (JobAttachment == null)
|
||||||
|
throw new ArgumentNullException("JobAttachment");
|
||||||
|
if (!JobAttachment.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
throw new ArgumentException("Invalid Image MimeType for Attachment");
|
||||||
|
|
||||||
|
var filename = JobAttachment.RepositoryFilename(dbContext);
|
||||||
|
return new FileImageExpressionResult(filename);
|
||||||
|
}
|
||||||
|
public static FileMontageImageExpressionResult JobAttachmentImageMontage(Job Job, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (Job == null)
|
||||||
|
throw new ArgumentNullException("Job");
|
||||||
|
if (Job.JobAttachments == null)
|
||||||
|
throw new ArgumentException("Job.JobAttachments is null", "Job");
|
||||||
|
|
||||||
|
var attachments = Job.JobAttachments.Where(a => a.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||||
|
|
||||||
|
if (attachments.Count > 0)
|
||||||
|
{
|
||||||
|
var attachmentFilepaths = attachments.Select(a => a.RepositoryFilename(dbContext)).ToList();
|
||||||
|
|
||||||
|
return new FileMontageImageExpressionResult(attachmentFilepaths);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public static FileMontageImageExpressionResult JobAttachmentsImageMontage(ArrayList JobAttachments, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (JobAttachments == null)
|
||||||
|
throw new ArgumentNullException("JobAttachments");
|
||||||
|
|
||||||
|
var attachments = JobAttachments.Cast<JobAttachment>().Where(a => a.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||||
|
|
||||||
|
if (attachments.Count > 0)
|
||||||
|
{
|
||||||
|
var attachmentFilepaths = attachments.Select(a => a.RepositoryFilename(dbContext)).ToList();
|
||||||
|
|
||||||
|
return new FileMontageImageExpressionResult(attachmentFilepaths);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BitmapImageExpressionResult ImageFromStream(Stream ImageStream)
|
||||||
|
{
|
||||||
|
if (ImageStream == null)
|
||||||
|
throw new ArgumentNullException("ImageStream");
|
||||||
|
|
||||||
|
return new BitmapImageExpressionResult(Bitmap.FromStream(ImageStream));
|
||||||
|
}
|
||||||
|
public static BitmapImageExpressionResult ImageFromByteArray(byte[] ImageByteArray)
|
||||||
|
{
|
||||||
|
if (ImageByteArray == null)
|
||||||
|
throw new ArgumentNullException("ImageByteArray");
|
||||||
|
|
||||||
|
return new BitmapImageExpressionResult(Bitmap.FromStream(new MemoryStream(ImageByteArray)));
|
||||||
|
}
|
||||||
|
public static BitmapImageExpressionResult DeviceModelImage(DeviceModel DeviceModel)
|
||||||
|
{
|
||||||
|
if (DeviceModel == null)
|
||||||
|
throw new ArgumentNullException("DeviceModel");
|
||||||
|
|
||||||
|
using (Stream deviceModelImage = DeviceModel.Image())
|
||||||
|
{
|
||||||
|
if (deviceModelImage == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return ImageFromStream(deviceModelImage);
|
||||||
|
}
|
||||||
|
//if (DeviceModel.Image == null || DeviceModel.Image.Length == 0)
|
||||||
|
// return null;
|
||||||
|
|
||||||
|
//return ImageFromByteArray(DeviceModel.Image);
|
||||||
|
}
|
||||||
|
public static BitmapImageExpressionResult OrganisationLogo()
|
||||||
|
{
|
||||||
|
var configCache = new Disco.Data.Configuration.ConfigurationContext(null);
|
||||||
|
BitmapImageExpressionResult result;
|
||||||
|
using (var orgLogo = configCache.OrganisationLogo)
|
||||||
|
{
|
||||||
|
result = ImageFromStream(orgLogo);
|
||||||
|
}
|
||||||
|
result.LosslessFormat = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.BI.Expressions;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions.ImageResultImplementations
|
||||||
|
{
|
||||||
|
public abstract class BaseImageExpressionResult : IImageExpressionResult
|
||||||
|
{
|
||||||
|
public byte Quality { get; set; }
|
||||||
|
public bool LosslessFormat { get; set; }
|
||||||
|
public bool ShowField { get; set; }
|
||||||
|
public string BackgroundColour { get; set; }
|
||||||
|
public bool BackgroundPreferTransparent { get; set; }
|
||||||
|
|
||||||
|
public BaseImageExpressionResult()
|
||||||
|
{
|
||||||
|
this.LosslessFormat = false;
|
||||||
|
this.Quality = 90;
|
||||||
|
this.ShowField = false;
|
||||||
|
this.BackgroundPreferTransparent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Stream GetImage(int Width, int Height);
|
||||||
|
|
||||||
|
protected Stream RenderImage(Image SourceImage, int Width, int Height)
|
||||||
|
{
|
||||||
|
if (SourceImage == null)
|
||||||
|
throw new ArgumentNullException("SourceImage");
|
||||||
|
if (Width <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException("Width", "Width must be > 0");
|
||||||
|
if (Height <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException("Height", "Height must be > 0");
|
||||||
|
|
||||||
|
Brush backgroundBrush = null;
|
||||||
|
if (!LosslessFormat || !BackgroundPreferTransparent)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(this.BackgroundColour))
|
||||||
|
backgroundBrush = Brushes.White;
|
||||||
|
else
|
||||||
|
backgroundBrush = new SolidBrush(ColorTranslator.FromHtml(this.BackgroundColour));
|
||||||
|
}
|
||||||
|
|
||||||
|
using (Image resizedImage = SourceImage.ResizeImage(Width, Height, backgroundBrush))
|
||||||
|
{
|
||||||
|
return OutputImage(resizedImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Stream OutputImage(Image SourceImage)
|
||||||
|
{
|
||||||
|
MemoryStream imageStream = new MemoryStream();
|
||||||
|
if (LosslessFormat)
|
||||||
|
{ // Lossless Format - PNG
|
||||||
|
SourceImage.SavePng(imageStream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Lossy Format - JPG
|
||||||
|
byte quality = Math.Min((byte)100, Math.Max((byte)1, this.Quality));
|
||||||
|
SourceImage.SaveJpg(quality, imageStream);
|
||||||
|
}
|
||||||
|
imageStream.Position = 0;
|
||||||
|
return imageStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions.ImageResultImplementations
|
||||||
|
{
|
||||||
|
public class BitmapImageExpressionResult : BaseImageExpressionResult
|
||||||
|
{
|
||||||
|
public Image Image { get; set; }
|
||||||
|
|
||||||
|
public BitmapImageExpressionResult(Image Image)
|
||||||
|
{
|
||||||
|
if (Image == null)
|
||||||
|
throw new ArgumentNullException("Image");
|
||||||
|
|
||||||
|
this.Image = Image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream GetImage(int Width, int Height)
|
||||||
|
{
|
||||||
|
return this.RenderImage(this.Image, Width, Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions.ImageResultImplementations
|
||||||
|
{
|
||||||
|
public class FileImageExpressionResult : BaseImageExpressionResult
|
||||||
|
{
|
||||||
|
public string AbsoluteFilePath { get; set; }
|
||||||
|
|
||||||
|
public FileImageExpressionResult(string AbsoluteFilePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(AbsoluteFilePath))
|
||||||
|
throw new ArgumentNullException("AbsoluteFilePath");
|
||||||
|
if (!File.Exists(AbsoluteFilePath))
|
||||||
|
throw new FileNotFoundException("Image not found", AbsoluteFilePath);
|
||||||
|
|
||||||
|
this.AbsoluteFilePath = AbsoluteFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream GetImage(int Width, int Height)
|
||||||
|
{
|
||||||
|
using (Image SourceImage = Bitmap.FromFile(this.AbsoluteFilePath))
|
||||||
|
{
|
||||||
|
return this.RenderImage(SourceImage, Width, Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions.ImageResultImplementations
|
||||||
|
{
|
||||||
|
public class FileMontageImageExpressionResult : BaseImageExpressionResult
|
||||||
|
{
|
||||||
|
public List<string> AbsoluteFilePaths { get; set; }
|
||||||
|
public bool MontageHorizontalLayout { get; set; }
|
||||||
|
public bool MontageVerticalLayout { get; set; }
|
||||||
|
public bool MontageTableLayout { get; set; }
|
||||||
|
public int Padding { get; set; }
|
||||||
|
|
||||||
|
public FileMontageImageExpressionResult(List<string> AbsoluteFilePaths)
|
||||||
|
{
|
||||||
|
if (AbsoluteFilePaths == null)
|
||||||
|
throw new ArgumentNullException("AbsoluteFilePaths");
|
||||||
|
if (AbsoluteFilePaths.Count == 0)
|
||||||
|
throw new ArgumentException("AbsoluteFilePaths is empty", "AbsoluteFilePaths");
|
||||||
|
|
||||||
|
this.AbsoluteFilePaths = AbsoluteFilePaths;
|
||||||
|
this.MontageTableLayout = true;
|
||||||
|
this.Padding = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream GetImage(int Width, int Height)
|
||||||
|
{
|
||||||
|
List<Image> Images = new List<Image>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load Images
|
||||||
|
foreach (string imageFilePath in this.AbsoluteFilePaths)
|
||||||
|
Images.Add(Bitmap.FromFile(imageFilePath));
|
||||||
|
|
||||||
|
// Build Montage
|
||||||
|
using (Bitmap montageImage = new Bitmap(Width, Height))
|
||||||
|
{
|
||||||
|
using (Graphics montageGraphics = Graphics.FromImage(montageImage))
|
||||||
|
{
|
||||||
|
montageGraphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||||
|
montageGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||||
|
montageGraphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||||
|
|
||||||
|
// Draw Background
|
||||||
|
if (!LosslessFormat || !BackgroundPreferTransparent)
|
||||||
|
{
|
||||||
|
Brush backgroundBrush = Brushes.White;
|
||||||
|
if (!string.IsNullOrEmpty(this.BackgroundColour))
|
||||||
|
backgroundBrush = new SolidBrush(ColorTranslator.FromHtml(this.BackgroundColour));
|
||||||
|
montageGraphics.FillRectangle(backgroundBrush, montageGraphics.VisibleClipBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.MontageHorizontalLayout)
|
||||||
|
DoHorizontalLayout(Images, montageGraphics);
|
||||||
|
else
|
||||||
|
if (this.MontageVerticalLayout)
|
||||||
|
DoVirticalLayout(Images, montageGraphics);
|
||||||
|
else
|
||||||
|
DoTableLayout(Images, montageGraphics);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.OutputImage(montageImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception) { throw; }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Dispose of any Images
|
||||||
|
if (Images != null)
|
||||||
|
foreach (Image i in Images)
|
||||||
|
i.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoHorizontalLayout(List<Image> Images, Graphics MontageGraphics)
|
||||||
|
{
|
||||||
|
|
||||||
|
float imageScale;
|
||||||
|
float imagePosition = 0;
|
||||||
|
int imagesWidthTotal = Images.Sum(i => i.Width);
|
||||||
|
int imagesHeightMax = Images.Max(i => i.Height);
|
||||||
|
int imagesPadding = ((Images.Count - 1) * this.Padding);
|
||||||
|
|
||||||
|
imageScale = (float)(MontageGraphics.VisibleClipBounds.Width - imagesPadding) / (float)imagesWidthTotal;
|
||||||
|
if ((MontageGraphics.VisibleClipBounds.Height / (float)imagesHeightMax) < imageScale)
|
||||||
|
imageScale = (float)MontageGraphics.VisibleClipBounds.Height / (float)imagesHeightMax;
|
||||||
|
foreach (Image image in Images)
|
||||||
|
{
|
||||||
|
MontageGraphics.DrawImageResized(image, imageScale, imagePosition, 0);
|
||||||
|
imagePosition += (imageScale * image.Width) + this.Padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void DoVirticalLayout(List<Image> Images, Graphics MontageGraphics)
|
||||||
|
{
|
||||||
|
float imageScale;
|
||||||
|
float imagePosition = 0;
|
||||||
|
int imagesWidthMax = Images.Max(i => i.Width);
|
||||||
|
int imagesHeightTotal = Images.Sum(i => i.Height);
|
||||||
|
int imagesPadding = ((Images.Count - 1) * this.Padding);
|
||||||
|
|
||||||
|
imageScale = (float)(MontageGraphics.VisibleClipBounds.Height - imagesPadding) / (float)imagesHeightTotal;
|
||||||
|
if ((MontageGraphics.VisibleClipBounds.Width / (float)imagesWidthMax) < imageScale)
|
||||||
|
imageScale = (float)MontageGraphics.VisibleClipBounds.Width / (float)imagesWidthMax;
|
||||||
|
foreach (Image image in Images)
|
||||||
|
{
|
||||||
|
MontageGraphics.DrawImageResized(image, imageScale, 0, imagePosition);
|
||||||
|
imagePosition += (imageScale * image.Height) + this.Padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void DoTableLayout(List<Image> Images, Graphics MontageGraphics)
|
||||||
|
{
|
||||||
|
var stageSize = MontageGraphics.VisibleClipBounds.Size.ToSize();
|
||||||
|
var itemAverageSize = new SizeF(Images.Average(i => (float)i.Size.Width), Images.Average(i => (float)i.Size.Height));
|
||||||
|
|
||||||
|
var calculatedLayout = CalculateColumnCount(stageSize, itemAverageSize, Images.Count);
|
||||||
|
|
||||||
|
SizeF cellSize = new SizeF((MontageGraphics.VisibleClipBounds.Width - ((calculatedLayout.Item1 - 1) * this.Padding)) / calculatedLayout.Item1,
|
||||||
|
(MontageGraphics.VisibleClipBounds.Height - ((calculatedLayout.Item2 - 1) * this.Padding)) / calculatedLayout.Item2);
|
||||||
|
|
||||||
|
int imageIndex = 0;
|
||||||
|
for (int rowIndex = 0; rowIndex < calculatedLayout.Item2; rowIndex++)
|
||||||
|
{
|
||||||
|
for (int columnIndex = 0; columnIndex < calculatedLayout.Item1; columnIndex++)
|
||||||
|
{
|
||||||
|
if (imageIndex < Images.Count)
|
||||||
|
{
|
||||||
|
var image = Images[imageIndex];
|
||||||
|
var cellPoint = new PointF((cellSize.Width * columnIndex) + (this.Padding * columnIndex), (cellSize.Height * rowIndex) + (this.Padding * rowIndex));
|
||||||
|
MontageGraphics.Clip = new Region(new RectangleF(cellPoint, cellSize));
|
||||||
|
MontageGraphics.DrawImageResized(image);
|
||||||
|
imageIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tuple<int, int, double> CalculateColumnCount(Size StageSize, SizeF ItemAverageSize, int ItemCount)
|
||||||
|
{
|
||||||
|
double? bestUsedSpace = null;
|
||||||
|
int bestColumnCount = 1;
|
||||||
|
int bestRowCount = 1;
|
||||||
|
double bestItemRatio = 1;
|
||||||
|
|
||||||
|
for (int columnCount = 1; columnCount <= ItemCount; columnCount++)
|
||||||
|
{
|
||||||
|
int rowCount = (int)Math.Ceiling((double)ItemCount / (double)columnCount);
|
||||||
|
|
||||||
|
int requiredWidthPadding = (columnCount - 1) * this.Padding;
|
||||||
|
int requiredHeightPadding = (rowCount - 1) * this.Padding;
|
||||||
|
Size usableStageSize = new Size(StageSize.Width - requiredWidthPadding, StageSize.Height - requiredHeightPadding);
|
||||||
|
double stageWidthRatio = (float)usableStageSize.Width / (float)usableStageSize.Height;
|
||||||
|
double stageHeightRatio = (float)usableStageSize.Height / (float)usableStageSize.Width;
|
||||||
|
|
||||||
|
int requiredWidth = (int)Math.Ceiling(ItemAverageSize.Width * columnCount);
|
||||||
|
int requiredHeight = (int)Math.Ceiling(ItemAverageSize.Height * rowCount);
|
||||||
|
|
||||||
|
int usedSpace = requiredWidth * requiredHeight;
|
||||||
|
int stageArea = Math.Max((requiredWidth * (int)Math.Ceiling(requiredWidth * stageHeightRatio)),
|
||||||
|
(requiredHeight * (int)Math.Ceiling(requiredHeight * stageWidthRatio)));
|
||||||
|
double usedStageSpace = (double)usedSpace / stageArea;
|
||||||
|
if (bestUsedSpace == null || bestUsedSpace < usedStageSpace)
|
||||||
|
{
|
||||||
|
bestUsedSpace = usedStageSpace;
|
||||||
|
bestColumnCount = columnCount;
|
||||||
|
bestRowCount = rowCount;
|
||||||
|
bestItemRatio = Math.Min((double)usableStageSize.Width / (double)requiredWidth, (double)usableStageSize.Height / (double)requiredHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple<int, int, double>(bestColumnCount, bestRowCount, bestItemRatio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using Disco.BI.Interop.ActiveDirectory;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions.Extensions
|
||||||
|
{
|
||||||
|
public static class UserExt
|
||||||
|
{
|
||||||
|
public static object GetActiveDirectoryObjectValue(User User, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
var adUserAccount = User.ActiveDirectoryAccount(PropertyName);
|
||||||
|
if (adUserAccount != null)
|
||||||
|
return adUserAccount.GetPropertyValue(PropertyName, Index);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetActiveDirectoryStringValue(User User, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
var objectValue = GetActiveDirectoryObjectValue(User, PropertyName, Index);
|
||||||
|
string stringValue = objectValue as string;
|
||||||
|
if (stringValue == null && objectValue != null)
|
||||||
|
stringValue = objectValue.ToString();
|
||||||
|
return stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetActiveDirectoryIntegerValue(User User, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
var objectValue = GetActiveDirectoryObjectValue(User, PropertyName, Index);
|
||||||
|
if (objectValue == null)
|
||||||
|
return default(int);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int intValue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
intValue = (int)Convert.ChangeType(objectValue, typeof(int));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return intValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public interface IExpressionPart
|
||||||
|
{
|
||||||
|
string RawSource { get; set; }
|
||||||
|
string Source { get; set; }
|
||||||
|
bool ErrorsAllowed { get; set; }
|
||||||
|
bool ParseError { get; }
|
||||||
|
string ParseErrorMessage { get; }
|
||||||
|
bool IsDynamic { get; set; }
|
||||||
|
object Evaluate(object ExpressionContext, System.Collections.IDictionary Variables);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Disco.BI.Expressions
|
||||||
|
{
|
||||||
|
public class TextExpressionPart : IExpressionPart
|
||||||
|
{
|
||||||
|
private string _Source;
|
||||||
|
|
||||||
|
bool IExpressionPart.ErrorsAllowed
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string IExpressionPart.Source
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this._Source;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string IExpressionPart.RawSource
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this._Source;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool IExpressionPart.IsDynamic
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool ParseError
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ParseErrorMessage
|
||||||
|
{
|
||||||
|
get { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextExpressionPart(string Source)
|
||||||
|
{
|
||||||
|
this._Source = Source;
|
||||||
|
}
|
||||||
|
object IExpressionPart.Evaluate(object ExpressionContext, System.Collections.IDictionary Variables)
|
||||||
|
{
|
||||||
|
return this._Source;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class AttachmentActionExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
#region Delete
|
||||||
|
public static bool CanDelete(this DeviceAttachment da)
|
||||||
|
{
|
||||||
|
return true; // Placeholder - Currently Can Always Delete;
|
||||||
|
}
|
||||||
|
public static void OnDelete(this DeviceAttachment da, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!da.CanDelete())
|
||||||
|
throw new InvalidOperationException("Deletion of Attachment is Denied");
|
||||||
|
|
||||||
|
da.RepositoryDelete(dbContext);
|
||||||
|
dbContext.DeviceAttachments.Remove(da);
|
||||||
|
}
|
||||||
|
public static bool CanDelete(this JobAttachment ja)
|
||||||
|
{
|
||||||
|
return true; // Placeholder - Currently Can Always Delete;
|
||||||
|
}
|
||||||
|
public static void OnDelete(this JobAttachment ja, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!ja.CanDelete())
|
||||||
|
throw new InvalidOperationException("Deletion of Attachment is Denied");
|
||||||
|
|
||||||
|
ja.RepositoryDelete(dbContext);
|
||||||
|
dbContext.JobAttachments.Remove(ja);
|
||||||
|
}
|
||||||
|
public static bool CanDelete(this UserAttachment ua)
|
||||||
|
{
|
||||||
|
return true; // Placeholder - Currently Can Always Delete;
|
||||||
|
}
|
||||||
|
public static void OnDelete(this UserAttachment ua, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!ua.CanDelete())
|
||||||
|
throw new InvalidOperationException("Deletion of Attachment is Denied");
|
||||||
|
|
||||||
|
ua.RepositoryDelete(dbContext);
|
||||||
|
dbContext.UserAttachments.Remove(ua);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using System.IO;
|
||||||
|
using Disco.BI.DocumentTemplateBI;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class AttachmentExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static bool ImportPdfAttachment(this DocumentUniqueIdentifier UniqueIdentifier, DiscoDataContext dbContext, System.IO.Stream PdfContent, byte[] PdfThumbnail)
|
||||||
|
{
|
||||||
|
|
||||||
|
UniqueIdentifier.LoadComponents(dbContext);
|
||||||
|
DocumentTemplate documentTemplate = UniqueIdentifier.DocumentTemplate;
|
||||||
|
string filename;
|
||||||
|
string comments;
|
||||||
|
|
||||||
|
if (documentTemplate == null)
|
||||||
|
{
|
||||||
|
filename = string.Format("{0}_{1:yyyyMMdd-HHmmss}.pdf", UniqueIdentifier.DataId, UniqueIdentifier.TimeStamp);
|
||||||
|
comments = string.Format("Uploaded: {0:s}", UniqueIdentifier.TimeStamp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filename = string.Format("{0}_{1:yyyyMMdd-HHmmss}.pdf", UniqueIdentifier.TemplateTypeId, UniqueIdentifier.TimeStamp);
|
||||||
|
comments = string.Format("Generated: {0:s}", UniqueIdentifier.TimeStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
User creatorUser = UserBI.UserCache.GetUser(UniqueIdentifier.CreatorId, dbContext);
|
||||||
|
if (creatorUser == null)
|
||||||
|
{
|
||||||
|
// No Creator User (or Username invalid)
|
||||||
|
creatorUser = UserBI.UserCache.CurrentUser;
|
||||||
|
}
|
||||||
|
switch (UniqueIdentifier.DataScope)
|
||||||
|
{
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||||
|
Device d = (Device)UniqueIdentifier.Data;
|
||||||
|
d.CreateAttachment(dbContext, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, documentTemplate, PdfThumbnail);
|
||||||
|
return true;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||||
|
Job j = (Job)UniqueIdentifier.Data;
|
||||||
|
j.CreateAttachment(dbContext, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, documentTemplate, PdfThumbnail);
|
||||||
|
return true;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||||
|
User u = (User)UniqueIdentifier.Data;
|
||||||
|
u.CreateAttachment(dbContext, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, documentTemplate, PdfThumbnail);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RepositoryFilename(this DeviceAttachment da, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return Path.Combine(DataStore.CreateLocation(dbContext, "DeviceAttachments", da.Timestamp), string.Format("{0}_{1}_file", da.DeviceSerialNumber, da.Id));
|
||||||
|
}
|
||||||
|
public static string RepositoryFilename(this JobAttachment ja, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return Path.Combine(DataStore.CreateLocation(dbContext, "JobAttachments", ja.Timestamp), string.Format("{0}_{1}_file", ja.JobId, ja.Id));
|
||||||
|
}
|
||||||
|
public static string RepositoryFilename(this UserAttachment ua, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return Path.Combine(DataStore.CreateLocation(dbContext, "UserAttachments", ua.Timestamp), string.Format("{0}_{1}_file", ua.UserId, ua.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string RepositoryThumbnailFilenameInternal(string DirectoryPath, string Filename)
|
||||||
|
{
|
||||||
|
return Path.Combine(DirectoryPath, Filename);
|
||||||
|
}
|
||||||
|
public static string RepositoryThumbnailFilename(this DeviceAttachment da, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return RepositoryThumbnailFilenameInternal(DataStore.CreateLocation(dbContext, "DeviceAttachments", da.Timestamp), string.Format("{0}_{1}_thumb.jpg", da.DeviceSerialNumber, da.Id));
|
||||||
|
}
|
||||||
|
public static string RepositoryThumbnailFilename(this JobAttachment ja, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return RepositoryThumbnailFilenameInternal(DataStore.CreateLocation(dbContext, "JobAttachments", ja.Timestamp), string.Format("{0}_{1}_thumb.jpg", ja.JobId, ja.Id));
|
||||||
|
}
|
||||||
|
public static string RepositoryThumbnailFilename(this UserAttachment ua, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return RepositoryThumbnailFilenameInternal(DataStore.CreateLocation(dbContext, "UserAttachments", ua.Timestamp), string.Format("{0}_{1}_thumb.jpg", ua.UserId, ua.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RepositoryDelete(this DeviceAttachment da, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
RepositoryDelete(da.RepositoryFilename(dbContext), da.RepositoryThumbnailFilename(dbContext));
|
||||||
|
}
|
||||||
|
public static void RepositoryDelete(this JobAttachment ja, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
RepositoryDelete(ja.RepositoryFilename(dbContext), ja.RepositoryThumbnailFilename(dbContext));
|
||||||
|
}
|
||||||
|
public static void RepositoryDelete(this UserAttachment ua, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
RepositoryDelete(ua.RepositoryFilename(dbContext), ua.RepositoryThumbnailFilename(dbContext));
|
||||||
|
}
|
||||||
|
private static void RepositoryDelete(params string[] filePaths)
|
||||||
|
{
|
||||||
|
foreach (string filePath in filePaths)
|
||||||
|
{
|
||||||
|
if (File.Exists(filePath))
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SaveAttachment(this DeviceAttachment da, DiscoDataContext dbContext, Stream FileContent)
|
||||||
|
{
|
||||||
|
string filePath = da.RepositoryFilename(dbContext);
|
||||||
|
SaveAttachment(filePath, FileContent);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string SaveAttachment(this JobAttachment ja, DiscoDataContext dbContext, Stream FileContent)
|
||||||
|
{
|
||||||
|
string filePath = ja.RepositoryFilename(dbContext);
|
||||||
|
SaveAttachment(filePath, FileContent);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string SaveAttachment(this UserAttachment ua, DiscoDataContext dbContext, Stream FileContent)
|
||||||
|
{
|
||||||
|
string filePath = ua.RepositoryFilename(dbContext);
|
||||||
|
SaveAttachment(filePath, FileContent);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string SaveThumbnailAttachment(this DeviceAttachment da, DiscoDataContext dbContext, byte[] FileContent)
|
||||||
|
{
|
||||||
|
string filePath = da.RepositoryThumbnailFilename(dbContext);
|
||||||
|
File.WriteAllBytes(filePath, FileContent);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string SaveThumbnailAttachment(this JobAttachment ja, DiscoDataContext dbContext, byte[] FileContent)
|
||||||
|
{
|
||||||
|
string filePath = ja.RepositoryThumbnailFilename(dbContext);
|
||||||
|
File.WriteAllBytes(filePath, FileContent);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string SaveThumbnailAttachment(this UserAttachment ua, DiscoDataContext dbContext, byte[] FileContent)
|
||||||
|
{
|
||||||
|
string filePath = ua.RepositoryThumbnailFilename(dbContext);
|
||||||
|
File.WriteAllBytes(filePath, FileContent);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
private static void SaveAttachment(string FilePath, Stream FileContent)
|
||||||
|
{
|
||||||
|
using (FileStream sw = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
FileContent.CopyTo(sw);
|
||||||
|
sw.Flush();
|
||||||
|
sw.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GenerateThumbnail(this DeviceAttachment da, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
string filePath = da.RepositoryThumbnailFilename(dbContext);
|
||||||
|
AttachmentBI.Utilities.GenerateThumbnail(da.RepositoryFilename(dbContext), da.MimeType, filePath);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string GenerateThumbnail(this JobAttachment ja, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
string filePath = ja.RepositoryThumbnailFilename(dbContext);
|
||||||
|
AttachmentBI.Utilities.GenerateThumbnail(ja.RepositoryFilename(dbContext), ja.MimeType, filePath);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string GenerateThumbnail(this UserAttachment ua, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
string filePath = ua.RepositoryThumbnailFilename(dbContext);
|
||||||
|
AttachmentBI.Utilities.GenerateThumbnail(ua.RepositoryFilename(dbContext), ua.MimeType, filePath);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string GenerateThumbnail(this DeviceAttachment da, DiscoDataContext dbContext, Stream SourceFile)
|
||||||
|
{
|
||||||
|
string filePath = da.RepositoryThumbnailFilename(dbContext);
|
||||||
|
AttachmentBI.Utilities.GenerateThumbnail(SourceFile, da.MimeType, filePath);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string GenerateThumbnail(this JobAttachment ja, DiscoDataContext dbContext, Stream SourceFile)
|
||||||
|
{
|
||||||
|
string filePath = ja.RepositoryThumbnailFilename(dbContext);
|
||||||
|
AttachmentBI.Utilities.GenerateThumbnail(SourceFile, ja.MimeType, filePath);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
public static string GenerateThumbnail(this UserAttachment ua, DiscoDataContext dbContext, Stream SourceFile)
|
||||||
|
{
|
||||||
|
string filePath = ua.RepositoryThumbnailFilename(dbContext);
|
||||||
|
AttachmentBI.Utilities.GenerateThumbnail(SourceFile, ua.MimeType, filePath);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.ClientServices;
|
||||||
|
using System.Web;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class ClientServicesExtensions
|
||||||
|
{
|
||||||
|
public static EnrolResponse BuildResponse(this Enrol request)
|
||||||
|
{
|
||||||
|
if (HttpContext.Current == null)
|
||||||
|
throw new PlatformNotSupportedException("This function can only be accessed from within ASP.NET");
|
||||||
|
|
||||||
|
string username = null;
|
||||||
|
if (HttpContext.Current.Request.IsAuthenticated)
|
||||||
|
username = HttpContext.Current.User.Identity.Name;
|
||||||
|
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
EnrolResponse response = DeviceBI.DeviceEnrol.Enrol(dbContext, username, request);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WhoAmIResponse BuildResponse(this WhoAmI request)
|
||||||
|
{
|
||||||
|
if (HttpContext.Current == null)
|
||||||
|
throw new PlatformNotSupportedException("This function can only be accessed from within ASP.NET");
|
||||||
|
|
||||||
|
string username = null;
|
||||||
|
if (HttpContext.Current.Request.IsAuthenticated)
|
||||||
|
username = HttpContext.Current.User.Identity.Name;
|
||||||
|
|
||||||
|
if (username == null)
|
||||||
|
throw new InvalidOperationException("Unauthenticated Http Context");
|
||||||
|
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
User user = UserBI.UserCache.GetUser(username, dbContext, true);
|
||||||
|
WhoAmIResponse response = new WhoAmIResponse()
|
||||||
|
{
|
||||||
|
Username = user.Id,
|
||||||
|
DisplayName = user.DisplayName,
|
||||||
|
Type = user.Type
|
||||||
|
};
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MacEnrolResponse BuildResponse(this MacEnrol request)
|
||||||
|
{
|
||||||
|
if (HttpContext.Current == null)
|
||||||
|
throw new PlatformNotSupportedException("This function can only be accessed from within ASP.NET");
|
||||||
|
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
MacEnrolResponse response = DeviceBI.DeviceEnrol.MacEnrol(dbContext, request, false);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.BI.Interop.ActiveDirectory;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class DeviceActionExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static bool CanCreateJob(this Device d)
|
||||||
|
{
|
||||||
|
return !d.DecommissionedDate.HasValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Decommission
|
||||||
|
public static bool CanDecommission(this Device d)
|
||||||
|
{
|
||||||
|
if (d.DecommissionedDate.HasValue)
|
||||||
|
return false; // Already Decommissioned
|
||||||
|
|
||||||
|
if (d.AssignedUserId != null)
|
||||||
|
return false; // User Assigned to Device
|
||||||
|
|
||||||
|
if (d.Jobs.Count(j => !j.ClosedDate.HasValue) > 0)
|
||||||
|
return false; // Device linked to > 0 Open Jobs
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static void OnDecommission(this Device d)
|
||||||
|
{
|
||||||
|
if (!d.CanDecommission())
|
||||||
|
throw new InvalidOperationException("Decommission of Device is Denied");
|
||||||
|
|
||||||
|
d.DecommissionedDate = DateTime.Now;
|
||||||
|
|
||||||
|
// Disable AD Account
|
||||||
|
if (d.ComputerName != null)
|
||||||
|
{
|
||||||
|
var adAccount = d.ActiveDirectoryAccount();
|
||||||
|
if (adAccount != null)
|
||||||
|
{
|
||||||
|
adAccount.DisableAccount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#region Recommission
|
||||||
|
public static bool CanRecommission(this Device d)
|
||||||
|
{
|
||||||
|
return d.DecommissionedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnRecommission(this Device d)
|
||||||
|
{
|
||||||
|
if (!d.CanRecommission())
|
||||||
|
throw new InvalidOperationException("Recommission of Device is Denied");
|
||||||
|
|
||||||
|
d.DecommissionedDate = null;
|
||||||
|
|
||||||
|
// Enable AD Account
|
||||||
|
if (d.ComputerName != null)
|
||||||
|
{
|
||||||
|
var adAccount = d.ActiveDirectoryAccount();
|
||||||
|
if (adAccount != null)
|
||||||
|
{
|
||||||
|
adAccount.EnableAccount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Delete
|
||||||
|
public static bool CanDelete(this Device d)
|
||||||
|
{
|
||||||
|
return d.DecommissionedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnDelete(this Device d, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Delete Jobs
|
||||||
|
foreach (Job j in dbContext.Jobs.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||||
|
{
|
||||||
|
if (j.UserId == null)
|
||||||
|
{ // No User associated, thus must Delete whole Job
|
||||||
|
if (j.CanDelete())
|
||||||
|
j.OnDelete(dbContext);
|
||||||
|
else
|
||||||
|
throw new InvalidOperationException(string.Format("Deletion of Device is Denied (See Job# {0})", j.Id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// User associated to Job, thus just remove Devices' association
|
||||||
|
j.DeviceSerialNumber = null;
|
||||||
|
|
||||||
|
// Write Job Log
|
||||||
|
JobLog jobLog = new JobLog()
|
||||||
|
{
|
||||||
|
JobId = j.Id,
|
||||||
|
TechUserId = UserBI.UserCache.CurrentUser.Id,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = string.Format("Device Deleted{0}{0}Serial Number: {1}{0}Computer Name: {2}{0}Model: {3}{0}Profile: {4}",
|
||||||
|
Environment.NewLine, d.SerialNumber, d.ComputerName, d.DeviceModel, d.DeviceProfile)
|
||||||
|
};
|
||||||
|
dbContext.JobLogs.Add(jobLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable Wireless Certificates
|
||||||
|
foreach (var wc in dbContext.DeviceCertificates.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||||
|
{
|
||||||
|
wc.DeviceSerialNumber = null;
|
||||||
|
wc.Enabled = false;
|
||||||
|
}
|
||||||
|
// Delete Device Details
|
||||||
|
foreach (var dd in dbContext.DeviceDetails.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||||
|
dbContext.DeviceDetails.Remove(dd);
|
||||||
|
// Delete Device Attachments
|
||||||
|
foreach (var da in dbContext.DeviceAttachments.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||||
|
{
|
||||||
|
da.RepositoryDelete(dbContext);
|
||||||
|
dbContext.DeviceAttachments.Remove(da);
|
||||||
|
}
|
||||||
|
// Delete Device User Assignments
|
||||||
|
foreach (var dua in dbContext.DeviceUserAssignments.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||||
|
dbContext.DeviceUserAssignments.Remove(dua);
|
||||||
|
|
||||||
|
dbContext.Devices.Remove(d);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class DeviceBatchExtensions
|
||||||
|
{
|
||||||
|
public static bool CanDelete(this DeviceBatch db, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Can't Delete if Contains Devices
|
||||||
|
var deviceCount = dbContext.Devices.Count(d => d.DeviceBatchId == db.Id);
|
||||||
|
if (deviceCount > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Delete(this DeviceBatch db, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!db.CanDelete(dbContext))
|
||||||
|
throw new InvalidOperationException("The state of this Device Batch doesn't allow it to be deleted");
|
||||||
|
|
||||||
|
// Delete Batch
|
||||||
|
dbContext.DeviceBatches.Remove(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Services.Plugins;
|
||||||
|
using Disco.Services.Plugins.Features.CertificateProvider;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class DeviceCertificateExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static Tuple<DeviceCertificate, List<string>> AllocateCertificate(this Device device, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(device.DeviceProfile.CertificateProviderId))
|
||||||
|
{
|
||||||
|
// REMOVED 2012-07-18 G# - Plugin is responsible for checking
|
||||||
|
//var deviceCertificates = dbContext.DeviceCertificates.Where(c =>
|
||||||
|
// c.DeviceSerialNumber == device.SerialNumber &&
|
||||||
|
// c.ProviderId == device.DeviceProfile.CertificateProviderId &&
|
||||||
|
// c.Enabled == true).ToList();
|
||||||
|
|
||||||
|
// Load Plugin
|
||||||
|
PluginFeatureManifest featureManifest = Plugins.GetPluginFeature(device.DeviceProfile.CertificateProviderId, typeof(CertificateProviderFeature));
|
||||||
|
|
||||||
|
using (CertificateProviderFeature providerFeature = featureManifest.CreateInstance<CertificateProviderFeature>())
|
||||||
|
{
|
||||||
|
// REMOVED 2012-07-18 G# - Plugin is responsible for checking
|
||||||
|
// Already Allocated Certificate
|
||||||
|
//if (deviceCertificates.Count > 0)
|
||||||
|
// return new Tuple<DeviceCertificate, List<string>>(deviceCertificates[0], providerPlugin.RemoveExistingCertificateNames());
|
||||||
|
//else
|
||||||
|
|
||||||
|
return providerFeature.AllocateCertificate(dbContext, device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device Profile does not allow certificate allocation
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Disco.BI.Interop.ActiveDirectory;
|
||||||
|
using Disco.Data.Configuration;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.DocumentTemplates;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Disco.Models.Interop.ActiveDirectory;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class DeviceExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static string ComputerNameRender(this Device device, DiscoDataContext context)
|
||||||
|
{
|
||||||
|
DeviceProfile deviceProfile = device.DeviceProfile;
|
||||||
|
Expressions.Expression computerNameTemplateExpression = null;
|
||||||
|
computerNameTemplateExpression = Expressions.ExpressionCache.GetValue(DeviceProfileExtensions.ComputerNameExpressionCacheModule, deviceProfile.Id.ToString(), () =>
|
||||||
|
{
|
||||||
|
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
|
||||||
|
//return Expressions.Expression.TokenizeSingleDynamic(null, deviceProfile.Configuration(context).ComputerNameTemplate, 0);
|
||||||
|
return Expressions.Expression.TokenizeSingleDynamic(null, deviceProfile.ComputerNameTemplate, 0);
|
||||||
|
});
|
||||||
|
System.Collections.IDictionary evaluatorVariables = Expressions.Expression.StandardVariables(null, context, UserBI.UserCache.CurrentUser, System.DateTime.Now, null);
|
||||||
|
string rendered;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rendered = computerNameTemplateExpression.EvaluateFirst<string>(device, evaluatorVariables);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("An error occurred rendering the computer name: [{0}] {1}", ex.GetType().Name, ex.Message), ex.InnerException);
|
||||||
|
}
|
||||||
|
if (rendered == null || rendered.Length > 24)
|
||||||
|
{
|
||||||
|
throw new System.InvalidOperationException("The rendered computer name would be invalid or longer than 24 characters");
|
||||||
|
}
|
||||||
|
return rendered.ToString();
|
||||||
|
}
|
||||||
|
public static System.Collections.Generic.List<DocumentTemplate> AvailableDocumentTemplates(this Device d, DiscoDataContext Context, User User, System.DateTime TimeStamp)
|
||||||
|
{
|
||||||
|
List<DocumentTemplate> ats = Context.DocumentTemplates
|
||||||
|
.Where(at => at.Scope == Disco.Models.Repository.DocumentTemplate.DocumentTemplateScopes.Device).ToList();
|
||||||
|
|
||||||
|
return ats.Where(at => at.FilterExpressionMatches(d, Context, User, TimeStamp, DocumentState.DefaultState())).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool UpdateLastNetworkLogonDate(this Device Device)
|
||||||
|
{
|
||||||
|
return ActiveDirectoryUpdateLastNetworkLogonDateJob.UpdateLastNetworkLogonDate(Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeviceAttachment CreateAttachment(this Device Device, DiscoDataContext dbContext, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
|
||||||
|
|
||||||
|
DeviceAttachment da = new DeviceAttachment()
|
||||||
|
{
|
||||||
|
DeviceSerialNumber = Device.SerialNumber,
|
||||||
|
TechUserId = CreatorUser.Id,
|
||||||
|
Filename = Filename,
|
||||||
|
MimeType = MimeType,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = Comments
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DocumentTemplate != null)
|
||||||
|
da.DocumentTemplateId = DocumentTemplate.Id;
|
||||||
|
|
||||||
|
dbContext.DeviceAttachments.Add(da);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
da.SaveAttachment(dbContext, Content);
|
||||||
|
Content.Position = 0;
|
||||||
|
if (PdfThumbnail == null)
|
||||||
|
da.GenerateThumbnail(dbContext, Content);
|
||||||
|
else
|
||||||
|
da.SaveThumbnailAttachment(dbContext, PdfThumbnail);
|
||||||
|
|
||||||
|
return da;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Device AddOffline(this Device d, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Just Include:
|
||||||
|
// - Serial Number
|
||||||
|
// - Asset Number
|
||||||
|
// - Profile Id
|
||||||
|
// - Assigned User Id
|
||||||
|
// - Batch
|
||||||
|
|
||||||
|
// Batch
|
||||||
|
DeviceBatch db = default(DeviceBatch);
|
||||||
|
if (d.DeviceBatchId.HasValue)
|
||||||
|
db = dbContext.DeviceBatches.Find(d.DeviceBatchId.Value);
|
||||||
|
|
||||||
|
// Default Device Model
|
||||||
|
DeviceModel dm = default(DeviceModel);
|
||||||
|
if (db != null && db.DefaultDeviceModelId.HasValue)
|
||||||
|
dm = dbContext.DeviceModels.Find(db.DefaultDeviceModelId); // From Batch
|
||||||
|
else
|
||||||
|
dm = dbContext.DeviceModels.Find(1); // Default
|
||||||
|
|
||||||
|
Device d2 = new Device()
|
||||||
|
{
|
||||||
|
SerialNumber = d.SerialNumber.ToUpper(),
|
||||||
|
AssetNumber = d.AssetNumber,
|
||||||
|
Location = d.Location,
|
||||||
|
CreatedDate = DateTime.Now,
|
||||||
|
DeviceProfileId = d.DeviceProfileId,
|
||||||
|
DeviceProfile = dbContext.DeviceProfiles.Find(d.DeviceProfileId),
|
||||||
|
AllowUnauthenticatedEnrol = true,
|
||||||
|
Active = true,
|
||||||
|
DeviceModelId = dm.Id,
|
||||||
|
DeviceModel = dm,
|
||||||
|
DeviceBatchId = d.DeviceBatchId,
|
||||||
|
DeviceBatch = db
|
||||||
|
};
|
||||||
|
|
||||||
|
dbContext.Devices.Add(d2);
|
||||||
|
if (!string.IsNullOrEmpty(d.AssignedUserId))
|
||||||
|
{
|
||||||
|
User u = UserBI.UserCache.GetUser(d.AssignedUserId, dbContext);
|
||||||
|
d2.AssignDevice(dbContext, u);
|
||||||
|
}
|
||||||
|
|
||||||
|
return d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeviceUserAssignment AssignDevice(this Device d, DiscoDataContext dbContext, User u)
|
||||||
|
{
|
||||||
|
DeviceUserAssignment newDua = default(DeviceUserAssignment);
|
||||||
|
|
||||||
|
// Mark existing assignments as Unassigned
|
||||||
|
foreach (var dua in dbContext.DeviceUserAssignments.Where(m => m.DeviceSerialNumber == d.SerialNumber && !m.UnassignedDate.HasValue))
|
||||||
|
dua.UnassignedDate = DateTime.Now;
|
||||||
|
|
||||||
|
if (u != null)
|
||||||
|
{
|
||||||
|
// Add new Assignment
|
||||||
|
newDua = new DeviceUserAssignment()
|
||||||
|
{
|
||||||
|
DeviceSerialNumber = d.SerialNumber,
|
||||||
|
AssignedUserId = u.Id,
|
||||||
|
AssignedDate = DateTime.Now
|
||||||
|
};
|
||||||
|
dbContext.DeviceUserAssignments.Add(newDua);
|
||||||
|
|
||||||
|
d.AssignedUserId = u.Id;
|
||||||
|
d.AssignedUser = u;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.AssignedUserId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update AD Account
|
||||||
|
if (!string.IsNullOrEmpty(d.ComputerName) && d.ComputerName.Length <= 24)
|
||||||
|
{
|
||||||
|
var adMachineAccount = Interop.ActiveDirectory.ActiveDirectory.GetMachineAccount(d.ComputerName);
|
||||||
|
if (adMachineAccount != null)
|
||||||
|
{
|
||||||
|
if (newDua == null)
|
||||||
|
adMachineAccount.SetDescription(string.Empty);
|
||||||
|
else
|
||||||
|
adMachineAccount.SetDescription(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newDua;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ActiveDirectoryMachineAccount ActiveDirectoryAccount(this Device Device, params string[] AdditionalProperties)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Device.ComputerName))
|
||||||
|
return Interop.ActiveDirectory.ActiveDirectory.GetMachineAccount(Device.ComputerName, AdditionalProperties: AdditionalProperties);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class DeviceModelExtensions
|
||||||
|
{
|
||||||
|
public static bool ImageImport(this DeviceModel deviceModel, Stream ImageStream)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (Bitmap inputBitmap = new Bitmap(ImageStream))
|
||||||
|
{
|
||||||
|
using (Image outputBitmap = inputBitmap.ResizeImage(255, 255))
|
||||||
|
{
|
||||||
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
outputBitmap.SavePng(ms);
|
||||||
|
ms.Position = 0;
|
||||||
|
|
||||||
|
var deviceModelImagePath = deviceModel.ImageFilePath();
|
||||||
|
|
||||||
|
|
||||||
|
using (var storeStream = new FileStream(deviceModelImagePath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
ms.CopyTo(storeStream);
|
||||||
|
}
|
||||||
|
//deviceModel.Image = ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileStream Image(this DeviceModel deviceModel)
|
||||||
|
{
|
||||||
|
var deviceModelImagePath = deviceModel.ImageFilePath();
|
||||||
|
|
||||||
|
if (File.Exists(deviceModelImagePath))
|
||||||
|
return new FileStream(deviceModelImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ImageFilePath(this DeviceModel deviceModel)
|
||||||
|
{
|
||||||
|
var configCache = new Disco.Data.Configuration.ConfigurationContext(null);
|
||||||
|
|
||||||
|
var deviceModelImagesDataStore = DataStore.CreateLocation(configCache, "DeviceModelImages");
|
||||||
|
|
||||||
|
return Path.Combine(deviceModelImagesDataStore, string.Format("{0}.png", deviceModel.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ImageHash(this DeviceModel deviceModel)
|
||||||
|
{
|
||||||
|
var deviceModelImagePath = deviceModel.ImageFilePath();
|
||||||
|
|
||||||
|
if (File.Exists(deviceModelImagePath))
|
||||||
|
return File.GetLastWriteTimeUtc(deviceModelImagePath).ToBinary().ToString();
|
||||||
|
else
|
||||||
|
return "-1";
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Actions
|
||||||
|
// Added 2012-11-26 G# - Need ability to delete Device Models
|
||||||
|
public static bool CanDelete(this DeviceModel dm, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Can't Delete Default Model (Id: 1)
|
||||||
|
if (dm.Id == 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Can't Delete if Contains Devices
|
||||||
|
if (dbContext.Devices.Count(d => d.DeviceModelId == dm.Id) > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static void Delete(this DeviceModel dm, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!dm.CanDelete(dbContext))
|
||||||
|
throw new InvalidOperationException("The state of this Device Model doesn't allow it to be deleted");
|
||||||
|
|
||||||
|
// Delete Model
|
||||||
|
dbContext.DeviceModels.Remove(dm);
|
||||||
|
}
|
||||||
|
// End Added 2012-11-26 G#
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Data.Configuration.Modules;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class DeviceProfileExtensions
|
||||||
|
{
|
||||||
|
public const string ComputerNameExpressionCacheModule = "ComputerNameTemplate";
|
||||||
|
|
||||||
|
public static void ComputerNameInvalidateCache(this DeviceProfile deviceProfile)
|
||||||
|
{
|
||||||
|
Expressions.ExpressionCache.InvalidateKey(ComputerNameExpressionCacheModule, deviceProfile.Id.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanDelete(this DeviceProfile dp, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Can't Delete Default Profile (Id: 1)
|
||||||
|
if (dp.Id == 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Can't Delete if Contains Devices
|
||||||
|
if (dbContext.Devices.Count(d => d.DeviceProfileId == dp.Id) > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static void Delete(this DeviceProfile dp, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!dp.CanDelete(dbContext))
|
||||||
|
throw new InvalidOperationException("The state of this Device Profile doesn't allow it to be deleted");
|
||||||
|
|
||||||
|
// Update Defaults
|
||||||
|
if (dbContext.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId == dp.Id)
|
||||||
|
dbContext.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId = 1;
|
||||||
|
if (dbContext.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId == dp.Id)
|
||||||
|
dbContext.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId = 1;
|
||||||
|
|
||||||
|
// Delete Profile
|
||||||
|
dbContext.DeviceProfiles.Remove(dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
|
||||||
|
//public static DeviceProfileConfiguration Configuration(this DeviceProfile dp, DiscoDataContext dbContext)
|
||||||
|
//{
|
||||||
|
// return dbContext.DiscoConfiguration.DeviceProfiles.DeviceProfile(dp);
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.DocumentTemplates;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using iTextSharp.text.pdf;
|
||||||
|
using Disco.BI.Expressions;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using Disco.BI.DocumentTemplateBI;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class DocumentTemplateExtensions
|
||||||
|
{
|
||||||
|
private const string DocumentTemplateExpressionCacheTemplate = "DocumentTemplate_{0}";
|
||||||
|
|
||||||
|
public static string RepositoryFilename(this DocumentTemplate dt, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return System.IO.Path.Combine(DataStore.CreateLocation(dbContext, "DocumentTemplates"), string.Format("{0}.pdf", dt.Id));
|
||||||
|
}
|
||||||
|
public static string SavePdfTemplate(this DocumentTemplate dt, DiscoDataContext dbContext, Stream TemplateFile)
|
||||||
|
{
|
||||||
|
string filePath = dt.RepositoryFilename(dbContext);
|
||||||
|
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
TemplateFile.CopyTo(fs);
|
||||||
|
}
|
||||||
|
Expressions.ExpressionCache.InvalidModule(string.Format(DocumentTemplateExpressionCacheTemplate, dt.Id));
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DisposableImageCollection PdfPageImages(this PdfReader pdfReader, int PageNumber)
|
||||||
|
{
|
||||||
|
return Interop.Pdf.PdfImporter.GetPageImages(pdfReader, PageNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConcurrentDictionary<string, Expression> PdfExpressionsFromCache(this DocumentTemplate dt, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
string cacheModuleKey = string.Format(DocumentTemplateExpressionCacheTemplate, dt.Id);
|
||||||
|
var module = Expressions.ExpressionCache.GetModule(cacheModuleKey);
|
||||||
|
if (module == null)
|
||||||
|
{
|
||||||
|
// Cache
|
||||||
|
string templateFilename = dt.RepositoryFilename(dbContext);
|
||||||
|
PdfReader pdfReader = new PdfReader(templateFilename);
|
||||||
|
int pdfFieldOrdinal = 0;
|
||||||
|
foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys)
|
||||||
|
{
|
||||||
|
var pdfFieldValue = pdfReader.AcroFields.GetField(pdfFieldKey);
|
||||||
|
Expressions.ExpressionCache.SetValue(cacheModuleKey, pdfFieldKey, Expressions.Expression.Tokenize(pdfFieldKey, pdfFieldValue, pdfFieldOrdinal));
|
||||||
|
pdfFieldOrdinal++;
|
||||||
|
}
|
||||||
|
pdfReader.Close();
|
||||||
|
module = Expressions.ExpressionCache.GetModule(cacheModuleKey, true);
|
||||||
|
}
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<BI.Expressions.Expression> ExtractPdfExpressions(this DocumentTemplate dt, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return dt.PdfExpressionsFromCache(dbContext).Values.OrderBy(e => e.Ordinal).ToList();
|
||||||
|
}
|
||||||
|
public static System.IO.Stream GeneratePdfBulk(this DocumentTemplate dt, DiscoDataContext dbContext, User CreatorUser, System.DateTime Timestamp, params string[] DataObjectsIds)
|
||||||
|
{
|
||||||
|
return Interop.Pdf.PdfGenerator.GenerateBulkFromTemplate(dt, dbContext, CreatorUser, Timestamp, DataObjectsIds);
|
||||||
|
}
|
||||||
|
public static System.IO.Stream GeneratePdfBulk(this DocumentTemplate dt, DiscoDataContext dbContext, User CreatorUser, System.DateTime Timestamp, params object[] DataObjects)
|
||||||
|
{
|
||||||
|
return Interop.Pdf.PdfGenerator.GenerateBulkFromTemplate(dt, dbContext, CreatorUser, Timestamp, DataObjects);
|
||||||
|
}
|
||||||
|
public static System.IO.Stream GeneratePdf(this DocumentTemplate dt, DiscoDataContext dbContext, object Data, User CreatorUser, System.DateTime TimeStamp, DocumentState State, bool FlattenFields = false)
|
||||||
|
{
|
||||||
|
return Interop.Pdf.PdfGenerator.GenerateFromTemplate(dt, dbContext, Data, CreatorUser, TimeStamp, State, FlattenFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression FilterExpressionFromCache(this DocumentTemplate dt)
|
||||||
|
{
|
||||||
|
return ExpressionCache.GetValue("DocumentTemplateFilterExpression", dt.Id, () => { return Expression.TokenizeSingleDynamic(null, dt.FilterExpression, 0); });
|
||||||
|
}
|
||||||
|
public static void FilterExpressionInvalidateCache(this DocumentTemplate dt)
|
||||||
|
{
|
||||||
|
ExpressionCache.InvalidateKey("DocumentTemplateFilterExpression", dt.Id);
|
||||||
|
}
|
||||||
|
public static bool FilterExpressionMatches(this DocumentTemplate dt, object Data, DiscoDataContext DataContext, User User, System.DateTime TimeStamp, DocumentState State)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(dt.FilterExpression))
|
||||||
|
{
|
||||||
|
Expression compiledExpression = dt.FilterExpressionFromCache();
|
||||||
|
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, DataContext, User, TimeStamp, State);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object er = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
|
||||||
|
if (er is bool)
|
||||||
|
{
|
||||||
|
return (bool)er;
|
||||||
|
}
|
||||||
|
bool erBool;
|
||||||
|
if (bool.TryParse(er.ToString(), out erBool))
|
||||||
|
{
|
||||||
|
return erBool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static string GetDataId(this DocumentTemplate dt, object Data)
|
||||||
|
{
|
||||||
|
if (Data is string)
|
||||||
|
{
|
||||||
|
return (string)Data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (dt.Scope)
|
||||||
|
{
|
||||||
|
case Models.Repository.DocumentTemplate.DocumentTemplateScopes.Device:
|
||||||
|
if (!(Data is Device))
|
||||||
|
throw new ArgumentException("This Document Template is configured for Devices only", "Data");
|
||||||
|
Device d = (Device)Data;
|
||||||
|
return d.SerialNumber;
|
||||||
|
case Models.Repository.DocumentTemplate.DocumentTemplateScopes.Job:
|
||||||
|
if (!(Data is Job))
|
||||||
|
throw new ArgumentException("This Document Template is configured for Jobs only", "Data");
|
||||||
|
Job d2 = (Job)Data;
|
||||||
|
return d2.Id.ToString();
|
||||||
|
case Models.Repository.DocumentTemplate.DocumentTemplateScopes.User:
|
||||||
|
if (!(Data is User))
|
||||||
|
throw new ArgumentException("This Document Template is configured for Users only", "Data");
|
||||||
|
User d3 = (User)Data;
|
||||||
|
return d3.Id;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException("Invalid Document Template Scope");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string UniqueIdentifier(string DocumentTemplateId, string DataId, string CreatorId, System.DateTime Timestamp)
|
||||||
|
{
|
||||||
|
return string.Format("Disco|1|{0}|{1}|{2}|{3:s}",
|
||||||
|
DocumentTemplateId,
|
||||||
|
DataId,
|
||||||
|
CreatorId,
|
||||||
|
Timestamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public static string UniqueIdentifier(this DocumentTemplate dt, object Data, string CreatorId, System.DateTime Timestamp)
|
||||||
|
{
|
||||||
|
return string.Format("Disco|1|{0}|{1}|{2}|{3:s}",
|
||||||
|
dt.Id,
|
||||||
|
dt.GetDataId(System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(Data)),
|
||||||
|
CreatorId,
|
||||||
|
Timestamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public static string UniquePageIdentifier(this DocumentTemplate dt, object Data, string CreatorId, System.DateTime Timestamp, int Page)
|
||||||
|
{
|
||||||
|
return string.Format("Disco|1|{0}|{1}|{2}|{3:s}|{4}",
|
||||||
|
dt.Id,
|
||||||
|
dt.GetDataId(System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(Data)),
|
||||||
|
CreatorId,
|
||||||
|
Timestamp,
|
||||||
|
Page
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public static List<RectangleF> QRCodeLocations(this DocumentTemplate dt, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return DocumentTemplateBI.DocumentTemplateQRCodeLocationCache.GetLocations(dt, dbContext);
|
||||||
|
}
|
||||||
|
public static void Delete(this DocumentTemplate dt, DiscoDataContext Context)
|
||||||
|
{
|
||||||
|
// Find & Rename all references
|
||||||
|
foreach (DeviceAttachment a in Context.DeviceAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
||||||
|
{
|
||||||
|
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
|
||||||
|
if (a.Comments.Length > 500)
|
||||||
|
a.Comments = a.Comments.Substring(0, 500);
|
||||||
|
a.DocumentTemplateId = null;
|
||||||
|
a.DocumentTemplate = null;
|
||||||
|
}
|
||||||
|
foreach (JobAttachment a in Context.JobAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
||||||
|
{
|
||||||
|
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
|
||||||
|
if (a.Comments.Length > 500)
|
||||||
|
a.Comments = a.Comments.Substring(0, 500);
|
||||||
|
a.DocumentTemplateId = null;
|
||||||
|
a.DocumentTemplate = null;
|
||||||
|
}
|
||||||
|
foreach (UserAttachment a in Context.UserAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
||||||
|
{
|
||||||
|
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
|
||||||
|
if (a.Comments.Length > 500)
|
||||||
|
a.Comments = a.Comments.Substring(0, 500);
|
||||||
|
a.DocumentTemplateId = null;
|
||||||
|
a.DocumentTemplate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete SubTypes
|
||||||
|
dt.JobSubTypes.Clear();
|
||||||
|
|
||||||
|
// Delete Template
|
||||||
|
string templateRepositoryFilename = dt.RepositoryFilename(Context);
|
||||||
|
if (System.IO.File.Exists(templateRepositoryFilename))
|
||||||
|
System.IO.File.Delete(templateRepositoryFilename);
|
||||||
|
|
||||||
|
// Remove from Cache
|
||||||
|
dt.FilterExpressionInvalidateCache();
|
||||||
|
|
||||||
|
// Delete Document Template from Repository
|
||||||
|
Context.DocumentTemplates.Remove(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,415 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.Config;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Services.Plugins;
|
||||||
|
using Disco.Services.Plugins.Features.WarrantyProvider;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class JobActionExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
#region Device Held
|
||||||
|
public static bool CanDeviceHeld(this Job j)
|
||||||
|
{
|
||||||
|
return (!j.ClosedDate.HasValue) && (j.DeviceSerialNumber != null) &&
|
||||||
|
(!j.DeviceHeld.HasValue || j.DeviceReturnedDate.HasValue);
|
||||||
|
}
|
||||||
|
public static void OnDeviceHeld(this Job j, User Technician)
|
||||||
|
{
|
||||||
|
if (!j.CanDeviceHeld())
|
||||||
|
throw new InvalidOperationException("Holding Device was Denied");
|
||||||
|
|
||||||
|
j.DeviceHeld = DateTime.Now;
|
||||||
|
j.DeviceHeldTechUserId = Technician.Id;
|
||||||
|
j.DeviceReadyForReturn = null;
|
||||||
|
j.DeviceReadyForReturnTechUserId = null;
|
||||||
|
j.DeviceReturnedDate = null;
|
||||||
|
j.DeviceReturnedTechUserId = null;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Device Ready for Return
|
||||||
|
public static bool CanDeviceReadyForReturn(this Job j)
|
||||||
|
{
|
||||||
|
return (!j.ClosedDate.HasValue) && j.DeviceHeld.HasValue &&
|
||||||
|
!j.DeviceReadyForReturn.HasValue && !j.DeviceReturnedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnDeviceReadyForReturn(this Job j, User Technician)
|
||||||
|
{
|
||||||
|
if (!j.CanDeviceReadyForReturn())
|
||||||
|
throw new InvalidOperationException("Device Ready for Return was Denied");
|
||||||
|
|
||||||
|
j.DeviceReadyForReturn = DateTime.Now;
|
||||||
|
j.DeviceReadyForReturnTechUserId = Technician.Id;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Device Returned
|
||||||
|
public static bool CanDeviceReturned(this Job j)
|
||||||
|
{
|
||||||
|
return (!j.ClosedDate.HasValue) && j.DeviceHeld.HasValue &&
|
||||||
|
!j.DeviceReturnedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnDeviceReturned(this Job j, User Technician)
|
||||||
|
{
|
||||||
|
if (!j.CanDeviceReturned())
|
||||||
|
throw new InvalidOperationException("Device Return was Denied");
|
||||||
|
|
||||||
|
j.DeviceReturnedDate = DateTime.Now;
|
||||||
|
j.DeviceReturnedTechUserId = Technician.Id;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Waiting For User Action
|
||||||
|
public static bool CanWaitingForUserAction(this Job j)
|
||||||
|
{
|
||||||
|
return !j.ClosedDate.HasValue && (j.UserId != null) && !j.WaitingForUserAction.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnWaitingForUserAction(this Job j, DiscoDataContext dbContext, User Technician, string Reason)
|
||||||
|
{
|
||||||
|
if (!j.CanWaitingForUserAction())
|
||||||
|
throw new InvalidOperationException("Waiting for User Action was Denied");
|
||||||
|
|
||||||
|
j.WaitingForUserAction = DateTime.Now;
|
||||||
|
|
||||||
|
// Write Log
|
||||||
|
JobLog jobLog = new JobLog()
|
||||||
|
{
|
||||||
|
JobId = j.Id,
|
||||||
|
TechUserId = Technician.Id,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = string.Format("Waiting on User Action{0}Reason: {1}", Environment.NewLine, Reason)
|
||||||
|
};
|
||||||
|
dbContext.JobLogs.Add(jobLog);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Not Waiting For User Action
|
||||||
|
public static bool CanNotWaitingForUserAction(this Job j)
|
||||||
|
{
|
||||||
|
return j.WaitingForUserAction.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnNotWaitingForUserAction(this Job j, DiscoDataContext dbContext, User Technician, string Resolution)
|
||||||
|
{
|
||||||
|
if (!j.CanNotWaitingForUserAction())
|
||||||
|
throw new InvalidOperationException("Not Waiting for User Action was Denied");
|
||||||
|
|
||||||
|
j.WaitingForUserAction = null;
|
||||||
|
|
||||||
|
// Write Log
|
||||||
|
JobLog jobLog = new JobLog()
|
||||||
|
{
|
||||||
|
JobId = j.Id,
|
||||||
|
TechUserId = Technician.Id,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = string.Format("User Action Resolved{0}Resolution: {1}", Environment.NewLine, Resolution)
|
||||||
|
};
|
||||||
|
dbContext.JobLogs.Add(jobLog);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Log Warranty
|
||||||
|
public static bool CanLogWarranty(this Job j)
|
||||||
|
{
|
||||||
|
return !j.ClosedDate.HasValue &&
|
||||||
|
(j.DeviceSerialNumber != null) &&
|
||||||
|
j.JobTypeId == JobType.JobTypeIds.HWar &&
|
||||||
|
string.IsNullOrEmpty(j.JobMetaWarranty.ExternalReference);
|
||||||
|
}
|
||||||
|
public static void OnLogWarranty(this Job j, DiscoDataContext dbContext, string FaultDescription, PluginFeatureManifest WarrantyProviderDefinition, OrganisationAddress Address, User TechUser, Dictionary<string, string> WarrantyProviderProperties)
|
||||||
|
{
|
||||||
|
if (!j.CanLogWarranty())
|
||||||
|
throw new InvalidOperationException("Log Warranty was Denied");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(FaultDescription))
|
||||||
|
FaultDescription = j.GenerateFaultDescriptionFooter(dbContext, WarrantyProviderDefinition);
|
||||||
|
else
|
||||||
|
FaultDescription = string.Concat(FaultDescription, Environment.NewLine, Environment.NewLine, j.GenerateFaultDescriptionFooter(dbContext, WarrantyProviderDefinition));
|
||||||
|
|
||||||
|
using (WarrantyProviderFeature WarrantyProvider = WarrantyProviderDefinition.CreateInstance<WarrantyProviderFeature>())
|
||||||
|
{
|
||||||
|
string providerRef = WarrantyProvider.SubmitJob(dbContext, j, Address, TechUser, FaultDescription, WarrantyProviderProperties);
|
||||||
|
|
||||||
|
j.JobMetaWarranty.ExternalLoggedDate = DateTime.Now;
|
||||||
|
j.JobMetaWarranty.ExternalName = WarrantyProvider.WarrantyProviderId;
|
||||||
|
|
||||||
|
if (providerRef.Length > 100)
|
||||||
|
j.JobMetaWarranty.ExternalReference = providerRef.Substring(0, 100);
|
||||||
|
else
|
||||||
|
j.JobMetaWarranty.ExternalReference = providerRef;
|
||||||
|
|
||||||
|
// Write Log
|
||||||
|
JobLog jobLog = new JobLog()
|
||||||
|
{
|
||||||
|
JobId = j.Id,
|
||||||
|
TechUserId = TechUser.Id,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = string.Format("Warranty Claim Submitted{0}{0}Provider: {1}{0}Repair Address: {2}{0}Provider Reference: {3}{0}{0}{4}", Environment.NewLine, WarrantyProvider.Manifest.Name, Address.Name, providerRef, FaultDescription)
|
||||||
|
};
|
||||||
|
dbContext.JobLogs.Add(jobLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Convert HWar to HNWar
|
||||||
|
public static bool CanConvertHWarToHNWar(this Job j)
|
||||||
|
{
|
||||||
|
return !j.ClosedDate.HasValue && (j.DeviceSerialNumber != null) &&
|
||||||
|
j.JobTypeId == JobType.JobTypeIds.HWar && string.IsNullOrEmpty(j.JobMetaWarranty.ExternalReference);
|
||||||
|
}
|
||||||
|
public static void OnConvertHWarToHNWar(this Job j, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
if (!j.CanConvertHWarToHNWar())
|
||||||
|
throw new InvalidOperationException("Convert HWar to HNWar was Denied");
|
||||||
|
|
||||||
|
var techUser = UserBI.UserCache.CurrentUser;
|
||||||
|
|
||||||
|
// Remove JobMetaWarranty
|
||||||
|
if (j.JobMetaWarranty != null)
|
||||||
|
dbContext.JobMetaWarranties.Remove(j.JobMetaWarranty);
|
||||||
|
|
||||||
|
// Add JobMetaNonWarranty
|
||||||
|
var metaHNWar = new JobMetaNonWarranty() { Job = j };
|
||||||
|
dbContext.JobMetaNonWarranties.Add(metaHNWar);
|
||||||
|
|
||||||
|
// Swap Job Sub Types
|
||||||
|
List<string> jobSubTypes = j.JobSubTypes.Select(jst => jst.Id).ToList();
|
||||||
|
j.JobSubTypes.Clear();
|
||||||
|
foreach (var jst in dbContext.JobSubTypes.Where(i => i.JobTypeId == JobType.JobTypeIds.HNWar && jobSubTypes.Contains(i.Id)))
|
||||||
|
j.JobSubTypes.Add(jst);
|
||||||
|
|
||||||
|
// Add Components
|
||||||
|
var components = dbContext.DeviceComponents.Include("JobSubTypes").Where(c => !c.DeviceModelId.HasValue || c.DeviceModelId == j.Device.DeviceModelId);
|
||||||
|
var jobComponents = new List<DeviceComponent>();
|
||||||
|
foreach (var component in components)
|
||||||
|
{
|
||||||
|
if (!component.DeviceModelId.HasValue)
|
||||||
|
{
|
||||||
|
jobComponents.Add(component);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var st in component.JobSubTypes)
|
||||||
|
{
|
||||||
|
foreach (var jst in j.JobSubTypes)
|
||||||
|
{
|
||||||
|
if (st.JobTypeId == jst.JobTypeId && st.Id == jst.Id)
|
||||||
|
{
|
||||||
|
jobComponents.Add(component);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jobComponents.Contains(component))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var component in jobComponents)
|
||||||
|
{
|
||||||
|
dbContext.JobComponents.Add(new JobComponent()
|
||||||
|
{
|
||||||
|
Job = j,
|
||||||
|
TechUserId = techUser.Id,
|
||||||
|
Cost = component.Cost,
|
||||||
|
Description = component.Description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write Log
|
||||||
|
JobLog jobLog = new JobLog()
|
||||||
|
{
|
||||||
|
JobId = j.Id,
|
||||||
|
TechUserId = techUser.Id,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = string.Format("Job Type Converted{0}From: {1}{0}To: {2}", Environment.NewLine, dbContext.JobTypes.Find(JobType.JobTypeIds.HWar), dbContext.JobTypes.Find(JobType.JobTypeIds.HNWar))
|
||||||
|
};
|
||||||
|
dbContext.JobLogs.Add(jobLog);
|
||||||
|
|
||||||
|
j.JobTypeId = JobType.JobTypeIds.HNWar;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Warranty Completed
|
||||||
|
public static bool CanWarrantyCompleted(this Job j)
|
||||||
|
{
|
||||||
|
return (j.JobTypeId == JobType.JobTypeIds.HWar) &&
|
||||||
|
j.JobMetaWarranty.ExternalLoggedDate.HasValue &&
|
||||||
|
!j.JobMetaWarranty.ExternalCompletedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnWarrantyCompleted(this Job j)
|
||||||
|
{
|
||||||
|
if (!j.CanWarrantyCompleted())
|
||||||
|
throw new InvalidOperationException("Warranty Completed was Denied");
|
||||||
|
|
||||||
|
j.JobMetaWarranty.ExternalCompletedDate = DateTime.Now;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Insurance Claim Form Sent
|
||||||
|
public static bool CanInsuranceClaimFormSent(this Job j)
|
||||||
|
{
|
||||||
|
return (j.JobTypeId == JobType.JobTypeIds.HNWar) &&
|
||||||
|
j.JobMetaNonWarranty.IsInsuranceClaim &&
|
||||||
|
!j.JobMetaInsurance.ClaimFormSentDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnInsuranceClaimFormSent(this Job j)
|
||||||
|
{
|
||||||
|
if (!j.CanInsuranceClaimFormSent())
|
||||||
|
throw new InvalidOperationException("Insurance Claim Form Sent was Denied");
|
||||||
|
|
||||||
|
var techUser = UserBI.UserCache.CurrentUser;
|
||||||
|
|
||||||
|
j.JobMetaInsurance.ClaimFormSentDate = DateTime.Now;
|
||||||
|
j.JobMetaInsurance.ClaimFormSentUserId = techUser.Id;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Log Repair
|
||||||
|
public static bool CanLogRepair(this Job j)
|
||||||
|
{
|
||||||
|
return (j.JobTypeId == JobType.JobTypeIds.HNWar) &&
|
||||||
|
(j.DeviceSerialNumber != null) &&
|
||||||
|
!j.JobMetaNonWarranty.RepairerLoggedDate.HasValue &&
|
||||||
|
!j.JobMetaNonWarranty.RepairerCompletedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnLogRepair(this Job j, string RepairerName, string RepairerReference)
|
||||||
|
{
|
||||||
|
if (!j.CanLogRepair())
|
||||||
|
throw new InvalidOperationException("Log Repair was Denied");
|
||||||
|
|
||||||
|
if (j.JobMetaNonWarranty.RepairerName != RepairerName)
|
||||||
|
j.JobMetaNonWarranty.RepairerName = RepairerName;
|
||||||
|
if (j.JobMetaNonWarranty.RepairerReference != RepairerReference)
|
||||||
|
j.JobMetaNonWarranty.RepairerReference = RepairerReference;
|
||||||
|
j.JobMetaNonWarranty.RepairerLoggedDate = DateTime.Now;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Repair Complete
|
||||||
|
public static bool CanRepairComplete(this Job j)
|
||||||
|
{
|
||||||
|
return (j.JobTypeId == JobType.JobTypeIds.HNWar) &&
|
||||||
|
j.JobMetaNonWarranty.RepairerLoggedDate.HasValue &&
|
||||||
|
!j.JobMetaNonWarranty.RepairerCompletedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnRepairComplete(this Job j)
|
||||||
|
{
|
||||||
|
if (!j.CanRepairComplete())
|
||||||
|
throw new InvalidOperationException("Repair Complete was Denied");
|
||||||
|
|
||||||
|
j.JobMetaNonWarranty.RepairerCompletedDate = DateTime.Now;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Close
|
||||||
|
public static bool CanClose(this Job j)
|
||||||
|
{
|
||||||
|
if (j.ClosedDate.HasValue)
|
||||||
|
return false; // Job already Closed
|
||||||
|
|
||||||
|
if (j.DeviceHeld.HasValue && !j.DeviceReturnedDate.HasValue)
|
||||||
|
return false; // Device not returned to User
|
||||||
|
|
||||||
|
if (j.WaitingForUserAction.HasValue)
|
||||||
|
return false; // Job waiting on User Action
|
||||||
|
|
||||||
|
switch (j.JobTypeId)
|
||||||
|
{
|
||||||
|
case JobType.JobTypeIds.HWar:
|
||||||
|
if (!string.IsNullOrEmpty(j.JobMetaWarranty.ExternalReference) && !j.JobMetaWarranty.ExternalCompletedDate.HasValue)
|
||||||
|
return false; // Job Logged (Warranty) but not completed
|
||||||
|
break;
|
||||||
|
case JobType.JobTypeIds.HNWar:
|
||||||
|
if (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && !j.JobMetaNonWarranty.RepairerCompletedDate.HasValue)
|
||||||
|
return false; // Job Logged (Repair) but not completed
|
||||||
|
if (j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue && (!j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue || !j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue))
|
||||||
|
return false; // Accounting Charge Required, but not added or paid
|
||||||
|
|
||||||
|
// Removed Rule: 2012-05-31 - A Job can be closed if the decision has been made for the user not to pay...
|
||||||
|
//if (j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue)
|
||||||
|
// return false; // Accounting Charge Added, but not paid
|
||||||
|
|
||||||
|
if (j.JobMetaNonWarranty.IsInsuranceClaim && !j.JobMetaInsurance.ClaimFormSentDate.HasValue)
|
||||||
|
return false; // Is Insurance Claim, but claim form not sent
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static void OnClose(this Job j, User Technician)
|
||||||
|
{
|
||||||
|
if (!j.CanClose())
|
||||||
|
throw new InvalidOperationException("Close was Denied");
|
||||||
|
|
||||||
|
j.ClosedDate = DateTime.Now;
|
||||||
|
j.ClosedTechUserId = Technician.Id;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Reopen
|
||||||
|
public static bool CanReopen(this Job j)
|
||||||
|
{
|
||||||
|
return j.ClosedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnReopen(this Job j)
|
||||||
|
{
|
||||||
|
if (!j.CanReopen())
|
||||||
|
throw new InvalidOperationException("Reopen was Denied");
|
||||||
|
|
||||||
|
j.ClosedDate = null;
|
||||||
|
j.ClosedTechUserId = null;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Delete
|
||||||
|
public static bool CanDelete(this Job j)
|
||||||
|
{
|
||||||
|
return j.ClosedDate.HasValue;
|
||||||
|
}
|
||||||
|
public static void OnDelete(this Job j, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Job Sub Types
|
||||||
|
j.JobSubTypes.Clear();
|
||||||
|
|
||||||
|
// Job Attachments
|
||||||
|
foreach (var ja in j.JobAttachments.ToArray())
|
||||||
|
ja.OnDelete(dbContext);
|
||||||
|
j.JobAttachments.Clear();
|
||||||
|
|
||||||
|
// Job Components
|
||||||
|
foreach (var jc in j.JobComponents.ToArray())
|
||||||
|
dbContext.JobComponents.Remove(jc);
|
||||||
|
j.JobComponents.Clear();
|
||||||
|
|
||||||
|
// Job Logs
|
||||||
|
foreach (var jl in j.JobLogs.ToArray())
|
||||||
|
dbContext.JobLogs.Remove(jl);
|
||||||
|
j.JobLogs.Clear();
|
||||||
|
|
||||||
|
// Job Meta
|
||||||
|
if (j.JobMetaInsurance != null)
|
||||||
|
{
|
||||||
|
dbContext.JobMetaInsurances.Remove(j.JobMetaInsurance);
|
||||||
|
j.JobMetaInsurance = null;
|
||||||
|
}
|
||||||
|
if (j.JobMetaNonWarranty != null)
|
||||||
|
{
|
||||||
|
dbContext.JobMetaNonWarranties.Remove(j.JobMetaNonWarranty);
|
||||||
|
j.JobMetaNonWarranty = null;
|
||||||
|
}
|
||||||
|
if (j.JobMetaWarranty != null)
|
||||||
|
{
|
||||||
|
dbContext.JobMetaWarranties.Remove(j.JobMetaWarranty);
|
||||||
|
j.JobMetaWarranty = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job
|
||||||
|
dbContext.Jobs.Remove(j);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using System.IO;
|
||||||
|
using Disco.Models.BI.DocumentTemplates;
|
||||||
|
using Disco.Services.Plugins;
|
||||||
|
using Disco.Models.BI.Job;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class JobExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void BroadcastUpdate(this Job j)
|
||||||
|
{
|
||||||
|
if (j.UserId != null)
|
||||||
|
Interop.SignalRHandlers.UserHeldDevices.UserJobUpdated(j.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JobAttachment CreateAttachment(this Job Job, DiscoDataContext dbContext, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
|
||||||
|
|
||||||
|
JobAttachment ja = new JobAttachment()
|
||||||
|
{
|
||||||
|
JobId = Job.Id,
|
||||||
|
TechUserId = CreatorUser.Id,
|
||||||
|
Filename = Filename,
|
||||||
|
MimeType = MimeType,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = Comments
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DocumentTemplate != null)
|
||||||
|
ja.DocumentTemplateId = DocumentTemplate.Id;
|
||||||
|
|
||||||
|
dbContext.JobAttachments.Add(ja);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
ja.SaveAttachment(dbContext, Content);
|
||||||
|
Content.Position = 0;
|
||||||
|
if (PdfThumbnail == null)
|
||||||
|
ja.GenerateThumbnail(dbContext, Content);
|
||||||
|
else
|
||||||
|
ja.SaveThumbnailAttachment(dbContext, PdfThumbnail);
|
||||||
|
|
||||||
|
return ja;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Tuple<string, string> Status(this Job j)
|
||||||
|
{
|
||||||
|
var statusId = j.CalculateStatusId();
|
||||||
|
return new Tuple<string, string>(statusId, JobBI.Utilities.JobStatusDescription(statusId, j));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JobTableModel.JobTableItemModelIncludeStatus ToJobTableItemModelIncludeStatus(this Job j)
|
||||||
|
{
|
||||||
|
var i = new JobTableModel.JobTableItemModelIncludeStatus()
|
||||||
|
{
|
||||||
|
Id = j.Id,
|
||||||
|
OpenedDate = j.OpenedDate,
|
||||||
|
ClosedDate = j.ClosedDate,
|
||||||
|
TypeId = j.JobTypeId,
|
||||||
|
TypeDescription = j.JobType.Description,
|
||||||
|
Location = j.DeviceHeldLocation,
|
||||||
|
|
||||||
|
WaitingForUserAction = j.WaitingForUserAction,
|
||||||
|
DeviceReadyForReturn = j.DeviceReadyForReturn,
|
||||||
|
DeviceHeld = j.DeviceHeld,
|
||||||
|
DeviceReturnedDate = j.DeviceReturnedDate
|
||||||
|
};
|
||||||
|
|
||||||
|
if (j.Device != null)
|
||||||
|
{
|
||||||
|
i.DeviceSerialNumber = j.DeviceSerialNumber;
|
||||||
|
i.DeviceModelDescription = j.Device.DeviceModel.Description;
|
||||||
|
i.DeviceAddressId = j.Device.DeviceProfile.DefaultOrganisationAddress;
|
||||||
|
|
||||||
|
if (j.JobMetaWarranty != null)
|
||||||
|
{
|
||||||
|
i.JobMetaWarranty_ExternalReference = j.JobMetaWarranty.ExternalReference;
|
||||||
|
i.JobMetaWarranty_ExternalCompletedDate = j.JobMetaWarranty.ExternalCompletedDate;
|
||||||
|
i.JobMetaWarranty_ExternalName = j.JobMetaWarranty.ExternalName;
|
||||||
|
}
|
||||||
|
if (j.JobMetaNonWarranty != null)
|
||||||
|
{
|
||||||
|
i.JobMetaNonWarranty_RepairerLoggedDate = j.JobMetaNonWarranty.RepairerLoggedDate;
|
||||||
|
i.JobMetaNonWarranty_RepairerCompletedDate = j.JobMetaNonWarranty.RepairerCompletedDate;
|
||||||
|
i.JobMetaNonWarranty_AccountingChargeAddedDate = j.JobMetaNonWarranty.AccountingChargeAddedDate;
|
||||||
|
i.JobMetaNonWarranty_AccountingChargePaidDate = j.JobMetaNonWarranty.AccountingChargePaidDate;
|
||||||
|
i.JobMetaNonWarranty_AccountingChargeRequiredDate = j.JobMetaNonWarranty.AccountingChargeRequiredDate;
|
||||||
|
i.JobMetaNonWarranty_IsInsuranceClaim = j.JobMetaNonWarranty.IsInsuranceClaim;
|
||||||
|
i.JobMetaNonWarranty_RepairerName = j.JobMetaNonWarranty.RepairerName;
|
||||||
|
if (j.JobMetaInsurance != null)
|
||||||
|
{
|
||||||
|
i.JobMetaInsurance_ClaimFormSentDate = j.JobMetaInsurance.ClaimFormSentDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (j.User != null)
|
||||||
|
{
|
||||||
|
i.UserId = j.UserId;
|
||||||
|
i.UserDisplayName = j.User.DisplayName;
|
||||||
|
}
|
||||||
|
if (j.OpenedTechUser != null)
|
||||||
|
{
|
||||||
|
i.OpenedTechUserId = j.OpenedTechUserId;
|
||||||
|
i.OpenedTechUserDisplayName = j.OpenedTechUser.DisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CalculateStatusId(this Job j)
|
||||||
|
{
|
||||||
|
return j.ToJobTableItemModelIncludeStatus().CalculateStatusId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CalculateStatusId(this JobTableModel.JobTableItemModelIncludeStatus j)
|
||||||
|
{
|
||||||
|
if (j.ClosedDate.HasValue)
|
||||||
|
return Job.JobStatusIds.Closed;
|
||||||
|
|
||||||
|
if (j.TypeId == JobType.JobTypeIds.HWar)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(j.JobMetaWarranty_ExternalReference) && !j.JobMetaWarranty_ExternalCompletedDate.HasValue)
|
||||||
|
return Job.JobStatusIds.AwaitingWarrantyRepair; // Job Logged - but not marked as completed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j.TypeId == JobType.JobTypeIds.HNWar)
|
||||||
|
{
|
||||||
|
if (j.JobMetaNonWarranty_RepairerLoggedDate.HasValue && !j.JobMetaNonWarranty_RepairerCompletedDate.HasValue)
|
||||||
|
return Job.JobStatusIds.AwaitingRepairs; // Repairs logged - but not complete
|
||||||
|
if (j.JobMetaNonWarranty_AccountingChargeAddedDate.HasValue && !j.JobMetaNonWarranty_AccountingChargePaidDate.HasValue)
|
||||||
|
return Job.JobStatusIds.AwaitingAccountingPayment; // Accounting Charge Added, but not paid
|
||||||
|
if (j.JobMetaNonWarranty_AccountingChargeRequiredDate.HasValue && (!j.JobMetaNonWarranty_AccountingChargePaidDate.HasValue || !j.JobMetaNonWarranty_AccountingChargeAddedDate.HasValue))
|
||||||
|
return Job.JobStatusIds.AwaitingAccountingCharge; // Accounting Charge Required, but not added or paid
|
||||||
|
if (j.JobMetaNonWarranty_RepairerLoggedDate.HasValue && j.JobMetaNonWarranty_IsInsuranceClaim.Value && !j.JobMetaInsurance_ClaimFormSentDate.HasValue)
|
||||||
|
return Job.JobStatusIds.AwaitingInsuranceProcessing; // Is insurance claim, but no Claim Form Sent
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j.WaitingForUserAction.HasValue)
|
||||||
|
return Job.JobStatusIds.AwaitingUserAction; // Awaiting for User
|
||||||
|
|
||||||
|
if (j.DeviceReadyForReturn.HasValue && !j.DeviceReturnedDate.HasValue)
|
||||||
|
return Job.JobStatusIds.AwaitingDeviceReturn; // Device not returned to User
|
||||||
|
|
||||||
|
return Job.JobStatusIds.Open;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DocumentTemplate> AvailableDocumentTemplates(this Job j, DiscoDataContext dbContext, User User, DateTime TimeStamp)
|
||||||
|
{
|
||||||
|
var dts = dbContext.DocumentTemplates.Include("JobSubTypes")
|
||||||
|
.Where(dt => dt.Scope == DocumentTemplate.DocumentTemplateScopes.Job)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var dt in dts.ToArray())
|
||||||
|
{
|
||||||
|
if (dt.JobSubTypes.Count != 0)
|
||||||
|
{ // Filter Applied
|
||||||
|
bool match = false;
|
||||||
|
foreach (var st in j.JobSubTypes)
|
||||||
|
{
|
||||||
|
if (dt.JobSubTypes.Contains(st))
|
||||||
|
{
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match)
|
||||||
|
dts.Remove(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate Filters
|
||||||
|
dts = dts.Where(dt => dt.FilterExpressionMatches(j, dbContext, User, TimeStamp, DocumentState.DefaultState())).ToList();
|
||||||
|
|
||||||
|
return dts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime ValidateDateAfterOpened(this Job j, DateTime d)
|
||||||
|
{
|
||||||
|
if (d < j.OpenedDate)
|
||||||
|
{
|
||||||
|
if (d > j.OpenedDate.AddMinutes(-1))
|
||||||
|
return j.OpenedDate;
|
||||||
|
else
|
||||||
|
throw new ArgumentException("The Date must be >= the Open Date.", "d");
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GenerateFaultDescription(this Job j, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.AppendLine("Faulty Components:");
|
||||||
|
foreach (var jst in j.JobSubTypes)
|
||||||
|
sb.Append("- ").AppendLine(jst.Description).AppendLine(" - ");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GenerateFaultDescriptionFooter(this Job j, DiscoDataContext dbContext, PluginFeatureManifest WarrantyProviderDefinition)
|
||||||
|
{
|
||||||
|
var versionDisco = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
|
return string.Format("Automation by Disco v{0}.{1:0000}.{2:0000} (Provider: {3} v{4})",
|
||||||
|
versionDisco.Major, versionDisco.Minor, versionDisco.Build, WarrantyProviderDefinition.Id, WarrantyProviderDefinition.PluginManifest.Version.ToString(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateSubTypes(this Job j, DiscoDataContext dbContext, List<JobSubType> SubTypes, bool AddComponents, User TechUser)
|
||||||
|
{
|
||||||
|
if (SubTypes == null || SubTypes.Count == 0)
|
||||||
|
throw new ArgumentException("The Job must contain at least one Sub Type");
|
||||||
|
|
||||||
|
List<JobSubType> addedSubTypes = new List<JobSubType>();
|
||||||
|
List<JobSubType> removedSubTypes = new List<JobSubType>();
|
||||||
|
|
||||||
|
// Removed Sub Types
|
||||||
|
foreach (var t in j.JobSubTypes.ToArray())
|
||||||
|
if (!SubTypes.Contains(t))
|
||||||
|
{
|
||||||
|
removedSubTypes.Add(t);
|
||||||
|
j.JobSubTypes.Remove(t);
|
||||||
|
}
|
||||||
|
// Added Sub Types
|
||||||
|
foreach (var t in SubTypes)
|
||||||
|
if (!j.JobSubTypes.Contains(t))
|
||||||
|
{
|
||||||
|
addedSubTypes.Add(t);
|
||||||
|
j.JobSubTypes.Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write Log
|
||||||
|
if (addedSubTypes.Count > 0 || removedSubTypes.Count > 0)
|
||||||
|
{
|
||||||
|
StringBuilder logBuilder = new StringBuilder();
|
||||||
|
logBuilder.AppendLine("Updated Job Sub Types");
|
||||||
|
if (removedSubTypes.Count > 0)
|
||||||
|
{
|
||||||
|
logBuilder.AppendLine("Removed:");
|
||||||
|
foreach (var t in removedSubTypes)
|
||||||
|
logBuilder.Append("- ").AppendLine(t.ToString());
|
||||||
|
}
|
||||||
|
if (addedSubTypes.Count > 0)
|
||||||
|
{
|
||||||
|
logBuilder.AppendLine("Added:");
|
||||||
|
foreach (var t in addedSubTypes)
|
||||||
|
logBuilder.Append("- ").AppendLine(t.ToString());
|
||||||
|
}
|
||||||
|
dbContext.JobLogs.Add(new JobLog()
|
||||||
|
{
|
||||||
|
JobId = j.Id,
|
||||||
|
TechUserId = TechUser.Id,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = logBuilder.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Components
|
||||||
|
if (AddComponents && addedSubTypes.Count > 0 && j.DeviceSerialNumber != null)
|
||||||
|
{
|
||||||
|
var components = dbContext.DeviceComponents.Include("JobSubTypes").Where(c => !c.DeviceModelId.HasValue || c.DeviceModelId == j.Device.DeviceModelId);
|
||||||
|
var addedComponents = new List<DeviceComponent>();
|
||||||
|
foreach (var c in components)
|
||||||
|
{
|
||||||
|
foreach (var st in c.JobSubTypes)
|
||||||
|
{
|
||||||
|
foreach (var jst in addedSubTypes)
|
||||||
|
{
|
||||||
|
if (st.JobTypeId == jst.JobTypeId && st.Id == jst.Id)
|
||||||
|
{
|
||||||
|
addedComponents.Add(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addedComponents.Contains(c))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var c in addedComponents)
|
||||||
|
{
|
||||||
|
if (!j.JobComponents.Any(jc => jc.Description.Equals(c.Description, StringComparison.InvariantCultureIgnoreCase)))
|
||||||
|
{ // Job Component with matching Description doesn't exist.
|
||||||
|
dbContext.JobComponents.Add(new JobComponent()
|
||||||
|
{
|
||||||
|
Job = j,
|
||||||
|
TechUserId = TechUser.Id,
|
||||||
|
Cost = c.Cost,
|
||||||
|
Description = c.Description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class JobFlagExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Dictionary<string, Dictionary<long, string>> allFlags;
|
||||||
|
private static void CacheAllFlags()
|
||||||
|
{
|
||||||
|
if (allFlags == null)
|
||||||
|
{
|
||||||
|
var fType = typeof(Job.UserManagementFlags);
|
||||||
|
var fMembers = fType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||||
|
|
||||||
|
var flags = new Dictionary<string, Dictionary<long, string>>();
|
||||||
|
foreach (var f in fMembers)
|
||||||
|
{
|
||||||
|
DisplayAttribute display = (DisplayAttribute)(f.GetCustomAttributes(typeof(DisplayAttribute), false)[0]);
|
||||||
|
string gn = display.GroupName;
|
||||||
|
Dictionary<long, string> g;
|
||||||
|
if (!flags.TryGetValue(gn, out g))
|
||||||
|
{
|
||||||
|
g = new Dictionary<long, string>();
|
||||||
|
flags.Add(gn, g);
|
||||||
|
}
|
||||||
|
g[(long)f.GetRawConstantValue()] = display.Name;
|
||||||
|
}
|
||||||
|
allFlags = flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, List<Tuple<long, string, bool>>> ValidFlagsGrouped(this Job j)
|
||||||
|
{
|
||||||
|
Dictionary<string, List<Tuple<long, string, bool>>> validFlags = new Dictionary<string, List<Tuple<long, string, bool>>>();
|
||||||
|
|
||||||
|
CacheAllFlags();
|
||||||
|
|
||||||
|
var currentFlags = j.Flags ?? 0;
|
||||||
|
|
||||||
|
foreach (var jt in j.JobSubTypes)
|
||||||
|
{
|
||||||
|
Dictionary<long, string> g;
|
||||||
|
if (allFlags.TryGetValue(jt.Id, out g))
|
||||||
|
{
|
||||||
|
validFlags[jt.Id] = g.Select(f => new Tuple<long, string, bool>(f.Key, f.Value, ((currentFlags & f.Key) == f.Key))).ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
validFlags[jt.Id] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validFlags;
|
||||||
|
}
|
||||||
|
public static Dictionary<long, Tuple<string, bool>> ValidFlags(this Job j)
|
||||||
|
{
|
||||||
|
Dictionary<long, Tuple<string, bool>> validFlags = new Dictionary<long, Tuple<string, bool>>();
|
||||||
|
|
||||||
|
CacheAllFlags();
|
||||||
|
|
||||||
|
var currentFlags = j.Flags ?? 0;
|
||||||
|
|
||||||
|
foreach (var jt in j.JobSubTypes)
|
||||||
|
{
|
||||||
|
Dictionary<long, string> g;
|
||||||
|
if (allFlags.TryGetValue(jt.Id, out g))
|
||||||
|
{
|
||||||
|
foreach (var f in g)
|
||||||
|
validFlags[f.Key] = new Tuple<string, bool>(string.Format("{0}: {1}", jt.Description, f.Value), ((currentFlags & f.Key) == f.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validFlags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.BI.Job;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class JobTableExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void Fill(this JobTableModel model, DiscoDataContext dbContext, IQueryable<Job> Jobs)
|
||||||
|
{
|
||||||
|
if (model.ShowStatus)
|
||||||
|
{
|
||||||
|
|
||||||
|
var jobItems = Jobs.Select(j => new JobTableModel.JobTableItemModelIncludeStatus()
|
||||||
|
{
|
||||||
|
Id = j.Id,
|
||||||
|
DeviceAddressId = j.Device.DeviceProfile.DefaultOrganisationAddress,
|
||||||
|
OpenedDate = j.OpenedDate,
|
||||||
|
ClosedDate = j.ClosedDate,
|
||||||
|
TypeId = j.JobTypeId,
|
||||||
|
TypeDescription = j.JobType.Description,
|
||||||
|
DeviceSerialNumber = j.Device.SerialNumber,
|
||||||
|
DeviceModelDescription = j.Device.DeviceModel.Description,
|
||||||
|
UserId = j.UserId,
|
||||||
|
UserDisplayName = j.User.DisplayName,
|
||||||
|
OpenedTechUserId = j.OpenedTechUserId,
|
||||||
|
OpenedTechUserDisplayName = j.OpenedTechUser.DisplayName,
|
||||||
|
Location = j.DeviceHeldLocation,
|
||||||
|
|
||||||
|
JobMetaWarranty_ExternalReference = j.JobMetaWarranty.ExternalReference,
|
||||||
|
JobMetaWarranty_ExternalCompletedDate = j.JobMetaWarranty.ExternalCompletedDate,
|
||||||
|
JobMetaNonWarranty_RepairerLoggedDate = j.JobMetaNonWarranty.RepairerLoggedDate,
|
||||||
|
JobMetaNonWarranty_RepairerCompletedDate = j.JobMetaNonWarranty.RepairerCompletedDate,
|
||||||
|
JobMetaNonWarranty_AccountingChargeAddedDate = j.JobMetaNonWarranty.AccountingChargeAddedDate,
|
||||||
|
JobMetaNonWarranty_AccountingChargePaidDate = j.JobMetaNonWarranty.AccountingChargePaidDate,
|
||||||
|
JobMetaNonWarranty_AccountingChargeRequiredDate = j.JobMetaNonWarranty.AccountingChargeRequiredDate,
|
||||||
|
JobMetaNonWarranty_IsInsuranceClaim = j.JobMetaNonWarranty.IsInsuranceClaim,
|
||||||
|
JobMetaInsurance_ClaimFormSentDate = j.JobMetaInsurance.ClaimFormSentDate,
|
||||||
|
|
||||||
|
WaitingForUserAction = j.WaitingForUserAction,
|
||||||
|
DeviceReadyForReturn = j.DeviceReadyForReturn,
|
||||||
|
DeviceHeld = j.DeviceHeld,
|
||||||
|
DeviceReturnedDate = j.DeviceReturnedDate,
|
||||||
|
JobMetaWarranty_ExternalName = j.JobMetaWarranty.ExternalName,
|
||||||
|
JobMetaNonWarranty_RepairerName = j.JobMetaNonWarranty.RepairerName
|
||||||
|
});
|
||||||
|
|
||||||
|
model.Items = new List<JobTableModel.JobTableItemModel>();
|
||||||
|
foreach (var j in jobItems)
|
||||||
|
{
|
||||||
|
j.StatusId = j.CalculateStatusId();
|
||||||
|
j.StatusDescription = JobBI.Utilities.JobStatusDescription(j.StatusId, j);
|
||||||
|
|
||||||
|
model.Items.Add(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
model.Items = Jobs.Select(j => new JobTableModel.JobTableItemModel()
|
||||||
|
{
|
||||||
|
Id = j.Id,
|
||||||
|
DeviceAddressId = j.Device.DeviceProfile.DefaultOrganisationAddress,
|
||||||
|
OpenedDate = j.OpenedDate,
|
||||||
|
ClosedDate = j.ClosedDate,
|
||||||
|
TypeId = j.JobTypeId,
|
||||||
|
TypeDescription = j.JobType.Description,
|
||||||
|
DeviceSerialNumber = j.Device.SerialNumber,
|
||||||
|
DeviceModelDescription = j.Device.DeviceModel.Description,
|
||||||
|
UserId = j.UserId,
|
||||||
|
UserDisplayName = j.User.DisplayName,
|
||||||
|
OpenedTechUserId = j.OpenedTechUserId,
|
||||||
|
OpenedTechUserDisplayName = j.OpenedTechUser.DisplayName,
|
||||||
|
Location = j.DeviceHeldLocation
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model.ShowDeviceAddress.HasValue)
|
||||||
|
model.ShowDeviceAddress = dbContext.DiscoConfiguration.MultiSiteMode;
|
||||||
|
|
||||||
|
if (model.ShowDeviceAddress.Value)
|
||||||
|
{
|
||||||
|
foreach (var j in model.Items)
|
||||||
|
if (j.DeviceAddressId.HasValue)
|
||||||
|
j.DeviceAddress = dbContext.DiscoConfiguration.OrganisationAddresses.GetAddress(j.DeviceAddressId.Value).Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using System.IO;
|
||||||
|
using Disco.Models.BI.DocumentTemplates;
|
||||||
|
using Disco.Models.Interop.ActiveDirectory;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class UserExtensions
|
||||||
|
{
|
||||||
|
public static UserAttachment CreateAttachment(this User User, DiscoDataContext dbContext, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
|
||||||
|
|
||||||
|
UserAttachment ua = new UserAttachment()
|
||||||
|
{
|
||||||
|
UserId = User.Id,
|
||||||
|
TechUserId = CreatorUser.Id,
|
||||||
|
Filename = Filename,
|
||||||
|
MimeType = MimeType,
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Comments = Comments
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DocumentTemplate != null)
|
||||||
|
ua.DocumentTemplateId = DocumentTemplate.Id;
|
||||||
|
|
||||||
|
dbContext.UserAttachments.Add(ua);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
ua.SaveAttachment(dbContext, Content);
|
||||||
|
Content.Position = 0;
|
||||||
|
if (PdfThumbnail == null)
|
||||||
|
ua.GenerateThumbnail(dbContext, Content);
|
||||||
|
else
|
||||||
|
ua.SaveThumbnailAttachment(dbContext, PdfThumbnail);
|
||||||
|
|
||||||
|
return ua;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DocumentTemplate> AvailableDocumentTemplates(this User u, DiscoDataContext dbContext, User User, DateTime TimeStamp)
|
||||||
|
{
|
||||||
|
var dts = dbContext.DocumentTemplates.Include("JobSubTypes")
|
||||||
|
.Where(dt => dt.Scope == DocumentTemplate.DocumentTemplateScopes.User)
|
||||||
|
.ToArray()
|
||||||
|
.Where(dt => dt.FilterExpressionMatches(u, dbContext, User, TimeStamp, DocumentState.DefaultState())).ToList();
|
||||||
|
|
||||||
|
return dts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DeviceUserAssignment> CurrentDeviceUserAssignments(this User u)
|
||||||
|
{
|
||||||
|
return u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue).ToList();
|
||||||
|
}
|
||||||
|
public static ActiveDirectoryUserAccount ActiveDirectoryAccount(this User User, params string[] AdditionalProperties)
|
||||||
|
{
|
||||||
|
return Interop.ActiveDirectory.ActiveDirectory.GetUserAccount(User.Id, AdditionalProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class UtilityExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static string StreamToString(this System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
if (stream.Position != 0 && stream.CanSeek)
|
||||||
|
{
|
||||||
|
stream.Position = 0;
|
||||||
|
}
|
||||||
|
using (System.IO.StreamReader sr = new System.IO.StreamReader(stream))
|
||||||
|
{
|
||||||
|
return sr.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Date/Time Extensions
|
||||||
|
public static string ToFuzzy(this DateTime d)
|
||||||
|
{
|
||||||
|
var n = DateTime.Now;
|
||||||
|
|
||||||
|
// Today
|
||||||
|
if (d.Date == n.Date)
|
||||||
|
{
|
||||||
|
if (d < n)
|
||||||
|
{
|
||||||
|
// Earlier
|
||||||
|
if (d > n.AddMinutes(-1))
|
||||||
|
return "A moment ago";
|
||||||
|
|
||||||
|
if (d > n.AddMinutes(-10))
|
||||||
|
return "A few minutes ago";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Later
|
||||||
|
if (d < n.AddMinutes(1))
|
||||||
|
return "In a moment";
|
||||||
|
|
||||||
|
if (d < n.AddMinutes(10))
|
||||||
|
return "In a few minutes";
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format("Today at {0:h:mm tt}", d);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.Date < n.Date)
|
||||||
|
{
|
||||||
|
// PAST
|
||||||
|
var dif = n.Subtract(d);
|
||||||
|
|
||||||
|
// Yesterday
|
||||||
|
if (d.Date == n.Date.AddDays(-1))
|
||||||
|
return string.Format("Yesterday at {0:h:mm tt}", d);
|
||||||
|
// Last Week
|
||||||
|
if (dif.TotalDays <= 7)
|
||||||
|
return string.Format("Last {0:dddd} at {0:h:mm tt}", d);
|
||||||
|
// Within 8 Weeks
|
||||||
|
if (d > n.Date.AddMonths(-2))
|
||||||
|
return string.Format("{0} Weeks ago, {1:ddd, d MMM}", (int)(dif.TotalDays / 7), d);
|
||||||
|
// Same Year
|
||||||
|
if (d.Year == n.Year)
|
||||||
|
return string.Format("{0} Months ago, {1:ddd, d MMM}", (int)(dif.TotalDays / 30), d);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Future
|
||||||
|
var dif = d.Subtract(n);
|
||||||
|
|
||||||
|
// Tomorrow
|
||||||
|
if (d.Date == n.Date.AddDays(1))
|
||||||
|
return string.Format("Tomorrow at {0:h:mm tt}", d);
|
||||||
|
// Next Week
|
||||||
|
if (dif.TotalDays <= 7)
|
||||||
|
return string.Format("Next {0:dddd} at {0:h:mm tt}", d);
|
||||||
|
// < 2 Month
|
||||||
|
if (d < n.Date.AddMonths(2))
|
||||||
|
return string.Format("In {0} Weeks, {1:ddd, d MMM}", (int)(dif.TotalDays / 7), d);
|
||||||
|
// Same Year
|
||||||
|
if (d.Year == n.Year)
|
||||||
|
return string.Format("In {0} Months, {1:ddd, d MMM}", (int)(dif.TotalDays / 30), d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.ToString("ddd, d MMM yyyy");
|
||||||
|
}
|
||||||
|
public static string ToFuzzy(this DateTime? d, string NullValue = "N/A")
|
||||||
|
{
|
||||||
|
if (d.HasValue)
|
||||||
|
return ToFuzzy(d.Value);
|
||||||
|
else
|
||||||
|
return NullValue;
|
||||||
|
}
|
||||||
|
public static string ToFullDateTime(this DateTime d)
|
||||||
|
{
|
||||||
|
return d.ToString("ddd, d MMM yyyy @ h:mm:sstt");
|
||||||
|
}
|
||||||
|
public static string ToFullDateTime(this DateTime? d, string NullValue = "N/A")
|
||||||
|
{
|
||||||
|
if (d.HasValue)
|
||||||
|
return ToFullDateTime(d.Value);
|
||||||
|
else
|
||||||
|
return NullValue;
|
||||||
|
}
|
||||||
|
public static long ToSortableDateTime(this DateTime? d)
|
||||||
|
{
|
||||||
|
if (d.HasValue)
|
||||||
|
return d.Value.ToBinary();
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public static long ToSortableDateTime(this DateTime d)
|
||||||
|
{
|
||||||
|
return d.ToBinary();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Image Extensions
|
||||||
|
public static Bitmap ResizeImage(this Image Source, int Width, int Height, Brush BackgroundColor = null)
|
||||||
|
{
|
||||||
|
Bitmap destination = new Bitmap(Width, Height);
|
||||||
|
destination.SetResolution(72, 72);
|
||||||
|
using (Graphics destinationGraphics = Graphics.FromImage(destination))
|
||||||
|
{
|
||||||
|
destinationGraphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||||
|
destinationGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||||
|
destinationGraphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||||
|
|
||||||
|
if (BackgroundColor != null)
|
||||||
|
destinationGraphics.FillRectangle(BackgroundColor, destinationGraphics.VisibleClipBounds);
|
||||||
|
|
||||||
|
float ratio = Math.Min((float)(destination.Width) / (float)(Source.Width), (float)(destination.Height) / (float)(Source.Height));
|
||||||
|
|
||||||
|
destinationGraphics.DrawImageResized(Source, ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
public static void DrawImageResized(this Graphics graphics, Image SourceImage, float? Scale = null, float LocationX = -1, float LocationY = -1)
|
||||||
|
{
|
||||||
|
RectangleF clipBounds = graphics.VisibleClipBounds;
|
||||||
|
if (Scale == null) // Calculate Scale
|
||||||
|
Scale = Math.Min(clipBounds.Width / SourceImage.Width, clipBounds.Height / SourceImage.Height);
|
||||||
|
float newWidth = SourceImage.Width * Scale.Value;
|
||||||
|
float newHeight = SourceImage.Height * Scale.Value;
|
||||||
|
float newLeft = LocationX;
|
||||||
|
float newTop = LocationY;
|
||||||
|
|
||||||
|
if (newLeft < 0 || newTop < 0)
|
||||||
|
{
|
||||||
|
if (newWidth < clipBounds.Width)
|
||||||
|
newLeft = (clipBounds.Width - newWidth) / 2;
|
||||||
|
else
|
||||||
|
newLeft = 0;
|
||||||
|
if (newHeight < clipBounds.Height)
|
||||||
|
newTop = (clipBounds.Height - newHeight) / 2;
|
||||||
|
else
|
||||||
|
newTop = 0;
|
||||||
|
}
|
||||||
|
newLeft += clipBounds.Left;
|
||||||
|
newTop += clipBounds.Top;
|
||||||
|
|
||||||
|
graphics.DrawImage(SourceImage, new RectangleF(newLeft, newTop, newWidth, newHeight), new RectangleF(0, 0, SourceImage.Width, SourceImage.Height), GraphicsUnit.Pixel);
|
||||||
|
}
|
||||||
|
public static void EmbedIconOverlay(this Image Source, Image Icon)
|
||||||
|
{
|
||||||
|
int top = Math.Max(0, Source.Height - Icon.Height);
|
||||||
|
int left = Math.Max(0, Source.Width - Icon.Width);
|
||||||
|
|
||||||
|
using (Graphics sourceGraphics = Graphics.FromImage(Source))
|
||||||
|
{
|
||||||
|
sourceGraphics.DrawImage(Icon, left, top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void SavePng(this Image Source, string Filename)
|
||||||
|
{
|
||||||
|
using (FileStream outStream = new FileStream(Filename, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
SavePng(Source, outStream);
|
||||||
|
outStream.Flush();
|
||||||
|
outStream.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void SavePng(this Image Source, Stream OutStream)
|
||||||
|
{
|
||||||
|
Source.Save(OutStream, ImageFormat.Png);
|
||||||
|
}
|
||||||
|
public static Stream SavePng(this Image Source)
|
||||||
|
{
|
||||||
|
MemoryStream outStream = new MemoryStream();
|
||||||
|
Source.SavePng(outStream);
|
||||||
|
outStream.Position = 0;
|
||||||
|
return outStream;
|
||||||
|
}
|
||||||
|
public static Stream SaveJpg(this Image Source, int Quality)
|
||||||
|
{
|
||||||
|
MemoryStream outStream = new MemoryStream();
|
||||||
|
Source.SaveJpg(Quality, outStream);
|
||||||
|
outStream.Position = 0;
|
||||||
|
return outStream;
|
||||||
|
}
|
||||||
|
public static void SaveJpg(this Image Source, int Quality, string Filename)
|
||||||
|
{
|
||||||
|
using (FileStream outStream = new FileStream(Filename, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
SaveJpg(Source, Quality, outStream);
|
||||||
|
outStream.Flush();
|
||||||
|
outStream.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void SaveJpg(this Image Source, int Quality, Stream OutStream)
|
||||||
|
{
|
||||||
|
ImageCodecInfo jpgCodec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType.Equals("image/jpeg", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
|
||||||
|
if (jpgCodec != null)
|
||||||
|
{
|
||||||
|
if (Quality < 0 || Quality > 100)
|
||||||
|
throw new ArgumentOutOfRangeException("Quality", "Quality must be a positive integer <= 100");
|
||||||
|
using (EncoderParameters ep = new EncoderParameters(1))
|
||||||
|
{
|
||||||
|
ep.Param[0] = new EncoderParameter(Encoder.Quality, Quality);
|
||||||
|
Source.Save(OutStream, jpgCodec, ep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback
|
||||||
|
Source.Save(OutStream, ImageFormat.Jpeg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static ImageMontage BuildImageMontage(this IEnumerable<Image> Images, int MaxHeight = -1, int MaxWidth = -1, bool EnforceDimensions = false)
|
||||||
|
{
|
||||||
|
if (EnforceDimensions && (MaxHeight < 0 || MaxWidth < 0))
|
||||||
|
throw new ArgumentOutOfRangeException("EnforceDimensions", "Dimensions can only be enforced when a MaxHeight and MaxWidth is supplied");
|
||||||
|
|
||||||
|
Dictionary<Image, int> imageLocations = new Dictionary<Image, int>();
|
||||||
|
double imageScale = 1.0;
|
||||||
|
int totalHeight = Images.Max(i => i.Height);
|
||||||
|
int totalWidth = Images.Sum(i => i.Width);
|
||||||
|
if (MaxHeight > 0 && totalHeight > MaxHeight)
|
||||||
|
{
|
||||||
|
imageScale = (double)MaxHeight / (double)totalHeight;
|
||||||
|
}
|
||||||
|
if (MaxWidth > 0 && totalWidth > MaxWidth)
|
||||||
|
{
|
||||||
|
imageScale = System.Math.Min(imageScale, (double)MaxWidth / (double)totalWidth);
|
||||||
|
}
|
||||||
|
int scaledHeight = EnforceDimensions ? MaxHeight : (int)System.Math.Round((double)totalHeight * imageScale);
|
||||||
|
int scaledWidth = EnforceDimensions ? MaxWidth : (int)System.Math.Round((double)totalWidth * imageScale);
|
||||||
|
System.Drawing.Bitmap imageResult = new System.Drawing.Bitmap(scaledWidth, scaledHeight);
|
||||||
|
imageResult.SetResolution(72f, 72f);
|
||||||
|
|
||||||
|
using (Graphics g = Graphics.FromImage(imageResult))
|
||||||
|
{
|
||||||
|
g.FillRectangle(Brushes.White, new Rectangle(Point.Empty, imageResult.Size));
|
||||||
|
|
||||||
|
int imageResultNextOffset = 0;
|
||||||
|
foreach (Image i in Images)
|
||||||
|
{
|
||||||
|
Rectangle imagePosition = new Rectangle(imageResultNextOffset, 0, (int)System.Math.Round((double)i.Width * imageScale), (int)System.Math.Round((double)i.Height * imageScale));
|
||||||
|
System.Drawing.Rectangle imageSize = new System.Drawing.Rectangle(0, 0, i.Width, i.Height);
|
||||||
|
g.DrawImage(i, imagePosition, imageSize, System.Drawing.GraphicsUnit.Pixel);
|
||||||
|
imageLocations[i] = imageResultNextOffset;
|
||||||
|
imageResultNextOffset += imagePosition.Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ImageMontage() { Montage = imageResult, MontageScale = imageScale, MontageSourceImageOffsets = imageLocations };
|
||||||
|
}
|
||||||
|
public class ImageMontage : IDisposable
|
||||||
|
{
|
||||||
|
|
||||||
|
public Image Montage { get; set; }
|
||||||
|
public double MontageScale { get; set; }
|
||||||
|
public Dictionary<Image, int> MontageSourceImageOffsets { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Montage != null)
|
||||||
|
{
|
||||||
|
Montage.Dispose();
|
||||||
|
Montage = null;
|
||||||
|
}
|
||||||
|
if (MontageSourceImageOffsets != null)
|
||||||
|
{
|
||||||
|
MontageSourceImageOffsets.Clear();
|
||||||
|
MontageSourceImageOffsets = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Color InterpolateColours(this Color Start, Color End, double Progress)
|
||||||
|
{
|
||||||
|
if (Progress > 1 || Progress < 0)
|
||||||
|
throw new ArgumentOutOfRangeException("Progress", "Progress must be >= 0 && <= 1");
|
||||||
|
|
||||||
|
return Color.FromArgb(
|
||||||
|
(byte)(Start.A * (1 - Progress) + (End.A * Progress)),
|
||||||
|
(byte)(Start.R * (1 - Progress) + (End.R * Progress)),
|
||||||
|
(byte)(Start.G * (1 - Progress) + (End.G * Progress)),
|
||||||
|
(byte)(Start.B * (1 - Progress) + (End.B * Progress))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Disco.Models.Repository;
|
||||||
|
using System;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
namespace Disco.BI.Extensions
|
||||||
|
{
|
||||||
|
public static class WirelessCertificateExtensions
|
||||||
|
{
|
||||||
|
public static System.DateTime? CertificateExpirationDate(this DeviceCertificate wc)
|
||||||
|
{
|
||||||
|
if (wc.Content == null || wc.Content.Length == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
X509Certificate2 c = new X509Certificate2(wc.Content, "password");
|
||||||
|
return c.NotAfter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
using Disco.Models.Interop.ActiveDirectory;
|
||||||
|
using Disco.BI.DeviceBI;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.DirectoryServices;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
public static class ActiveDirectory
|
||||||
|
{
|
||||||
|
public static ActiveDirectoryMachineAccount GetMachineAccount(string ComputerName, System.Guid? UUIDNetbootGUID = null, System.Guid? MacAddressNetbootGUID = null, params string[] AdditionalProperties)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(ComputerName))
|
||||||
|
throw new System.ArgumentException("Invalid Computer Name - Empty", "ComputerName");
|
||||||
|
if (ComputerName.Contains("\\"))
|
||||||
|
ComputerName = ComputerName.Substring(checked(ComputerName.IndexOf("\\") + 1));
|
||||||
|
if (ComputerName.Length > 24)
|
||||||
|
throw new System.ArgumentException("Invalid Computer Name - Length > 24", "ComputerName");
|
||||||
|
string sAMAccountName = ComputerName;
|
||||||
|
if (!sAMAccountName.EndsWith("$"))
|
||||||
|
sAMAccountName = string.Format("{0}$", sAMAccountName);
|
||||||
|
|
||||||
|
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
|
||||||
|
{
|
||||||
|
var loadProperties = new List<string> { "name", "distinguishedName", "sAMAccountName", "objectSid", "dNSHostName", "netbootGUID", "isCriticalSystemObject" };
|
||||||
|
loadProperties.AddRange(AdditionalProperties);
|
||||||
|
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(sAMAccountName={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), loadProperties.ToArray(), SearchScope.Subtree))
|
||||||
|
{
|
||||||
|
SearchResult dResult = dSearcher.FindOne();
|
||||||
|
if (dResult != null)
|
||||||
|
{
|
||||||
|
return ActiveDirectory.DirectorySearchResultToMachineAccount(dResult, AdditionalProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UUIDNetbootGUID.HasValue)
|
||||||
|
{
|
||||||
|
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(netbootGUID={0}))", ActiveDirectoryHelpers.FormatGuidForLdapQuery(UUIDNetbootGUID.Value)), loadProperties.ToArray(), SearchScope.Subtree))
|
||||||
|
{
|
||||||
|
SearchResult dResult = dSearcher.FindOne();
|
||||||
|
if (dResult != null)
|
||||||
|
{
|
||||||
|
return ActiveDirectory.DirectorySearchResultToMachineAccount(dResult, AdditionalProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (MacAddressNetbootGUID.HasValue)
|
||||||
|
{
|
||||||
|
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(netbootGUID={0}))", ActiveDirectoryHelpers.FormatGuidForLdapQuery(MacAddressNetbootGUID.Value)), loadProperties.ToArray(), SearchScope.Subtree))
|
||||||
|
{
|
||||||
|
SearchResult dResult = dSearcher.FindOne();
|
||||||
|
if (dResult != null)
|
||||||
|
{
|
||||||
|
return ActiveDirectory.DirectorySearchResultToMachineAccount(dResult, AdditionalProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static ActiveDirectoryMachineAccount DirectorySearchResultToMachineAccount(SearchResult result, params string[] AdditionalProperties)
|
||||||
|
{
|
||||||
|
string name = result.Properties["name"][0].ToString();
|
||||||
|
string sAMAccountName = result.Properties["sAMAccountName"][0].ToString();
|
||||||
|
string distinguishedName = result.Properties["distinguishedName"][0].ToString();
|
||||||
|
string objectSid = ActiveDirectoryHelpers.ConvertBytesToSIDString((byte[])result.Properties["objectSid"][0]);
|
||||||
|
|
||||||
|
var dNSNameProperty = result.Properties["dNSHostName"];
|
||||||
|
string dNSName = null;
|
||||||
|
if (dNSNameProperty.Count > 0)
|
||||||
|
dNSName = dNSNameProperty[0].ToString();
|
||||||
|
else
|
||||||
|
dNSName = string.Format("{0}.{1}", sAMAccountName.TrimEnd('$'), ActiveDirectoryHelpers.DefaultDomainQualifiedName);
|
||||||
|
|
||||||
|
bool isCriticalSystemObject = (bool)result.Properties["isCriticalSystemObject"][0];
|
||||||
|
|
||||||
|
System.Guid netbootGUIDResult = default(System.Guid);
|
||||||
|
ResultPropertyValueCollection netbootGUIDProp = result.Properties["netbootGUID"];
|
||||||
|
if (netbootGUIDProp.Count > 0)
|
||||||
|
{
|
||||||
|
netbootGUIDResult = new System.Guid((byte[])netbootGUIDProp[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional Properties
|
||||||
|
Dictionary<string, object[]> additionalProperties = new Dictionary<string, object[]>();
|
||||||
|
foreach (string propertyName in AdditionalProperties)
|
||||||
|
{
|
||||||
|
var property = result.Properties[propertyName];
|
||||||
|
var propertyValues = new List<object>();
|
||||||
|
for (int index = 0; index < property.Count; index++)
|
||||||
|
propertyValues.Add(property[index]);
|
||||||
|
additionalProperties.Add(propertyName, propertyValues.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ActiveDirectoryMachineAccount
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
DistinguishedName = distinguishedName,
|
||||||
|
sAMAccountName = sAMAccountName,
|
||||||
|
ObjectSid = objectSid,
|
||||||
|
NetbootGUID = netbootGUIDResult,
|
||||||
|
Path = result.Path,
|
||||||
|
Domain = ActiveDirectoryHelpers.DefaultDomainNetBiosName,
|
||||||
|
DnsName = dNSName,
|
||||||
|
IsCriticalSystemObject = isCriticalSystemObject,
|
||||||
|
LoadedProperties = additionalProperties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private static ActiveDirectoryUserAccount SearchResultToActiveDirectoryUserAccount(SearchResult result, params string[] AdditionalProperties)
|
||||||
|
{
|
||||||
|
string name = result.Properties["name"][0].ToString();
|
||||||
|
string username = result.Properties["sAMAccountName"][0].ToString();
|
||||||
|
string distinguishedName = result.Properties["distinguishedName"][0].ToString();
|
||||||
|
string objectSid = ActiveDirectoryHelpers.ConvertBytesToSIDString((byte[])result.Properties["objectSid"][0]);
|
||||||
|
|
||||||
|
ResultPropertyValueCollection displayNameProp = result.Properties["displayName"];
|
||||||
|
string displayName = username;
|
||||||
|
if (displayNameProp.Count > 0)
|
||||||
|
displayName = displayNameProp[0].ToString();
|
||||||
|
string surname = null;
|
||||||
|
ResultPropertyValueCollection surnameProp = result.Properties["sn"];
|
||||||
|
if (surnameProp.Count > 0)
|
||||||
|
surname = surnameProp[0].ToString();
|
||||||
|
string givenName = null;
|
||||||
|
ResultPropertyValueCollection givenNameProp = result.Properties["givenName"];
|
||||||
|
if (givenNameProp.Count > 0)
|
||||||
|
givenName = givenNameProp[0].ToString();
|
||||||
|
string email = null;
|
||||||
|
ResultPropertyValueCollection emailProp = result.Properties["mail"];
|
||||||
|
if (emailProp.Count > 0)
|
||||||
|
email = emailProp[0].ToString();
|
||||||
|
string phone = null;
|
||||||
|
ResultPropertyValueCollection phoneProp = result.Properties["telephoneNumber"];
|
||||||
|
if (phoneProp.Count > 0)
|
||||||
|
phone = phoneProp[0].ToString();
|
||||||
|
|
||||||
|
IEnumerable<string> groupCNs = result.Properties["memberOf"].Cast<string>();
|
||||||
|
List<string> groups = ActiveDirectoryCachedGroups.GetGroups(groupCNs).Select(g => g.ToLower()).ToList();
|
||||||
|
|
||||||
|
//foreach (string groupCN in result.Properties["memberOf"])
|
||||||
|
//{
|
||||||
|
// Removed 2012-11-30 G# - Moved to Recursive Cache
|
||||||
|
//var groupCNlower = groupCN.ToLower();
|
||||||
|
//if (groupCNlower.StartsWith("cn="))
|
||||||
|
// groups.Add(groupCNlower.Substring(3, groupCNlower.IndexOf(",") - 3));
|
||||||
|
// End Removed 2012-11-30 G#
|
||||||
|
//}
|
||||||
|
|
||||||
|
string type = null;
|
||||||
|
if (groups.Contains("domain admins") || groups.Contains("disco admins"))
|
||||||
|
{
|
||||||
|
type = "Admin";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (groups.Contains("staff"))
|
||||||
|
{
|
||||||
|
type = "Staff";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (groups.Contains("students"))
|
||||||
|
{
|
||||||
|
type = "Student";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional Properties
|
||||||
|
Dictionary<string, object[]> additionalProperties = new Dictionary<string, object[]>();
|
||||||
|
foreach (string propertyName in AdditionalProperties)
|
||||||
|
{
|
||||||
|
var property = result.Properties[propertyName];
|
||||||
|
var propertyValues = new List<object>();
|
||||||
|
for (int index = 0; index < property.Count; index++)
|
||||||
|
propertyValues.Add(property[index]);
|
||||||
|
additionalProperties.Add(propertyName, propertyValues.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ActiveDirectoryUserAccount
|
||||||
|
{
|
||||||
|
Domain = ActiveDirectoryHelpers.DefaultDomainNetBiosName,
|
||||||
|
Name = name,
|
||||||
|
Surname = surname,
|
||||||
|
GivenName = givenName,
|
||||||
|
Email = email,
|
||||||
|
Phone = phone,
|
||||||
|
DistinguishedName = distinguishedName,
|
||||||
|
sAMAccountName = username,
|
||||||
|
DisplayName = displayName,
|
||||||
|
ObjectSid = objectSid,
|
||||||
|
Type = type,
|
||||||
|
Path = result.Path,
|
||||||
|
LoadedProperties = additionalProperties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public static ActiveDirectoryUserAccount GetUserAccount(string Username, params string[] AdditionalProperties)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Username))
|
||||||
|
throw new System.ArgumentException("Invalid User Account", "Username");
|
||||||
|
string sAMAccountName = Username;
|
||||||
|
if (sAMAccountName.Contains("\\"))
|
||||||
|
sAMAccountName = sAMAccountName.Substring(checked(sAMAccountName.IndexOf("\\") + 1));
|
||||||
|
|
||||||
|
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultLdapRoot)
|
||||||
|
{
|
||||||
|
var loadProperties = new List<string> {
|
||||||
|
"name",
|
||||||
|
"distinguishedName",
|
||||||
|
"sAMAccountName",
|
||||||
|
"objectSid",
|
||||||
|
"displayName",
|
||||||
|
"sn",
|
||||||
|
"givenName",
|
||||||
|
"memberOf",
|
||||||
|
"mail",
|
||||||
|
"telephoneNumber"
|
||||||
|
};
|
||||||
|
loadProperties.AddRange(AdditionalProperties);
|
||||||
|
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=user)(sAMAccountName={0}))", ActiveDirectoryHelpers.EscapeLdapQuery(sAMAccountName)), loadProperties.ToArray(), SearchScope.Subtree))
|
||||||
|
{
|
||||||
|
SearchResult dResult = dSearcher.FindOne();
|
||||||
|
if (dResult != null)
|
||||||
|
return ActiveDirectory.SearchResultToActiveDirectoryUserAccount(dResult, AdditionalProperties);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string OfflineDomainJoinProvision(ref ActiveDirectoryMachineAccount ExistingAccount, string ComputerName, string OrganisationalUnit = null, string EnrolSessionId = null)
|
||||||
|
{
|
||||||
|
if (ExistingAccount != null && ExistingAccount.IsCriticalSystemObject)
|
||||||
|
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", ExistingAccount.DistinguishedName));
|
||||||
|
|
||||||
|
string DJoinResult = null;
|
||||||
|
if (string.IsNullOrWhiteSpace(ComputerName) || ComputerName.Length > 24)
|
||||||
|
throw new System.ArgumentException("Invalid Computer Name; > 0 and <= 24", "ComputerName");
|
||||||
|
|
||||||
|
// Added 2012-10-25 G#
|
||||||
|
// Ensure Specified OU Exists
|
||||||
|
if (!string.IsNullOrEmpty(OrganisationalUnit))
|
||||||
|
{
|
||||||
|
var ouPath = string.Format("{0}{1},{2}", ActiveDirectoryHelpers.DefaultLdapPath, OrganisationalUnit, ActiveDirectoryHelpers.DefaultDomainQualifiedName);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (DirectoryEntry ou = new DirectoryEntry(ouPath))
|
||||||
|
{
|
||||||
|
if (ou == null)
|
||||||
|
{
|
||||||
|
throw new Exception("OU's Directory Entry couldn't be found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("An error occurred while trying to locate the specified OU: {0}", ouPath), "OrganisationalUnit", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End Added 2012-10-25 G#
|
||||||
|
|
||||||
|
// Delete Existing
|
||||||
|
if (ExistingAccount != null)
|
||||||
|
ExistingAccount.DeleteAccount();
|
||||||
|
|
||||||
|
string tempFileName = System.IO.Path.GetTempFileName();
|
||||||
|
string argumentOU = (!string.IsNullOrWhiteSpace(OrganisationalUnit)) ? string.Format(" /MACHINEOU \"{0},{1}\"", OrganisationalUnit, ActiveDirectoryHelpers.DefaultDomainQualifiedName) : string.Empty;
|
||||||
|
string arguments = string.Format("/PROVISION /DOMAIN \"{0}\" /DCNAME \"{1}\" /MACHINE \"{2}\"{3} /REUSE /SAVEFILE \"{4}\"",
|
||||||
|
ActiveDirectoryHelpers.DefaultDomainName,
|
||||||
|
ActiveDirectoryHelpers.DefaultDomainPDCName,
|
||||||
|
ComputerName,
|
||||||
|
argumentOU,
|
||||||
|
tempFileName
|
||||||
|
);
|
||||||
|
ProcessStartInfo commandStarter = new ProcessStartInfo("DJOIN.EXE", arguments)
|
||||||
|
{
|
||||||
|
CreateNoWindow = true,
|
||||||
|
ErrorDialog = false,
|
||||||
|
LoadUserProfile = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false
|
||||||
|
};
|
||||||
|
if (EnrolSessionId != null)
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(EnrolSessionId, string.Format("{0} {1}{2}", "DJOIN.EXE", arguments, System.Environment.NewLine));
|
||||||
|
}
|
||||||
|
|
||||||
|
string stdOutput;
|
||||||
|
string stdError;
|
||||||
|
using (Process commandProc = Process.Start(commandStarter))
|
||||||
|
{
|
||||||
|
commandProc.WaitForExit(20000);
|
||||||
|
stdOutput = commandProc.StandardOutput.ReadToEnd();
|
||||||
|
stdError = commandProc.StandardError.ReadToEnd();
|
||||||
|
}
|
||||||
|
if (EnrolSessionId != null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(stdOutput))
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(EnrolSessionId, stdOutput + System.Environment.NewLine);
|
||||||
|
if (!string.IsNullOrWhiteSpace(stdError))
|
||||||
|
EnrolmentLog.LogSessionDiagnosticInformation(EnrolSessionId, stdError + System.Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(tempFileName))
|
||||||
|
{
|
||||||
|
DJoinResult = System.Convert.ToBase64String(System.IO.File.ReadAllBytes(tempFileName));
|
||||||
|
System.IO.File.Delete(tempFileName);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(DJoinResult))
|
||||||
|
throw new System.InvalidOperationException(string.Format("Domain Join Unsuccessful{0}Error: {1}{0}Output: {2}", System.Environment.NewLine, stdError, stdOutput));
|
||||||
|
ExistingAccount = ActiveDirectory.GetMachineAccount(ComputerName);
|
||||||
|
return DJoinResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ActiveDirectoryUserAccount> SearchUsers(string term)
|
||||||
|
{
|
||||||
|
List<ActiveDirectoryUserAccount> users = new List<ActiveDirectoryUserAccount>();
|
||||||
|
string defaultQualifiedDomainName = ActiveDirectoryHelpers.DefaultDomainQualifiedName;
|
||||||
|
string defaultNetBiosDomainName = ActiveDirectoryHelpers.DefaultDomainNetBiosName;
|
||||||
|
term = ActiveDirectoryHelpers.EscapeLdapQuery(term);
|
||||||
|
using (DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}", defaultQualifiedDomainName)))
|
||||||
|
{
|
||||||
|
using (DirectorySearcher searcher = new DirectorySearcher(entry, string.Format("(&(objectClass=User)(objectCategory=Person)(|(sAMAccountName=*{0}*)(displayName=*{0}*)))", term), new string[]
|
||||||
|
{
|
||||||
|
"name",
|
||||||
|
"distinguishedName",
|
||||||
|
"sAMAccountName",
|
||||||
|
"objectSid",
|
||||||
|
"displayName",
|
||||||
|
"sn",
|
||||||
|
"givenName",
|
||||||
|
"memberOf",
|
||||||
|
"mail",
|
||||||
|
"telephoneNumber"
|
||||||
|
}, SearchScope.Subtree))
|
||||||
|
{
|
||||||
|
searcher.SizeLimit = 30;
|
||||||
|
SearchResultCollection results = searcher.FindAll();
|
||||||
|
foreach (SearchResult result in results)
|
||||||
|
{
|
||||||
|
users.Add(ActiveDirectory.SearchResultToActiveDirectoryUserAccount(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
public static List<ActiveDirectoryOrganisationalUnit> GetOrganisationalUnitStructure()
|
||||||
|
{
|
||||||
|
ActiveDirectoryOrganisationalUnit DomainOUs = new ActiveDirectoryOrganisationalUnit
|
||||||
|
{
|
||||||
|
Children = new System.Collections.Generic.List<ActiveDirectoryOrganisationalUnit>()
|
||||||
|
};
|
||||||
|
string defaultQualifiedDomainName = ActiveDirectoryHelpers.DefaultDomainQualifiedName;
|
||||||
|
|
||||||
|
using (DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}", defaultQualifiedDomainName)))
|
||||||
|
{
|
||||||
|
ActiveDirectory.GetOrganisationalUnitStructure_Recursive(ref DomainOUs, entry);
|
||||||
|
}
|
||||||
|
return DomainOUs.Children;
|
||||||
|
}
|
||||||
|
private static void GetOrganisationalUnitStructure_Recursive(ref ActiveDirectoryOrganisationalUnit ParentOU, DirectoryEntry Container)
|
||||||
|
{
|
||||||
|
using (DirectorySearcher searcher = new DirectorySearcher(Container, "(objectCategory=organizationalUnit)", new string[]
|
||||||
|
{
|
||||||
|
"name",
|
||||||
|
"distinguishedName"
|
||||||
|
}, SearchScope.OneLevel))
|
||||||
|
{
|
||||||
|
using (SearchResultCollection results = searcher.FindAll())
|
||||||
|
{
|
||||||
|
foreach (SearchResult result in results)
|
||||||
|
{
|
||||||
|
string i = result.Properties["name"][0].ToString();
|
||||||
|
string dn = result.Properties["distinguishedName"][0].ToString();
|
||||||
|
ActiveDirectoryOrganisationalUnit ChildOU = new ActiveDirectoryOrganisationalUnit
|
||||||
|
{
|
||||||
|
Name = i,
|
||||||
|
Path = dn.Substring(0, dn.IndexOf(",DC=")),
|
||||||
|
Children = new List<ActiveDirectoryOrganisationalUnit>()
|
||||||
|
};
|
||||||
|
ActiveDirectory.GetOrganisationalUnitStructure_Recursive(ref ChildOU, result.GetDirectoryEntry());
|
||||||
|
if (ChildOU.Children.Count == 0)
|
||||||
|
ChildOU.Children = null;
|
||||||
|
ParentOU.Children.Add(ChildOU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.DirectoryServices;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
using Quartz;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
public class ActiveDirectoryCachedGroups : ScheduledTask
|
||||||
|
{
|
||||||
|
private static ConcurrentDictionary<string, Tuple<ADCachedGroup, DateTime>> _Cache = new ConcurrentDictionary<string, Tuple<ADCachedGroup, DateTime>>();
|
||||||
|
private const long CacheTimeoutTicks = 6000000000; // 10 Minutes
|
||||||
|
|
||||||
|
public static IEnumerable<string> GetGroups(IEnumerable<string> GroupCNs)
|
||||||
|
{
|
||||||
|
List<ADCachedGroup> groups = new List<ADCachedGroup>();
|
||||||
|
|
||||||
|
foreach (var groupCN in GroupCNs)
|
||||||
|
foreach (var group in GetGroupsRecursive(groupCN, new Stack<ADCachedGroup>()))
|
||||||
|
if (!groups.Contains(group))
|
||||||
|
{
|
||||||
|
groups.Add(group);
|
||||||
|
yield return group.FriendlyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static IEnumerable<string> GetGroups(string GroupCN)
|
||||||
|
{
|
||||||
|
foreach (var group in GetGroupsRecursive(GroupCN, new Stack<ADCachedGroup>()))
|
||||||
|
yield return group.FriendlyName;
|
||||||
|
}
|
||||||
|
private static IEnumerable<ADCachedGroup> GetGroupsRecursive(string GroupCN, Stack<ADCachedGroup> RecursiveTree)
|
||||||
|
{
|
||||||
|
var group = GetGroup(GroupCN);
|
||||||
|
|
||||||
|
if (group != null && !RecursiveTree.Contains(group))
|
||||||
|
{
|
||||||
|
yield return group;
|
||||||
|
|
||||||
|
if (group.MemberOf != null)
|
||||||
|
{
|
||||||
|
RecursiveTree.Push(group);
|
||||||
|
|
||||||
|
foreach (var memberOfGroupCN in group.MemberOf)
|
||||||
|
foreach (var memberOfGroup in GetGroupsRecursive(memberOfGroupCN, RecursiveTree))
|
||||||
|
yield return memberOfGroup;
|
||||||
|
|
||||||
|
RecursiveTree.Pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ADCachedGroup GetGroup(string GroupCN)
|
||||||
|
{
|
||||||
|
// Check Cache
|
||||||
|
Tuple<ADCachedGroup, DateTime> groupRecord = TryCache(GroupCN);
|
||||||
|
|
||||||
|
if (groupRecord == null)
|
||||||
|
{
|
||||||
|
// Load from AD
|
||||||
|
var group = ADCachedGroup.LoadFromAD(GroupCN);
|
||||||
|
SetValue(GroupCN, group);
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return from Cache
|
||||||
|
return groupRecord.Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Tuple<ADCachedGroup, DateTime> TryCache(string GroupCN)
|
||||||
|
{
|
||||||
|
string groupCN = GroupCN.ToLower();
|
||||||
|
Tuple<ADCachedGroup, DateTime> groupRecord;
|
||||||
|
if (_Cache.TryGetValue(groupCN, out groupRecord))
|
||||||
|
{
|
||||||
|
if (groupRecord.Item2 > DateTime.Now)
|
||||||
|
return groupRecord;
|
||||||
|
else
|
||||||
|
_Cache.TryRemove(groupCN, out groupRecord);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static bool SetValue(string GroupCN, ADCachedGroup GroupRecord)
|
||||||
|
{
|
||||||
|
string key = GroupCN.ToLower();
|
||||||
|
Tuple<ADCachedGroup, DateTime> groupRecord = new Tuple<ADCachedGroup, DateTime>(GroupRecord, DateTime.Now.AddTicks(CacheTimeoutTicks));
|
||||||
|
if (_Cache.ContainsKey(key))
|
||||||
|
{
|
||||||
|
Tuple<ADCachedGroup, DateTime> oldGroupRecord;
|
||||||
|
if (_Cache.TryGetValue(key, out oldGroupRecord))
|
||||||
|
{
|
||||||
|
return _Cache.TryUpdate(key, groupRecord, oldGroupRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _Cache.TryAdd(key, groupRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ADCachedGroup
|
||||||
|
{
|
||||||
|
public string CN { get; private set; }
|
||||||
|
public string FriendlyName { get; private set; }
|
||||||
|
|
||||||
|
public List<string> MemberOf { get; private set; }
|
||||||
|
|
||||||
|
public static ADCachedGroup LoadFromAD(string CN)
|
||||||
|
{
|
||||||
|
ADCachedGroup group = null;
|
||||||
|
|
||||||
|
using (DirectoryEntry groupDE = new DirectoryEntry(string.Concat(ActiveDirectoryHelpers.DefaultLdapPath, CN)))
|
||||||
|
{
|
||||||
|
if (groupDE != null)
|
||||||
|
{
|
||||||
|
group = new ADCachedGroup()
|
||||||
|
{
|
||||||
|
CN = CN
|
||||||
|
};
|
||||||
|
|
||||||
|
group.FriendlyName = (string)groupDE.Properties["sAMAccountName"].Value;
|
||||||
|
|
||||||
|
var groupMemberOf = groupDE.Properties["memberOf"];
|
||||||
|
if (groupMemberOf != null && groupMemberOf.Count > 0)
|
||||||
|
{
|
||||||
|
group.MemberOf = groupMemberOf.Cast<string>().ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ADCachedGroup()
|
||||||
|
{
|
||||||
|
// Private Constructor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CleanStaleCache()
|
||||||
|
{
|
||||||
|
var groupKeys = _Cache.Keys.ToArray();
|
||||||
|
foreach (string groupKey in groupKeys)
|
||||||
|
{
|
||||||
|
Tuple<ADCachedGroup, DateTime> groupRecord;
|
||||||
|
if (_Cache.TryGetValue(groupKey, out groupRecord))
|
||||||
|
{
|
||||||
|
if (groupRecord.Item2 <= DateTime.Now)
|
||||||
|
_Cache.TryRemove(groupKey, out groupRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string TaskName { get { return "AD Group Cache - Clean Stale Cache"; } }
|
||||||
|
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
public override bool LogExceptionsOnly { get { return true; } }
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Run @ every 15mins
|
||||||
|
|
||||||
|
// Next 15min interval
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
int mins = (15 - (now.Minute % 15));
|
||||||
|
if (mins < 10)
|
||||||
|
mins += 15;
|
||||||
|
DateTimeOffset startAt = new DateTimeOffset(now).AddMinutes(mins).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
|
||||||
|
|
||||||
|
TriggerBuilder triggerBuilder = TriggerBuilder.Create().StartAt(startAt).
|
||||||
|
WithSchedule(SimpleScheduleBuilder.RepeatMinutelyForever(15));
|
||||||
|
|
||||||
|
this.ScheduleTask(triggerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
CleanStaleCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.DirectoryServices;
|
||||||
|
using System.DirectoryServices.ActiveDirectory;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
internal static class ActiveDirectoryHelpers
|
||||||
|
{
|
||||||
|
#region Static Cached Properties
|
||||||
|
private static string _DefaultDomainName;
|
||||||
|
private static string _DefaultDomainPDCName;
|
||||||
|
private static System.Collections.Generic.List<string> _DefaultDomainDCNames;
|
||||||
|
private static string _DefaultDomainNetBiosName;
|
||||||
|
private static string _DefaultDomainQualifiedName;
|
||||||
|
private static string _DefaultLdapPath;
|
||||||
|
private static bool _DetermineDomainProperties_Loaded = false;
|
||||||
|
private static object _DetermineDomainProperties_Lock = new object();
|
||||||
|
internal static string DefaultDomainName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers.DetermineDomainProperties();
|
||||||
|
return ActiveDirectoryHelpers._DefaultDomainName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static string DefaultDomainPDCName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers.DetermineDomainProperties();
|
||||||
|
return ActiveDirectoryHelpers._DefaultDomainPDCName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static System.Collections.Generic.List<string> DefaultDomainDCNames
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers.DetermineDomainProperties();
|
||||||
|
return ActiveDirectoryHelpers._DefaultDomainDCNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static string DefaultDomainNetBiosName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers.DetermineDomainProperties();
|
||||||
|
return ActiveDirectoryHelpers._DefaultDomainNetBiosName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static string DefaultDomainQualifiedName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers.DetermineDomainProperties();
|
||||||
|
return ActiveDirectoryHelpers._DefaultDomainQualifiedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static string DefaultLdapPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers.DetermineDomainProperties();
|
||||||
|
return ActiveDirectoryHelpers._DefaultLdapPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static string DefaultDCLdapPath(string DC)
|
||||||
|
{
|
||||||
|
return string.Format("LDAP://{0}/", DC);
|
||||||
|
}
|
||||||
|
internal static DirectoryEntry DefaultLdapRoot
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new DirectoryEntry(string.Concat(ActiveDirectoryHelpers.DefaultLdapPath, ActiveDirectoryHelpers.DefaultDomainQualifiedName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static DirectoryEntry DefaultDCLdapRoot(string DC)
|
||||||
|
{
|
||||||
|
return new DirectoryEntry(string.Concat(ActiveDirectoryHelpers.DefaultDCLdapPath(DC), ActiveDirectoryHelpers.DefaultDomainQualifiedName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DetermineDomainProperties()
|
||||||
|
{
|
||||||
|
if (!ActiveDirectoryHelpers._DetermineDomainProperties_Loaded)
|
||||||
|
{
|
||||||
|
lock (ActiveDirectoryHelpers._DetermineDomainProperties_Lock)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!ActiveDirectoryHelpers._DetermineDomainProperties_Loaded)
|
||||||
|
{
|
||||||
|
using (Domain domain = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain)))
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers._DefaultDomainName = domain.Name;
|
||||||
|
ActiveDirectoryHelpers._DefaultDomainPDCName = domain.PdcRoleOwner.Name;
|
||||||
|
ActiveDirectoryHelpers._DefaultDomainDCNames = new System.Collections.Generic.List<string>(domain.DomainControllers.Count);
|
||||||
|
foreach (DomainController dc in domain.DomainControllers)
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers._DefaultDomainDCNames.Add(dc.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveDirectoryHelpers._DefaultDomainQualifiedName = string.Format("DC={0}", ActiveDirectoryHelpers._DefaultDomainName.Replace(".", ",DC="));
|
||||||
|
ActiveDirectoryHelpers._DefaultLdapPath = string.Format("LDAP://{0}/", ActiveDirectoryHelpers._DefaultDomainPDCName);
|
||||||
|
using (DirectoryEntry entry = new DirectoryEntry(string.Format("{0}CN=Partitions,CN=Configuration,{1}", ActiveDirectoryHelpers._DefaultLdapPath, ActiveDirectoryHelpers._DefaultDomainQualifiedName)))
|
||||||
|
{
|
||||||
|
using (DirectorySearcher searcher = new DirectorySearcher(entry, "(&(objectClass=crossRef)(nETBIOSName=*))", new string[] { "nETBIOSName" }))
|
||||||
|
{
|
||||||
|
SearchResult result = searcher.FindOne();
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers._DefaultDomainNetBiosName = result.Properties["nETBIOSName"][0].ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ActiveDirectoryHelpers._DefaultDomainNetBiosName = ActiveDirectoryHelpers._DefaultDomainQualifiedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveDirectoryHelpers._DetermineDomainProperties_Loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
private static extern bool ConvertSidToStringSid(byte[] pSID, ref System.Text.StringBuilder ptrSid);
|
||||||
|
internal static string ConvertBytesToSIDString(byte[] SID)
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder sidString = new System.Text.StringBuilder();
|
||||||
|
bool flag = ActiveDirectoryHelpers.ConvertSidToStringSid(SID, ref sidString);
|
||||||
|
string ConvertBytesToSIDString;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
ConvertBytesToSIDString = sidString.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConvertBytesToSIDString = null;
|
||||||
|
}
|
||||||
|
return ConvertBytesToSIDString;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string EscapeLdapQuery(string query)
|
||||||
|
{
|
||||||
|
return query.Replace("*", "\\2a").Replace("(", "\\28").Replace(")", "\\29").Replace("\\", "\\5c").Replace("NUL", "\\00").Replace("/", "\\2f");
|
||||||
|
}
|
||||||
|
internal static string FormatGuidForLdapQuery(System.Guid g)
|
||||||
|
{
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||||
|
byte[] array = g.ToByteArray();
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
byte b = array[i];
|
||||||
|
sb.Append("\\");
|
||||||
|
sb.Append(b.ToString("X2"));
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
using Disco.Models.Interop.ActiveDirectory;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.DirectoryServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Management;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
public static class ActiveDirectoryMachineAccountExtensions
|
||||||
|
{
|
||||||
|
public static void DeleteAccount(this ActiveDirectoryMachineAccount account)
|
||||||
|
{
|
||||||
|
if (account.IsCriticalSystemObject)
|
||||||
|
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||||
|
|
||||||
|
using (DirectoryEntry machineDE = new DirectoryEntry(account.Path))
|
||||||
|
{
|
||||||
|
DeleteAccountRecursive(machineDE);
|
||||||
|
|
||||||
|
using (var machineDEParent = machineDE.Parent)
|
||||||
|
{
|
||||||
|
machineDEParent.Children.Remove(machineDE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void DeleteAccountRecursive(DirectoryEntry parent)
|
||||||
|
{
|
||||||
|
List<DirectoryEntry> children = new List<DirectoryEntry>();
|
||||||
|
foreach (DirectoryEntry child in parent.Children)
|
||||||
|
children.Add(child);
|
||||||
|
|
||||||
|
foreach (var child in children)
|
||||||
|
{
|
||||||
|
DeleteAccountRecursive(child);
|
||||||
|
parent.Children.Remove(child);
|
||||||
|
child.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void SetNetbootGUID(this ActiveDirectoryMachineAccount account, System.Guid updatedNetbootGUID)
|
||||||
|
{
|
||||||
|
if (account.IsCriticalSystemObject)
|
||||||
|
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||||
|
|
||||||
|
using (DirectoryEntry machineDE = new DirectoryEntry(account.Path))
|
||||||
|
{
|
||||||
|
PropertyValueCollection netbootGUIDProp = machineDE.Properties["netbootGUID"];
|
||||||
|
bool flag = netbootGUIDProp.Count > 0;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
netbootGUIDProp.Clear();
|
||||||
|
}
|
||||||
|
netbootGUIDProp.Add(updatedNetbootGUID.ToByteArray());
|
||||||
|
machineDE.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void SetDescription(this ActiveDirectoryMachineAccount account, string Description)
|
||||||
|
{
|
||||||
|
using (DirectoryEntry machineDE = new DirectoryEntry(account.Path))
|
||||||
|
{
|
||||||
|
PropertyValueCollection descriptionProp = machineDE.Properties["description"];
|
||||||
|
bool flag = descriptionProp.Count > 0;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
descriptionProp.Clear();
|
||||||
|
}
|
||||||
|
flag = !string.IsNullOrEmpty(Description);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
descriptionProp.Add(Description);
|
||||||
|
}
|
||||||
|
machineDE.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void SetDescription(this ActiveDirectoryMachineAccount account, Device Device)
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder descriptionBuilder = new System.Text.StringBuilder();
|
||||||
|
bool flag = Device.AssignedUserId != null;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
descriptionBuilder.Append(Device.AssignedUser.Id);
|
||||||
|
descriptionBuilder.Append(" (");
|
||||||
|
descriptionBuilder.Append(Device.AssignedUser.DisplayName);
|
||||||
|
descriptionBuilder.Append("); ");
|
||||||
|
}
|
||||||
|
flag = Device.DeviceModelId.HasValue;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
descriptionBuilder.Append(Device.DeviceModel.Description);
|
||||||
|
descriptionBuilder.Append("; ");
|
||||||
|
}
|
||||||
|
descriptionBuilder.Append(Device.DeviceProfile.Description);
|
||||||
|
descriptionBuilder.Append("; ");
|
||||||
|
string description = descriptionBuilder.ToString().Trim();
|
||||||
|
flag = (description.Length > 1024);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
description = description.Substring(0, 1024);
|
||||||
|
}
|
||||||
|
account.SetDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DisableAccount(this ActiveDirectoryMachineAccount account)
|
||||||
|
{
|
||||||
|
if (account.IsCriticalSystemObject)
|
||||||
|
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||||
|
|
||||||
|
using (DirectoryEntry machineDE = new DirectoryEntry(account.Path))
|
||||||
|
{
|
||||||
|
int accountControl = (int)machineDE.Properties["userAccountControl"][0];
|
||||||
|
int updatedAccountControl = (accountControl | 2);
|
||||||
|
if (accountControl != updatedAccountControl)
|
||||||
|
{
|
||||||
|
machineDE.Properties["userAccountControl"][0] = updatedAccountControl;
|
||||||
|
machineDE.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void EnableAccount(this ActiveDirectoryMachineAccount account)
|
||||||
|
{
|
||||||
|
if (account.IsCriticalSystemObject)
|
||||||
|
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||||
|
|
||||||
|
using (DirectoryEntry machineDE = new DirectoryEntry(account.Path))
|
||||||
|
{
|
||||||
|
int accountControl = (int)machineDE.Properties["userAccountControl"][0];
|
||||||
|
if ((accountControl & 2) == 2)
|
||||||
|
{
|
||||||
|
int updatedAccountControl = (accountControl ^ 2);
|
||||||
|
machineDE.Properties["userAccountControl"][0] = updatedAccountControl;
|
||||||
|
machineDE.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool UpdateNetbootGUID(this ActiveDirectoryMachineAccount account, string UUID, string MACAddress)
|
||||||
|
{
|
||||||
|
if (account.IsCriticalSystemObject)
|
||||||
|
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||||
|
|
||||||
|
System.Guid netbootGUID = System.Guid.Empty;
|
||||||
|
bool flag = !string.IsNullOrWhiteSpace(UUID);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
netbootGUID = ActiveDirectoryMachineAccountExtensions.NetbootGUIDFromUUID(UUID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flag = !string.IsNullOrWhiteSpace(MACAddress);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
netbootGUID = ActiveDirectoryMachineAccountExtensions.NetbootGUIDFromMACAddress(MACAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flag = (netbootGUID != System.Guid.Empty && netbootGUID != account.NetbootGUID);
|
||||||
|
bool UpdateNetbootGUID;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
account.SetNetbootGUID(netbootGUID);
|
||||||
|
UpdateNetbootGUID = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateNetbootGUID = false;
|
||||||
|
}
|
||||||
|
return UpdateNetbootGUID;
|
||||||
|
}
|
||||||
|
internal static System.Guid NetbootGUIDFromMACAddress(string MACAddress)
|
||||||
|
{
|
||||||
|
string strippedMACAddress = MACAddress.Trim().Replace(":", string.Empty).Replace("-", string.Empty);
|
||||||
|
bool flag = strippedMACAddress.Length == 12;
|
||||||
|
System.Guid NetbootGUIDFromMACAddress;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
System.Guid guid = new System.Guid(string.Format("00000000-0000-0000-0000-{0}", strippedMACAddress));
|
||||||
|
NetbootGUIDFromMACAddress = guid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetbootGUIDFromMACAddress = System.Guid.Empty;
|
||||||
|
}
|
||||||
|
return NetbootGUIDFromMACAddress;
|
||||||
|
}
|
||||||
|
internal static System.Guid NetbootGUIDFromUUID(string UUID)
|
||||||
|
{
|
||||||
|
System.Guid result = new System.Guid(UUID);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object GetPropertyValue(this ActiveDirectoryMachineAccount account, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
switch (PropertyName.ToLower())
|
||||||
|
{
|
||||||
|
case "name":
|
||||||
|
return account.Name;
|
||||||
|
case "samaccountname":
|
||||||
|
return account.sAMAccountName;
|
||||||
|
case "distinguishedname":
|
||||||
|
return account.DistinguishedName;
|
||||||
|
case "objectsid":
|
||||||
|
return account.ObjectSid;
|
||||||
|
case "netbootguid":
|
||||||
|
return account.NetbootGUID;
|
||||||
|
default:
|
||||||
|
object[] adProperty;
|
||||||
|
if (account.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length)
|
||||||
|
return adProperty[Index];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IPStatus PingComputer(this ActiveDirectoryMachineAccount account, int Timeout = 2000)
|
||||||
|
{
|
||||||
|
using (var p = new Ping())
|
||||||
|
{
|
||||||
|
PingReply reply = p.Send(account.DnsName, Timeout);
|
||||||
|
return reply.Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Didn't Work - WMI Limitation?
|
||||||
|
// G# - 2012-06-18
|
||||||
|
//public static void OnlineRenameComputer(this ActiveDirectoryMachineAccount account, string NewComputerName)
|
||||||
|
//{
|
||||||
|
// if (account.IsCriticalSystemObject)
|
||||||
|
// throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||||
|
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// IPStatus pingResult = account.PingComputer();
|
||||||
|
// if (pingResult != IPStatus.Success)
|
||||||
|
// throw new Exception(string.Format("Ping Error Result: {0}", pingResult.ToString()));
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// throw new Exception(string.Format("Error trying to Ping the Device: {0}; {1}", account.DnsName, ex.Message), ex);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ConnectionOptions wmiConnectionOptions = new ConnectionOptions()
|
||||||
|
// {
|
||||||
|
// Authentication = AuthenticationLevel.PacketPrivacy,
|
||||||
|
// Impersonation = ImpersonationLevel.Impersonate,
|
||||||
|
// EnablePrivileges = true,
|
||||||
|
// Timeout = new TimeSpan(0, 0, 6)
|
||||||
|
// };
|
||||||
|
// ManagementPath wmiPath = new ManagementPath()
|
||||||
|
// {
|
||||||
|
// Server = account.DnsName,
|
||||||
|
// NamespacePath = @"root\cimv2",
|
||||||
|
// ClassName = "Win32_ComputerSystem"
|
||||||
|
// };
|
||||||
|
|
||||||
|
// ManagementScope wmiScope = new ManagementScope(wmiPath, wmiConnectionOptions);
|
||||||
|
|
||||||
|
// ObjectGetOptions wmiGetOptions = new ObjectGetOptions() { Timeout = new TimeSpan(0, 1, 0) };
|
||||||
|
|
||||||
|
// using (ManagementClass wmiClass = new ManagementClass(wmiScope, wmiPath, wmiGetOptions))
|
||||||
|
// {
|
||||||
|
// foreach (ManagementObject wmiWin32ComputerSystem in wmiClass.GetInstances())
|
||||||
|
// {
|
||||||
|
// UInt32 result = (UInt32)wmiWin32ComputerSystem.InvokeMethod("Rename", new object[] { NewComputerName });
|
||||||
|
// if (result != 0)
|
||||||
|
// throw new Exception(string.Format("Error Renaming Computer; WMI Remote Method 'Rename' returned: {0}", result));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static void MoveOrganisationUnit(this ActiveDirectoryMachineAccount account, string NewOrganisationUnit)
|
||||||
|
{
|
||||||
|
if (account.IsCriticalSystemObject)
|
||||||
|
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||||
|
|
||||||
|
if (!account.ParentDistinguishedName.Equals(NewOrganisationUnit, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
string ouPath;
|
||||||
|
if (string.IsNullOrWhiteSpace(NewOrganisationUnit))
|
||||||
|
ouPath = string.Format("{0}CN=Computers,{1}", ActiveDirectoryHelpers.DefaultLdapPath, ActiveDirectoryHelpers.DefaultDomainQualifiedName);
|
||||||
|
else
|
||||||
|
ouPath = string.Format("{0}{1},{2}", ActiveDirectoryHelpers.DefaultLdapPath, NewOrganisationUnit, ActiveDirectoryHelpers.DefaultDomainQualifiedName);
|
||||||
|
|
||||||
|
using (DirectoryEntry ou = new DirectoryEntry(ouPath))
|
||||||
|
{
|
||||||
|
using (DirectoryEntry i = new DirectoryEntry(account.Path) { UsePropertyCache = false })
|
||||||
|
{
|
||||||
|
i.MoveTo(ou);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
public class ActiveDirectoryOrganisationalUnit
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
public List<ActiveDirectoryOrganisationalUnit> Children { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Services.Logging;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Quartz;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.DirectoryServices;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Reflection;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
namespace Disco.BI.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
public class ActiveDirectoryUpdateLastNetworkLogonDateJob : ScheduledTask
|
||||||
|
{
|
||||||
|
|
||||||
|
public override string TaskName { get { return "Active Directory - Update Last Network Logon Dates Task"; } }
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// ActiveDirectoryUpdateLastNetworkLogonDateJob @ 11:30pm
|
||||||
|
TriggerBuilder triggerBuilder = TriggerBuilder.Create().
|
||||||
|
WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(23, 30));
|
||||||
|
|
||||||
|
this.ScheduleTask(triggerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
int changeCount;
|
||||||
|
|
||||||
|
this.Status.UpdateStatus(1, "Starting", "Connecting to the Database and initializing the environment");
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
UpdateLastNetworkLogonDates(dbContext, this.Status);
|
||||||
|
this.Status.UpdateStatus(95, "Updating Database", "Writing last network logon dates to the Database");
|
||||||
|
changeCount = dbContext.SaveChanges();
|
||||||
|
this.Status.UpdateStatus(100, "Finished", string.Format("{0} Device last network logon dates updated", changeCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemLog.LogInformation(new string[]
|
||||||
|
{
|
||||||
|
"Updated LastNetworkLogon Device Property for Device/s",
|
||||||
|
changeCount.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void InitalizeScheduledTask(DiscoDataContext dbContext, IScheduler Scheduler)
|
||||||
|
//{
|
||||||
|
// // UpdateLastNetworkLogonDates @ 11:30pm
|
||||||
|
// IJobDetail jobDetail = new JobDetailImpl("UpdateLastNetworkLogonDates", typeof(ActiveDirectoryUpdateLastNetworkLogonDateJob));
|
||||||
|
// ITrigger trigger = TriggerBuilder.Create().
|
||||||
|
// WithIdentity("UpdateLastNetworkLogonDatesTrigger").
|
||||||
|
// StartNow().
|
||||||
|
// WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(23, 30)).
|
||||||
|
// Build();
|
||||||
|
// Scheduler.ScheduleJob(jobDetail, trigger);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//void IJob.Execute(IJobExecutionContext context)
|
||||||
|
//{
|
||||||
|
// DiscoDataContext dbContext = new DiscoDataContext();
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// ActiveDirectoryUpdateLastNetworkLogonDateJob.UpdateLastNetworkLogonDates(dbContext);
|
||||||
|
// int changeCount = dbContext.SaveChanges();
|
||||||
|
// SystemLog.LogInformation(new string[]
|
||||||
|
// {
|
||||||
|
// "Updated LastNetworkLogon Device Property for Device/s",
|
||||||
|
// changeCount.ToString()
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// catch (System.Exception ex)
|
||||||
|
// {
|
||||||
|
// SystemLog.LogException("ActiveDirectoryUpdateLastNetworkLogonDateJob", ex);
|
||||||
|
// }
|
||||||
|
// finally
|
||||||
|
// {
|
||||||
|
// bool flag = dbContext != null;
|
||||||
|
// if (flag)
|
||||||
|
// {
|
||||||
|
// ((System.IDisposable)dbContext).Dispose();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
public static bool UpdateLastNetworkLogonDate(Device Device)
|
||||||
|
{
|
||||||
|
System.DateTime? computerLastLogonDate = Device.LastNetworkLogonDate;
|
||||||
|
if (!string.IsNullOrEmpty(Device.ComputerName))
|
||||||
|
{
|
||||||
|
foreach (var dcName in ActiveDirectoryHelpers.DefaultDomainDCNames)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Ping p = new Ping();
|
||||||
|
PingReply pr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pr = p.Send(dcName, 500);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
((System.IDisposable)p).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pr.Status == IPStatus.Success)
|
||||||
|
{
|
||||||
|
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultDCLdapRoot(dcName))
|
||||||
|
{
|
||||||
|
DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, string.Format("(&(objectClass=computer)(sAMAccountName={0}$))", ActiveDirectoryHelpers.EscapeLdapQuery(Device.ComputerName)), new string[]
|
||||||
|
{
|
||||||
|
"lastLogon"
|
||||||
|
}, SearchScope.Subtree);
|
||||||
|
SearchResult dResult = dSearcher.FindOne();
|
||||||
|
if (dResult != null)
|
||||||
|
{
|
||||||
|
ResultPropertyValueCollection dProp = dResult.Properties["lastLogon"];
|
||||||
|
if (dProp != null && dProp.Count > 0)
|
||||||
|
{
|
||||||
|
long lastLogonInt = (long)dProp[0];
|
||||||
|
if (lastLogonInt > 0L)
|
||||||
|
{
|
||||||
|
System.DateTime computerNameDate = System.DateTime.FromFileTime(lastLogonInt);
|
||||||
|
if (computerLastLogonDate.HasValue)
|
||||||
|
{
|
||||||
|
if (System.DateTime.Compare(computerLastLogonDate.Value, computerNameDate) < 0)
|
||||||
|
{
|
||||||
|
computerLastLogonDate = computerNameDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
computerLastLogonDate = computerNameDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SystemLog.LogError(new string[]
|
||||||
|
{
|
||||||
|
string.Format("Unable to ping Domain Controller: '{0}' (ref: Disco.BI.Interop.ActiveDirectory.ActiveDirectoryUpdateLastNetworkLogonDateJob.UpdateDeviceLastNetworkLogonDate)", dcName)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
SystemLog.LogException("UpdateDeviceLastNetworkLogonDate", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool UpdateLastNetworkLogonDate;
|
||||||
|
if (computerLastLogonDate.HasValue)
|
||||||
|
{
|
||||||
|
if (!Device.LastNetworkLogonDate.HasValue)
|
||||||
|
{
|
||||||
|
Device.LastNetworkLogonDate = computerLastLogonDate;
|
||||||
|
UpdateLastNetworkLogonDate = true;
|
||||||
|
return UpdateLastNetworkLogonDate;
|
||||||
|
}
|
||||||
|
if (System.DateTime.Compare(computerLastLogonDate.Value, Device.LastNetworkLogonDate.Value) > 0)
|
||||||
|
{
|
||||||
|
Device.LastNetworkLogonDate = computerLastLogonDate;
|
||||||
|
UpdateLastNetworkLogonDate = true;
|
||||||
|
return UpdateLastNetworkLogonDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateLastNetworkLogonDate = false;
|
||||||
|
return UpdateLastNetworkLogonDate;
|
||||||
|
}
|
||||||
|
public static void UpdateLastNetworkLogonDates(DiscoDataContext context, ScheduledTaskStatus status = null)
|
||||||
|
{
|
||||||
|
System.Collections.Generic.Dictionary<string, System.DateTime> computerLastLogonDates = new System.Collections.Generic.Dictionary<string, System.DateTime>();
|
||||||
|
|
||||||
|
int progressDCCountTotal = ActiveDirectoryHelpers.DefaultDomainDCNames.Count;
|
||||||
|
int progressDCCount = 0;
|
||||||
|
double progressDCProgress = 0;
|
||||||
|
if (progressDCCountTotal > 0)
|
||||||
|
progressDCProgress = 90 / progressDCCountTotal;
|
||||||
|
|
||||||
|
foreach (var dcName in ActiveDirectoryHelpers.DefaultDomainDCNames)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PingReply pr;
|
||||||
|
using (Ping p = new Ping())
|
||||||
|
{
|
||||||
|
pr = p.Send(dcName, 2000);
|
||||||
|
}
|
||||||
|
if (pr.Status == IPStatus.Success)
|
||||||
|
{
|
||||||
|
using (DirectoryEntry dRootEntry = ActiveDirectoryHelpers.DefaultDCLdapRoot(dcName))
|
||||||
|
{
|
||||||
|
double progressDCStart = 5 + (progressDCCount * progressDCProgress);
|
||||||
|
if (status != null)
|
||||||
|
{
|
||||||
|
status.UpdateStatus(progressDCStart, string.Format("Querying Domain Controller: {0}", dcName), "Searching...");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (DirectorySearcher dSearcher = new DirectorySearcher(dRootEntry, "(objectClass=computer)", new string[] { "sAMAccountName", "lastLogon" }, SearchScope.Subtree))
|
||||||
|
{
|
||||||
|
using (SearchResultCollection dResults = dSearcher.FindAll())
|
||||||
|
{
|
||||||
|
|
||||||
|
int progressItemCount = 0;
|
||||||
|
double progressItemProgress = dResults.Count == 0 ? 0 : (progressDCProgress / dResults.Count);
|
||||||
|
|
||||||
|
foreach (SearchResult dResult in dResults)
|
||||||
|
{
|
||||||
|
ResultPropertyValueCollection dProp = dResult.Properties["sAMAccountName"];
|
||||||
|
if (dProp != null && dProp.Count > 0)
|
||||||
|
{
|
||||||
|
string computerName = ((string)dProp[0]).TrimEnd(new char[] { '$' }).ToUpper();
|
||||||
|
|
||||||
|
if (status != null)
|
||||||
|
if (progressItemCount % 150 == 0) // Only Update Status every 150 devices
|
||||||
|
status.UpdateStatus(progressDCStart + (progressItemProgress * progressItemCount), string.Format("Analysing Device: {0}", computerName));
|
||||||
|
|
||||||
|
dProp = dResult.Properties["lastLogon"];
|
||||||
|
if (dProp != null && dProp.Count > 0)
|
||||||
|
{
|
||||||
|
long lastLogonInt = (long)dProp[0];
|
||||||
|
if (lastLogonInt > 0L)
|
||||||
|
{
|
||||||
|
System.DateTime computerNameDate = System.DateTime.FromFileTime(lastLogonInt);
|
||||||
|
System.DateTime existingDate;
|
||||||
|
if (computerLastLogonDates.TryGetValue(computerName, out existingDate))
|
||||||
|
{
|
||||||
|
if (System.DateTime.Compare(existingDate, computerNameDate) < 0)
|
||||||
|
{
|
||||||
|
computerLastLogonDates[computerName] = computerNameDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
computerLastLogonDates[computerName] = computerNameDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
progressItemCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SystemLog.LogError(new string[]
|
||||||
|
{
|
||||||
|
string.Format("Unable to ping Domain Controller: '{0}' (ref: Disco.BI.Interop.ActiveDirectory.ActiveDirectoryUpdateLastNetworkLogonDateJob.UpdateLastNetworkLogonDates)", dcName)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
SystemLog.LogException("UpdateLastNetworkLogonDates", ex);
|
||||||
|
}
|
||||||
|
progressDCCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (Device d in context.Devices.Where(device => device.ComputerName != null))
|
||||||
|
{
|
||||||
|
DateTime computerLastLogonDate;
|
||||||
|
if (computerLastLogonDates.TryGetValue(d.ComputerName.ToUpper(), out computerLastLogonDate))
|
||||||
|
{
|
||||||
|
if (d.LastNetworkLogonDate.HasValue)
|
||||||
|
{
|
||||||
|
if (System.DateTime.Compare(d.LastNetworkLogonDate.Value, computerLastLogonDate) < 0)
|
||||||
|
{
|
||||||
|
d.LastNetworkLogonDate = computerLastLogonDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.LastNetworkLogonDate = computerLastLogonDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using Disco.Models.Interop.ActiveDirectory;
|
||||||
|
using System;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
namespace Disco.BI.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
internal static class ActiveDirectoryUserAccountExtensions
|
||||||
|
{
|
||||||
|
public static bool HasRole(this ActiveDirectoryUserAccount account, string Role)
|
||||||
|
{
|
||||||
|
return account.Groups != null && account.Groups.Contains(Role.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object GetPropertyValue(this ActiveDirectoryUserAccount account, string PropertyName, int Index = 0)
|
||||||
|
{
|
||||||
|
switch (PropertyName.ToLower())
|
||||||
|
{
|
||||||
|
case "name":
|
||||||
|
return account.Name;
|
||||||
|
case "samaccountname":
|
||||||
|
return account.sAMAccountName;
|
||||||
|
case "distinguishedname":
|
||||||
|
return account.DistinguishedName;
|
||||||
|
case "objectsid":
|
||||||
|
return account.ObjectSid;
|
||||||
|
case "sn":
|
||||||
|
return account.Surname;
|
||||||
|
case "givenname":
|
||||||
|
return account.GivenName;
|
||||||
|
case "mail":
|
||||||
|
return account.Email;
|
||||||
|
case "telephonenumber":
|
||||||
|
return account.Phone;
|
||||||
|
default:
|
||||||
|
object[] adProperty;
|
||||||
|
if (account.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length)
|
||||||
|
return adProperty[Index];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.Interop.Community;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.Community
|
||||||
|
{
|
||||||
|
public static class UpdateCheck
|
||||||
|
{
|
||||||
|
private static string UpdateUrl(DiscoDataContext db)
|
||||||
|
{
|
||||||
|
// Special case for DiscoCommunity Hosting Network
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ip = (from addr in Dns.GetHostEntry(Dns.GetHostName()).AddressList
|
||||||
|
where addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork
|
||||||
|
&& addr.ToString().StartsWith("10.131.200.")
|
||||||
|
select addr).FirstOrDefault();
|
||||||
|
if (ip != null)
|
||||||
|
{
|
||||||
|
return "http://hades3:9393/base/DiscoUpdate/V1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{} // Ignore Errors
|
||||||
|
|
||||||
|
return "http://discoict.com.au/base/DiscoUpdate/V1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CurrentDiscoVersion()
|
||||||
|
{
|
||||||
|
var AssemblyVersion = typeof(UpdateCheck).Assembly.GetName().Version;
|
||||||
|
return string.Format("{0}.{1}.{2:0000}.{3:0000}", AssemblyVersion.Major, AssemblyVersion.Minor, AssemblyVersion.Build, AssemblyVersion.Revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UpdateResponse Check(DiscoDataContext db, bool UseProxy, ScheduledTaskStatus status = null)
|
||||||
|
{
|
||||||
|
if (status != null)
|
||||||
|
status.UpdateStatus(10, "Building Update Request");
|
||||||
|
|
||||||
|
var request = BuildRequest(db);
|
||||||
|
//var requestJson = JsonConvert.SerializeObject(request);
|
||||||
|
|
||||||
|
if (status != null)
|
||||||
|
status.UpdateStatus(40, "Sending Request");
|
||||||
|
|
||||||
|
var DiscoBIVersion = CurrentDiscoVersion();
|
||||||
|
|
||||||
|
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(UpdateUrl(db));
|
||||||
|
webRequest.ContentType = "application/json";
|
||||||
|
webRequest.Method = WebRequestMethods.Http.Post;
|
||||||
|
webRequest.UserAgent = string.Format("Disco/{0} (Update)", DiscoBIVersion);
|
||||||
|
|
||||||
|
using (var wrStream = webRequest.GetRequestStream())
|
||||||
|
{
|
||||||
|
XmlSerializer xml = new XmlSerializer(typeof(UpdateRequestV1));
|
||||||
|
xml.Serialize(wrStream, request);
|
||||||
|
}
|
||||||
|
if (status != null)
|
||||||
|
status.UpdateStatus(50, "Waiting for Response");
|
||||||
|
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
|
||||||
|
{
|
||||||
|
if (webResponse.StatusCode == HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
if (status != null)
|
||||||
|
status.UpdateStatus(90, "Reading Response");
|
||||||
|
UpdateResponse result;
|
||||||
|
using (var wResStream = webResponse.GetResponseStream())
|
||||||
|
{
|
||||||
|
XmlSerializer xml = new XmlSerializer(typeof(UpdateResponse));
|
||||||
|
result = (UpdateResponse)xml.Deserialize(wResStream);
|
||||||
|
}
|
||||||
|
//var result = JsonConvert.DeserializeObject<UpdateResponse>(responseContent);
|
||||||
|
db.DiscoConfiguration.UpdateLastCheck = result;
|
||||||
|
db.SaveChanges();
|
||||||
|
|
||||||
|
status.SetFinishedMessage(string.Format("The update server reported Version {0} is the latest.", result.Version));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (status != null)
|
||||||
|
status.SetTaskException(new WebException(string.Format("Server responded with: [{0}] {1}", webResponse.StatusCode, webResponse.StatusDescription)));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UpdateRequestV1 BuildRequest(DiscoDataContext db)
|
||||||
|
{
|
||||||
|
var m = new UpdateRequestV1();
|
||||||
|
|
||||||
|
m.DeploymentId = db.DiscoConfiguration.DeploymentId;
|
||||||
|
|
||||||
|
m.CurrentDiscoVersion = CurrentDiscoVersion();
|
||||||
|
|
||||||
|
m.OrganisationName = db.DiscoConfiguration.OrganisationName;
|
||||||
|
m.BroadbandDoeWanId = GetBroadbandDoeWanId();
|
||||||
|
m.BetaDeployment = db.DiscoConfiguration.UpdateBetaDeployment;
|
||||||
|
|
||||||
|
m.Stat_JobCounts = db.Jobs.GroupBy(j => j.JobTypeId).Select(g => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = g.Key, Count = g.Count() }).ToList();
|
||||||
|
m.Stat_OpenJobCounts = db.Jobs.Where(j => j.ClosedDate == null).GroupBy(j => j.JobTypeId).Select(g => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = g.Key, Count = g.Count() }).ToList();
|
||||||
|
var activeThreshold = DateTime.Now.AddDays(-60);
|
||||||
|
m.Stat_ActiveDeviceModelCounts = db.DeviceModels.Select(dm => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = dm.Manufacturer + ";" + dm.Model, Count = dm.Devices.Count(d => d.DecommissionedDate == null && (d.LastNetworkLogonDate == null || d.LastNetworkLogonDate > activeThreshold)) }).ToList();
|
||||||
|
m.Stat_UserCounts = db.Users.GroupBy(u => u.Type).Select(g => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = g.Key, Count = g.Count() }).ToList();
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region DoE Query
|
||||||
|
public static string GetBroadbandDoeWanId()
|
||||||
|
{
|
||||||
|
// DnsQuery for broadband.doe.wan
|
||||||
|
IPHostEntry doeWanDnsEntry;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doeWanDnsEntry = Dns.GetHostEntry("broadband.doe.wan");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{ return null; } // Fail on error
|
||||||
|
|
||||||
|
// Try using IPSearch feature
|
||||||
|
XDocument doeWanIPSearchResult = TryDownloadDoeIPSearch(false);
|
||||||
|
if (doeWanIPSearchResult == null)
|
||||||
|
doeWanIPSearchResult = TryDownloadDoeIPSearch(true);
|
||||||
|
|
||||||
|
if (doeWanIPSearchResult == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return doeWanIPSearchResult.Element("resultset").Element("site").Element("number").Value.ToLower();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{ return null; } // Fail on error
|
||||||
|
}
|
||||||
|
private static XDocument TryDownloadDoeIPSearch(bool useProxy)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var DiscoBIVersion = CurrentDiscoVersion();
|
||||||
|
|
||||||
|
HttpWebRequest wReq = (HttpWebRequest)HttpWebRequest.Create("http://broadband.doe.wan/ipsearch/showresult.php");
|
||||||
|
if (!useProxy)
|
||||||
|
wReq.Proxy = new WebProxy(); // Empty Proxy Config
|
||||||
|
wReq.Method = WebRequestMethods.Http.Post;
|
||||||
|
wReq.ContentType = "application/x-www-form-urlencoded";
|
||||||
|
wReq.UserAgent = string.Format("Disco/{0}", DiscoBIVersion);
|
||||||
|
using (var wrStream = wReq.GetRequestStream())
|
||||||
|
{
|
||||||
|
using (var wrStreamWriter = new StreamWriter(wrStream))
|
||||||
|
{
|
||||||
|
wrStreamWriter.Write("mode=whoami");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using (HttpWebResponse wRes = (HttpWebResponse)wReq.GetResponse())
|
||||||
|
{
|
||||||
|
if (wRes.StatusCode == HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
using (var wResStream = wRes.GetResponseStream())
|
||||||
|
{
|
||||||
|
return XDocument.Load(wResStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{ return null; } // Fail on error
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Services.Logging;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
using Quartz;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.Community
|
||||||
|
{
|
||||||
|
public class UpdateCheckTask : ScheduledTask
|
||||||
|
{
|
||||||
|
public override string TaskName { get { return "Disco Community - Check for Update"; } }
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
|
||||||
|
public static ScheduledTaskStatus ScheduleNow()
|
||||||
|
{
|
||||||
|
|
||||||
|
var runningTasks = ScheduledTasks.GetTaskStatuses(typeof(UpdateCheckTask)).Where(ts => ts.IsRunning).ToList();
|
||||||
|
if (runningTasks.Count > 0)
|
||||||
|
return runningTasks.First();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var t = new UpdateCheckTask();
|
||||||
|
return t.ScheduleTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static ScheduledTaskStatus RunningStatus
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ScheduledTasks.GetTaskStatuses(typeof(UpdateCheckTask)).Where(ts => ts.IsRunning).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static DateTime? NextScheduled
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var runningTasks = ScheduledTasks.GetTaskStatuses(typeof(UpdateCheckTask)).ToList();
|
||||||
|
DateTime timestamp = DateTime.MaxValue;
|
||||||
|
foreach (var t in runningTasks)
|
||||||
|
{
|
||||||
|
if (t.NextScheduledTimestamp != null && t.NextScheduledTimestamp.Value < timestamp)
|
||||||
|
timestamp = t.NextScheduledTimestamp.Value;
|
||||||
|
}
|
||||||
|
if (timestamp == DateTime.MaxValue)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(Data.Repository.DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// ActiveDirectoryUpdateLastNetworkLogonDateJob @ 11:30pm
|
||||||
|
var rnd = new Random();
|
||||||
|
|
||||||
|
var rndHour = rnd.Next(12, 23);
|
||||||
|
var rndMinute = rnd.Next(0, 59);
|
||||||
|
|
||||||
|
TriggerBuilder triggerBuilder = TriggerBuilder.Create().
|
||||||
|
WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(rndHour, rndMinute));
|
||||||
|
|
||||||
|
this.ScheduleTask(triggerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
using (DiscoDataContext db = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateCheck.Check(db, true, this.Status);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Could be proxy error - try again without proxy:
|
||||||
|
UpdateCheck.Check(db, false, this.Status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop
|
||||||
|
{
|
||||||
|
public static class MimeTypes
|
||||||
|
{
|
||||||
|
public static string ResolveMimeType(string Filename)
|
||||||
|
{
|
||||||
|
string fileExtension;
|
||||||
|
if (Filename.Contains("."))
|
||||||
|
fileExtension = Filename.Substring(Filename.LastIndexOf(".") + 1).ToLower();
|
||||||
|
else
|
||||||
|
fileExtension = Filename.ToLower();
|
||||||
|
|
||||||
|
// Try Known Mime Types
|
||||||
|
switch (fileExtension)
|
||||||
|
{
|
||||||
|
case "pdf":
|
||||||
|
return "application/pdf";
|
||||||
|
case "doc":
|
||||||
|
return "application/msword";
|
||||||
|
case "docx":
|
||||||
|
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||||
|
case "docm":
|
||||||
|
return "application/vnd.ms-word.document.macroEnabled.12";
|
||||||
|
case "xml":
|
||||||
|
return "text/xml";
|
||||||
|
case "xls":
|
||||||
|
return "application/vnd.ms-excel";
|
||||||
|
case "xlsx":
|
||||||
|
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||||
|
case "xlsm":
|
||||||
|
return "application/vnd.ms-excel.sheet.macroEnabled.12";
|
||||||
|
case "csv":
|
||||||
|
return "application/vnd.ms-excel";
|
||||||
|
case "jpg":
|
||||||
|
return "image/jpeg";
|
||||||
|
case "gif":
|
||||||
|
return "image/gif";
|
||||||
|
case "png":
|
||||||
|
return "image/png";
|
||||||
|
case "bmp":
|
||||||
|
return "image/bmp";
|
||||||
|
case "avi":
|
||||||
|
return "video/avi";
|
||||||
|
case "mpeg":
|
||||||
|
case "mpg":
|
||||||
|
return "video/mpeg";
|
||||||
|
case "mp3":
|
||||||
|
return "audio/mpeg";
|
||||||
|
case "mp4":
|
||||||
|
return "video/mp4";
|
||||||
|
case "wmv":
|
||||||
|
return "video/x-ms-wmv";
|
||||||
|
case "mov":
|
||||||
|
return "video/quicktime";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check System Registry
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RegistryKey regExtensionKey = Registry.ClassesRoot.OpenSubKey("." + fileExtension);
|
||||||
|
if (regExtensionKey != null)
|
||||||
|
{
|
||||||
|
string regExtensionContentType = regExtensionKey.GetValue("Content Type") as string;
|
||||||
|
if (regExtensionContentType != null)
|
||||||
|
{
|
||||||
|
return regExtensionContentType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore Errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return Default
|
||||||
|
return "unknown/unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.DocumentTemplates;
|
||||||
|
using System.IO;
|
||||||
|
using iTextSharp.text.pdf;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Disco.BI.Expressions;
|
||||||
|
using System.Collections;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using Disco.Models.BI.Expressions;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.Pdf
|
||||||
|
{
|
||||||
|
public static class PdfGenerator
|
||||||
|
{
|
||||||
|
|
||||||
|
public static System.IO.Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext dbContext, User CreatorUser, System.DateTime Timestamp, params object[] DataObjects)
|
||||||
|
{
|
||||||
|
if (DataObjects.Length > 0)
|
||||||
|
{
|
||||||
|
List<Stream> generatedPdfs = new List<Stream>(DataObjects.Length);
|
||||||
|
using (Models.BI.DocumentTemplates.DocumentState state = Models.BI.DocumentTemplates.DocumentState.DefaultState())
|
||||||
|
{
|
||||||
|
foreach (object d in DataObjects)
|
||||||
|
{
|
||||||
|
generatedPdfs.Add(dt.GeneratePdf(dbContext, d, CreatorUser, Timestamp, state, true));
|
||||||
|
state.SequenceNumber++;
|
||||||
|
state.FlushScopeCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (generatedPdfs.Count == 1)
|
||||||
|
{
|
||||||
|
return generatedPdfs[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Stream bulkPdf = DocumentTemplateBI.Utilities.JoinPdfs(generatedPdfs.ToArray());
|
||||||
|
foreach (Stream singlePdf in generatedPdfs)
|
||||||
|
singlePdf.Dispose();
|
||||||
|
return bulkPdf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static System.IO.Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext dbContext, User CreatorUser, System.DateTime Timestamp, params string[] DataObjectsIds)
|
||||||
|
{
|
||||||
|
object[] DataObjects;
|
||||||
|
|
||||||
|
switch (dt.Scope)
|
||||||
|
{
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||||
|
DataObjects = dbContext.Devices.Where(d => DataObjectsIds.Contains(d.SerialNumber)).ToArray();
|
||||||
|
break;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||||
|
int[] intDataObjectsIds = DataObjectsIds.Select(i => int.Parse(i)).ToArray();
|
||||||
|
DataObjects = dbContext.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).ToArray();
|
||||||
|
break;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||||
|
DataObjects = new object[DataObjectsIds.Length];
|
||||||
|
for (int idIndex = 0; idIndex < DataObjectsIds.Length; idIndex++)
|
||||||
|
{
|
||||||
|
DataObjects[idIndex] = UserBI.UserCache.GetUser(DataObjectsIds[idIndex], dbContext, true);
|
||||||
|
if (DataObjects[idIndex] == null)
|
||||||
|
throw new Exception(string.Format("Unknown Username specified: {0}", DataObjectsIds[idIndex]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException("Invalid DocumentType Scope");
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateBulkFromTemplate(dt, dbContext, CreatorUser, Timestamp, DataObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static System.IO.Stream GenerateFromTemplate(DocumentTemplate dt, DiscoDataContext dbContext, object Data, User CreatorUser, System.DateTime TimeStamp, DocumentState State, bool FlattenFields = false)
|
||||||
|
{
|
||||||
|
// Validate Data
|
||||||
|
switch (dt.Scope)
|
||||||
|
{
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||||
|
if (!(Data is Device))
|
||||||
|
throw new ArgumentException("This AttachmentType is configured for Devices only", "Data");
|
||||||
|
break;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||||
|
if (!(Data is Job))
|
||||||
|
throw new ArgumentException("This AttachmentType is configured for Jobs only", "Data");
|
||||||
|
break;
|
||||||
|
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||||
|
if (!(Data is User))
|
||||||
|
throw new ArgumentException("This AttachmentType is configured for Users only", "Data");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException("Invalid AttachmentType Scope");
|
||||||
|
}
|
||||||
|
|
||||||
|
dbContext.Configuration.LazyLoadingEnabled = true;
|
||||||
|
|
||||||
|
// Override FlattenFields if Document Template instructs.
|
||||||
|
if (dt.FlattenForm)
|
||||||
|
FlattenFields = true;
|
||||||
|
|
||||||
|
ConcurrentDictionary<string, Expression> expressionCache = dt.PdfExpressionsFromCache(dbContext);
|
||||||
|
|
||||||
|
string templateFilename = dt.RepositoryFilename(dbContext);
|
||||||
|
PdfReader pdfReader = new PdfReader(templateFilename);
|
||||||
|
|
||||||
|
MemoryStream pdfGeneratedStream = new MemoryStream();
|
||||||
|
PdfStamper pdfStamper = new PdfStamper(pdfReader, pdfGeneratedStream);
|
||||||
|
|
||||||
|
pdfStamper.FormFlattening = FlattenFields;
|
||||||
|
pdfStamper.Writer.CloseStream = false;
|
||||||
|
|
||||||
|
IDictionary expressionVariables = Expression.StandardVariables(dt, dbContext, CreatorUser, TimeStamp, State);
|
||||||
|
|
||||||
|
foreach (string pdfFieldKey in pdfStamper.AcroFields.Fields.Keys)
|
||||||
|
{
|
||||||
|
if (pdfFieldKey.Equals("DiscoAttachmentId", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey];
|
||||||
|
string fieldValue = dt.UniqueIdentifier(Data, CreatorUser.Id, TimeStamp);
|
||||||
|
if (FlattenFields)
|
||||||
|
pdfStamper.AcroFields.SetField(pdfFieldKey, String.Empty);
|
||||||
|
else
|
||||||
|
pdfStamper.AcroFields.SetField(pdfFieldKey, fieldValue);
|
||||||
|
|
||||||
|
IList<AcroFields.FieldPosition> pdfFieldPositions = pdfStamper.AcroFields.GetFieldPositions(pdfFieldKey);
|
||||||
|
for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++)
|
||||||
|
{
|
||||||
|
AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal];
|
||||||
|
string pdfBarcodeContent = dt.UniquePageIdentifier(Data, CreatorUser.Id, TimeStamp, pdfFieldPosition.page);
|
||||||
|
BarcodeQRCode pdfBarcode = new BarcodeQRCode(pdfBarcodeContent, (int)pdfFieldPosition.position.Width, (int)pdfFieldPosition.position.Height, null);
|
||||||
|
iTextSharp.text.Image pdfBarcodeImage = pdfBarcode.GetImage();
|
||||||
|
pdfBarcodeImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
|
||||||
|
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfBarcodeImage);
|
||||||
|
}
|
||||||
|
// Hide Fields
|
||||||
|
PdfDictionary field = fields.GetValue(0);
|
||||||
|
if ((PdfName)field.Get(PdfName.TYPE) == PdfName.ANNOT)
|
||||||
|
{
|
||||||
|
field.Put(PdfName.F, new PdfNumber(6));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PdfArray fieldKids = (PdfArray)field.Get(PdfName.KIDS);
|
||||||
|
foreach (PdfIndirectReference fieldKidRef in fieldKids)
|
||||||
|
{
|
||||||
|
((PdfDictionary)pdfReader.GetPdfObject(fieldKidRef.Number)).Put(PdfName.F, new PdfNumber(6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Expression fieldExpression = null;
|
||||||
|
if (expressionCache.TryGetValue(pdfFieldKey, out fieldExpression))
|
||||||
|
{
|
||||||
|
if (fieldExpression.IsDynamic)
|
||||||
|
{
|
||||||
|
Tuple<string, bool, object> fieldExpressionResult = fieldExpression.Evaluate(Data, expressionVariables);
|
||||||
|
|
||||||
|
if (fieldExpressionResult.Item3 != null)
|
||||||
|
{
|
||||||
|
IImageExpressionResult imageResult = (fieldExpressionResult.Item3 as IImageExpressionResult);
|
||||||
|
if (imageResult != null)
|
||||||
|
{
|
||||||
|
// Output Image
|
||||||
|
AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey];
|
||||||
|
IList<AcroFields.FieldPosition> pdfFieldPositions = pdfStamper.AcroFields.GetFieldPositions(pdfFieldKey);
|
||||||
|
for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++)
|
||||||
|
{
|
||||||
|
AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal];
|
||||||
|
iTextSharp.text.Image pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage((int)pdfFieldPosition.position.Width, (int)pdfFieldPosition.position.Height));
|
||||||
|
pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
|
||||||
|
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage);
|
||||||
|
}
|
||||||
|
if (!fieldExpressionResult.Item2 && !imageResult.ShowField)
|
||||||
|
{
|
||||||
|
// Hide Fields
|
||||||
|
PdfDictionary field = fields.GetValue(0);
|
||||||
|
if ((PdfName)field.Get(PdfName.TYPE) == PdfName.ANNOT)
|
||||||
|
{
|
||||||
|
field.Put(PdfName.F, new PdfNumber(6));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PdfArray fieldKids = (PdfArray)field.Get(PdfName.KIDS);
|
||||||
|
foreach (PdfIndirectReference fieldKidRef in fieldKids)
|
||||||
|
{
|
||||||
|
((PdfDictionary)pdfReader.GetPdfObject(fieldKidRef.Number)).Put(PdfName.F, new PdfNumber(6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfStamper.AcroFields.SetField(pdfFieldKey, fieldExpressionResult.Item1);
|
||||||
|
|
||||||
|
if (fieldExpressionResult.Item2) // Expression Error
|
||||||
|
{
|
||||||
|
AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey];
|
||||||
|
for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++)
|
||||||
|
{
|
||||||
|
PdfDictionary field = fields.GetValue(pdfFieldOrdinal);
|
||||||
|
PdfDictionary fieldMK;
|
||||||
|
if (field.Contains(PdfName.MK))
|
||||||
|
fieldMK = field.GetAsDict(PdfName.MK);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fieldMK = new PdfDictionary(PdfName.MK);
|
||||||
|
field.Put(PdfName.MK, fieldMK);
|
||||||
|
}
|
||||||
|
fieldMK.Put(PdfName.BC, new PdfArray(new float[] { 1, 0, 0 }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Pdf template field expressions are out of sync with the expression cache");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State.FlushFieldCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfStamper.Close();
|
||||||
|
pdfReader.Close();
|
||||||
|
|
||||||
|
if (dt.Scope == DocumentTemplate.DocumentTemplateScopes.Job)
|
||||||
|
{
|
||||||
|
// Write Job Log
|
||||||
|
|
||||||
|
Job j = (Job)Data;
|
||||||
|
JobLog jl = new JobLog()
|
||||||
|
{
|
||||||
|
JobId = j.Id,
|
||||||
|
TechUserId = CreatorUser.Id,
|
||||||
|
Timestamp = DateTime.Now
|
||||||
|
};
|
||||||
|
jl.Comments = string.Format("Document Generated{0}{1} [{2}]", Environment.NewLine, dt.Description, dt.Id);
|
||||||
|
dbContext.JobLogs.Add(jl);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfGeneratedStream.Position = 0;
|
||||||
|
return pdfGeneratedStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using iTextSharp.text.pdf;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
using Disco.BI.DocumentTemplateBI.Importer;
|
||||||
|
using Disco.BI.DocumentTemplateBI;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using com.google.zxing;
|
||||||
|
using com.google.zxing.multi.qrcode;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using System.Web.Caching;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System.Collections;
|
||||||
|
using com.google.zxing.common;
|
||||||
|
using BitMiracle.LibTiff.Classic;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.Pdf
|
||||||
|
{
|
||||||
|
public static class PdfImporter
|
||||||
|
{
|
||||||
|
|
||||||
|
private class DetectImageResult : IDisposable
|
||||||
|
{
|
||||||
|
public Result Result { get; set; }
|
||||||
|
public Point ResultOffset { get; set; }
|
||||||
|
public double ResultScale { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do Nothing; yet...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DetectPageResult : IDisposable
|
||||||
|
{
|
||||||
|
public int PageNumber { get; set; }
|
||||||
|
public DocumentUniqueIdentifier DetectedIdentifier { get; set; }
|
||||||
|
public Disco.BI.Extensions.UtilityExtensions.ImageMontage ThumbnailImage { get; set; }
|
||||||
|
public MemoryStream AttachmentThumbnailImage { get; set; }
|
||||||
|
public Disco.BI.Extensions.UtilityExtensions.ImageMontage UndetectedPageImage { get; set; }
|
||||||
|
|
||||||
|
public void DrawThumbnailImageResult(DetectImageResult Result, Image DetectedImage)
|
||||||
|
{
|
||||||
|
if (Result.Result.ResultPoints.Length == 4)
|
||||||
|
{ // Draw Square on Thumbnail
|
||||||
|
using (Graphics thumbnailGraphics = Graphics.FromImage(ThumbnailImage.Montage))
|
||||||
|
{
|
||||||
|
var thumbnailOffset = ThumbnailImage.MontageSourceImageOffsets[DetectedImage];
|
||||||
|
|
||||||
|
var linePoints = Result.Result.ResultPoints.Select(p => new Point((int)(thumbnailOffset + ((Result.ResultOffset.X + p.X) * Result.ResultScale * ThumbnailImage.MontageScale)), (int)((p.Y + Result.ResultOffset.Y) * Result.ResultScale * ThumbnailImage.MontageScale))).ToArray();
|
||||||
|
using (GraphicsPath graphicsPath = new GraphicsPath())
|
||||||
|
{
|
||||||
|
for (int linePointIndex = 0; linePointIndex < (linePoints.Length - 1); linePointIndex++)
|
||||||
|
graphicsPath.AddLine(linePoints[linePointIndex], linePoints[linePointIndex + 1]);
|
||||||
|
graphicsPath.AddLine(linePoints[linePoints.Length - 1], linePoints[0]);
|
||||||
|
using (SolidBrush graphicsBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
|
||||||
|
thumbnailGraphics.FillPath(graphicsBrush, graphicsPath);
|
||||||
|
using (Pen graphicsPen = new Pen(Color.FromArgb(200, 255, 0, 0), 2))
|
||||||
|
thumbnailGraphics.DrawPath(graphicsPen, graphicsPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (ThumbnailImage != null)
|
||||||
|
{
|
||||||
|
ThumbnailImage.Dispose();
|
||||||
|
ThumbnailImage = null;
|
||||||
|
}
|
||||||
|
if (AttachmentThumbnailImage != null)
|
||||||
|
{
|
||||||
|
AttachmentThumbnailImage.Dispose();
|
||||||
|
AttachmentThumbnailImage = null;
|
||||||
|
}
|
||||||
|
if (UndetectedPageImage != null)
|
||||||
|
{
|
||||||
|
UndetectedPageImage.Dispose();
|
||||||
|
UndetectedPageImage = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DetectImageResult DetectImage(DiscoDataContext dbContext, Bitmap pageImageOriginal, string SessionId, IEnumerable<DocumentTemplate> detectDocumentTemplates)
|
||||||
|
{
|
||||||
|
Bitmap pageImage = pageImageOriginal;
|
||||||
|
double pageImageModifiedScale = 1;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Resize if Resolution > 80; Set to 72 Dpi
|
||||||
|
if (pageImage.HorizontalResolution > 80 || pageImage.VerticalResolution > 80)
|
||||||
|
{
|
||||||
|
pageImageModifiedScale = pageImage.HorizontalResolution / 72;
|
||||||
|
int newWidth = (int)((72 / pageImage.HorizontalResolution) * pageImage.Width);
|
||||||
|
int newHeight = (int)((72 / pageImage.VerticalResolution) * pageImage.Height);
|
||||||
|
pageImage = pageImage.ResizeImage(newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result zxingResult = default(Result);
|
||||||
|
Point zxingResultOffset = Point.Empty;
|
||||||
|
QRCodeMultiReader zxingMfr = new QRCodeMultiReader();
|
||||||
|
Hashtable zxingMfrHints = new Hashtable();
|
||||||
|
zxingMfrHints.Add(DecodeHintType.TRY_HARDER, true);
|
||||||
|
// Look in 'Known' locations
|
||||||
|
foreach (DocumentTemplate dt in detectDocumentTemplates)
|
||||||
|
{
|
||||||
|
var locationBag = dt.QRCodeLocations(dbContext);
|
||||||
|
foreach (var location in locationBag)
|
||||||
|
{
|
||||||
|
System.Drawing.Rectangle region = new Rectangle(
|
||||||
|
(int)(pageImage.Width * location.Left),
|
||||||
|
(int)(pageImage.Width * location.Top),
|
||||||
|
(int)(pageImage.Width * location.Width),
|
||||||
|
(int)(pageImage.Height * location.Height));
|
||||||
|
RGBLuminanceSource zxingSource;
|
||||||
|
using (Bitmap pageImageRegion = new Bitmap(region.Width, region.Height))
|
||||||
|
{
|
||||||
|
using (Graphics pageImageRegionGraphics = Graphics.FromImage(pageImageRegion))
|
||||||
|
{
|
||||||
|
pageImageRegionGraphics.DrawImage(pageImage, 0, 0, region, GraphicsUnit.Pixel);
|
||||||
|
}
|
||||||
|
zxingSource = new RGBLuminanceSource(pageImageRegion, region.Width, region.Height);
|
||||||
|
}
|
||||||
|
var zxingHB = new HybridBinarizer(zxingSource);
|
||||||
|
var zxingBB = new BinaryBitmap(zxingHB);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
zxingResult = zxingMfr.decode(zxingBB, zxingMfrHints);
|
||||||
|
zxingResultOffset = region.Location;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (ReaderException)
|
||||||
|
{
|
||||||
|
// Ignore Location Errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (zxingResult != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (zxingResult == null)
|
||||||
|
{
|
||||||
|
// Not found with 'known' locations
|
||||||
|
// Try whole image
|
||||||
|
var zxingSource = new RGBLuminanceSource(pageImage, pageImage.Width, pageImage.Height);
|
||||||
|
var zxingHB = new HybridBinarizer(zxingSource);
|
||||||
|
var zxingBB = new BinaryBitmap(zxingHB);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
zxingResult = zxingMfr.decode(zxingBB, zxingMfrHints);
|
||||||
|
}
|
||||||
|
catch (ReaderException)
|
||||||
|
{
|
||||||
|
// Ignore Errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zxingResult != null)
|
||||||
|
return new DetectImageResult() { Result = zxingResult, ResultOffset = zxingResultOffset, ResultScale = pageImageModifiedScale };
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (pageImageOriginal != pageImage)
|
||||||
|
pageImage.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DetectPageResult DetectPage(DiscoDataContext dbContext, PdfReader pdfReader, int PageNumber, string SessionId, string DataStoreSessionCacheLocation, IEnumerable<DocumentTemplate> detectDocumentTemplates)
|
||||||
|
{
|
||||||
|
DetectPageResult result = new DetectPageResult() { PageNumber = PageNumber };
|
||||||
|
|
||||||
|
DocumentImporterLog.LogImportPageProgress(SessionId, PageNumber, 10, "Loading Page Images");
|
||||||
|
|
||||||
|
using (DisposableImageCollection pageImages = pdfReader.PdfPageImages(PageNumber))
|
||||||
|
{
|
||||||
|
if (pageImages.Count > 0)
|
||||||
|
{
|
||||||
|
result.ThumbnailImage = pageImages.BuildImageMontage(256, 256);
|
||||||
|
var pageThumbnailFilename = Path.Combine(DataStoreSessionCacheLocation, string.Format("{0}-{1}", SessionId, PageNumber));
|
||||||
|
|
||||||
|
result.ThumbnailImage.Montage.SavePng(pageThumbnailFilename);
|
||||||
|
DocumentImporterLog.LogImportPageImageUpdate(SessionId, PageNumber);
|
||||||
|
|
||||||
|
double pageProgressInterval = 90 / pageImages.Count;
|
||||||
|
|
||||||
|
foreach (var pageImageOriginal in pageImages)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.LogImportPageProgress(SessionId, PageNumber, (int)(10 + (pageProgressInterval * pageImages.IndexOf(pageImageOriginal))), String.Format("Processing Page Image {0} of {1}", pageImages.IndexOf(pageImageOriginal) + 1, pageImages.Count));
|
||||||
|
|
||||||
|
using (var zxingResult = DetectImage(dbContext, pageImageOriginal, SessionId, detectDocumentTemplates))
|
||||||
|
{
|
||||||
|
if (zxingResult != null)
|
||||||
|
{
|
||||||
|
if (DocumentUniqueIdentifier.IsDocumentUniqueIdentifier(zxingResult.Result.Text))
|
||||||
|
{
|
||||||
|
result.DrawThumbnailImageResult(zxingResult, pageImageOriginal);
|
||||||
|
result.ThumbnailImage.Montage.SavePng(pageThumbnailFilename);
|
||||||
|
DocumentImporterLog.LogImportPageImageUpdate(SessionId, PageNumber);
|
||||||
|
|
||||||
|
result.AttachmentThumbnailImage = new MemoryStream();
|
||||||
|
using (var attachmentThumbImage = pageImages.BuildImageMontage(48, 48, true))
|
||||||
|
{
|
||||||
|
using (Image mimeTypeIcon = Disco.Properties.Resources.MimeType_pdf16)
|
||||||
|
attachmentThumbImage.Montage.EmbedIconOverlay(mimeTypeIcon);
|
||||||
|
attachmentThumbImage.Montage.SaveJpg(95, result.AttachmentThumbnailImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.DetectedIdentifier = new DocumentUniqueIdentifier(zxingResult.Result.Text, PageNumber.ToString());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page Unassigned
|
||||||
|
result.UndetectedPageImage = pageImages.BuildImageMontage(700, 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ProcessPdfAttachment(string Filename, DiscoDataContext dbContext, string SessionId, Cache HttpCache)
|
||||||
|
{
|
||||||
|
var dataStoreUnassignedLocation = DataStore.CreateLocation(dbContext, "DocumentDropBox_Unassigned");
|
||||||
|
|
||||||
|
DocumentImporterLog.LogImportProgress(SessionId, 0, "Reading File");
|
||||||
|
|
||||||
|
using (FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
||||||
|
{
|
||||||
|
var pdfReader = new PdfReader(fs);
|
||||||
|
|
||||||
|
var pdfPagesAssigned = new Dictionary<int, Tuple<DocumentUniqueIdentifier, byte[]>>();
|
||||||
|
|
||||||
|
var dataStoreSessionPagesCacheLocation = DataStore.CreateLocation(dbContext, "Cache\\DocumentDropBox_SessionPages");
|
||||||
|
var detectDocumentTemplates = dbContext.DocumentTemplates.ToArray();
|
||||||
|
|
||||||
|
double progressInterval = 70 / pdfReader.NumberOfPages;
|
||||||
|
|
||||||
|
for (int PageNumber = 1; PageNumber <= pdfReader.NumberOfPages; PageNumber++)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.LogImportProgress(SessionId, (int)(PageNumber * progressInterval), string.Format("Processing Page {0} of {1}", PageNumber, pdfReader.NumberOfPages));
|
||||||
|
DocumentImporterLog.LogImportPageStarting(SessionId, PageNumber);
|
||||||
|
|
||||||
|
using (var pageResult = DetectPage(dbContext, pdfReader, PageNumber, SessionId, dataStoreSessionPagesCacheLocation, detectDocumentTemplates))
|
||||||
|
{
|
||||||
|
if (pageResult.DetectedIdentifier != null)
|
||||||
|
{
|
||||||
|
var docId = pageResult.DetectedIdentifier;
|
||||||
|
pdfPagesAssigned.Add(PageNumber, new Tuple<DocumentUniqueIdentifier, byte[]>(docId, pageResult.AttachmentThumbnailImage.ToArray()));
|
||||||
|
|
||||||
|
docId.LoadComponents(dbContext);
|
||||||
|
DocumentImporterLog.LogImportPageDetected(SessionId, PageNumber, docId.DocumentUniqueId, docId.DocumentTemplate.Description, docId.DocumentTemplate.Scope, docId.DataId, docId.DataDescription);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Undetected Page - Write Preview-Images while still in Memory
|
||||||
|
DocumentImporterLog.LogImportPageUndetected(SessionId, PageNumber);
|
||||||
|
|
||||||
|
// Thumbnail:
|
||||||
|
string unassignedImageThumbnailFilename = Path.Combine(dataStoreUnassignedLocation, string.Format("{0}_{1}_thumbnail.png", SessionId, PageNumber));
|
||||||
|
pageResult.ThumbnailImage.Montage.SavePng(unassignedImageThumbnailFilename);
|
||||||
|
// Large Preview
|
||||||
|
string unassignedImageFilename = Path.Combine(dataStoreUnassignedLocation, string.Format("{0}_{1}.jpg", SessionId, PageNumber));
|
||||||
|
pageResult.UndetectedPageImage.Montage.SaveJpg(90, unassignedImageFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out Assigned Documents
|
||||||
|
var assignedDocuments = pdfPagesAssigned.GroupBy(u => u.Value.Item1.DocumentUniqueId).ToList();
|
||||||
|
if (assignedDocuments.Count > 0)
|
||||||
|
{
|
||||||
|
progressInterval = 20 / assignedDocuments.Count;
|
||||||
|
|
||||||
|
foreach (var documentPortion in assignedDocuments)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.LogImportProgress(SessionId, (int)(70 + (assignedDocuments.IndexOf(documentPortion) * progressInterval)), string.Format("Importing Documents {0} of {1}", assignedDocuments.IndexOf(documentPortion) + 1, assignedDocuments.Count));
|
||||||
|
|
||||||
|
var documentPortionInfo = documentPortion.First().Value;
|
||||||
|
var documentPortionIdentifier = documentPortionInfo.Item1;
|
||||||
|
var documentPortionThumbnail = documentPortionInfo.Item2;
|
||||||
|
|
||||||
|
if (!documentPortionIdentifier.LoadComponents(dbContext))
|
||||||
|
{
|
||||||
|
// Unknown Document Unique Id
|
||||||
|
foreach (var dp in documentPortion)
|
||||||
|
{
|
||||||
|
var tag = int.Parse(dp.Value.Item1.Tag);
|
||||||
|
if (pdfPagesAssigned.ContainsKey(tag))
|
||||||
|
pdfPagesAssigned.Remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (MemoryStream msBuilder = new MemoryStream())
|
||||||
|
{
|
||||||
|
var pdfDoc = new iTextSharp.text.Document();
|
||||||
|
var pdfCopy = new PdfCopy(pdfDoc, msBuilder);
|
||||||
|
|
||||||
|
pdfDoc.Open();
|
||||||
|
pdfCopy.CloseStream = false;
|
||||||
|
|
||||||
|
foreach (var dp in documentPortion.OrderBy(dg => dg.Value.Item1.Page))
|
||||||
|
{
|
||||||
|
var pageSize = pdfReader.GetPageSizeWithRotation(dp.Key);
|
||||||
|
var page = pdfCopy.GetImportedPage(pdfReader, dp.Key);
|
||||||
|
|
||||||
|
pdfDoc.SetPageSize(pageSize);
|
||||||
|
pdfDoc.NewPage();
|
||||||
|
|
||||||
|
pdfCopy.AddPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfDoc.Close();
|
||||||
|
pdfCopy.Close();
|
||||||
|
|
||||||
|
msBuilder.Position = 0;
|
||||||
|
|
||||||
|
var attachmentSuccess = documentPortionIdentifier.ImportPdfAttachment(dbContext, msBuilder, documentPortionThumbnail);
|
||||||
|
|
||||||
|
if (!attachmentSuccess)
|
||||||
|
{ // Unable to add Attachment
|
||||||
|
foreach (var dp in documentPortion)
|
||||||
|
{
|
||||||
|
var tag = int.Parse(dp.Value.Item1.Tag);
|
||||||
|
if (pdfPagesAssigned.ContainsKey(tag))
|
||||||
|
pdfPagesAssigned.Remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out Unassigned Pages
|
||||||
|
List<int> pdfPagesUnassigned = new List<int>();
|
||||||
|
for (int PageNumber = 1; PageNumber <= pdfReader.NumberOfPages; PageNumber++)
|
||||||
|
if (!pdfPagesAssigned.ContainsKey(PageNumber))
|
||||||
|
pdfPagesUnassigned.Add(PageNumber);
|
||||||
|
if (pdfPagesUnassigned.Count > 0)
|
||||||
|
{
|
||||||
|
progressInterval = 10 / pdfPagesUnassigned.Count;
|
||||||
|
//dataStoreUnassignedLocation
|
||||||
|
foreach (var PageNumber in pdfPagesUnassigned)
|
||||||
|
{
|
||||||
|
DocumentImporterLog.LogImportProgress(SessionId, (int)(90 + (pdfPagesUnassigned.IndexOf(PageNumber) * progressInterval)), string.Format("Processing Undetected Documents {0} of {1}", pdfPagesUnassigned.IndexOf(PageNumber) + 1, pdfPagesUnassigned.Count));
|
||||||
|
|
||||||
|
using (MemoryStream msBuilder = new MemoryStream())
|
||||||
|
{
|
||||||
|
var pdfDoc = new iTextSharp.text.Document();
|
||||||
|
var pdfCopy = new PdfCopy(pdfDoc, msBuilder);
|
||||||
|
|
||||||
|
pdfDoc.Open();
|
||||||
|
pdfCopy.CloseStream = false;
|
||||||
|
|
||||||
|
var pageSize = pdfReader.GetPageSizeWithRotation(PageNumber);
|
||||||
|
var page = pdfCopy.GetImportedPage(pdfReader, PageNumber);
|
||||||
|
pdfDoc.SetPageSize(pageSize);
|
||||||
|
pdfDoc.NewPage();
|
||||||
|
|
||||||
|
pdfCopy.AddPage(page);
|
||||||
|
pdfDoc.Close();
|
||||||
|
pdfCopy.Close();
|
||||||
|
|
||||||
|
File.WriteAllBytes(Path.Combine(dataStoreUnassignedLocation, string.Format("{0}_{1}.pdf", SessionId, PageNumber)), msBuilder.ToArray());
|
||||||
|
|
||||||
|
DocumentImporterLog.LogImportPageUndetectedStored(SessionId, PageNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentImporterLog.LogImportProgress(SessionId, 100, "Finished Importing Document");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static bool ProcessPdfAttachment(string Filename, DiscoDataContext dbContext, string DocumentTemplateId, string DataId, string UserId, DateTime Timestamp)
|
||||||
|
{
|
||||||
|
using (FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
||||||
|
{
|
||||||
|
DocumentUniqueIdentifier identifier = new DocumentUniqueIdentifier(DocumentTemplateId, DataId, UserId, Timestamp);
|
||||||
|
identifier.LoadComponents(dbContext);
|
||||||
|
return identifier.ImportPdfAttachment(dbContext, fs, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DisposableImageCollection GetPageImages(PdfReader pdfReader, int PageNumber)
|
||||||
|
{
|
||||||
|
var pageImages = new DisposableImageCollection();
|
||||||
|
|
||||||
|
var pdfPage = pdfReader.GetPageN(PageNumber);
|
||||||
|
PdfDictionary pdfPageResouces = (PdfDictionary)((PdfDictionary)pdfPage.GetDirectObject(PdfName.RESOURCES)).GetDirectObject(PdfName.XOBJECT);
|
||||||
|
|
||||||
|
foreach (var pdfResKey in pdfPageResouces.Keys)
|
||||||
|
{
|
||||||
|
var pdfRes = pdfPageResouces.GetDirectObject(pdfResKey);
|
||||||
|
if (pdfRes.IsStream())
|
||||||
|
{
|
||||||
|
var pdfResStream = (PdfStream)pdfRes;
|
||||||
|
var pdfResSubType = pdfResStream.Get(PdfName.SUBTYPE);
|
||||||
|
if (pdfResSubType != null && pdfResSubType == PdfName.IMAGE)
|
||||||
|
{
|
||||||
|
if (pdfResStream.Get(PdfName.FILTER) == PdfName.CCITTFAXDECODE)
|
||||||
|
{ // TIFF
|
||||||
|
// Try Using GDI+ for TIFF...
|
||||||
|
var width = ((PdfNumber)(pdfResStream.Get(PdfName.WIDTH))).IntValue;
|
||||||
|
var height = ((PdfNumber)(pdfResStream.Get(PdfName.HEIGHT))).IntValue;
|
||||||
|
var bpc = ((PdfNumber)(pdfResStream.Get(PdfName.BITSPERCOMPONENT))).IntValue;
|
||||||
|
|
||||||
|
var compressionMethod = Compression.CCITTFAX3;
|
||||||
|
|
||||||
|
var decodeParams = pdfResStream.GetAsDict(PdfName.DECODEPARMS);
|
||||||
|
if (decodeParams != null && decodeParams.Contains(PdfName.K) && decodeParams.GetAsNumber(PdfName.K).IntValue < 0)
|
||||||
|
compressionMethod = Compression.CCITTFAX4;
|
||||||
|
|
||||||
|
using (MemoryStream tiffStream = PdfToTiffStream(PdfReader.GetStreamBytesRaw((PRStream)pdfResStream), width, height, bpc, compressionMethod))
|
||||||
|
{
|
||||||
|
pageImages.Add((Bitmap)Bitmap.FromStream(tiffStream));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pdfResStream.Get(PdfName.FILTER) == PdfName.DCTDECODE)
|
||||||
|
{ // JPG
|
||||||
|
using (MemoryStream jpgStream = new MemoryStream(PdfReader.GetStreamBytesRaw((PRStream)pdfResStream)))
|
||||||
|
{
|
||||||
|
pageImages.Add((Bitmap)Bitmap.FromStream(jpgStream, true, true));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MemoryStream PdfToTiffStream(byte[] PdfStream, int Width, int Height, int BitsPerComponent, Compression CompressionMethod)
|
||||||
|
{
|
||||||
|
var ms = new MemoryStream();
|
||||||
|
|
||||||
|
Tiff tif = Tiff.ClientOpen("in-memory", "w", ms, new TiffStream());
|
||||||
|
tif.SetField(TiffTag.IMAGEWIDTH, Width);
|
||||||
|
tif.SetField(TiffTag.IMAGELENGTH, Height);
|
||||||
|
tif.SetField(TiffTag.COMPRESSION, CompressionMethod);
|
||||||
|
tif.SetField(TiffTag.BITSPERSAMPLE, BitsPerComponent);
|
||||||
|
tif.SetField(TiffTag.SAMPLESPERPIXEL, 1);
|
||||||
|
tif.WriteRawStrip(0, PdfStream, PdfStream.Length);
|
||||||
|
tif.Flush();
|
||||||
|
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//using System;
|
||||||
|
//using System.Collections.Generic;
|
||||||
|
//using System.Linq;
|
||||||
|
//using System.Text;
|
||||||
|
//using Disco.Data.Repository;
|
||||||
|
//using Quartz;
|
||||||
|
|
||||||
|
//namespace Disco.BI.Interop.PluginServices
|
||||||
|
//{
|
||||||
|
// interface IDiscoScheduledTask
|
||||||
|
// {
|
||||||
|
// void InitalizeScheduledTask(DiscoDataContext dbContext, IScheduler Scheduler);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//using System;
|
||||||
|
//using System.Collections.Generic;
|
||||||
|
//using System.Linq;
|
||||||
|
//using System.Text;
|
||||||
|
//using Disco.Data.Repository;
|
||||||
|
//using Quartz;
|
||||||
|
|
||||||
|
//namespace Disco.BI.Interop.PluginServices
|
||||||
|
//{
|
||||||
|
// public static class Utilities
|
||||||
|
// {
|
||||||
|
|
||||||
|
// public static void InitalizeScheduledTasks(DiscoDataContext dbContext, ISchedulerFactory SchedulerFactory)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// var scheduler = SchedulerFactory.GetScheduler();
|
||||||
|
|
||||||
|
// // Discover IDiscoScheduledTasks (Only from Disco Assemblies)
|
||||||
|
// var appDomain = AppDomain.CurrentDomain;
|
||||||
|
|
||||||
|
// var scheduledTaskTypes = (from a in appDomain.GetAssemblies()
|
||||||
|
// where !a.GlobalAssemblyCache && !a.IsDynamic && a.FullName.StartsWith("Disco.", StringComparison.InvariantCultureIgnoreCase)
|
||||||
|
// from type in a.GetTypes()
|
||||||
|
// where typeof(IDiscoScheduledTask).IsAssignableFrom(type) && !type.IsAbstract
|
||||||
|
// select type);
|
||||||
|
// foreach (Type scheduledTaskType in scheduledTaskTypes)
|
||||||
|
// {
|
||||||
|
// IDiscoScheduledTask instance = (IDiscoScheduledTask)Activator.CreateInstance(scheduledTaskType);
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// instance.InitalizeScheduledTask(dbContext, scheduler);
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// if (instance == null)
|
||||||
|
// Logging.SystemLog.LogException("Initializing Scheduled Task; Disco.BI.Interop.Plugins.Utilities.InitalizeScheduledTasks()", ex);
|
||||||
|
// else
|
||||||
|
// Logging.SystemLog.LogException(string.Format("Initializing Scheduled Task: '{0}'; Disco.BI.Interop.Plugins.Utilities.InitalizeScheduledTasks()", instance.GetType().Name), ex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using SignalR;
|
||||||
|
using SignalR.Hosting.AspNet;
|
||||||
|
using SignalR.Infrastructure;
|
||||||
|
|
||||||
|
namespace Disco.BI.Interop.SignalRHandlers
|
||||||
|
{
|
||||||
|
public class UserHeldDevices : PersistentConnection
|
||||||
|
{
|
||||||
|
|
||||||
|
internal static void UserJobUpdated(string JobUserId)
|
||||||
|
{
|
||||||
|
var connectionManager = GlobalHost.ConnectionManager;
|
||||||
|
var connectionContext = connectionManager.GetConnectionContext<UserHeldDevices>();
|
||||||
|
if (connectionContext != null)
|
||||||
|
connectionContext.Connection.Broadcast(JobUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.BI.Job;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
|
||||||
|
namespace Disco.BI.JobBI
|
||||||
|
{
|
||||||
|
public static class Searching
|
||||||
|
{
|
||||||
|
public static JobTableModel Search(DiscoDataContext dbContext, string Term, int? LimitCount = null, bool IncludeJobStatus = true, bool SearchDetails = false)
|
||||||
|
{
|
||||||
|
int termInt = default(int);
|
||||||
|
|
||||||
|
IQueryable<Job> query = default(IQueryable<Job>);
|
||||||
|
|
||||||
|
if (int.TryParse(Term, out termInt))
|
||||||
|
{
|
||||||
|
// Term is a Number (int)
|
||||||
|
if (SearchDetails)
|
||||||
|
{
|
||||||
|
query = BuildJobTableModel(dbContext).Where(j =>
|
||||||
|
j.Id == termInt ||
|
||||||
|
j.DeviceHeldLocation.Contains(Term) ||
|
||||||
|
j.Device.SerialNumber.Contains(Term) ||
|
||||||
|
j.Device.AssetNumber.Contains(Term) ||
|
||||||
|
j.User.Id == Term ||
|
||||||
|
j.User.Surname.Contains(Term) ||
|
||||||
|
j.User.GivenName.Contains(Term) ||
|
||||||
|
j.User.DisplayName.Contains(Term) ||
|
||||||
|
j.JobLogs.Any(jl => jl.Comments.Contains(Term)) ||
|
||||||
|
j.JobAttachments.Any(ja => ja.Comments.Contains(Term)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query = BuildJobTableModel(dbContext).Where(j =>
|
||||||
|
j.Id == termInt ||
|
||||||
|
j.DeviceHeldLocation.Contains(Term) ||
|
||||||
|
j.Device.SerialNumber.Contains(Term) ||
|
||||||
|
j.Device.AssetNumber.Contains(Term) ||
|
||||||
|
j.User.Id == Term ||
|
||||||
|
j.User.Surname.Contains(Term) ||
|
||||||
|
j.User.GivenName.Contains(Term) ||
|
||||||
|
j.User.DisplayName.Contains(Term));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (SearchDetails)
|
||||||
|
{
|
||||||
|
query = BuildJobTableModel(dbContext).Where(j =>
|
||||||
|
j.DeviceHeldLocation.Contains(Term) ||
|
||||||
|
j.Device.SerialNumber.Contains(Term) ||
|
||||||
|
j.Device.AssetNumber.Contains(Term) ||
|
||||||
|
j.User.Id == Term ||
|
||||||
|
j.User.Surname.Contains(Term) ||
|
||||||
|
j.User.GivenName.Contains(Term) ||
|
||||||
|
j.User.DisplayName.Contains(Term) ||
|
||||||
|
j.JobLogs.Any(jl => jl.Comments.Contains(Term)) ||
|
||||||
|
j.JobAttachments.Any(ja => ja.Comments.Contains(Term)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query = BuildJobTableModel(dbContext).Where(j =>
|
||||||
|
j.DeviceHeldLocation.Contains(Term) ||
|
||||||
|
j.Device.SerialNumber.Contains(Term) ||
|
||||||
|
j.Device.AssetNumber.Contains(Term) ||
|
||||||
|
j.User.Id == Term ||
|
||||||
|
j.User.Surname.Contains(Term) ||
|
||||||
|
j.User.GivenName.Contains(Term) ||
|
||||||
|
j.User.DisplayName.Contains(Term));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LimitCount.HasValue)
|
||||||
|
query = query.Take(LimitCount.Value);
|
||||||
|
|
||||||
|
JobTableModel model = new JobTableModel() { ShowStatus = IncludeJobStatus };
|
||||||
|
model.Fill(dbContext, query);
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<Job> BuildJobTableModel(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
return dbContext.Jobs.Include("JobType").Include("Device").Include("User").Include("OpenedTechUser");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Quartz;
|
||||||
|
using Disco.Models.BI.Job.Statistics;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Quartz.Impl;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
|
||||||
|
namespace Disco.BI.JobBI.Statistics
|
||||||
|
{
|
||||||
|
public class DailyOpenedClosed : ScheduledTask
|
||||||
|
{
|
||||||
|
|
||||||
|
private static List<DailyOpenedClosedItem> _data;
|
||||||
|
private static object _dataLock = new object();
|
||||||
|
|
||||||
|
|
||||||
|
public override string TaskName { get { return "Job Statistics - Daily Opened/Closed Task"; } }
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Trigger Daily @ 12:29am
|
||||||
|
TriggerBuilder triggerBuilder = TriggerBuilder.Create().WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(0, 29));
|
||||||
|
|
||||||
|
this.ScheduleTask(triggerBuilder);
|
||||||
|
}
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
using (var dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
UpdateDataHistory(dbContext, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void InitalizeScheduledTask(DiscoDataContext dbContext, IScheduler Scheduler)
|
||||||
|
//{
|
||||||
|
// // Run @ 12:29am
|
||||||
|
// IJobDetail jobDetail = new JobDetailImpl("JobStatisticsDailyOpenedClosed", typeof(DailyOpenedClosed));
|
||||||
|
// ITrigger trigger = TriggerBuilder.Create().
|
||||||
|
// WithIdentity("JobStatisticsDailyOpenedClosedTrigger").
|
||||||
|
// StartNow().
|
||||||
|
// WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(0, 29)).
|
||||||
|
// Build();
|
||||||
|
// Scheduler.ScheduleJob(jobDetail, trigger);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public void Execute(IJobExecutionContext context)
|
||||||
|
//{
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// using (var dbContext = new DiscoDataContext())
|
||||||
|
// {
|
||||||
|
// UpdateDataHistory(dbContext, true);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// Logging.SystemLog.LogException("Disco.BI.JobBI.Statistics.DailyOpenedClosed", ex);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
private static void UpdateDataHistory(DiscoDataContext dbContext, bool Refresh = false)
|
||||||
|
{
|
||||||
|
DateTime historyEnd = DateTime.Now.AddDays(-1).Date;
|
||||||
|
|
||||||
|
if (Refresh || _data == null || _data.Count == 0 || _data.Last().Timestamp < historyEnd)
|
||||||
|
{
|
||||||
|
lock (_dataLock)
|
||||||
|
{
|
||||||
|
if (Refresh || _data == null || _data.Count == 0 || _data.Last().Timestamp < historyEnd)
|
||||||
|
{
|
||||||
|
DateTime historyStart = DateTime.Now.AddDays(-28).Date;
|
||||||
|
|
||||||
|
// Initialize Memory Store
|
||||||
|
List<DailyOpenedClosedItem> resultData;
|
||||||
|
if (Refresh || _data == null)
|
||||||
|
resultData = new List<DailyOpenedClosedItem>();
|
||||||
|
else
|
||||||
|
resultData = _data;
|
||||||
|
|
||||||
|
// Remove Old Data
|
||||||
|
while (resultData.Count > 0 && resultData[0].Timestamp < historyStart)
|
||||||
|
resultData.RemoveAt(0);
|
||||||
|
|
||||||
|
// Calculate Update Scope
|
||||||
|
DateTime processDate = historyStart;
|
||||||
|
if (resultData.Count > 0)
|
||||||
|
processDate = resultData.Last().Timestamp.AddDays(-1);
|
||||||
|
|
||||||
|
// Cache Data
|
||||||
|
while (processDate <= historyEnd)
|
||||||
|
{
|
||||||
|
resultData.Add(Data(dbContext, processDate));
|
||||||
|
processDate = processDate.AddDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_data = resultData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DailyOpenedClosedItem Data(DiscoDataContext dbContext, DateTime ProcessDate)
|
||||||
|
{
|
||||||
|
DateTime processDateStart = ProcessDate;
|
||||||
|
DateTime processDateEnd = ProcessDate.AddDays(1);
|
||||||
|
|
||||||
|
int totalJobs = dbContext.Jobs.Where(j => j.OpenedDate < processDateEnd && (!j.ClosedDate.HasValue || j.ClosedDate > processDateEnd)).Count();
|
||||||
|
int openedJobs = dbContext.Jobs.Where(j => j.OpenedDate > processDateStart && j.OpenedDate < processDateEnd).Count();
|
||||||
|
int closedJobs = dbContext.Jobs.Where(j => j.ClosedDate > processDateStart && j.ClosedDate < processDateEnd).Count();
|
||||||
|
|
||||||
|
return new DailyOpenedClosedItem()
|
||||||
|
{
|
||||||
|
Timestamp = ProcessDate,
|
||||||
|
TotalJobs = totalJobs,
|
||||||
|
OpenedJobs = openedJobs,
|
||||||
|
ClosedJobs = closedJobs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DailyOpenedClosedItem> Data(DiscoDataContext dbContext, bool FilterUnimportantWeekends = false)
|
||||||
|
{
|
||||||
|
List<DailyOpenedClosedItem> resultData;
|
||||||
|
|
||||||
|
UpdateDataHistory(dbContext);
|
||||||
|
|
||||||
|
if (FilterUnimportantWeekends)
|
||||||
|
resultData = _data.Where(i => (i.Timestamp.DayOfWeek != DayOfWeek.Saturday && i.Timestamp.DayOfWeek != DayOfWeek.Sunday) ||
|
||||||
|
(i.OpenedJobs > 0 || i.ClosedJobs > 0)).ToList();
|
||||||
|
else
|
||||||
|
resultData = _data.ToList();
|
||||||
|
|
||||||
|
resultData.Add(Data(dbContext, DateTime.Today));
|
||||||
|
|
||||||
|
return resultData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.Job;
|
||||||
|
|
||||||
|
namespace Disco.BI.JobBI
|
||||||
|
{
|
||||||
|
public static class Utilities
|
||||||
|
{
|
||||||
|
public static Job Create(DiscoDataContext dbContext, Device device, User user, JobType type, List<JobSubType> subTypes, User initialTech)
|
||||||
|
{
|
||||||
|
Job j = new Job()
|
||||||
|
{
|
||||||
|
JobType = type,
|
||||||
|
OpenedTechUserId = initialTech.Id,
|
||||||
|
OpenedTechUser = initialTech,
|
||||||
|
OpenedDate = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Device
|
||||||
|
if (device != null)
|
||||||
|
{
|
||||||
|
j.Device = device;
|
||||||
|
j.DeviceSerialNumber = device.SerialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
j.User = user;
|
||||||
|
j.UserId = user.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub Types
|
||||||
|
List<JobSubType> jobSubTypes = subTypes.ToList();
|
||||||
|
j.JobSubTypes = jobSubTypes;
|
||||||
|
|
||||||
|
dbContext.Jobs.Add(j);
|
||||||
|
|
||||||
|
switch (type.Id)
|
||||||
|
{
|
||||||
|
case JobType.JobTypeIds.HWar:
|
||||||
|
dbContext.JobMetaWarranties.Add(new JobMetaWarranty() { Job = j });
|
||||||
|
break;
|
||||||
|
case JobType.JobTypeIds.HNWar:
|
||||||
|
dbContext.JobMetaNonWarranties.Add(new JobMetaNonWarranty() { Job = j });
|
||||||
|
if (device != null)
|
||||||
|
{
|
||||||
|
// Add Job Components
|
||||||
|
var components = dbContext.DeviceComponents.Include("JobSubTypes").Where(c => !c.DeviceModelId.HasValue || c.DeviceModelId == j.Device.DeviceModelId);
|
||||||
|
var addedComponents = new List<DeviceComponent>();
|
||||||
|
foreach (var c in components)
|
||||||
|
{
|
||||||
|
if (c.JobSubTypes.Count == 0)
|
||||||
|
{ // No Filter
|
||||||
|
addedComponents.Add(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var st in c.JobSubTypes)
|
||||||
|
{
|
||||||
|
foreach (var jst in jobSubTypes)
|
||||||
|
{
|
||||||
|
if (st.JobTypeId == jst.JobTypeId && st.Id == jst.Id)
|
||||||
|
{
|
||||||
|
addedComponents.Add(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addedComponents.Contains(c))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var c in addedComponents)
|
||||||
|
dbContext.JobComponents.Add(new JobComponent()
|
||||||
|
{
|
||||||
|
Job = j,
|
||||||
|
TechUserId = initialTech.Id,
|
||||||
|
Cost = c.Cost,
|
||||||
|
Description = c.Description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string JobStatusDescription(string StatusId, Job j = null)
|
||||||
|
{
|
||||||
|
switch (StatusId)
|
||||||
|
{
|
||||||
|
case Job.JobStatusIds.Open:
|
||||||
|
return "Open";
|
||||||
|
case Job.JobStatusIds.Closed:
|
||||||
|
return "Closed";
|
||||||
|
case Job.JobStatusIds.AwaitingWarrantyRepair:
|
||||||
|
if (j == null)
|
||||||
|
return "Awaiting Warranty Repair";
|
||||||
|
else
|
||||||
|
if (j.DeviceHeld.HasValue)
|
||||||
|
return string.Format("Awaiting Warranty Repair ({0})", j.JobMetaWarranty.ExternalName);
|
||||||
|
else
|
||||||
|
return string.Format("Awaiting Warranty Repair - Not Held ({0})", j.JobMetaWarranty.ExternalName);
|
||||||
|
case Job.JobStatusIds.AwaitingRepairs:
|
||||||
|
if (j == null)
|
||||||
|
return "Awaiting Repairs";
|
||||||
|
else
|
||||||
|
if (j.DeviceHeld.HasValue)
|
||||||
|
return string.Format("Awaiting Repairs ({0})", j.JobMetaNonWarranty.RepairerName);
|
||||||
|
else
|
||||||
|
return string.Format("Awaiting Repairs - Not Held ({0})", j.JobMetaNonWarranty.RepairerName);
|
||||||
|
case Job.JobStatusIds.AwaitingDeviceReturn:
|
||||||
|
return "Awaiting Device Return";
|
||||||
|
case Job.JobStatusIds.AwaitingUserAction:
|
||||||
|
return "Awaiting User Action";
|
||||||
|
case Job.JobStatusIds.AwaitingAccountingPayment:
|
||||||
|
return "Awaiting Accounting Payment";
|
||||||
|
case Job.JobStatusIds.AwaitingAccountingCharge:
|
||||||
|
return "Awaiting Accounting Charge";
|
||||||
|
case Job.JobStatusIds.AwaitingInsuranceProcessing:
|
||||||
|
return "Awaiting Insurance Processing";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string JobStatusDescription(string StatusId, JobTableModel.JobTableItemModelIncludeStatus j = null)
|
||||||
|
{
|
||||||
|
switch (StatusId)
|
||||||
|
{
|
||||||
|
case Job.JobStatusIds.Open:
|
||||||
|
return "Open";
|
||||||
|
case Job.JobStatusIds.Closed:
|
||||||
|
return "Closed";
|
||||||
|
case Job.JobStatusIds.AwaitingWarrantyRepair:
|
||||||
|
if (j == null)
|
||||||
|
return "Awaiting Warranty Repair";
|
||||||
|
else
|
||||||
|
if (j.DeviceHeld.HasValue)
|
||||||
|
return string.Format("Awaiting Warranty Repair ({0})", j.JobMetaWarranty_ExternalName);
|
||||||
|
else
|
||||||
|
return string.Format("Awaiting Warranty Repair - Not Held ({0})", j.JobMetaWarranty_ExternalName);
|
||||||
|
case Job.JobStatusIds.AwaitingRepairs:
|
||||||
|
if (j == null)
|
||||||
|
return "Awaiting Repairs";
|
||||||
|
else
|
||||||
|
if (j.DeviceHeld.HasValue)
|
||||||
|
return string.Format("Awaiting Repairs ({0})", j.JobMetaNonWarranty_RepairerName);
|
||||||
|
else
|
||||||
|
return string.Format("Awaiting Repairs - Not Held ({0})", j.JobMetaNonWarranty_RepairerName);
|
||||||
|
case Job.JobStatusIds.AwaitingDeviceReturn:
|
||||||
|
return "Awaiting Device Return";
|
||||||
|
case Job.JobStatusIds.AwaitingUserAction:
|
||||||
|
return "Awaiting User Action";
|
||||||
|
case Job.JobStatusIds.AwaitingAccountingPayment:
|
||||||
|
return "Awaiting Accounting Payment";
|
||||||
|
case Job.JobStatusIds.AwaitingAccountingCharge:
|
||||||
|
return "Awaiting Accounting Charge";
|
||||||
|
case Job.JobStatusIds.AwaitingInsuranceProcessing:
|
||||||
|
return "Awaiting Insurance Processing";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.BI.Search;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
|
namespace Disco.BI.UserBI
|
||||||
|
{
|
||||||
|
public static class Searching
|
||||||
|
{
|
||||||
|
|
||||||
|
public static List<User> SearchUpstream(string Term)
|
||||||
|
{
|
||||||
|
return Interop.ActiveDirectory.ActiveDirectory.SearchUsers(Term).Select(adU => adU.ToRepositoryUser()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<UserSearchResultItem> Search_SelectUserSearchResultItems(IQueryable<User> Query, int? LimitCount = null)
|
||||||
|
{
|
||||||
|
if (LimitCount.HasValue)
|
||||||
|
Query = Query.Take(LimitCount.Value);
|
||||||
|
|
||||||
|
return Query.Select(u => new UserSearchResultItem()
|
||||||
|
{
|
||||||
|
Id = u.Id,
|
||||||
|
Surname = u.Surname,
|
||||||
|
GivenName = u.GivenName,
|
||||||
|
DisplayName = u.DisplayName,
|
||||||
|
AssignedDevicesCount = u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue).Count(),
|
||||||
|
JobCount = u.Jobs.Count()
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<UserSearchResultItem> Search(DiscoDataContext dbContext, string Term, int? LimitCount = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Term) || Term.Length < 2)
|
||||||
|
throw new ArgumentException("Search Term must contain at least two characters", "Term");
|
||||||
|
|
||||||
|
// Search Active Directory & Import Relevant Users
|
||||||
|
var adImportedUsers = Interop.ActiveDirectory.ActiveDirectory.SearchUsers(Term).Select(adU => adU.ToRepositoryUser());
|
||||||
|
foreach (var adU in adImportedUsers)
|
||||||
|
{
|
||||||
|
var existingUser = dbContext.Users.Find(adU.Id);
|
||||||
|
if (existingUser != null)
|
||||||
|
existingUser.UpdateSelf(adU);
|
||||||
|
else
|
||||||
|
dbContext.Users.Add(adU);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
UserCache.InvalidateValue(adU.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Search_SelectUserSearchResultItems(dbContext.Users.Where(u =>
|
||||||
|
u.Id.Contains(Term) ||
|
||||||
|
u.Surname.Contains(Term) ||
|
||||||
|
u.GivenName.Contains(Term) ||
|
||||||
|
u.DisplayName.Contains(Term)
|
||||||
|
), LimitCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using System.Web;
|
||||||
|
using Quartz;
|
||||||
|
using Quartz.Impl;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
|
||||||
|
namespace Disco.BI.UserBI
|
||||||
|
{
|
||||||
|
public class UserCache : ScheduledTask
|
||||||
|
{
|
||||||
|
private static ConcurrentDictionary<string, Tuple<User, DateTime>> _Cache = new ConcurrentDictionary<string, Tuple<User, DateTime>>();
|
||||||
|
private const long CacheTimeoutTicks = 6000000000; // 10 Minutes
|
||||||
|
private const string CacheHttpRequestKey = "Disco_CurrentUser";
|
||||||
|
|
||||||
|
public static User CurrentUser
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string username = null;
|
||||||
|
User user;
|
||||||
|
|
||||||
|
// Check for ASP.NET
|
||||||
|
if (HttpContext.Current != null)
|
||||||
|
{
|
||||||
|
if (HttpContext.Current.Request.IsAuthenticated)
|
||||||
|
{
|
||||||
|
user = (User)HttpContext.Current.Items[CacheHttpRequestKey];
|
||||||
|
if (user != null)
|
||||||
|
return user;
|
||||||
|
|
||||||
|
username = HttpContext.Current.User.Identity.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
//throw new PlatformNotSupportedException("ASP.NET Authentication is not correctly configured");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// User default User
|
||||||
|
if (username == null)
|
||||||
|
{
|
||||||
|
username = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = GetUser(username);
|
||||||
|
|
||||||
|
if (HttpContext.Current != null && HttpContext.Current.Request.IsAuthenticated)
|
||||||
|
{
|
||||||
|
// Cache in current request
|
||||||
|
HttpContext.Current.Items[CacheHttpRequestKey] = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User GetUser(string Username)
|
||||||
|
{
|
||||||
|
// Check Cache
|
||||||
|
User u = TryUserCache(Username);
|
||||||
|
|
||||||
|
if (u == null)
|
||||||
|
{
|
||||||
|
// Load from Repository
|
||||||
|
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
u = GetUser(Username, dbContext, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User GetUser(string Username, DiscoDataContext dbContext, bool ForceRefresh = false)
|
||||||
|
{
|
||||||
|
User u = null;
|
||||||
|
|
||||||
|
// Check Cache
|
||||||
|
if (!ForceRefresh)
|
||||||
|
u = TryUserCache(Username);
|
||||||
|
|
||||||
|
if (u == null)
|
||||||
|
{
|
||||||
|
string username = Username.ToLower();
|
||||||
|
u = UserBI.Utilities.LoadUser(dbContext, username);
|
||||||
|
SetValue(username, u);
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static User TryUserCache(string Username)
|
||||||
|
{
|
||||||
|
string username = Username.ToLower();
|
||||||
|
Tuple<User, DateTime> userRecord;
|
||||||
|
if (_Cache.TryGetValue(username, out userRecord))
|
||||||
|
{
|
||||||
|
if (userRecord.Item2 > DateTime.Now)
|
||||||
|
return userRecord.Item1;
|
||||||
|
else
|
||||||
|
_Cache.TryRemove(username, out userRecord);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool InvalidateValue(string Key)
|
||||||
|
{
|
||||||
|
Tuple<User, DateTime> userRecord;
|
||||||
|
return _Cache.TryRemove(Key.ToLower(), out userRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool SetValue(string Key, User User)
|
||||||
|
{
|
||||||
|
string key = Key.ToLower();
|
||||||
|
Tuple<User, DateTime> userRecord = new Tuple<User, DateTime>(User, DateTime.Now.AddTicks(CacheTimeoutTicks));
|
||||||
|
if (_Cache.ContainsKey(key))
|
||||||
|
{
|
||||||
|
Tuple<User, DateTime> oldUser;
|
||||||
|
if (_Cache.TryGetValue(key, out oldUser))
|
||||||
|
{
|
||||||
|
return _Cache.TryUpdate(key, userRecord, oldUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _Cache.TryAdd(key, userRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CleanStaleCache()
|
||||||
|
{
|
||||||
|
var usernames = _Cache.Keys.ToArray();
|
||||||
|
foreach (string username in usernames)
|
||||||
|
{
|
||||||
|
Tuple<User, DateTime> userRecord;
|
||||||
|
if (_Cache.TryGetValue(username, out userRecord))
|
||||||
|
{
|
||||||
|
if (userRecord.Item2 <= DateTime.Now)
|
||||||
|
_Cache.TryRemove(username, out userRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void InitalizeScheduledTask(DiscoDataContext dbContext, IScheduler Scheduler)
|
||||||
|
//{
|
||||||
|
// // Run @ every 15mins
|
||||||
|
|
||||||
|
// // Next 15min interval
|
||||||
|
// DateTime now = DateTime.Now;
|
||||||
|
// int mins = (15 - (now.Minute % 15));
|
||||||
|
// if (mins < 10)
|
||||||
|
// mins += 15;
|
||||||
|
// DateTimeOffset startAt = new DateTimeOffset(now).AddMinutes(mins).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
|
||||||
|
|
||||||
|
// IJobDetail jobDetail = new JobDetailImpl("UserCache_CleanStaleCache", typeof(UserCache));
|
||||||
|
// ITrigger trigger = TriggerBuilder.Create().
|
||||||
|
// WithIdentity("UserCache_CleanStaleCacheTrigger").StartAt(startAt).
|
||||||
|
// WithSchedule(SimpleScheduleBuilder.RepeatMinutelyForever(15)).
|
||||||
|
// Build();
|
||||||
|
// Scheduler.ScheduleJob(jobDetail, trigger);
|
||||||
|
//}
|
||||||
|
|
||||||
|
public override string TaskName { get { return "User Cache - Clean Stale Cache"; } }
|
||||||
|
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
public override bool LogExceptionsOnly { get { return true; } }
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
// Run @ every 15mins
|
||||||
|
|
||||||
|
// Next 15min interval
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
int mins = (15 - (now.Minute % 15));
|
||||||
|
if (mins < 10)
|
||||||
|
mins += 15;
|
||||||
|
DateTimeOffset startAt = new DateTimeOffset(now).AddMinutes(mins).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
|
||||||
|
|
||||||
|
TriggerBuilder triggerBuilder = TriggerBuilder.Create().StartAt(startAt).
|
||||||
|
WithSchedule(SimpleScheduleBuilder.RepeatMinutelyForever(15));
|
||||||
|
|
||||||
|
this.ScheduleTask(triggerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
CleanStaleCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.BI.Search;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.DirectoryServices.ActiveDirectory;
|
||||||
|
using Disco.Services.Logging;
|
||||||
|
|
||||||
|
namespace Disco.BI.UserBI
|
||||||
|
{
|
||||||
|
public static class Utilities
|
||||||
|
{
|
||||||
|
|
||||||
|
public static User LoadUser(DiscoDataContext dbContext, string Username)
|
||||||
|
{
|
||||||
|
// Machine Account ?
|
||||||
|
if (Username.EndsWith("$"))
|
||||||
|
{
|
||||||
|
return Interop.ActiveDirectory.ActiveDirectory.GetMachineAccount(Username).ToRepositoryUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
// User Account
|
||||||
|
User user = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ADUser = Interop.ActiveDirectory.ActiveDirectory.GetUserAccount(Username);
|
||||||
|
if (ADUser == null)
|
||||||
|
throw new ArgumentException(string.Format("Invalid Username: '{0}'", Username), "Username");
|
||||||
|
user = ADUser.ToRepositoryUser();
|
||||||
|
}
|
||||||
|
catch (COMException ex)
|
||||||
|
{
|
||||||
|
// If "Server is not operational" then Try Cache
|
||||||
|
if (ex.ErrorCode != -2147016646)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
SystemLog.LogException("Primary Domain Controller Down? Disco.BI.UserBI.Utilities.LoadUser", ex);
|
||||||
|
}
|
||||||
|
catch (ActiveDirectoryOperationException ex)
|
||||||
|
{
|
||||||
|
// Try From Cache...
|
||||||
|
SystemLog.LogException("Primary Domain Controller Down? Disco.BI.UserBI.Utilities.LoadUser", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Repository
|
||||||
|
User existingUser;
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
string username = Username.Contains(@"\") ? Username.Substring(Username.IndexOf(@"\") + 1) : Username;
|
||||||
|
existingUser = dbContext.Users.Find(username);
|
||||||
|
if (existingUser == null)
|
||||||
|
throw new ArgumentException(string.Format("Invalid User - Not In Disco DB: '{0}'", Username), "Username");
|
||||||
|
else
|
||||||
|
return existingUser;
|
||||||
|
}
|
||||||
|
existingUser = dbContext.Users.Find(user.Id);
|
||||||
|
if (existingUser == null)
|
||||||
|
{
|
||||||
|
dbContext.Users.Add(user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
existingUser.UpdateSelf(user);
|
||||||
|
user = existingUser;
|
||||||
|
}
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
using Disco.BI.Wireless.eduSTAR;
|
||||||
|
using Disco.Data.Configuration;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.BI.Extensions;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
namespace Disco.BI.Wireless
|
||||||
|
{
|
||||||
|
public abstract class BaseWirelessProvider
|
||||||
|
{
|
||||||
|
protected DiscoDataContext dbContext;
|
||||||
|
private static object _CertificateAllocateLock = System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(new object());
|
||||||
|
public static BaseWirelessProvider GetProvider(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
string provider = dbContext.DiscoConfiguration.Wireless.Provider;
|
||||||
|
if (provider == "eduSTAR")
|
||||||
|
{
|
||||||
|
return new eduSTARWirelessProvider(dbContext);
|
||||||
|
}
|
||||||
|
throw new System.NotSupportedException(string.Format("Wireless Provider Not Supported: '{0}'", dbContext.DiscoConfiguration.Wireless.Provider));
|
||||||
|
}
|
||||||
|
protected BaseWirelessProvider(DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
this.dbContext = dbContext;
|
||||||
|
}
|
||||||
|
private DeviceCertificate CertificateAllocate(ref Device repoDevice)
|
||||||
|
{
|
||||||
|
lock (BaseWirelessProvider._CertificateAllocateLock)
|
||||||
|
{
|
||||||
|
this.FillCertificateAutoBuffer();
|
||||||
|
int timeout = 60;
|
||||||
|
int freeCertCount = this.dbContext.DeviceCertificates.Where(c => c.DeviceSerialNumber == null && c.Enabled).Count();
|
||||||
|
while (!(freeCertCount > 0 | timeout <= 0))
|
||||||
|
{
|
||||||
|
System.Threading.Thread.Sleep(500);
|
||||||
|
freeCertCount = this.dbContext.DeviceCertificates.Where(c => c.DeviceSerialNumber == null && c.Enabled).Count();
|
||||||
|
timeout--;
|
||||||
|
}
|
||||||
|
DeviceCertificate cert = this.dbContext.DeviceCertificates.Where(c => c.DeviceSerialNumber == null && c.Enabled).FirstOrDefault();
|
||||||
|
if (cert == null)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.LogAllocationFailed(repoDevice.SerialNumber);
|
||||||
|
throw new System.InvalidOperationException("Unable to Allocate a Wireless Certificate");
|
||||||
|
}
|
||||||
|
WirelessCertificatesLog.LogAllocated(cert.Name, repoDevice.SerialNumber);
|
||||||
|
cert.DeviceSerialNumber = repoDevice.SerialNumber;
|
||||||
|
cert.AllocatedDate = System.DateTime.Now;
|
||||||
|
this.dbContext.SaveChanges();
|
||||||
|
return cert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public DeviceCertificate Enrol(Device repoDevice)
|
||||||
|
{
|
||||||
|
DeviceCertificate allocatedCert = this.dbContext.DeviceCertificates.Where(c => c.DeviceSerialNumber == repoDevice.SerialNumber && c.Enabled).FirstOrDefault();
|
||||||
|
if (allocatedCert != null)
|
||||||
|
{
|
||||||
|
return allocatedCert;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
|
||||||
|
//if (repoDevice.DeviceProfile.Configuration(this.dbContext).AllocateWirelessCertificate)
|
||||||
|
if (repoDevice.DeviceProfile.AllocateCertificate)
|
||||||
|
{
|
||||||
|
allocatedCert = this.CertificateAllocate(ref repoDevice);
|
||||||
|
return allocatedCert;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected abstract void FillCertificateAutoBuffer();
|
||||||
|
public abstract void FillCertificateBuffer(int Amount);
|
||||||
|
public abstract System.Collections.Generic.List<string> RemoveExistingCertificateNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
using Disco.Logging;
|
||||||
|
using Disco.Logging.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
namespace Disco.BI.Wireless
|
||||||
|
{
|
||||||
|
public class WirelessCertificatesLog : LogBase
|
||||||
|
{
|
||||||
|
public enum EventTypeIds
|
||||||
|
{
|
||||||
|
RetrievalStarting = 10,
|
||||||
|
RetrievalProgress,
|
||||||
|
RetrievalFinished,
|
||||||
|
RetrievalWarning = 15,
|
||||||
|
RetrievalError,
|
||||||
|
RetrievalCertificateStarting = 20,
|
||||||
|
RetrievalCertificateFinished = 22,
|
||||||
|
RetrievalCertificateWarning = 25,
|
||||||
|
RetrievalCertificateError,
|
||||||
|
Allocated = 40,
|
||||||
|
AllocationFailed = 50
|
||||||
|
}
|
||||||
|
private const int _ModuleId = 60;
|
||||||
|
private static bool _IsCertificateRetrievalProcessing;
|
||||||
|
private static string _CertificateRetrievalStatus;
|
||||||
|
private static int _CertificateRetrievalProgress;
|
||||||
|
public static WirelessCertificatesLog Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (WirelessCertificatesLog)LogContext.LogModules[60];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static bool IsCertificateRetrievalProcessing
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return WirelessCertificatesLog._IsCertificateRetrievalProcessing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override string ModuleDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "Wireless Certificates";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override int ModuleId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override string ModuleName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "WirelessCertificates";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public WirelessCertificatesLog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
private static void Log(WirelessCertificatesLog.EventTypeIds EventTypeId, params object[] Args)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Current.Log((int)EventTypeId, Args);
|
||||||
|
}
|
||||||
|
public static void LogRetrievalStarting(int CertificateCount, int CertificateIdFrom, int CertificateIdTo)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalStarting, new object[]
|
||||||
|
{
|
||||||
|
CertificateCount,
|
||||||
|
CertificateIdFrom,
|
||||||
|
CertificateIdTo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogRetrievalFinished()
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalFinished, new object[0]);
|
||||||
|
}
|
||||||
|
public static void LogRetrievalWarning(string Message)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalWarning, new object[]
|
||||||
|
{
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogRetrievalError(string Message)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalError, new object[]
|
||||||
|
{
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogRetrievalCertificateStarting(string CertificateId)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalCertificateStarting, new object[]
|
||||||
|
{
|
||||||
|
CertificateId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogRetrievalCertificateFinished(string CertificateId)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalCertificateFinished, new object[]
|
||||||
|
{
|
||||||
|
CertificateId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogRetrievalCertificateWarning(string CertificateId, string Message)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalCertificateWarning, new object[]
|
||||||
|
{
|
||||||
|
CertificateId,
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogRetrievalCertificateError(string CertificateId, string Message)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalCertificateError, new object[]
|
||||||
|
{
|
||||||
|
CertificateId,
|
||||||
|
Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogAllocated(string CertificateId, string DeviceSerialNumber)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.Allocated, new object[]
|
||||||
|
{
|
||||||
|
CertificateId,
|
||||||
|
DeviceSerialNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogAllocationFailed(string DeviceSerialNumber)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.AllocationFailed, new object[]
|
||||||
|
{
|
||||||
|
DeviceSerialNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static void LogCertificateRetrievalProgress(bool? IsProcessing, int? Progress, string Status)
|
||||||
|
{
|
||||||
|
bool flag = IsProcessing.HasValue;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog._IsCertificateRetrievalProcessing = IsProcessing.Value;
|
||||||
|
}
|
||||||
|
flag = WirelessCertificatesLog._IsCertificateRetrievalProcessing;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
bool flag2 = Status != null;
|
||||||
|
if (flag2)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog._CertificateRetrievalStatus = Status;
|
||||||
|
}
|
||||||
|
flag2 = Progress.HasValue;
|
||||||
|
if (flag2)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog._CertificateRetrievalProgress = Progress.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog._CertificateRetrievalStatus = null;
|
||||||
|
WirelessCertificatesLog._CertificateRetrievalProgress = 0;
|
||||||
|
}
|
||||||
|
WirelessCertificatesLog.Log(WirelessCertificatesLog.EventTypeIds.RetrievalProgress, new object[]
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog._IsCertificateRetrievalProcessing,
|
||||||
|
WirelessCertificatesLog._CertificateRetrievalProgress,
|
||||||
|
WirelessCertificatesLog._CertificateRetrievalStatus
|
||||||
|
});
|
||||||
|
}
|
||||||
|
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
|
||||||
|
{
|
||||||
|
return new System.Collections.Generic.List<LogEventType>
|
||||||
|
{
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 10,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Starting",
|
||||||
|
Format = "Starting retrieval of {0} certificate/s ({1} to {2})",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 11,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Progress",
|
||||||
|
Format = "Processing: {0}; {1}% Complete; Status: {2}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = false,
|
||||||
|
UseDisplay = false
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 12,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Finished",
|
||||||
|
Format = "Retrieval of Certificates Complete",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 15,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Warning",
|
||||||
|
Format = "Retrieval Warning: {0}",
|
||||||
|
Severity = 1,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 16,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Error",
|
||||||
|
Format = "Retrieval Error: {0}",
|
||||||
|
Severity = 2,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 20,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Certificate Starting",
|
||||||
|
Format = "Retrieving Certificate: {0}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 22,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Certificate Finished",
|
||||||
|
Format = "Certificate Retrieved: {0}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 25,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Certificate Warning",
|
||||||
|
Format = "{0} Certificate Warning: {1}",
|
||||||
|
Severity = 1,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 26,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Retrieval Certificate Error",
|
||||||
|
Format = "{0} Certificate Error: {1}",
|
||||||
|
Severity = 2,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 40,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Allocated",
|
||||||
|
Format = "Certificate {0} allocated to {1}",
|
||||||
|
Severity = 0,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
},
|
||||||
|
new LogEventType
|
||||||
|
{
|
||||||
|
Id = 50,
|
||||||
|
ModuleId = 60,
|
||||||
|
Name = "Allocation Failed",
|
||||||
|
Format = "No certificates available for Device: {0}",
|
||||||
|
Severity = 2,
|
||||||
|
UseLive = true,
|
||||||
|
UsePersist = true,
|
||||||
|
UseDisplay = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
using Disco.BI.Wireless.eduSTAR.eduSTARWirelessCertService;
|
||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Ionic.Zip;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.ServiceModel;
|
||||||
|
using System.ServiceModel.Channels;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Disco.BI.Wireless.eduSTAR
|
||||||
|
{
|
||||||
|
public class eduSTARWirelessProvider : BaseWirelessProvider
|
||||||
|
{
|
||||||
|
private class BulkLoadCertificatesContract
|
||||||
|
{
|
||||||
|
public int Start { get; set; }
|
||||||
|
public int Count { get; set; }
|
||||||
|
}
|
||||||
|
private static object _BulkLoadThreadLock = new object();
|
||||||
|
private static System.Threading.Thread _BulkLoadThread;
|
||||||
|
public eduSTARWirelessProvider(DiscoDataContext dbContext)
|
||||||
|
: base(dbContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
protected override void FillCertificateAutoBuffer()
|
||||||
|
{
|
||||||
|
int freeCertCount = this.dbContext.DeviceCertificates.Where(c => c.DeviceSerialNumber == null && c.Enabled).Count();
|
||||||
|
if (freeCertCount <= this.dbContext.DiscoConfiguration.Wireless.CertificateAutoBufferLow)
|
||||||
|
{
|
||||||
|
this.BulkLoadCertificates(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override void FillCertificateBuffer(int Amount)
|
||||||
|
{
|
||||||
|
this.BulkLoadCertificates(Amount);
|
||||||
|
}
|
||||||
|
public override System.Collections.Generic.List<string> RemoveExistingCertificateNames()
|
||||||
|
{
|
||||||
|
return new System.Collections.Generic.List<string>
|
||||||
|
{
|
||||||
|
"(eduPaSS)",
|
||||||
|
"(CN=Computers, ?DC=services, ?DC=education, ?DC=vic, ?DC=gov, ?DC=au)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private void BulkLoadCertificates(int Amount = 0)
|
||||||
|
{
|
||||||
|
if (eduSTARWirelessProvider._BulkLoadThread == null)
|
||||||
|
{
|
||||||
|
lock (eduSTARWirelessProvider._BulkLoadThreadLock)
|
||||||
|
{
|
||||||
|
if (eduSTARWirelessProvider._BulkLoadThread == null)
|
||||||
|
{
|
||||||
|
int start = 0;
|
||||||
|
if (this.dbContext.DeviceCertificates.Count() > 0)
|
||||||
|
{
|
||||||
|
start = this.dbContext.DeviceCertificates.Max(c => c.ProviderIndex) + 1;
|
||||||
|
}
|
||||||
|
int buffer = this.dbContext.DeviceCertificates.Count(c => c.DeviceSerialNumber == null && c.Enabled);
|
||||||
|
int count = this.dbContext.DiscoConfiguration.Wireless.CertificateAutoBufferMax - buffer;
|
||||||
|
if (Amount > 0)
|
||||||
|
{
|
||||||
|
count = Amount;
|
||||||
|
}
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
eduSTARWirelessProvider.BulkLoadCertificatesContract contract = new eduSTARWirelessProvider.BulkLoadCertificatesContract
|
||||||
|
{
|
||||||
|
Start = start,
|
||||||
|
Count = count
|
||||||
|
};
|
||||||
|
System.Threading.ParameterizedThreadStart threadStart = delegate(object a0)
|
||||||
|
{
|
||||||
|
this.BulkLoadCertificatesStart((eduSTARWirelessProvider.BulkLoadCertificatesContract)a0);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
eduSTARWirelessProvider._BulkLoadThread = new System.Threading.Thread(threadStart);
|
||||||
|
eduSTARWirelessProvider._BulkLoadThread.Start(contract);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void BulkLoadCertificatesStart(eduSTARWirelessProvider.BulkLoadCertificatesContract contract)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.LogRetrievalStarting(contract.Count, contract.Start, contract.Start + contract.Count - 1);
|
||||||
|
WirelessCertificatesLog.LogCertificateRetrievalProgress(true, 0, string.Format("Starting Bulk Retrieval (Loading {0} Certificate/s)", contract.Count));
|
||||||
|
DiscoDataContext dbLocalContext = new DiscoDataContext();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WirelessCertServiceSoapClient proxy = this.GetProxy();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int num = contract.Start + contract.Count - 1;
|
||||||
|
int index = contract.Start;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int num2 = num;
|
||||||
|
if (index > num2)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
WirelessCertificatesLog.LogCertificateRetrievalProgress(true, (int)System.Math.Round(unchecked(((double)checked(index - contract.Start) + 0.5) / (double)contract.Count * 100.0)), string.Format("Retrieving Certificate {0} of {1}", index - contract.Start + 1, contract.Count));
|
||||||
|
DeviceCertificate cert = this.LoadCertificate(index, proxy, dbLocalContext);
|
||||||
|
dbLocalContext.DeviceCertificates.Add(cert);
|
||||||
|
dbLocalContext.SaveChanges();
|
||||||
|
WirelessCertificatesLog.LogRetrievalCertificateFinished(cert.Name);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bool flag = proxy != null;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
((System.IDisposable)proxy).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bool flag = dbLocalContext != null;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
((System.IDisposable)dbLocalContext).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.LogRetrievalError(string.Format("[{0}] {1}", ex.GetType().Name, ex.Message));
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
lock (eduSTARWirelessProvider._BulkLoadThreadLock)
|
||||||
|
{
|
||||||
|
eduSTARWirelessProvider._BulkLoadThread = null;
|
||||||
|
}
|
||||||
|
WirelessCertificatesLog.LogRetrievalFinished();
|
||||||
|
WirelessCertificatesLog.LogCertificateRetrievalProgress(false, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private DeviceCertificate LoadCertificate(int Index, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
DeviceCertificate LoadCertificate;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WirelessCertServiceSoapClient proxy = this.GetProxy();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadCertificate = this.LoadCertificate(Index, proxy, dbContext);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bool flag = proxy != null;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
((System.IDisposable)proxy).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.LogRetrievalCertificateError(Index.ToString(), string.Format("[{0}] {1}", ex.GetType().Name, ex.Message));
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
return LoadCertificate;
|
||||||
|
}
|
||||||
|
private DeviceCertificate LoadCertificate(int Index, WirelessCertServiceSoapClient Proxy, DiscoDataContext dbContext)
|
||||||
|
{
|
||||||
|
bool flag = string.IsNullOrWhiteSpace(dbContext.DiscoConfiguration.Wireless.eduSTAR_ServiceAccountSchoolId);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
throw new System.ArgumentException("Wireless Certificates: Invalid ServiceAccount SchoolId");
|
||||||
|
}
|
||||||
|
flag = string.IsNullOrWhiteSpace(dbContext.DiscoConfiguration.Wireless.eduSTAR_ServiceAccountUsername);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
throw new System.ArgumentException("Wireless Certificates: Invalid ServiceAccount Username");
|
||||||
|
}
|
||||||
|
flag = string.IsNullOrWhiteSpace(dbContext.DiscoConfiguration.Wireless.eduSTAR_ServiceAccountPassword);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
throw new System.ArgumentException("Wireless Certificates: Invalid ServiceAccount Password");
|
||||||
|
}
|
||||||
|
DeviceCertificate cert = new DeviceCertificate
|
||||||
|
{
|
||||||
|
ProviderIndex = Index,
|
||||||
|
Name = string.Format("{0}-{1}", dbContext.DiscoConfiguration.Wireless.eduSTAR_ServiceAccountSchoolId, Index.ToString("00000")),
|
||||||
|
Enabled = true
|
||||||
|
};
|
||||||
|
WirelessCertificatesLog.LogRetrievalCertificateStarting(cert.Name);
|
||||||
|
string response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = Proxy.GetWirelessCert(dbContext.DiscoConfiguration.Wireless.eduSTAR_ServiceAccountSchoolId, cert.Name, "password", dbContext.DiscoConfiguration.Wireless.eduSTAR_ServiceAccountUsername, dbContext.DiscoConfiguration.Wireless.eduSTAR_ServiceAccountPassword);
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.LogRetrievalCertificateError(cert.Name, ex.Message);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] responseBytes = System.Convert.FromBase64String(response);
|
||||||
|
System.IO.MemoryStream responseByteStream = new System.IO.MemoryStream(responseBytes);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ZipFile responseZip = ZipFile.Read(responseByteStream);
|
||||||
|
ZipEntry certFile = responseZip.FirstOrDefault((ZipEntry ze) => ze.FileName.EndsWith(".pfx", System.StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
System.IO.MemoryStream certByteStream = new System.IO.MemoryStream();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
certFile.Extract(certByteStream);
|
||||||
|
cert.Content = certByteStream.ToArray();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
flag = (certByteStream != null);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
((System.IDisposable)certByteStream).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
flag = (responseByteStream != null);
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
((System.IDisposable)responseByteStream).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex2)
|
||||||
|
{
|
||||||
|
if (response.Contains("Computer with this name already exists"))
|
||||||
|
{
|
||||||
|
WirelessCertificatesLog.LogRetrievalCertificateWarning(cert.Name, "Already exists on eduSTAR server, disabling and skipping.");
|
||||||
|
cert.ExpirationDate = System.DateTime.Now;
|
||||||
|
cert.Enabled = false;
|
||||||
|
cert.Content = null;
|
||||||
|
return cert;
|
||||||
|
}
|
||||||
|
throw new System.InvalidOperationException(string.Format("Unable to Uncompress (Server returned: {0})", response), ex2);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
X509Certificate2 x509Cert = new X509Certificate2(cert.Content, "password");
|
||||||
|
cert.ExpirationDate = x509Cert.NotAfter;
|
||||||
|
}
|
||||||
|
catch (System.Exception ex3)
|
||||||
|
{
|
||||||
|
throw new System.InvalidOperationException("Invalid Certificate returned by Server", ex3);
|
||||||
|
}
|
||||||
|
return cert;
|
||||||
|
}
|
||||||
|
private WirelessCertServiceSoapClient GetProxy()
|
||||||
|
{
|
||||||
|
BasicHttpBinding binding = new BasicHttpBinding();
|
||||||
|
|
||||||
|
// Don't Use Proxy
|
||||||
|
binding.UseDefaultWebProxy = false;
|
||||||
|
binding.ProxyAddress = null;
|
||||||
|
|
||||||
|
binding.Security.Mode = BasicHttpSecurityMode.Transport;
|
||||||
|
binding.MaxReceivedMessageSize = 524288L;
|
||||||
|
binding.ReaderQuotas.MaxStringContentLength = 524288;
|
||||||
|
EndpointAddress endpointAddress = new EndpointAddress(new Uri("https://www.eduweb.vic.gov.au/edustar/WirelessCertWS/wirelesscertws.asmx"), new AddressHeader[0]);
|
||||||
|
return new WirelessCertServiceSoapClient(binding, endpointAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>8.0.30703</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{095E6F94-3C34-47AE-BB83-46203535E0F6}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Disco</RootNamespace>
|
||||||
|
<AssemblyName>Disco.BI</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="BitMiracle.LibTiff.NET">
|
||||||
|
<HintPath>..\..\Resources\Libraries\LibTiff.NET\BitMiracle.LibTiff.NET.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="EntityFramework">
|
||||||
|
<HintPath>..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Ionic.Zip.Reduced">
|
||||||
|
<HintPath>..\..\Resources\Libraries\DotNetZip\Ionic.Zip.Reduced.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="itextsharp">
|
||||||
|
<HintPath>..\..\Resources\Libraries\iTextSharp\itextsharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<Private>True</Private>
|
||||||
|
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Quartz, Version=2.0.0.100, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\Resources\Libraries\Quartz\Quartz.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SignalR, Version=0.5.1.10822, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\SignalR.Server.0.5.3\lib\net40\SignalR.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SignalR.Hosting.AspNet, Version=0.5.1.10822, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\SignalR.Hosting.AspNet.0.5.3\lib\net45\SignalR.Hosting.AspNet.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SignalR.Hosting.Common">
|
||||||
|
<HintPath>..\packages\SignalR.Hosting.Common.0.5.3\lib\net40\SignalR.Hosting.Common.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Spring.Core">
|
||||||
|
<HintPath>..\..\Resources\Libraries\Spring.NET\Spring.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Data.Entity" />
|
||||||
|
<Reference Include="System.DirectoryServices" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
|
<Reference Include="System.ServiceModel" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Tamir.SharpSSH">
|
||||||
|
<HintPath>..\..\Resources\Libraries\SharpSSH\Tamir.SharpSSH.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="zxing">
|
||||||
|
<HintPath>..\..\Resources\Libraries\ZXing\zxing.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="BI\DataStore.cs" />
|
||||||
|
<Compile Include="BI\AttachmentBI\Utilities.cs" />
|
||||||
|
<Compile Include="BI\DeviceBI\BatchUtilities.cs" />
|
||||||
|
<Compile Include="BI\DeviceBI\Searching.cs" />
|
||||||
|
<Compile Include="BI\DisposableImageCollection.cs" />
|
||||||
|
<Compile Include="BI\DocumentTemplateBI\DocumentTemplateQRCodeLocationCache.cs" />
|
||||||
|
<Compile Include="BI\Expressions\EvaluateExpressionParseException.cs" />
|
||||||
|
<Compile Include="BI\Expressions\ExpressionCachePreloadTask.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\DataExt.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\DeviceExt.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\ImageExt.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\BaseImageExpressionResult.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\FileMontageImageExpressionResult.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\FileImageExpressionResult.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\BitmapImageExpressionResult.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Extensions\UserExt.cs" />
|
||||||
|
<Compile Include="BI\Extensions\AttachmentActionExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\AttachmentExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\ClientServicesExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\DeviceActionExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\DeviceBatchExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\DeviceCertificateExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\DeviceModelExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\DeviceProfileExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\JobActionExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\JobExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\JobFlagExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\JobTableExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\UserExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\WirelessCertificateExtensions.cs" />
|
||||||
|
<Compile Include="BI\Extensions\DeviceExtensions.cs" />
|
||||||
|
<Compile Include="BI\DeviceBI\EnrolSafeException.cs" />
|
||||||
|
<Compile Include="BI\DeviceBI\Enrol.cs" />
|
||||||
|
<Compile Include="BI\DeviceBI\EnrolmentLog.cs" />
|
||||||
|
<Compile Include="BI\Extensions\DocumentTemplateExtensions.cs" />
|
||||||
|
<Compile Include="BI\DocumentTemplateBI\Utilities.cs" />
|
||||||
|
<Compile Include="BI\DocumentTemplateBI\DocumentUniqueIdentifier.cs" />
|
||||||
|
<Compile Include="BI\Expressions\EvaluateExpressionPart.cs" />
|
||||||
|
<Compile Include="BI\Expressions\Expression.cs" />
|
||||||
|
<Compile Include="BI\Expressions\ExpressionTypeDescriptor.cs" />
|
||||||
|
<Compile Include="BI\Expressions\ExpressionTypeMemberDescriptor.cs" />
|
||||||
|
<Compile Include="BI\Expressions\IExpressionPart.cs" />
|
||||||
|
<Compile Include="BI\Expressions\TextExpressionPart.cs" />
|
||||||
|
<Compile Include="BI\DocumentTemplateBI\Importer\DocumentDropBoxMonitor.cs" />
|
||||||
|
<Compile Include="BI\DocumentTemplateBI\Importer\DocumentImporterJob.cs" />
|
||||||
|
<Compile Include="BI\DocumentTemplateBI\Importer\DocumentImporterCleanCacheJob.cs" />
|
||||||
|
<Compile Include="BI\DocumentTemplateBI\Importer\DocumentImporterLog.cs" />
|
||||||
|
<Compile Include="BI\Interop\ActiveDirectory\ActiveDirectory.cs" />
|
||||||
|
<Compile Include="BI\Interop\ActiveDirectory\ActiveDirectoryCachedGroups.cs" />
|
||||||
|
<Compile Include="BI\Interop\ActiveDirectory\ActiveDirectoryHelpers.cs" />
|
||||||
|
<Compile Include="BI\Interop\ActiveDirectory\ActiveDirectoryMachineAccountExtensions.cs" />
|
||||||
|
<Compile Include="BI\Interop\ActiveDirectory\ActiveDirectoryOrganisationalUnit.cs" />
|
||||||
|
<Compile Include="BI\Interop\ActiveDirectory\ActiveDirectoryUpdateLastNetworkLogonDateJob.cs" />
|
||||||
|
<Compile Include="BI\Interop\ActiveDirectory\ActiveDirectoryUserAccountExtensions.cs" />
|
||||||
|
<Compile Include="BI\Expressions\ExpressionCache.cs" />
|
||||||
|
<Compile Include="BI\Interop\Community\UpdateCheck.cs" />
|
||||||
|
<Compile Include="BI\Interop\Community\UpdateCheckTask.cs" />
|
||||||
|
<Compile Include="BI\Interop\MimeTypes.cs" />
|
||||||
|
<Compile Include="BI\Interop\Pdf\PdfGenerator.cs" />
|
||||||
|
<Compile Include="BI\Interop\Pdf\PdfImporter.cs" />
|
||||||
|
<Compile Include="BI\Interop\PluginServices\IDiscoScheduledTask.cs" />
|
||||||
|
<Compile Include="BI\Interop\PluginServices\Utilities.cs" />
|
||||||
|
<Compile Include="BI\Interop\SignalRHandlers\UserHeldDevices.cs" />
|
||||||
|
<Compile Include="BI\JobBI\Searching.cs" />
|
||||||
|
<Compile Include="BI\JobBI\Statistics\DailyOpenedClosed.cs" />
|
||||||
|
<Compile Include="BI\JobBI\Utilities.cs" />
|
||||||
|
<Compile Include="BI\UserBI\Searching.cs" />
|
||||||
|
<Compile Include="BI\UserBI\UserCache.cs" />
|
||||||
|
<Compile Include="BI\UserBI\Utilities.cs" />
|
||||||
|
<Compile Include="BI\Extensions\UtilityExtensions.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Disco.Data\Disco.Data.csproj">
|
||||||
|
<Project>{85A6BD19-2C64-4746-8F2C-A68A86E8C2D7}</Project>
|
||||||
|
<Name>Disco.Data</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Disco.Models\Disco.Models.csproj">
|
||||||
|
<Project>{FBC05512-FCCA-4B16-9E76-8C413C5DE6C9}</Project>
|
||||||
|
<Name>Disco.Models</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Disco.Services\Disco.Services.csproj">
|
||||||
|
<Project>{B80A737F-BD6A-4986-9182-DD7B932BD950}</Project>
|
||||||
|
<Name>Disco.Services</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="app.config" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\MimeType-img16.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\MimeType-pdf16.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\EmptyLogo.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\MimeType-doc48.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\MimeType-pdf48.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\MimeType-unknown48.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="BI\CertificateBI\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<UserProperties BuildVersion_UseGlobalSettings="True" BuildVersion_DetectChanges="False" BuildVersion_BuildAction="ReBuild" BuildVersion_StartDate="2001/1/1" />
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("Disco - Business Intelligence")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Disco")]
|
||||||
|
[assembly: AssemblyCopyright("")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("01887659-9fd6-4464-8493-cf0506ee2569")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.2.0131.2002")]
|
||||||
|
[assembly: AssemblyFileVersion("1.2.0131.2002")]
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.17929
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Disco.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Disco.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap MimeType_doc48 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("MimeType_doc48", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap MimeType_img16 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("MimeType_img16", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap MimeType_pdf16 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("MimeType_pdf16", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap MimeType_pdf48 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("MimeType_pdf48", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap MimeType_unknown48 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("MimeType_unknown48", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="MimeType_doc48" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\MimeType-doc48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
<data name="MimeType_img16" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\MimeType-img16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
<data name="MimeType_pdf16" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\MimeType-pdf16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
<data name="MimeType_pdf48" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\MimeType-pdf48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
<data name="MimeType_unknown48" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\MimeType-unknown48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
|
||||||
|
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
|
||||||
|
</configSections>
|
||||||
|
<system.serviceModel>
|
||||||
|
<bindings />
|
||||||
|
<client />
|
||||||
|
</system.serviceModel>
|
||||||
|
<entityFramework>
|
||||||
|
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
|
||||||
|
<parameters>
|
||||||
|
<parameter value="Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True" />
|
||||||
|
</parameters>
|
||||||
|
</defaultConnectionFactory>
|
||||||
|
</entityFramework>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="EntityFramework" version="5.0.0" targetFramework="net45" />
|
||||||
|
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" />
|
||||||
|
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
|
||||||
|
<package id="SignalR.Hosting.AspNet" version="0.5.3" targetFramework="net45" />
|
||||||
|
<package id="SignalR.Hosting.Common" version="0.5.3" targetFramework="net45" />
|
||||||
|
<package id="SignalR.Server" version="0.5.3" targetFramework="net45" />
|
||||||
|
</packages>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{D6B85A86-0FAA-4B04-BC9E-D01A08C03387}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Disco.Client</RootNamespace>
|
||||||
|
<AssemblyName>Disco.Client</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<StartupObject>Disco.Client.Program</StartupObject>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationIcon>Icon.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup />
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.DirectoryServices" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\Enrol.cs">
|
||||||
|
<Link>Models\ClientServices\Enrol.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\EnrolResponse.cs">
|
||||||
|
<Link>Models\ClientServices\EnrolResponse.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\MacEnrol.cs">
|
||||||
|
<Link>Models\ClientServices\MacEnrol.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\MacEnrolResponse.cs">
|
||||||
|
<Link>Models\ClientServices\MacEnrolResponse.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\MacSecureEnrolResponse.cs">
|
||||||
|
<Link>Models\ClientServices\MacSecureEnrolResponse.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\ServiceBase.cs">
|
||||||
|
<Link>Models\ClientServices\ServiceBase.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\WhoAmI.cs">
|
||||||
|
<Link>Models\ClientServices\WhoAmI.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Disco.Models\ClientServices\WhoAmIResponse.cs">
|
||||||
|
<Link>Models\ClientServices\WhoAmIResponse.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="ErrorReporting.cs" />
|
||||||
|
<Compile Include="Extensions\ClientServiceException.cs" />
|
||||||
|
<Compile Include="Extensions\ClientServicesExtensions.cs" />
|
||||||
|
<Compile Include="Extensions\EnrolExtensions.cs" />
|
||||||
|
<Compile Include="Extensions\WhoAmIExtensions.cs" />
|
||||||
|
<Compile Include="Interop\Certificates.cs" />
|
||||||
|
<Compile Include="Interop\LocalAuthentication.cs" />
|
||||||
|
<Compile Include="Interop\Network.cs" />
|
||||||
|
<Compile Include="Presentation.cs" />
|
||||||
|
<Compile Include="Interop\SystemAudit.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
<None Include="Package Creation\PreparationClient.zip" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="Properties\app.manifest" />
|
||||||
|
<None Include="Start.bat">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="PsExec.exe">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Icon.ico" />
|
||||||
|
<None Include="Package Creation\7z.dll" />
|
||||||
|
<None Include="Package Creation\7z.exe" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<UserProperties BuildVersion_BuildAction="ReBuild" BuildVersion_StartDate="2001/1/1" BuildVersion_DetectChanges="False" BuildVersion_UseGlobalSettings="True" />
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
|
<PropertyGroup>
|
||||||
|
<PostBuildEvent>DEL "$(ProjectDir)Package Creation\PreparationClient.zip"
|
||||||
|
"$(ProjectDir)Package Creation\7z.exe" a -tzip "$(ProjectDir)Package Creation\PreparationClient.zip" "$(TargetDir)*.*" -x!*.tmp -x!*.vshost.* -x!Newtonsoft.Json.xml -x!*.pdb
|
||||||
|
COPY "$(ProjectDir)Package Creation\PreparationClient.zip" "$(ProjectDir)..\Disco.Web\ClientBin"</PostBuildEvent>
|
||||||
|
</PropertyGroup>
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Client.Extensions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Disco.Client
|
||||||
|
{
|
||||||
|
public static class ErrorReporting
|
||||||
|
{
|
||||||
|
private const string ServicePathTemplate = "http://DISCO:9292/Services/Client/ClientError";
|
||||||
|
public static string DeviceIdentifier { get; set; }
|
||||||
|
public static string EnrolmentSessionId { get; set; }
|
||||||
|
|
||||||
|
public static void ReportError(Exception Ex, bool ReportToServer)
|
||||||
|
{
|
||||||
|
bool isClientServiceException = Ex is ClientServiceException;
|
||||||
|
|
||||||
|
ErrorReport report = new ErrorReport()
|
||||||
|
{
|
||||||
|
DeviceIdentifier = DeviceIdentifier,
|
||||||
|
SessionId = EnrolmentSessionId,
|
||||||
|
JsonException = Ex.IntenseExceptionSerialization()
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogToFile(report);
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogToEventLog(report);
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
|
||||||
|
// Don't log server errors back to the server
|
||||||
|
if (!isClientServiceException && ReportToServer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogToServer(report);
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Presentation.WriteFatalError(Ex);
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Log Actions
|
||||||
|
|
||||||
|
private static void LogToFile(ErrorReport report)
|
||||||
|
{
|
||||||
|
var logPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ErrorLog.csv");
|
||||||
|
|
||||||
|
using (var streamWriter = File.AppendText(logPath))
|
||||||
|
{
|
||||||
|
streamWriter.Write(DateTime.Now.ToString("s"));
|
||||||
|
streamWriter.Write(",");
|
||||||
|
streamWriter.Write(report.DeviceIdentifier);
|
||||||
|
streamWriter.Write(",\"");
|
||||||
|
streamWriter.Write(report.JsonException);
|
||||||
|
streamWriter.Write("\"");
|
||||||
|
streamWriter.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void LogToEventLog(ErrorReport report)
|
||||||
|
{
|
||||||
|
string eventSource = "Disco Client";
|
||||||
|
|
||||||
|
if (!EventLog.SourceExists(eventSource))
|
||||||
|
EventLog.CreateEventSource(eventSource, "Application");
|
||||||
|
|
||||||
|
EventLog.WriteEntry(eventSource, report.JsonException, EventLogEntryType.Error, 400);
|
||||||
|
}
|
||||||
|
private static void LogToServer(ErrorReport report)
|
||||||
|
{
|
||||||
|
string reportJson = JsonConvert.SerializeObject(report);
|
||||||
|
string reportResponse;
|
||||||
|
|
||||||
|
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(ServicePathTemplate);
|
||||||
|
request.UserAgent = string.Format("Disco-Client/{0}", Assembly.GetExecutingAssembly().GetName().Version.ToString(3));
|
||||||
|
request.ContentType = "application/json";
|
||||||
|
request.Method = WebRequestMethods.Http.Post;
|
||||||
|
request.UseDefaultCredentials = true;
|
||||||
|
request.Timeout = 300000; // 5 Minutes
|
||||||
|
|
||||||
|
using (StreamWriter requestWriter = new StreamWriter(request.GetRequestStream()))
|
||||||
|
{
|
||||||
|
requestWriter.Write(reportJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||||
|
{
|
||||||
|
using (StreamReader responseReader = new StreamReader(response.GetResponseStream()))
|
||||||
|
{
|
||||||
|
reportResponse = responseReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine("Error Report Logged to Server; Response: {0}", reportResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public class ErrorReport
|
||||||
|
{
|
||||||
|
public string SessionId { get; set; }
|
||||||
|
public string DeviceIdentifier { get; set; }
|
||||||
|
public string JsonException { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ex = e.ExceptionObject as Exception;
|
||||||
|
if (ex != null)
|
||||||
|
{
|
||||||
|
ReportError(ex, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Igore failure to Log Errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Exception Serialization
|
||||||
|
private static string IntenseExceptionSerialization(this Exception Ex)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(Ex);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encapsulatedEx = Ex.ToEncapsulatedException();
|
||||||
|
return JsonConvert.SerializeObject(encapsulatedEx);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encapsulatedEx = Ex.ToEncapsulatedException(0);
|
||||||
|
return JsonConvert.SerializeObject(encapsulatedEx);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(Ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EncapsulatedException ToEncapsulatedException(this Exception ex, int InnerRecursionDepth = 4)
|
||||||
|
{
|
||||||
|
EncapsulatedException inner = null;
|
||||||
|
if (InnerRecursionDepth > 0 && ex.InnerException != null)
|
||||||
|
inner = ex.InnerException.ToEncapsulatedException(--InnerRecursionDepth);
|
||||||
|
|
||||||
|
return new EncapsulatedException()
|
||||||
|
{
|
||||||
|
EncapsulatedType = ex.GetType().Name,
|
||||||
|
Message = ex.Message,
|
||||||
|
StackTrace = ex.StackTrace,
|
||||||
|
InnerException = inner
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public class EncapsulatedException
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
public string EncapsulatedType { get; set; }
|
||||||
|
public string StackTrace { get; set; }
|
||||||
|
public EncapsulatedException InnerException { get; set; }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Disco.Client.Extensions
|
||||||
|
{
|
||||||
|
public class ClientServiceException : Exception
|
||||||
|
{
|
||||||
|
public string ServiceFeature { get; private set; }
|
||||||
|
|
||||||
|
public ClientServiceException(string ServiceFeature, string ServerMessage) : base(ServerMessage)
|
||||||
|
{
|
||||||
|
this.ServiceFeature = ServiceFeature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Disco.Models.ClientServices;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Disco.Client.Extensions
|
||||||
|
{
|
||||||
|
public static class ClientServicesExtensions
|
||||||
|
{
|
||||||
|
public const string ServicePathAuthenticatedTemplate = "http://DISCO:9292/Services/Client/Authenticated/{0}";
|
||||||
|
public const string ServicePathUnauthenticatedTemplate = "http://DISCO:9292/Services/Client/Unauthenticated/{0}";
|
||||||
|
|
||||||
|
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> Service, bool Authenticated)
|
||||||
|
{
|
||||||
|
string jsonResponse;
|
||||||
|
string serviceUrl;
|
||||||
|
if (Authenticated)
|
||||||
|
serviceUrl = string.Format(ServicePathAuthenticatedTemplate, Service.Feature);
|
||||||
|
else
|
||||||
|
serviceUrl = string.Format(ServicePathUnauthenticatedTemplate, Service.Feature);
|
||||||
|
|
||||||
|
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceUrl);
|
||||||
|
request.UserAgent = string.Format("Disco-Client/{0}", Assembly.GetExecutingAssembly().GetName().Version.ToString(3));
|
||||||
|
request.ContentType = "application/json";
|
||||||
|
request.Method = WebRequestMethods.Http.Post;
|
||||||
|
request.UseDefaultCredentials = true;
|
||||||
|
request.Timeout = 300000; // 5 Minutes
|
||||||
|
string jsonRequest = JsonConvert.SerializeObject(Service);
|
||||||
|
|
||||||
|
using (StreamWriter requestWriter = new StreamWriter(request.GetRequestStream()))
|
||||||
|
{
|
||||||
|
requestWriter.Write(jsonRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||||
|
{
|
||||||
|
using (StreamReader responseReader = new StreamReader(response.GetResponseStream()))
|
||||||
|
{
|
||||||
|
jsonResponse = responseReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(jsonResponse))
|
||||||
|
return default(ResponseType);
|
||||||
|
else
|
||||||
|
return JsonConvert.DeserializeObject<ResponseType>(jsonResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Disco.Models.ClientServices;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Disco.Client.Extensions
|
||||||
|
{
|
||||||
|
public static class EnrolExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void Build(this Enrol enrol)
|
||||||
|
{
|
||||||
|
enrol.DeviceUUID = Interop.SystemAudit.DeviceUUID;
|
||||||
|
enrol.DeviceSerialNumber = Interop.SystemAudit.DeviceSerialNumber;
|
||||||
|
|
||||||
|
enrol.DeviceComputerName = Interop.LocalAuthentication.ComputerName;
|
||||||
|
|
||||||
|
enrol.DeviceManufacturer = Interop.SystemAudit.DeviceManufacturer;
|
||||||
|
enrol.DeviceModel = Interop.SystemAudit.DeviceModel;
|
||||||
|
enrol.DeviceModelType = Interop.SystemAudit.DeviceType;
|
||||||
|
|
||||||
|
enrol.DeviceIsPartOfDomain = Interop.SystemAudit.DeviceIsPartOfDomain;
|
||||||
|
|
||||||
|
// LAN
|
||||||
|
enrol.DeviceLanMacAddress = Interop.Network.PrimaryLanMacAddress;
|
||||||
|
|
||||||
|
// WAN
|
||||||
|
enrol.DeviceWlanMacAddress = Interop.Network.PrimaryWlanMacAddress;
|
||||||
|
|
||||||
|
// Certificates
|
||||||
|
enrol.DeviceCertificates = Interop.Certificates.GetCertificateSubjects(StoreName.My, StoreLocation.LocalMachine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Process(this EnrolResponse enrolResponse)
|
||||||
|
{
|
||||||
|
if (enrolResponse == null)
|
||||||
|
throw new ClientServiceException("Enrolment", "Server denied enrolment (Empty Response)");
|
||||||
|
|
||||||
|
ErrorReporting.EnrolmentSessionId = enrolResponse.SessionId;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(enrolResponse.ErrorMessage))
|
||||||
|
throw new ClientServiceException("Enrolment", enrolResponse.ErrorMessage);
|
||||||
|
|
||||||
|
// Offline Domain Join
|
||||||
|
bool requireReboot = enrolResponse.ApplyOfflineDomainJoin();
|
||||||
|
|
||||||
|
// Certificates
|
||||||
|
enrolResponse.ApplyDeviceCertificates();
|
||||||
|
|
||||||
|
// Device Owner
|
||||||
|
enrolResponse.ApplyDeviceAssignedUser();
|
||||||
|
|
||||||
|
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", "Device Enrolment Successfully Completed", false, 0, 1500);
|
||||||
|
|
||||||
|
Program.RebootRequired = requireReboot;
|
||||||
|
Program.AllowUninstall = enrolResponse.AllowBootstrapperUninstall;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes a Client Service Enrol Response for Offline Domain Join Actions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enrolResponse"></param>
|
||||||
|
/// <returns>Boolean indicating whether a reboot is required.</returns>
|
||||||
|
private static bool ApplyOfflineDomainJoin(this EnrolResponse enrolResponse)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(enrolResponse.OfflineDomainJoin))
|
||||||
|
{
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", string.Format("Performing Offline Domain Join:{0}Renaming Computer: {1} -> {2}", Environment.NewLine, Interop.LocalAuthentication.ComputerName, enrolResponse.DeviceComputerName), true, -1, 1500);
|
||||||
|
|
||||||
|
string odjFile = Path.GetTempFileName();
|
||||||
|
File.WriteAllBytes(odjFile, Convert.FromBase64String(enrolResponse.OfflineDomainJoin));
|
||||||
|
|
||||||
|
string odjWindowsPath = Environment.GetEnvironmentVariable("SystemRoot");
|
||||||
|
string odjProcessArguments = string.Format("/REQUESTODJ /LOADFILE \"{0}\" /WINDOWSPATH \"{1}\" /LOCALOS", odjFile, odjWindowsPath);
|
||||||
|
|
||||||
|
ProcessStartInfo odjProcessStartInfo = new ProcessStartInfo("DJOIN.EXE", odjProcessArguments)
|
||||||
|
{
|
||||||
|
CreateNoWindow = true,
|
||||||
|
ErrorDialog = false,
|
||||||
|
LoadUserProfile = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false
|
||||||
|
};
|
||||||
|
string odjResult;
|
||||||
|
using (Process odjProcess = System.Diagnostics.Process.Start(odjProcessStartInfo))
|
||||||
|
{
|
||||||
|
odjResult = odjProcess.StandardOutput.ReadToEnd();
|
||||||
|
odjProcess.WaitForExit(20000); // 20 Seconds
|
||||||
|
}
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", string.Format("Offline Domain Join Result:{0}{1}", Environment.NewLine, odjResult), true, -1, 3000);
|
||||||
|
|
||||||
|
if (File.Exists(odjFile))
|
||||||
|
File.Delete(odjFile);
|
||||||
|
|
||||||
|
// Flush Logged-On History
|
||||||
|
if (!string.IsNullOrEmpty(enrolResponse.DeviceDomainName))
|
||||||
|
{
|
||||||
|
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true)){
|
||||||
|
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DeviceDomainName, RegistryValueKind.String);
|
||||||
|
regWinlogon.SetValue("DefaultUserName", String.Empty, RegistryValueKind.String);
|
||||||
|
}
|
||||||
|
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
|
||||||
|
{
|
||||||
|
regLogonUI.DeleteValue("LastLoggedOnUser", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Reboot required
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No Domain Join
|
||||||
|
return false; // Reboot not required
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes a Client Service Enrol Response for Device Assigned User Actions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enrolResponse"></param>
|
||||||
|
private static void ApplyDeviceAssignedUser(this EnrolResponse enrolResponse)
|
||||||
|
{
|
||||||
|
// Only run task if Assigned User was specified
|
||||||
|
if (!string.IsNullOrWhiteSpace(enrolResponse.DeviceAssignedUserSID))
|
||||||
|
{
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", string.Format(@"Configuring permissions for the device owner:{0}{1} ({2}\{3})", Environment.NewLine, enrolResponse.DeviceAssignedUserName, enrolResponse.DeviceAssignedUserDomain, enrolResponse.DeviceAssignedUserUsername), true, -1, 3000);
|
||||||
|
|
||||||
|
Interop.LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.DeviceAssignedUserSID, enrolResponse.DeviceAssignedUserUsername, enrolResponse.DeviceAssignedUserDomain);
|
||||||
|
|
||||||
|
// Make Windows think this user was the last to logon
|
||||||
|
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||||
|
{
|
||||||
|
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DeviceAssignedUserDomain, RegistryValueKind.String);
|
||||||
|
regWinlogon.SetValue("DefaultUserName", enrolResponse.DeviceAssignedUserUsername, RegistryValueKind.String);
|
||||||
|
}
|
||||||
|
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
|
||||||
|
{
|
||||||
|
regLogonUI.SetValue("LastLoggedOnUser", string.Format(@"{0}\{1}", enrolResponse.DeviceAssignedUserDomain, enrolResponse.DeviceAssignedUserUsername), RegistryValueKind.String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes a Client Service Enrol Response for Device Certificate Actions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enrolResponse"></param>
|
||||||
|
private static void ApplyDeviceCertificates(this EnrolResponse enrolResponse)
|
||||||
|
{
|
||||||
|
// Only run if a Certificate was supplied
|
||||||
|
if (!string.IsNullOrEmpty(enrolResponse.DeviceCertificate))
|
||||||
|
{
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", "Configuring Wireless Certificates", true, -1, 1000);
|
||||||
|
|
||||||
|
var certPersonalBytes = Convert.FromBase64String(enrolResponse.DeviceCertificate);
|
||||||
|
var certPersonal = new X509Certificate2(certPersonalBytes, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||||
|
if (certPersonal == null)
|
||||||
|
throw new ClientServiceException("Enrolment > Device Certificate", "Unable to Import Device Certificate Provided, possibly check password.");
|
||||||
|
|
||||||
|
// Certificate Removal
|
||||||
|
if (enrolResponse.DeviceCertificateRemoveExisting != null && enrolResponse.DeviceCertificateRemoveExisting.Count > 0)
|
||||||
|
{
|
||||||
|
List<Regex> regExMatchesSubject = new List<Regex>();
|
||||||
|
foreach (var subjectRegEx in enrolResponse.DeviceCertificateRemoveExisting)
|
||||||
|
regExMatchesSubject.Add(new Regex(subjectRegEx, RegexOptions.IgnoreCase));
|
||||||
|
|
||||||
|
// Remove from 'My' Store
|
||||||
|
Interop.Certificates.RemoveCertificates(StoreName.My, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
|
||||||
|
// Remove from 'Root' Store
|
||||||
|
Interop.Certificates.RemoveCertificates(StoreName.Root, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
|
||||||
|
// Remove from 'CertificateAuthority' Store
|
||||||
|
Interop.Certificates.RemoveCertificates(StoreName.CertificateAuthority, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Certificate
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", string.Format("Configuring Wireless Certificates{0}Installing Certificate: {1}", Environment.NewLine, Interop.Certificates.GetCertificateFriendlyName(certPersonal)), true, -1);
|
||||||
|
Interop.Certificates.AddCertificate(StoreName.My, StoreLocation.LocalMachine, certPersonal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Disco.Models.ClientServices;
|
||||||
|
|
||||||
|
namespace Disco.Client.Extensions
|
||||||
|
{
|
||||||
|
public static class WhoAmIExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void Process(this WhoAmIResponse whoAmIResponse)
|
||||||
|
{
|
||||||
|
Program.IsAuthenticated = true;
|
||||||
|
whoAmIResponse.PresentResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PresentResponse(this WhoAmIResponse whoAmIResponse)
|
||||||
|
{
|
||||||
|
StringBuilder message = new StringBuilder();
|
||||||
|
message.AppendLine("Authenticated Connection:");
|
||||||
|
message.Append("Username: ").AppendLine(whoAmIResponse.Username);
|
||||||
|
message.Append("Name: ").Append(whoAmIResponse.DisplayName);
|
||||||
|
message.Append(" (").Append(whoAmIResponse.Type).AppendLine(")");
|
||||||
|
Presentation.UpdateStatus("Connection Established to Preparation Server", message.ToString(), false, 0, 1500);
|
||||||
|
}
|
||||||
|
public static void UnauthenticatedResponse()
|
||||||
|
{
|
||||||
|
Program.IsAuthenticated = false;
|
||||||
|
Presentation.UpdateStatus("Connection Established to Preparation Server", "Unauthenticated connection to the server...", false, 0, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 68 KiB |
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Disco.Client.Interop
|
||||||
|
{
|
||||||
|
public static class Certificates
|
||||||
|
{
|
||||||
|
|
||||||
|
public static string GetCertificateFriendlyName(X509Certificate2 Certificate)
|
||||||
|
{
|
||||||
|
string subject = Certificate.Subject;
|
||||||
|
return subject.Substring(subject.IndexOf("=") + 1, subject.IndexOf(",") - subject.IndexOf("=") - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<string> GetCertificateSubjects(StoreName StoreName, StoreLocation StoreLocation)
|
||||||
|
{
|
||||||
|
X509Store certStore = new X509Store(StoreName, StoreLocation);
|
||||||
|
certStore.Open(OpenFlags.ReadOnly);
|
||||||
|
var certSubjects = certStore.Certificates.Cast<X509Certificate2>().Select(c => c.Subject).ToList();
|
||||||
|
certStore.Close();
|
||||||
|
return certSubjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AddCertificate(StoreName StoreName, StoreLocation StoreLocation, X509Certificate2 Certificate)
|
||||||
|
{
|
||||||
|
X509Store certStore = new X509Store(StoreName, StoreLocation);
|
||||||
|
bool certAlreadyAdded = false;
|
||||||
|
|
||||||
|
certStore.Open(OpenFlags.ReadWrite);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (X509Certificate2 cert in certStore.Certificates)
|
||||||
|
{
|
||||||
|
if (cert.SerialNumber.Equals(Certificate.SerialNumber))
|
||||||
|
{
|
||||||
|
certAlreadyAdded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!certAlreadyAdded)
|
||||||
|
{
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", string.Format("Configuring Wireless Certificates{0}Adding Certificate: '{1}' from {2}@{3}", Environment.NewLine, GetCertificateFriendlyName(Certificate), StoreName.ToString(), StoreLocation.ToString()), true, -1, 3000);
|
||||||
|
certStore.Add(Certificate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception) { throw; }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
certStore.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !certAlreadyAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<string> RemoveCertificates(StoreName StoreName, StoreLocation StoreLocation, List<Regex> RegExMatchesSubject, X509Certificate2 CertificateException)
|
||||||
|
{
|
||||||
|
X509Store certStore = new X509Store(StoreName, StoreLocation);
|
||||||
|
List<string> results = new List<string>();
|
||||||
|
List<X509Certificate2> certStoreRemove = new List<X509Certificate2>();
|
||||||
|
|
||||||
|
certStore.Open(OpenFlags.ReadWrite);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (X509Certificate2 cert in certStore.Certificates)
|
||||||
|
{
|
||||||
|
if (!cert.SerialNumber.Equals(CertificateException.SerialNumber))
|
||||||
|
{
|
||||||
|
foreach (var subjectRegEx in RegExMatchesSubject)
|
||||||
|
{
|
||||||
|
if (subjectRegEx.IsMatch(cert.Subject))
|
||||||
|
{
|
||||||
|
certStoreRemove.Add(cert);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var cert in certStoreRemove)
|
||||||
|
{
|
||||||
|
results.Add(cert.Subject);
|
||||||
|
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", string.Format("Configuring Wireless Certificates{0}Removing Certificate: '{1}' from {2}@{3}", Environment.NewLine, GetCertificateFriendlyName(cert), StoreName.ToString(), StoreLocation.ToString()), true, -1, 1500);
|
||||||
|
certStore.Remove(cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception) { throw; }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
certStore.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.DirectoryServices;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Disco.Client.Interop
|
||||||
|
{
|
||||||
|
public static class LocalAuthentication
|
||||||
|
{
|
||||||
|
|
||||||
|
public static bool AddLocalGroupMembership(string GroupName, string UserSID, string Username, string UserDomain)
|
||||||
|
{
|
||||||
|
|
||||||
|
using (DirectoryEntry group = new DirectoryEntry(string.Format("WinNT://./{0},group", GroupName)))
|
||||||
|
{
|
||||||
|
// Check to see if the User is already a member
|
||||||
|
foreach (object memberRef in (IEnumerable)group.Invoke("Members"))
|
||||||
|
{
|
||||||
|
using (DirectoryEntry member = new DirectoryEntry(memberRef))
|
||||||
|
{
|
||||||
|
var memberPath = member.Path;
|
||||||
|
if (memberPath.Equals(string.Format("WinNT://{0}/{1}", UserDomain, Username), StringComparison.InvariantCultureIgnoreCase) ||
|
||||||
|
memberPath.Equals(string.Format("WinNT://{0}", UserSID), StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.Invoke("Add", string.Format("WinNT://{0}", UserSID));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CurrentUserDomain
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Environment.UserDomainName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string CurrentUserName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Environment.UserName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ComputerName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Environment.MachineName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Disco.Client.Interop
|
||||||
|
{
|
||||||
|
public static class Network
|
||||||
|
{
|
||||||
|
private static List<NetworkAdapterInfo> NetworkAdapters { get; set; }
|
||||||
|
private static NetworkAdapterInfo PrimaryLanNetworkAdapter { get; set; }
|
||||||
|
private static NetworkAdapterInfo PrimaryWlanNetworkAdapter { get; set; }
|
||||||
|
|
||||||
|
static Network()
|
||||||
|
{
|
||||||
|
// Get All Adapters
|
||||||
|
RetrieveLanAdapters();
|
||||||
|
|
||||||
|
if (NetworkAdapters.Count > 0)
|
||||||
|
{
|
||||||
|
// Only Retrieve Wlan Adapters if at least one adapter was found by WMI
|
||||||
|
RetrieveWlanAdapters();
|
||||||
|
|
||||||
|
// Determine Primary Adapters
|
||||||
|
|
||||||
|
// Lan
|
||||||
|
PrimaryLanNetworkAdapter = NetworkAdapters.Where(n => !n.IsWLanAdapter && n.NetConnectionId.StartsWith("Local Area Connection", StringComparison.InvariantCultureIgnoreCase)).OrderByDescending(n => n.Speed).FirstOrDefault();
|
||||||
|
// Might be too restrictive - remove name restriction just in case.
|
||||||
|
if (PrimaryLanNetworkAdapter == null)
|
||||||
|
PrimaryLanNetworkAdapter = NetworkAdapters.Where(n => !n.IsWLanAdapter).OrderByDescending(n => n.Speed).FirstOrDefault();
|
||||||
|
|
||||||
|
// Wan
|
||||||
|
PrimaryWlanNetworkAdapter = NetworkAdapters.Where(n => n.IsWLanAdapter).OrderByDescending(n => n.Speed).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RetrieveLanAdapters()
|
||||||
|
{
|
||||||
|
// Get NetworkAdapter Information
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT Index, GUID, MACAddress, Name, NetConnectionID, Speed FROM Win32_NetworkAdapter WHERE PhysicalAdapter=true AND MACAddress IS NOT NULL AND Name IS NOT NULL AND NetConnectionID IS NOT NULL AND Speed IS NOT NULL"))
|
||||||
|
{
|
||||||
|
using (ManagementObjectCollection mResults = mSearcher.Get())
|
||||||
|
{
|
||||||
|
NetworkAdapters = new List<NetworkAdapterInfo>();
|
||||||
|
foreach (var mResult in mResults.Cast<ManagementObject>())
|
||||||
|
{
|
||||||
|
NetworkAdapterInfo nic = new NetworkAdapterInfo()
|
||||||
|
{
|
||||||
|
Index = (UInt32)mResult.GetPropertyValue("Index"),
|
||||||
|
Guid = Guid.Parse((string)mResult.GetPropertyValue("GUID")),
|
||||||
|
MacAddress = mResult.GetPropertyValue("MACAddress").ToString(),
|
||||||
|
Name = mResult.GetPropertyValue("Name").ToString(),
|
||||||
|
NetConnectionId = mResult.GetPropertyValue("NetConnectionID").ToString(),
|
||||||
|
Speed = Convert.ToUInt64(mResult.GetPropertyValue("Speed")),
|
||||||
|
IsWLanAdapter = false
|
||||||
|
};
|
||||||
|
NetworkAdapters.Add(nic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("Disco Client was unable to retrieve NetworkAdapter information from WMI", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RetrieveWlanAdapters()
|
||||||
|
{
|
||||||
|
WLAN_INTERFACE_INFO_LIST wlanApiInterfaceList;
|
||||||
|
|
||||||
|
IntPtr wlanApiHandle = IntPtr.Zero;
|
||||||
|
uint wlanApiServiceVersion = 0;
|
||||||
|
|
||||||
|
if (WlanOpenHandle(WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanApiServiceVersion, ref wlanApiHandle) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
IntPtr wlanApiInterfaceListPointer = IntPtr.Zero;
|
||||||
|
|
||||||
|
if (WlanEnumInterfaces(wlanApiHandle, IntPtr.Zero, ref wlanApiInterfaceListPointer) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
wlanApiInterfaceList = new WLAN_INTERFACE_INFO_LIST(wlanApiInterfaceListPointer);
|
||||||
|
WlanFreeMemory(wlanApiInterfaceListPointer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error - No Wlan Adapters Reported
|
||||||
|
WlanCloseHandle(wlanApiHandle, IntPtr.Zero);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WlanCloseHandle(wlanApiHandle, IntPtr.Zero);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error - No Wlan Adapters Reported
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlanApiInterfaceList.InterfaceInfo != null)
|
||||||
|
{
|
||||||
|
foreach (var wlanApiAdapter in wlanApiInterfaceList.InterfaceInfo)
|
||||||
|
{
|
||||||
|
var wlanApiAdapterInfo = wlanApiAdapter;
|
||||||
|
var wmiAdapterInfo = NetworkAdapters.FirstOrDefault(n => n.Guid == wlanApiAdapterInfo.InterfaceGuid);
|
||||||
|
if (wmiAdapterInfo != null)
|
||||||
|
{
|
||||||
|
wmiAdapterInfo.IsWLanAdapter = true;
|
||||||
|
wmiAdapterInfo.WlanState = wlanApiAdapterInfo.isState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PrimaryLanMacAddress
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Return null if no Primary LAN Network Adapter found on this Device
|
||||||
|
|
||||||
|
return (PrimaryLanNetworkAdapter == null) ? null : PrimaryLanNetworkAdapter.MacAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string PrimaryWlanMacAddress
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Return null if no Primary WLAN Network Adapter found on this Device
|
||||||
|
|
||||||
|
return (PrimaryWlanNetworkAdapter == null) ? null : PrimaryWlanNetworkAdapter.MacAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NetworkAdapterInfo
|
||||||
|
{
|
||||||
|
public UInt32 Index { get; set; }
|
||||||
|
public Guid Guid { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string NetConnectionId { get; set; }
|
||||||
|
public string MacAddress { get; set; }
|
||||||
|
public UInt64 Speed { get; set; }
|
||||||
|
|
||||||
|
public bool IsWLanAdapter { get; set; }
|
||||||
|
public WLAN_INTERFACE_STATE WlanState { get; set; }
|
||||||
|
|
||||||
|
public string WlanStateDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (WlanState)
|
||||||
|
{
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_not_ready:
|
||||||
|
return "Not Ready";
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_connected:
|
||||||
|
return "Connected";
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_ad_hoc_network_formed:
|
||||||
|
return "Ad Hoc Network Formed";
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_disconnecting:
|
||||||
|
return "Disconnecting";
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_disconnected:
|
||||||
|
return "Disconnected";
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_associating:
|
||||||
|
return "Associating";
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_discovering:
|
||||||
|
return "Discovering";
|
||||||
|
case WLAN_INTERFACE_STATE.wlan_interface_state_authenticating:
|
||||||
|
return "Authenticating";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Wlan Win32 Interop
|
||||||
|
|
||||||
|
private const uint WLAN_API_VERSION_2_0 = 2; // Windows Vista WiFi API Version
|
||||||
|
private const int ERROR_SUCCESS = 0;
|
||||||
|
|
||||||
|
/// <summary >
|
||||||
|
/// Opens a connection to the server
|
||||||
|
/// </summary >
|
||||||
|
[DllImport("Wlanapi.dll")]
|
||||||
|
private static extern int WlanOpenHandle(
|
||||||
|
uint dwClientVersion,
|
||||||
|
IntPtr pReserved, //not in MSDN but required
|
||||||
|
[Out] out uint pdwNegotiatedVersion,
|
||||||
|
ref IntPtr ClientHandle);
|
||||||
|
|
||||||
|
/// <summary >
|
||||||
|
/// Closes a connection to the server
|
||||||
|
/// </summary >
|
||||||
|
[DllImport("Wlanapi", EntryPoint = "WlanCloseHandle")]
|
||||||
|
private static extern uint WlanCloseHandle(
|
||||||
|
[In] IntPtr hClientHandle,
|
||||||
|
IntPtr pReserved);
|
||||||
|
|
||||||
|
/// <summary >
|
||||||
|
/// Enumerates all wireless interfaces in the laptop
|
||||||
|
/// </summary >
|
||||||
|
[DllImport("Wlanapi", EntryPoint = "WlanEnumInterfaces")]
|
||||||
|
private static extern uint WlanEnumInterfaces(
|
||||||
|
[In] IntPtr hClientHandle,
|
||||||
|
IntPtr pReserved,
|
||||||
|
ref IntPtr ppInterfaceList);
|
||||||
|
|
||||||
|
/// <summary >
|
||||||
|
/// Frees memory returned by native WiFi functions
|
||||||
|
/// </summary >
|
||||||
|
[DllImport("Wlanapi", EntryPoint = "WlanFreeMemory")]
|
||||||
|
private static extern void WlanFreeMemory([In] IntPtr pMemory);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the state of the interface. e.g. connected, disconnected.
|
||||||
|
/// </summary>
|
||||||
|
private enum WLAN_INTERFACE_STATE
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_not_ready -> 0
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_not_ready = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_connected -> 1
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_connected = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_ad_hoc_network_formed -> 2
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_ad_hoc_network_formed = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_disconnecting -> 3
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_disconnecting = 3,
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_disconnected -> 4
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_disconnected = 4,
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_associating -> 5
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_associating = 5,
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_discovering -> 6
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_discovering = 6,
|
||||||
|
/// <summary>
|
||||||
|
/// wlan_interface_state_authenticating -> 7
|
||||||
|
/// </summary>
|
||||||
|
wlan_interface_state_authenticating = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary >
|
||||||
|
/// Stores interface info
|
||||||
|
/// </summary >
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
private struct WLAN_INTERFACE_INFO
|
||||||
|
{
|
||||||
|
/// GUID->_GUID
|
||||||
|
public Guid InterfaceGuid;
|
||||||
|
|
||||||
|
/// WCHAR[256]
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
||||||
|
public string strInterfaceDescription;
|
||||||
|
|
||||||
|
/// WLAN_INTERFACE_STATE->_WLAN_INTERFACE_STATE
|
||||||
|
public WLAN_INTERFACE_STATE isState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains an array of NIC information
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct WLAN_INTERFACE_INFO_LIST
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Length of <see cref="InterfaceInfo"/> array
|
||||||
|
/// </summary>
|
||||||
|
public Int32 dwNumberOfItems;
|
||||||
|
/// <summary>
|
||||||
|
/// This member is not used by the wireless service. Applications can use this member when processing individual interfaces.
|
||||||
|
/// </summary>
|
||||||
|
public Int32 dwIndex;
|
||||||
|
/// <summary>
|
||||||
|
/// Array of WLAN interfaces.
|
||||||
|
/// </summary>
|
||||||
|
public WLAN_INTERFACE_INFO[] InterfaceInfo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for WLAN_INTERFACE_INFO_LIST.
|
||||||
|
/// Constructor is needed because the InterfaceInfo member varies based on how many adapters are in the system.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pList">the unmanaged pointer containing the list.</param>
|
||||||
|
public WLAN_INTERFACE_INFO_LIST(IntPtr pList)
|
||||||
|
{
|
||||||
|
// The first 4 bytes are the number of WLAN_INTERFACE_INFO structures.
|
||||||
|
dwNumberOfItems = Marshal.ReadInt32(pList, 0);
|
||||||
|
|
||||||
|
// The next 4 bytes are the index of the current item in the unmanaged API.
|
||||||
|
dwIndex = Marshal.ReadInt32(pList, 4);
|
||||||
|
|
||||||
|
// Construct the array of WLAN_INTERFACE_INFO structures.
|
||||||
|
InterfaceInfo = new WLAN_INTERFACE_INFO[dwNumberOfItems];
|
||||||
|
|
||||||
|
for (int i = 0; i <= dwNumberOfItems - 1; i++)
|
||||||
|
{
|
||||||
|
// The offset of the array of structures is 8 bytes past the beginning.
|
||||||
|
// Then, take the index and multiply it by the number of bytes in the
|
||||||
|
// structure.
|
||||||
|
// The length of the WLAN_INTERFACE_INFO structure is 532 bytes - this
|
||||||
|
// was determined by doing a Marshall.SizeOf(WLAN_INTERFACE_INFO)
|
||||||
|
IntPtr pItemList = new IntPtr(pList.ToInt64() + (i * 532) + 8);
|
||||||
|
|
||||||
|
// Construct the WLAN_INTERFACE_INFO structure, marshal the unmanaged
|
||||||
|
// structure into it, then copy it to the array of structures.
|
||||||
|
InterfaceInfo[i] = (WLAN_INTERFACE_INFO)Marshal.PtrToStructure(pItemList, typeof(WLAN_INTERFACE_INFO));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Disco.Client.Interop
|
||||||
|
{
|
||||||
|
public static class SystemAudit
|
||||||
|
{
|
||||||
|
|
||||||
|
public static string DeviceSerialNumber { get; private set; }
|
||||||
|
public static string DeviceSMBIOSVersion { get; private set; }
|
||||||
|
public static string DeviceManufacturer { get; private set; }
|
||||||
|
public static string DeviceModel { get; private set; }
|
||||||
|
public static string DeviceType { get; private set; }
|
||||||
|
public static string DeviceUUID { get; private set; }
|
||||||
|
public static bool DeviceIsPartOfDomain { get; private set; }
|
||||||
|
|
||||||
|
static SystemAudit()
|
||||||
|
{
|
||||||
|
// Get BIOS Information
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT SerialNumber, SMBIOSBIOSVersion FROM Win32_BIOS WHERE PrimaryBios=true"))
|
||||||
|
{
|
||||||
|
using (ManagementObjectCollection mResults = mSearcher.Get())
|
||||||
|
{
|
||||||
|
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
|
||||||
|
{
|
||||||
|
if (mItem != null)
|
||||||
|
{
|
||||||
|
DeviceSerialNumber = mItem.GetPropertyValue("SerialNumber").ToString().Trim();
|
||||||
|
ErrorReporting.DeviceIdentifier = DeviceSerialNumber;
|
||||||
|
DeviceSMBIOSVersion = mItem.GetPropertyValue("SMBIOSBIOSVersion").ToString().Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("No Win32_BIOS WHERE PrimaryBios=true was found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("Disco Client was unable to retrieve BIOS information from WMI", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get System Information
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT Manufacturer, Model, PartOfDomain, PCSystemType FROM Win32_ComputerSystem"))
|
||||||
|
{
|
||||||
|
using (ManagementObjectCollection mResults = mSearcher.Get())
|
||||||
|
{
|
||||||
|
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
|
||||||
|
{
|
||||||
|
if (mItem != null)
|
||||||
|
{
|
||||||
|
DeviceManufacturer = mItem.GetPropertyValue("Manufacturer").ToString().Trim();
|
||||||
|
DeviceModel = mItem.GetPropertyValue("Model").ToString().Trim();
|
||||||
|
DeviceIsPartOfDomain = bool.Parse(mItem.GetPropertyValue("PartOfDomain").ToString());
|
||||||
|
DeviceType = PCSystemTypeToString((UInt16)mItem.GetPropertyValue("PCSystemType"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("No Win32_ComputerSystem was found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("Disco Client was unable to retrieve ComputerSystem information from WMI", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get System Product Information
|
||||||
|
string ComputerSystemProductSerialNumber;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT IdentifyingNumber, UUID FROM Win32_ComputerSystemProduct"))
|
||||||
|
{
|
||||||
|
using (ManagementObjectCollection mResults = mSearcher.Get())
|
||||||
|
{
|
||||||
|
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
|
||||||
|
{
|
||||||
|
if (mItem != null)
|
||||||
|
{
|
||||||
|
ComputerSystemProductSerialNumber = mItem.GetPropertyValue("IdentifyingNumber").ToString().Trim();
|
||||||
|
DeviceUUID = mItem.GetPropertyValue("UUID").ToString().Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("No Win32_ComputerSystemProduct was found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("Disco Client was unable to retrieve ComputerSystemProduct information from WMI", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added 2012-11-22 G# - Lenovo IdeaPad Serial SHIM
|
||||||
|
// http://www.discoict.com.au/forum/feature-requests/2012/11/serial-number-detection-on-ideapads.aspx
|
||||||
|
if (string.IsNullOrWhiteSpace(DeviceSerialNumber) ||
|
||||||
|
(DeviceManufacturer.Equals("LENOVO", StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
|
(DeviceModel.Equals("S10-3", StringComparison.InvariantCultureIgnoreCase) // S10-3
|
||||||
|
|| DeviceModel.Equals("2957", StringComparison.InvariantCultureIgnoreCase)))) // S10-2
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BaseBoard"))
|
||||||
|
{
|
||||||
|
using (ManagementObjectCollection mResults = mSearcher.Get())
|
||||||
|
{
|
||||||
|
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
|
||||||
|
{
|
||||||
|
if (mItem != null)
|
||||||
|
{
|
||||||
|
DeviceSerialNumber = mItem.GetPropertyValue("SerialNumber").ToString().Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("No Win32_BaseBoard was found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("Disco Client was unable to retrieve BaseBoard information from WMI", ex);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(DeviceSerialNumber))
|
||||||
|
DeviceSerialNumber = ComputerSystemProductSerialNumber;
|
||||||
|
}
|
||||||
|
// End Added 2012-11-22 G#
|
||||||
|
|
||||||
|
ErrorReporting.DeviceIdentifier = DeviceSerialNumber;
|
||||||
|
|
||||||
|
// Validate Device 'State'
|
||||||
|
if (string.IsNullOrWhiteSpace(DeviceSerialNumber))
|
||||||
|
throw new Exception("This device has no serial number stored in BIOS or BaseBoard");
|
||||||
|
if (DeviceSerialNumber.Length > 60)
|
||||||
|
throw new Exception(string.Format("The serial number reported by this device is over 60 characters long:{0}{1}", Environment.NewLine, DeviceSerialNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string PCSystemTypeToString(UInt16 PCSystemType)
|
||||||
|
{
|
||||||
|
switch (PCSystemType)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return "Unknown";
|
||||||
|
case 1:
|
||||||
|
return "Desktop";
|
||||||
|
case 2:
|
||||||
|
return "Mobile";
|
||||||
|
case 3:
|
||||||
|
return "Workstation";
|
||||||
|
case 4:
|
||||||
|
return "EnterpriseServer";
|
||||||
|
case 5:
|
||||||
|
return "SmallOfficeAndHomeOfficeServer";
|
||||||
|
case 6:
|
||||||
|
return "AppliancePC";
|
||||||
|
case 7:
|
||||||
|
return "PerformanceServer";
|
||||||
|
case 8:
|
||||||
|
return "Maximum";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||