initial source commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+70
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+181
@@ -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")]
|
||||
+113
@@ -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>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
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>
|
||||
Reference in New Issue
Block a user