Feature #33: Enhanced Device Importing

Dynamic device importing. better input parsing and 5 additional import
fields.
This commit is contained in:
Gary Sharp
2014-05-25 16:34:06 +10:00
parent 6a45348bdb
commit e9042f7666
68 changed files with 6775 additions and 3039 deletions
-272
View File
@@ -1,272 +0,0 @@
using Disco.BI.Extensions;
using Disco.Data.Repository;
using Disco.Models.BI.Device;
using Disco.Models.Repository;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using PopulateRecordReferences = System.Tuple<System.Collections.Generic.Dictionary<int, Disco.Models.Repository.DeviceModel>, System.Collections.Generic.Dictionary<int, Disco.Models.Repository.DeviceProfile>, System.Collections.Generic.Dictionary<int, Disco.Models.Repository.DeviceBatch>>;
namespace Disco.BI.DeviceBI.Importing
{
public static class Import
{
internal const string ImportParseCacheKey = "ImportParseResults_{0}";
public static ImportDeviceSession GetSession(string ImportParseTaskId)
{
string parseKey = string.Format(ImportParseCacheKey, ImportParseTaskId);
return (ImportDeviceSession)HttpRuntime.Cache.Get(parseKey);
}
internal static bool ImportRecord(this ImportDevice device, DiscoDataContext Database, PopulateRecordReferences references)
{
// Skips If Errors
if (device.Errors == null || device.Errors.Count == 0)
{
// Re-Populate & Skip If Errors
device.PopulateRecord(Database, references);
if (device.Errors == null || device.Errors.Count == 0)
{
Device discoDevice = device.Device;
if (discoDevice == null)
{
// New Device
discoDevice = new Device()
{
SerialNumber = device.SerialNumber.ToUpper(),
CreatedDate = DateTime.Now,
AllowUnauthenticatedEnrol = true,
};
Database.Devices.Add(discoDevice);
}
if (discoDevice.DeviceModelId != device.DeviceModelId)
discoDevice.DeviceModelId = device.DeviceModelId;
if (discoDevice.DeviceProfileId != device.DeviceProfileId)
discoDevice.DeviceProfileId = device.DeviceProfileId;
if (discoDevice.DeviceBatchId != device.DeviceBatchId)
discoDevice.DeviceBatchId = device.DeviceBatchId;
if (discoDevice.Location != device.Location)
discoDevice.Location = device.Location;
if (discoDevice.AssetNumber != device.AssetNumber)
discoDevice.AssetNumber = device.AssetNumber;
if (discoDevice.AssignedUserId != device.AssignedUserId)
{
discoDevice.AssignDevice(Database, device.AssignedUser);
}
Database.SaveChanges();
return true;
}
}
return false;
}
internal static PopulateRecordReferences GetPopulateRecordReferences(DiscoDataContext Database)
{
return new PopulateRecordReferences(
Database.DeviceModels.ToDictionary(dm => dm.Id),
Database.DeviceProfiles.ToDictionary(dp => dp.Id),
Database.DeviceBatches.ToDictionary(db => db.Id)
);
}
internal static void PopulateRecord(this ImportDevice device, DiscoDataContext Database, PopulateRecordReferences references)
{
var deviceModels = references.Item1;
var deviceProfiles = references.Item2;
var deviceBatches = references.Item3;
// SERIAL NUMBER - Existing Device
if (!device.Errors.ContainsKey("SerialNumber"))
{
device.Device = Database.Devices.Find(device.SerialNumber);
if (device.Device != null && device.Device.DecommissionedDate.HasValue)
device.Errors.Add("SerialNumber", "The device is decommissioned");
}
// DEVICE MODEL
if (!device.Errors.ContainsKey("DeviceModelId"))
{
DeviceModel deviceModel;
if (!device.DeviceModelId.HasValue)
device.DeviceModelId = 1; // Default 'Unknown Device Model'
if (!deviceModels.TryGetValue(device.DeviceModelId.Value, out deviceModel))
device.Errors.Add("DeviceModelId", string.Format("Unknown device model id: {0}", device.DeviceModelId));
else
device.DeviceModel = deviceModel;
}
// DEVICE PROFILE
if (!device.Errors.ContainsKey("DeviceProfileId"))
{
DeviceProfile deviceProfile;
if (!deviceProfiles.TryGetValue(device.DeviceProfileId, out deviceProfile))
device.Errors.Add("DeviceProfileId", string.Format("Unknown device profile id: {0}", device.DeviceProfileId));
else
device.DeviceProfile = deviceProfile;
}
// DEVICE BATCH
if (!device.Errors.ContainsKey("DeviceBatchId") && device.DeviceBatchId.HasValue)
{
DeviceBatch deviceBatch;
if (!deviceBatches.TryGetValue(device.DeviceBatchId.Value, out deviceBatch))
device.Errors.Add("DeviceBatchId", string.Format("Unknown device Batch id: {0}", device.DeviceBatchId));
else
device.DeviceBatch = deviceBatch;
}
// ASSIGNED USER
if (!device.Errors.ContainsKey("AssignedUserId") && device.AssignedUserId != null)
{
try
{
device.AssignedUser = UserService.GetUser(device.AssignedUserId, Database, true);
}
catch (ArgumentException)
{
device.Errors.Add("AssignedUserId", string.Format("Unknown user id: {0}", device.AssignedUserId));
}
}
}
internal static ImportDevice ParseRecord(this string[] record)
{
int csvFieldCount = record.Length;
if (csvFieldCount < 1)
throw new ArgumentException("At least one CSV field is required (Serial Number)");
string csvSerialNumber;
string csvDeviceModelId;
int deviceModelId = 1; // Default 'Unknown Device Model'
string csvDeviceProfileId;
int deviceProfileId = 1; // 'Default' Profile
string csvDeviceBatchId;
int deviceBatchId = 0; // No Batch
string csvAssignedUserId = null;
string csvLocation = null;
string csvAssetNumber = null;
Dictionary<string, string> errors = new Dictionary<string, string>();
// SERIAL NUMBER
csvSerialNumber = record[0];
if (string.IsNullOrWhiteSpace(csvSerialNumber))
errors.Add("SerialNumber", "The serial number is required");
else if (csvSerialNumber.Trim().Length > 60)
errors.Add("SerialNumber", "The serial number must be less than or equal to 60 characters");
if (csvFieldCount > 1)
{
// DEVICE MODEL
csvDeviceModelId = record[1];
if (!string.IsNullOrWhiteSpace(csvDeviceModelId))
if (!int.TryParse(csvDeviceModelId, out deviceModelId))
errors.Add("DeviceModelId", "The device model is optional, but when supplied must be a number");
else if (deviceModelId < 1)
errors.Add("DeviceModelId", "The device model is optional, but when supplied must be greater than 0");
if (csvFieldCount > 2)
{
// DEVICE PROFILE
csvDeviceProfileId = record[2];
if (!string.IsNullOrWhiteSpace(csvDeviceProfileId))
if (!int.TryParse(csvDeviceProfileId, out deviceProfileId))
errors.Add("DeviceProfileId", "The device profile is optional, but when supplied must be a number");
else if (deviceProfileId < 1)
errors.Add("DeviceProfileId", "The device profile is optional, but when supplied must be greater than 0");
if (csvFieldCount > 3)
{
// DEVICE BATCH
csvDeviceBatchId = record[3];
if (!string.IsNullOrWhiteSpace(csvDeviceBatchId))
if (!int.TryParse(csvDeviceBatchId, out deviceBatchId))
errors.Add("DeviceBatchId", "The device batch is optional, but when supplied must be a number");
else if (deviceBatchId < 1)
errors.Add("DeviceBatchId", "The device batch is optional, but when supplied must be greater than 0");
if (csvFieldCount > 4)
{
// ASSIGNED USER
csvAssignedUserId = record[4];
if (string.IsNullOrWhiteSpace(csvAssignedUserId))
csvAssignedUserId = null; // Not Assigned
else
{
if (csvAssignedUserId.Length > 50)
errors.Add("AssignedUserId", "The assigned user must be less than or equal to 50 characters");
else if (!csvAssignedUserId.Contains('\\')) // Assume Primary Domain
csvAssignedUserId = string.Format(@"{0}\{1}", ActiveDirectory.Context.PrimaryDomain.NetBiosName, csvAssignedUserId);
}
if (csvFieldCount > 5)
{
// LOCATION
csvLocation = record[5];
if (string.IsNullOrWhiteSpace(csvLocation))
csvLocation = null; // No Location Specified
else if (csvLocation.Length > 250)
errors.Add("Location", "The location must be less than or equal to 250 characters");
if (csvFieldCount > 6)
{
// ASSET NUMBER
csvAssetNumber = record[6];
if (string.IsNullOrWhiteSpace(csvAssetNumber))
csvAssetNumber = null; // No Location Specified
else if (csvAssetNumber.Length > 40)
errors.Add("AssetNumber", "The asset number must be less than or equal to 40 characters");
}
}
}
}
}
}
return new ImportDevice()
{
SerialNumber = csvSerialNumber.Trim(),
DeviceModelId = deviceModelId,
DeviceProfileId = deviceProfileId,
DeviceBatchId = deviceBatchId == 0 ? (int?)null : deviceBatchId,
AssignedUserId = csvAssignedUserId,
Location = csvLocation,
AssetNumber = csvAssetNumber,
Errors = errors
};
}
#region ImportDevice Extensions
public static string ImportStatus(this ImportDevice device)
{
if (device.Errors.Count > 0)
return "Error";
if (device.Device != null)
return "Update";
return "New";
}
#endregion
}
}
@@ -1,91 +0,0 @@
using Disco.Data.Repository;
using Disco.Models.BI.Device;
using Disco.Models.Repository;
using Disco.Services.Tasks;
using LumenWorks.Framework.IO.Csv;
using Quartz;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Caching;
namespace Disco.BI.DeviceBI.Importing
{
public class ImportParseTask : ScheduledTask
{
public override string TaskName { get { return "Import Devices - Parsing"; } }
public override bool SingleInstanceTask { get { return false; } }
public override bool CancelInitiallySupported { get { return false; } }
protected override void ExecuteTask()
{
string csvFilename = (string)this.ExecutionContext.JobDetail.JobDataMap["CsvFilename"];
MemoryStream csvStream = (MemoryStream)this.ExecutionContext.JobDetail.JobDataMap["CsvImport"];
this.Status.UpdateStatus(0, "Parsing CSV File", "Loading Records");
List<ImportDevice> records;
using (TextReader csvTextReader = new StreamReader(csvStream))
{
using (CsvReader csvReader = new CsvReader(csvTextReader, true))
{
csvReader.DefaultParseErrorAction = ParseErrorAction.ThrowException;
csvReader.MissingFieldAction = MissingFieldAction.ReplaceByNull;
records = csvReader.Select(record => record.ParseRecord()).ToList();
}
}
csvStream.Dispose();
this.Status.UpdateStatus(20, "Parsing CSV File", string.Format("Linking {0} Records", records.Count));
using (DiscoDataContext database = new DiscoDataContext())
{
var populateReferences = Import.GetPopulateRecordReferences(database);
DateTime lastUpdate = DateTime.Now;
foreach (var record in records)
{
record.PopulateRecord(database, populateReferences);
if (DateTime.Now.Subtract(lastUpdate).TotalSeconds > 1)
{
// Update every second
this.Status.UpdateStatus((int)Math.Floor((((double)(records.IndexOf(record) + 1) / records.Count) * 80)));
lastUpdate = DateTime.Now;
}
}
}
// Create Session Result
ImportDeviceSession session = new ImportDeviceSession()
{
ImportParseTaskId = this.Status.SessionId,
ImportFilename = csvFilename,
ImportDevices = records
};
// Set Results to Cache
string key = string.Format(Import.ImportParseCacheKey, this.Status.SessionId);
HttpRuntime.Cache.Insert(key, session, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(60), CacheItemPriority.NotRemovable, null);
}
public static ScheduledTaskStatus Run(Stream CsvImport, String CsvFilename)
{
MemoryStream csvStream = new MemoryStream();
CsvImport.CopyTo(csvStream);
csvStream.Position = 0;
var task = new ImportParseTask();
JobDataMap taskData = new JobDataMap() { { "CsvImport", csvStream }, { "CsvFilename", CsvFilename } };
return task.ScheduleTask(taskData);
}
}
}
@@ -1,70 +0,0 @@
using Disco.Data.Repository;
using Disco.Models.BI.Device;
using Disco.Services.Tasks;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace Disco.BI.DeviceBI.Importing
{
public class ImportProcessTask : ScheduledTask
{
public override string TaskName { get { return "Import Devices - Processing Changes"; } }
public override bool SingleInstanceTask { get { return false; } }
public override bool CancelInitiallySupported { get { return false; } }
protected override void ExecuteTask()
{
string importParseTaskId = (string)this.ExecutionContext.JobDetail.JobDataMap["ImportParseTaskId"];
if (string.IsNullOrWhiteSpace(importParseTaskId))
throw new ArgumentNullException("ImportParseTaskId");
ImportDeviceSession session = Import.GetSession(importParseTaskId);
if (session == null)
throw new InvalidOperationException("The session timed out (60 minutes), try importing again");
List<ImportDevice> records = session.ImportDevices;
int recordsImported = 0;
this.Status.UpdateStatus(0, "Processing Device Import", "Importing Devices");
using (DiscoDataContext database = new DiscoDataContext())
{
var populateReferences = Import.GetPopulateRecordReferences(database);
DateTime lastUpdate = DateTime.Now;
foreach (var record in records)
{
if (record.ImportRecord(database, populateReferences))
recordsImported++;
if (DateTime.Now.Subtract(lastUpdate).TotalSeconds > 1)
{
// Update every second
this.Status.UpdateStatus((int)Math.Floor((((double)(records.IndexOf(record) + 1) / records.Count) * 100)), string.Format("Importing: {0} ({1} of {2})", record.SerialNumber, records.IndexOf(record) + 1, records.Count));
lastUpdate = DateTime.Now;
}
}
}
this.Status.SetFinishedMessage(string.Format("Imported {0} of {1} Devices", recordsImported, records.Count));
}
public static ScheduledTaskStatus Run(string ImportParseTaskId)
{
if (string.IsNullOrWhiteSpace(ImportParseTaskId))
throw new ArgumentNullException("ImportParseTaskId");
var task = new ImportProcessTask();
JobDataMap taskData = new JobDataMap() { { "ImportParseTaskId", ImportParseTaskId } };
return task.ScheduleTask(taskData);
}
}
}
@@ -1,83 +0,0 @@
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();
}
}
@@ -1,304 +0,0 @@
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
}
};
}
}
}
@@ -1,283 +0,0 @@
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);
}
}
}
+1 -8
View File
@@ -46,10 +46,6 @@
<Reference Include="itextsharp">
<HintPath>..\Resources\Libraries\iTextSharp\itextsharp.dll</HintPath>
</Reference>
<Reference Include="LumenWorks.Framework.IO, Version=3.8.0.0, Culture=neutral, PublicKeyToken=5ad3ea2f85776344, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Resources\Libraries\LumenWorks.Framework.IO\LumenWorks.Framework.IO.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.1.2\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
@@ -124,9 +120,6 @@
<Compile Include="BI\AttachmentBI\Utilities.cs" />
<Compile Include="BI\DeviceBI\BatchUtilities.cs" />
<Compile Include="BI\DeviceBI\DeviceModelBI.cs" />
<Compile Include="BI\DeviceBI\Importing\Import.cs" />
<Compile Include="BI\DeviceBI\Importing\ImportParseTask.cs" />
<Compile Include="BI\DeviceBI\Importing\ImportProcessTask.cs" />
<Compile Include="BI\DeviceBI\Migration\LogMacAddressImporting.cs" />
<Compile Include="BI\DisposableImageCollection.cs" />
<Compile Include="BI\DocumentTemplateBI\DocumentTemplateQRCodeLocationCache.cs" />
@@ -242,7 +235,7 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_StartDate="2011/7/1" BuildVersion_BuildAction="Both" BuildVersion_DetectChanges="False" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" />
<UserProperties BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_DetectChanges="False" BuildVersion_BuildAction="Both" BuildVersion_StartDate="2011/7/1" />
</VisualStudio>
</ProjectExtensions>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
+6 -3
View File
@@ -105,6 +105,10 @@
<Compile Include="Services\Devices\Exporting\DeviceExportResult.cs" />
<Compile Include="Services\Devices\Exporting\DeviceExportTypes.cs" />
<Compile Include="Services\Devices\Exporting\DeviceExportOptions.cs" />
<Compile Include="Services\Devices\Importing\DeviceImportFieldTypes.cs" />
<Compile Include="Services\Devices\Importing\IDeviceImportRecord.cs" />
<Compile Include="Services\Devices\Importing\IDeviceImportContext.cs" />
<Compile Include="Services\Devices\Importing\IDeviceImportField.cs" />
<Compile Include="Services\Jobs\JobLists\JobLocationReference.cs" />
<Compile Include="Services\Jobs\JobLists\JobTableItemModel.cs" />
<Compile Include="Services\Jobs\JobLists\JobTableModel.cs" />
@@ -151,6 +155,7 @@
<Compile Include="UI\Config\Organisation\ConfigOrganisationIndexModel.cs" />
<Compile Include="UI\Device\DeviceAddOfflineModel.cs" />
<Compile Include="UI\Device\DeviceExportModel.cs" />
<Compile Include="UI\Device\DeviceImportHeadersModel.cs" />
<Compile Include="UI\Device\DeviceImportModel.cs" />
<Compile Include="UI\Device\DeviceImportReviewModel.cs" />
<Compile Include="UI\Device\DeviceIndexModel.cs" />
@@ -166,9 +171,7 @@
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\Devices\Importing\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
@@ -83,19 +83,19 @@ namespace Disco.Models.Services.Devices.Exporting
public bool ProfileShortName { get; set; }
// User
[Display(ShortName = "Assigned User", Name = "Identifier", Description = "The identifier of the user assigned with the device")]
[Display(ShortName = "Assigned User", Name = "Identifier", Description = "The identifier of the user assigned to the device")]
public bool AssignedUserId { get; set; }
[Display(ShortName = "Assigned User", Name = "Assigned Date", Description = "The date the device was assigned to the user")]
public bool AssignedUserDate { get; set; }
[Display(ShortName = "Assigned User", Name = "Display Name", Description = "The display name of the user assigned with the device")]
[Display(ShortName = "Assigned User", Name = "Display Name", Description = "The display name of the user assigned to the device")]
public bool AssignedUserDisplayName { get; set; }
[Display(ShortName = "Assigned User", Name = "Surname", Description = "The surname of the user assigned with the device")]
[Display(ShortName = "Assigned User", Name = "Surname", Description = "The surname of the user assigned to the device")]
public bool AssignedUserSurname { get; set; }
[Display(ShortName = "Assigned User", Name = "Given Name", Description = "The given name of the user assigned with the device")]
[Display(ShortName = "Assigned User", Name = "Given Name", Description = "The given name of the user assigned to the device")]
public bool AssignedUserGivenName { get; set; }
[Display(ShortName = "Assigned User", Name = "Phone Number", Description = "The phone number of the user assigned with the device")]
[Display(ShortName = "Assigned User", Name = "Phone Number", Description = "The phone number of the user assigned to the device")]
public bool AssignedUserPhoneNumber { get; set; }
[Display(ShortName = "Assigned User", Name = "Email Address", Description = "The email address of the user assigned with the device")]
[Display(ShortName = "Assigned User", Name = "Email Address", Description = "The email address of the user assigned to the device")]
public bool AssignedUserEmailAddress { get; set; }
// Jobs
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Models.Services.Devices.Importing
{
public enum DeviceImportFieldTypes
{
[Required, Display(Name = "Device Serial Number", Description = "The device serial number")]
DeviceSerialNumber,
[Display(Name = "Device Asset Number", Description = "The device asset number")]
DeviceAssetNumber,
[Display(Name = "Device Location", Description = "The device location")]
DeviceLocation,
[Display(Name = "Device Decommissioned Date", Description = "The date the device was decommissioned in Disco")]
DeviceDecommissionedDate,
[Display(Name = "Device Decommissioned Reason", Description = "The reason the device was decommissioned")]
DeviceDecommissionedReason,
[Display(Name = "Device LAN MAC Address", Description = "The LAN MAC Address associated with the device")]
DetailLanMacAddress,
[Display(Name = "Device Wireless LAN MAC Address", Description = "The Wireless LAN MAC Address associated with the device")]
DetailWLanMacAddress,
[Display(Name = "Device AC Adapter", Description = "The AC Adapter associated with the device")]
DetailACAdapter,
[Display(Name = "Model Identifier", Description = "The identifier of the device model associated with the device")]
ModelId,
[Display(Name = "Batch Identifier", Description = "The identifier of the device batch associated with the device")]
BatchId,
[Display(Name = "Profile Identifier", Description = "The identifier of the device profile associated with the device")]
ProfileId,
[Display(Name = "Assigned User Identifier", Description = "The identifier of the user assigned to the device")]
AssignedUserId,
[Display(Name = "Ignore Column", Description = "The column will be ignored during the import")]
IgnoreColumn
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Models.Services.Devices.Importing
{
public interface IDeviceImportContext
{
string SessionId { get; }
string Filename { get; }
List<Tuple<string, DeviceImportFieldTypes>> Header { get; }
List<Tuple<string, DeviceImportFieldTypes, Func<string[], string>, Type>> ParsedHeaders { get; }
List<string[]> RawData { get; }
List<IDeviceImportRecord> Records { get; }
int AffectedRecords { get; }
}
}
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Models.Services.Devices.Importing
{
public interface IDeviceImportField
{
DeviceImportFieldTypes FieldType { get; }
EntityState? FieldAction { get; }
string ErrorMessage { get; }
object RawParsedValue { get; }
string FriendlyValue { get; }
string FriendlyPreviousValue { get; }
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Models.Services.Devices.Importing
{
public interface IDeviceImportRecord
{
string DeviceSerialNumber { get; }
IEnumerable<IDeviceImportField> Fields { get; }
EntityState RecordAction { get; }
bool HasError { get; }
}
}
@@ -0,0 +1,9 @@
using Disco.Models.Services.Devices.Importing;
namespace Disco.Models.UI.Device
{
public interface DeviceImportHeadersModel : BaseUIModel
{
IDeviceImportContext Context { get; set; }
}
}
@@ -1,16 +1,15 @@
using Disco.Models.BI.Device;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Disco.Models.Services.Devices.Importing;
namespace Disco.Models.UI.Device
{
public interface DeviceImportReviewModel : BaseUIModel
{
string ImportParseTaskId { get; set; }
string ImportFilename { get; set; }
List<ImportDevice> ImportDevices { get; set; }
IDeviceImportContext Context { get; set; }
int StatisticErrorRecords { get; set; }
int StatisticNewRecords { get; set; }
int StatisticModifiedRecords { get; set; }
int StatisticUnmodifiedRecords { get; set; }
int StatisticImportRecords { get; }
}
}
@@ -0,0 +1,199 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using Disco.Services.Devices.Importing.Fields;
using Disco.Services.Tasks;
using LumenWorks.Framework.IO.Csv;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services.Devices.Importing
{
public static class DeviceImport
{
internal static Lazy<Dictionary<DeviceImportFieldTypes, Type>> FieldHandlers = new Lazy<Dictionary<DeviceImportFieldTypes, Type>>(() =>
{
return new Dictionary<DeviceImportFieldTypes, Type>()
{
{ DeviceImportFieldTypes.DeviceSerialNumber, typeof(DeviceSerialNumberImportField) },
{ DeviceImportFieldTypes.DeviceAssetNumber, typeof(DeviceAssetNumberImportField) },
{ DeviceImportFieldTypes.DeviceLocation, typeof(DeviceLocationImportField) },
{ DeviceImportFieldTypes.DeviceDecommissionedDate, typeof(DeviceDecommissionedDateImportField) },
{ DeviceImportFieldTypes.DeviceDecommissionedReason, typeof(DeviceDecommissionedReasonImportField) },
{ DeviceImportFieldTypes.DetailLanMacAddress, typeof(DetailLanMacAddressImportField) },
{ DeviceImportFieldTypes.DetailWLanMacAddress, typeof(DetailWLanMacAddressImportField) },
{ DeviceImportFieldTypes.DetailACAdapter, typeof(DetailACAdapterImportField) },
{ DeviceImportFieldTypes.ModelId, typeof(ModelIdImportField) },
{ DeviceImportFieldTypes.BatchId, typeof(BatchIdImportField) },
{ DeviceImportFieldTypes.ProfileId, typeof(ProfileIdImportField) },
{ DeviceImportFieldTypes.AssignedUserId, typeof(AssignedUserIdImportField) }
};
});
public static DeviceImportContext BeginImport(DiscoDataContext Database, string Filename, bool HasHeader, Stream FileContent)
{
if (FileContent == null)
throw new ArgumentNullException("FileContent");
if (string.IsNullOrWhiteSpace(Filename))
Filename = "<None Specified>";
DeviceImportContext context;
List<Tuple<string, DeviceImportFieldTypes>> header;
List<string[]> rawData;
using (TextReader csvTextReader = new StreamReader(FileContent))
{
using (CsvReader csvReader = new CsvReader(csvTextReader, HasHeader))
{
csvReader.DefaultParseErrorAction = ParseErrorAction.ThrowException;
csvReader.MissingFieldAction = MissingFieldAction.ReplaceByNull;
rawData = csvReader.ToList();
header = csvReader.GetFieldHeaders().Select(h => Tuple.Create(h, DeviceImportFieldTypes.IgnoreColumn)).ToList();
}
}
context = new DeviceImportContext(Filename, header, rawData);
context.GuessHeaderTypes(Database);
return context;
}
private static void GuessHeaderTypes(this DeviceImportContext Context, DiscoDataContext Database)
{
FieldHandlers.Value.ToList().ForEach(h =>
{
var instance = (DeviceImportFieldBase)Activator.CreateInstance(h.Value);
var column = instance.GuessHeader(Database, Context);
if (column.HasValue)
Context.Header[column.Value] = Tuple.Create(Context.Header[column.Value].Item1, instance.FieldType);
});
}
public static void UpdateHeaderTypes(this DeviceImportContext Context, List<DeviceImportFieldTypes> HeaderTypes)
{
if (HeaderTypes == null)
throw new ArgumentNullException("HeaderTypes");
if (HeaderTypes.Count != Context.Header.Count)
throw new ArgumentException("The number of Header Types supplied does not match the number of Headers", "HeaderTypes");
if (!HeaderTypes.Any(h => h == DeviceImportFieldTypes.DeviceSerialNumber))
throw new ArgumentException("At least one column must be the Device Serial Number", "HeaderTypes");
if (HeaderTypes.Where(h => h != DeviceImportFieldTypes.IgnoreColumn).GroupBy(h => h, (k, i) => Tuple.Create(k, i.Count())).Any(g => g.Item2 > 1))
throw new ArgumentException("Column types can only be specified once for each type", "HeaderTypes");
Context.Header = Context.Header.Zip(HeaderTypes, (h, ht) => Tuple.Create(h.Item1, ht)).ToList();
}
public static void ParseRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskBasicStatus Status)
{
if (Context.Header == null)
throw new InvalidOperationException("The Import Context has not been initialized");
if (Context.Header.Count == 0)
throw new InvalidOperationException("No Headers were found");
if (!Context.Header.Any(h => h.Item2 == DeviceImportFieldTypes.DeviceSerialNumber))
throw new ArgumentException("At least one column must be the Device Serial Number", "Header");
if (Context.RawData == null || Context.RawData.Count == 0)
throw new ArgumentException("No data was found in the import file", "RawData");
IDeviceImportCache cache;
if (Context.RawData.Count > 20)
cache = new DeviceImportInMemoryCache(Database);
else
cache = new DeviceImportDatabaseCache(Database);
Context.HeaderDeviceSerialNumberIndex = Context.Header.IndexOf(Context.Header.First(h => h.Item2 == DeviceImportFieldTypes.DeviceSerialNumber));
Context.ParsedHeaders = Context.Header
.Select((h, i) => Tuple.Create(h.Item1, h.Item2, i))
.Where(h => h.Item2 != DeviceImportFieldTypes.IgnoreColumn)
.Select(h => new Tuple<string, DeviceImportFieldTypes, Func<string[], string>, Type>(h.Item1, h.Item2, (f) => f[h.Item3], DeviceImport.FieldHandlers.Value[h.Item2]))
.ToList();
DateTime nextProgress = DateTime.Now;
Status.UpdateStatus(0, "Parsing Import Records", "Starting...");
Context.Records = Context.RawData.Select((d, recordIndex) =>
{
string deviceSerialNumber = Fields.DeviceSerialNumberImportField.ParseRawDeviceSerialNumber(d[Context.HeaderDeviceSerialNumberIndex]);
if (nextProgress <= DateTime.Now)
{
Status.UpdateStatus(((double)recordIndex / Context.RawData.Count) * 100, string.Format("Parsing: {0}", deviceSerialNumber));
nextProgress = DateTime.Now.AddSeconds(.5);
}
Device existingDevice = null;
if (Fields.DeviceSerialNumberImportField.IsDeviceSerialNumberValid(deviceSerialNumber))
existingDevice = cache.Devices.FirstOrDefault(device => device.SerialNumber == deviceSerialNumber);
var values = Context.ParsedHeaders
.ToDictionary(k => k.Item2, k => k.Item3(d));
var fields = Context.ParsedHeaders.Select(h =>
{
var f = (DeviceImportFieldBase)Activator.CreateInstance(h.Item4);
f.Parse(Database, cache, Context, recordIndex, deviceSerialNumber, existingDevice, values, h.Item3(d));
return f;
}).ToList();
EntityState recordAction;
if (fields.Any(f => !f.FieldAction.HasValue))
recordAction = EntityState.Detached;
else if (existingDevice == null)
recordAction = EntityState.Added;
else if (fields.Any(f => f.FieldAction == EntityState.Modified))
recordAction = EntityState.Modified;
else
recordAction = EntityState.Unchanged;
return new DeviceImportRecord(deviceSerialNumber, fields, recordAction);
}).Cast<IDeviceImportRecord>().ToList();
}
public static int ApplyRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskBasicStatus Status)
{
if (Context.Records == null)
throw new InvalidOperationException("Import Records have not been parsed");
if (Context.Records.Count == 0)
throw new InvalidOperationException("There are no records to import");
DateTime nextProgress = DateTime.Now;
Status.UpdateStatus(0, "Applying Import Records to Database", "Starting...");
int affectedRecords = 0;
foreach (var record in Context.Records.Cast<DeviceImportRecord>().Select((r, i) => Tuple.Create(r, i)))
{
if (nextProgress <= DateTime.Now)
{
Status.UpdateStatus(((double)record.Item2 / Context.Records.Count) * 100, string.Format("Applying: {0}", record.Item1.DeviceSerialNumber));
nextProgress = DateTime.Now.AddSeconds(.5);
}
if (record.Item1.Apply(Database))
affectedRecords++;
}
return affectedRecords;
}
}
}
@@ -0,0 +1,41 @@
using Disco.Data.Repository;
using Disco.Services.Tasks;
using Quartz;
using System;
namespace Disco.Services.Devices.Importing
{
public class DeviceImportApplyTask : ScheduledTask
{
private const string JobDataMapContext = "Context";
public override string TaskName { get { return "Import Devices - Applying Changes"; } }
public override bool SingleInstanceTask { get { return false; } }
public override bool CancelInitiallySupported { get { return false; } }
public static ScheduledTaskStatus ScheduleNow(DeviceImportContext Context)
{
if (Context == null)
throw new ArgumentNullException("Context");
// Build Data Map
var task = new DeviceImportApplyTask();
JobDataMap taskData = new JobDataMap() { { JobDataMapContext, Context } };
// Schedule Task
return task.ScheduleTask(taskData);
}
protected override void ExecuteTask()
{
var context = (DeviceImportContext)this.ExecutionContext.JobDetail.JobDataMap[JobDataMapContext];
using (DiscoDataContext Database = new DiscoDataContext())
{
context.AffectedRecords = context.ApplyRecords(Database, this.Status);
}
Status.SetFinishedMessage(string.Format("Successfully imported/updated {0} device{1}", context.AffectedRecords, context.AffectedRecords == 1 ? null : "s"));
}
}
}
@@ -0,0 +1,35 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using Disco.Services.Tasks;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing
{
public class DeviceImportContext : IDeviceImportContext
{
public string SessionId { get; private set; }
public string Filename { get; private set; }
public List<Tuple<string, DeviceImportFieldTypes>> Header { get; internal set; }
public List<Tuple<string, DeviceImportFieldTypes, Func<string[], string>, Type>> ParsedHeaders { get; internal set; }
internal int HeaderDeviceSerialNumberIndex { get; set; }
public List<string[]> RawData { get; private set; }
public List<IDeviceImportRecord> Records { get; internal set; }
public int AffectedRecords { get; internal set; }
internal DeviceImportContext(string Filename, List<Tuple<string, DeviceImportFieldTypes>> Header, List<string[]> RawData)
{
this.SessionId = Guid.NewGuid().ToString("D");
this.Filename = Filename;
this.Header = Header;
this.RawData = RawData;
}
}
}
@@ -0,0 +1,42 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services.Devices.Importing
{
internal class DeviceImportDatabaseCache : IDeviceImportCache
{
private DiscoDataContext Database;
public DeviceImportDatabaseCache(DiscoDataContext Database)
{
this.Database = Database;
}
public Device FindDevice(string DeviceSerialNumber)
{
return Database.Devices.FirstOrDefault(d => d.SerialNumber == DeviceSerialNumber);
}
public IEnumerable<Device> Devices
{
get { return Database.Devices; }
}
public IEnumerable<DeviceModel> DeviceModels
{
get { return Database.DeviceModels; }
}
public IEnumerable<DeviceProfile> DeviceProfiles
{
get { return Database.DeviceProfiles; }
}
public IEnumerable<DeviceBatch> DeviceBatches
{
get { return Database.DeviceBatches; }
}
}
}
@@ -0,0 +1,44 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services.Devices.Importing
{
internal abstract class DeviceImportFieldBase : IDeviceImportField
{
public abstract DeviceImportFieldTypes FieldType { get; }
public EntityState? FieldAction { get; protected set; }
public string ErrorMessage { get; protected set; }
public abstract object RawParsedValue { get; }
public abstract string FriendlyValue { get; }
public abstract string FriendlyPreviousValue { get; }
public abstract bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value);
public abstract bool Apply(DiscoDataContext Database, Device Device);
public abstract int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context);
#region Helpers
protected bool Error(string Message)
{
this.ErrorMessage = Message;
this.FieldAction = null;
return false;
}
protected bool Success(EntityState Action)
{
this.FieldAction = Action;
return true;
}
#endregion
}
}
@@ -0,0 +1,53 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services.Devices.Importing
{
internal class DeviceImportInMemoryCache : IDeviceImportCache
{
private DiscoDataContext Database;
private Lazy<IEnumerable<Device>> devices;
private Lazy<IEnumerable<DeviceModel>> deviceModels;
private Lazy<IEnumerable<DeviceProfile>> deviceProfiles;
private Lazy<IEnumerable<DeviceBatch>> deviceBatches;
public DeviceImportInMemoryCache(DiscoDataContext Database)
{
this.Database = Database;
this.devices = new Lazy<IEnumerable<Device>>(() => Database.Devices.Include("DeviceDetails").ToList());
this.deviceModels = new Lazy<IEnumerable<DeviceModel>>(() => Database.DeviceModels.ToList());
this.deviceProfiles = new Lazy<IEnumerable<DeviceProfile>>(() => Database.DeviceProfiles.ToList());
this.deviceBatches = new Lazy<IEnumerable<DeviceBatch>>(() => Database.DeviceBatches.ToList());
}
public Device FindDevice(string DeviceSerialNumber)
{
return devices.Value.FirstOrDefault(d => d.SerialNumber.Equals(DeviceSerialNumber, StringComparison.OrdinalIgnoreCase));
}
public IEnumerable<Device> Devices
{
get { return devices.Value; }
}
public IEnumerable<DeviceModel> DeviceModels
{
get { return deviceModels.Value; }
}
public IEnumerable<DeviceProfile> DeviceProfiles
{
get { return deviceProfiles.Value; }
}
public IEnumerable<DeviceBatch> DeviceBatches
{
get { return deviceBatches.Value; }
}
}
}
@@ -0,0 +1,39 @@
using Disco.Data.Repository;
using Disco.Services.Tasks;
using Quartz;
using System;
namespace Disco.Services.Devices.Importing
{
public class DeviceImportParseTask : ScheduledTask
{
private const string JobDataMapContext = "Context";
public override string TaskName { get { return "Import Devices - Parsing Records"; } }
public override bool SingleInstanceTask { get { return false; } }
public override bool CancelInitiallySupported { get { return false; } }
public static ScheduledTaskStatus ScheduleNow(DeviceImportContext Context)
{
if (Context == null)
throw new ArgumentNullException("Context");
// Build Data Map
var task = new DeviceImportParseTask();
JobDataMap taskData = new JobDataMap() { { JobDataMapContext, Context } };
// Schedule Task
return task.ScheduleTask(taskData);
}
protected override void ExecuteTask()
{
var context = (DeviceImportContext)this.ExecutionContext.JobDetail.JobDataMap[JobDataMapContext];
using (DiscoDataContext Database = new DiscoDataContext())
{
context.ParseRecords(Database, this.Status);
}
}
}
}
@@ -0,0 +1,85 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services.Devices.Importing
{
internal class DeviceImportRecord : IDeviceImportRecord
{
public string DeviceSerialNumber { get; private set; }
public IEnumerable<IDeviceImportField> Fields { get; private set; }
public EntityState RecordAction { get; private set; }
public bool HasError
{
get { return Fields.Any(f => !f.FieldAction.HasValue); }
}
internal DeviceImportRecord(string DeviceSerialNumber, IEnumerable<IDeviceImportField> Fields, EntityState RecordAction)
{
this.DeviceSerialNumber = DeviceSerialNumber;
this.Fields = Fields;
this.RecordAction = RecordAction;
}
public bool Apply(DiscoDataContext Database)
{
if (RecordAction == EntityState.Detached || !HasError)
{
Device device;
if (RecordAction == EntityState.Unchanged)
{
// Unchanged - No Action Required
return false;
}
else if (RecordAction == EntityState.Modified)
{
device = Database.Devices.Find(this.DeviceSerialNumber);
}
else if (RecordAction == EntityState.Added)
{
// Create Device
device = new Device()
{
SerialNumber = DeviceSerialNumber.ToUpper(),
CreatedDate = DateTime.Now,
AllowUnauthenticatedEnrol = true,
DeviceProfileId = Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId,
DeviceModelId = 1 // Default 'Unknown Device Model'
};
Database.Devices.Add(device);
}
else
{
// Invalid State
return false;
}
bool changesMade = false;
foreach (var field in Fields.Cast<DeviceImportFieldBase>())
{
changesMade = field.Apply(Database, device) || changesMade;
}
// Commit Changes
if (changesMade)
Database.SaveChanges();
return changesMade;
}
// Record has Errors
return false;
}
}
}
@@ -0,0 +1,144 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using Disco.Services.Users;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class AssignedUserIdImportField : DeviceImportFieldBase
{
private string parsedValue;
private string friendlyValue;
private string friendlyPreviousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.AssignedUserId; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return friendlyValue; } }
public override string FriendlyPreviousValue { get { return friendlyPreviousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
friendlyValue = Value;
if (string.IsNullOrWhiteSpace(Value))
{
friendlyValue = null;
parsedValue = null;
}
else
{
parsedValue = Value.Trim();
if (!parsedValue.Contains('\\'))
parsedValue = string.Format(@"{0}\{1}", Interop.ActiveDirectory.ActiveDirectory.Context.PrimaryDomain.NetBiosName, parsedValue);
friendlyValue = parsedValue;
if (parsedValue.Length > 50)
return Error("Cannot be more than 50 characters");
}
if (parsedValue != null)
{
// Check User Exists
// Try Database
User user = Database.Users.FirstOrDefault(u => u.UserId == parsedValue);
try
{
// Try Updating from AD
user = UserService.GetUser(parsedValue, Database);
}
catch (Exception ex)
{
if (user == null)
return Error(ex.Message);
}
parsedValue = user.UserId;
friendlyValue = string.Format("{0} [{1}]", user.DisplayName, user.UserId);
// Check Decommissioned
bool? importDecommissioning = null;
if (Values.ContainsKey(DeviceImportFieldTypes.DeviceDecommissionedDate) || Values.ContainsKey(DeviceImportFieldTypes.DeviceDecommissionedReason))
importDecommissioning = Values.ContainsKey(DeviceImportFieldTypes.DeviceDecommissionedDate) && !string.IsNullOrWhiteSpace(Values[DeviceImportFieldTypes.DeviceDecommissionedDate]) ||
Values.ContainsKey(DeviceImportFieldTypes.DeviceDecommissionedReason) && !string.IsNullOrWhiteSpace(Values[DeviceImportFieldTypes.DeviceDecommissionedReason]);
if (importDecommissioning.HasValue && importDecommissioning.Value)
return Error("Cannot assign a user to a device being decommissioned");
if (ExistingDevice != null && ExistingDevice.DecommissionedDate.HasValue && !importDecommissioning.HasValue)
{
return Error("Cannot assign a user to a decommissioned device");
}
}
if (ExistingDevice == null && parsedValue != null)
{
return Success(EntityState.Added);
}
else if (ExistingDevice != null && ExistingDevice.AssignedUserId != parsedValue)
{
if (ExistingDevice.AssignedUserId != null)
friendlyPreviousValue = string.Format("{0} [{1}]", ExistingDevice.AssignedUser.DisplayName, ExistingDevice.AssignedUser.UserId);
else
friendlyPreviousValue = null;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Modified)
{
// Remove Current Assignments
var currentAssignments = Device.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue);
foreach (var currentAssignment in currentAssignments)
{
currentAssignment.UnassignedDate = DateTime.Now;
}
// Add Assignment
if (parsedValue != null)
{
var assignment = new DeviceUserAssignment()
{
Device = Device,
DeviceSerialNumber = Device.SerialNumber,
AssignedUserId = parsedValue,
AssignedDate = DateTime.Now
};
Database.DeviceUserAssignments.Add(assignment);
}
Device.AssignedUserId = parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn &&
h.Item1.Item1.IndexOf("user id", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("userid", System.StringComparison.OrdinalIgnoreCase) >= 0
);
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,106 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class BatchIdImportField : DeviceImportFieldBase
{
private int? parsedValue;
private string friendlyValue;
private string friendlyPreviousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.BatchId; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return friendlyValue; } }
public override string FriendlyPreviousValue { get { return friendlyPreviousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
friendlyValue = Value;
// Validate
if (string.IsNullOrWhiteSpace(Value))
this.parsedValue = null; // Default = null
else
{
int valueInt;
if (int.TryParse(Value, out valueInt))
this.parsedValue = valueInt;
else
return Error("The Batch Identifier must be a number");
}
if (this.parsedValue.HasValue)
{
var b = Cache.DeviceBatches.FirstOrDefault(db => db.Id == parsedValue);
if (b == null)
return Error(string.Format("The identifier ({0}) does not match any Device Batch", Value));
friendlyValue = string.Format("{0} [{1}]", b.Name, b.Id);
}
else
friendlyValue = null;
if (ExistingDevice == null)
return Success(EntityState.Added);
else if (ExistingDevice != null && ExistingDevice.DeviceBatchId != parsedValue)
{
DeviceBatch previousBatch = null;
if (ExistingDevice.DeviceBatchId.HasValue)
previousBatch = Cache.DeviceBatches.FirstOrDefault(db => db.Id == ExistingDevice.DeviceBatchId.Value);
if (previousBatch != null)
friendlyPreviousValue = string.Format("{0} [{1}]", previousBatch.Name, previousBatch.Id);
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
Device.DeviceBatchId = this.parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && h.Item1.Item1.IndexOf("batch", System.StringComparison.OrdinalIgnoreCase) >= 0);
// All Integers Numbers
possibleColumns = possibleColumns.Where(h =>
{
int lastValue;
return Context.RawData.Select(v => v[h.Item2]).Take(100).Where(v => !string.IsNullOrWhiteSpace(v)).All(v => int.TryParse(v, out lastValue));
}).ToList();
// Multiple Columns, tighten column definition
if (possibleColumns.Count() > 1)
{
possibleColumns = possibleColumns
.Where(h =>
h.Item1.Item1.IndexOf("batchid", StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("batch id", StringComparison.OrdinalIgnoreCase) >= 0);
}
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,99 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DetailACAdapterImportField : DeviceImportFieldBase
{
private string parsedValue;
private string previousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DetailACAdapter; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue; } }
public override string FriendlyPreviousValue { get { return previousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
if (string.IsNullOrWhiteSpace(Value))
parsedValue = null;
else
{
parsedValue = Value.Trim();
}
if (ExistingDevice == null && parsedValue != null)
return Success(EntityState.Added);
else if (ExistingDevice != null)
{
var detail = ExistingDevice.DeviceDetails.FirstOrDefault(dd => dd.Scope == DeviceDetail.ScopeHardware && dd.Key == DeviceDetail.HardwareKeyACAdapter);
if (detail == null && parsedValue == null)
return Success(EntityState.Unchanged);
else if (detail == null && parsedValue != null)
{
return Success(EntityState.Modified);
}
else if (detail.Value != parsedValue)
{
previousValue = detail.Value;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
DeviceDetail detail = Database.DeviceDetails.FirstOrDefault(dd =>
dd.DeviceSerialNumber == Device.SerialNumber &&
dd.Scope == DeviceDetail.ScopeHardware &&
dd.Key == DeviceDetail.HardwareKeyACAdapter);
if (detail == null)
{
detail = new DeviceDetail()
{
Device = Device,
DeviceSerialNumber = Device.SerialNumber,
Scope = DeviceDetail.ScopeHardware,
Key = DeviceDetail.HardwareKeyACAdapter
};
Database.DeviceDetails.Add(detail);
}
detail.Value = parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn &&
(h.Item1.Item1.IndexOf("ac adapter", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("acadapter", System.StringComparison.OrdinalIgnoreCase) >= 0));
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,106 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DetailLanMacAddressImportField : DeviceImportFieldBase
{
private string parsedValue;
private string previousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DetailLanMacAddress; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue; } }
public override string FriendlyPreviousValue { get { return previousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
if (string.IsNullOrWhiteSpace(Value))
parsedValue = null;
else
{
parsedValue = Value.Trim();
}
if (ExistingDevice == null && parsedValue != null)
return Success(EntityState.Added);
else if (ExistingDevice != null)
{
var detail = ExistingDevice.DeviceDetails.FirstOrDefault(dd => dd.Scope == DeviceDetail.ScopeHardware && dd.Key == DeviceDetail.HardwareKeyLanMacAddress);
if (detail == null && parsedValue == null)
return Success(EntityState.Unchanged);
else if (detail == null && parsedValue != null)
{
return Success(EntityState.Modified);
}
else if (detail.Value != parsedValue)
{
previousValue = detail.Value;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
DeviceDetail detail = Database.DeviceDetails.FirstOrDefault(dd =>
dd.DeviceSerialNumber == Device.SerialNumber &&
dd.Scope == DeviceDetail.ScopeHardware &&
dd.Key == DeviceDetail.HardwareKeyLanMacAddress);
if (detail == null)
{
detail = new DeviceDetail()
{
Device = Device,
DeviceSerialNumber = Device.SerialNumber,
Scope = DeviceDetail.ScopeHardware,
Key = DeviceDetail.HardwareKeyLanMacAddress
};
Database.DeviceDetails.Add(detail);
}
detail.Value = parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && (
h.Item1.Item1.IndexOf("wlan", System.StringComparison.OrdinalIgnoreCase) < 0 &&
h.Item1.Item1.IndexOf("wireless", System.StringComparison.OrdinalIgnoreCase) < 0 && (
h.Item1.Item1.IndexOf("lan address", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("lan mac", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("lan mac address", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("lanaddress", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("lanmac", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("lanmacaddress", System.StringComparison.OrdinalIgnoreCase) >= 0
)));
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,108 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DetailWLanMacAddressImportField : DeviceImportFieldBase
{
private string parsedValue;
private string previousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DetailWLanMacAddress; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue; } }
public override string FriendlyPreviousValue { get { return previousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
if (string.IsNullOrWhiteSpace(Value))
parsedValue = null;
else
{
parsedValue = Value.Trim();
}
if (ExistingDevice == null && parsedValue != null)
return Success(EntityState.Added);
else if (ExistingDevice != null)
{
var detail = ExistingDevice.DeviceDetails.FirstOrDefault(dd => dd.Scope == DeviceDetail.ScopeHardware && dd.Key == DeviceDetail.HardwareKeyWLanMacAddress);
if (detail == null && parsedValue == null)
return Success(EntityState.Unchanged);
else if (detail == null && parsedValue != null)
{
return Success(EntityState.Modified);
}
else if (detail.Value != parsedValue)
{
previousValue = detail.Value;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
DeviceDetail detail = Database.DeviceDetails.FirstOrDefault(dd =>
dd.DeviceSerialNumber == Device.SerialNumber &&
dd.Scope == DeviceDetail.ScopeHardware &&
dd.Key == DeviceDetail.HardwareKeyWLanMacAddress);
if (detail == null)
{
detail = new DeviceDetail()
{
Device = Device,
DeviceSerialNumber = Device.SerialNumber,
Scope = DeviceDetail.ScopeHardware,
Key = DeviceDetail.HardwareKeyWLanMacAddress
};
Database.DeviceDetails.Add(detail);
}
detail.Value = parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && (
h.Item1.Item1.IndexOf("wireless lan mac address", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wireless lan address", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wireless mac address", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wireless mac", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wlan address", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wlan mac", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wlan mac address", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wlanaddress", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wlanmac", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("wlanmacaddress", System.StringComparison.OrdinalIgnoreCase) >= 0
));
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,68 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DeviceAssetNumberImportField : DeviceImportFieldBase
{
private string parsedValue;
private string previousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DeviceAssetNumber; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue; } }
public override string FriendlyPreviousValue { get { return previousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
if (string.IsNullOrWhiteSpace(Value))
parsedValue = null;
else
{
parsedValue = Value.Trim();
if (parsedValue.Length > 40)
return Error("Cannot be more than 40 characters");
}
if (ExistingDevice == null && parsedValue != null)
return Success(EntityState.Added);
else if (ExistingDevice != null && ExistingDevice.AssetNumber != parsedValue)
{
previousValue = ExistingDevice.AssetNumber;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
Device.AssetNumber = this.parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// 'asset' in column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && h.Item1.Item1.IndexOf("asset", System.StringComparison.OrdinalIgnoreCase) >= 0);
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,130 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DeviceDecommissionedDateImportField : DeviceImportFieldBase
{
private const string DateFormat = "yyyy-MM-dd HH:mm:ss";
private string rawValue;
private DateTime? parsedValue;
private DateTime? previousValue;
private bool setReason { get; set; }
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DeviceDecommissionedDate; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue.HasValue ? parsedValue.Value.ToString(DateFormat) : rawValue; } }
public override string FriendlyPreviousValue { get { return previousValue.HasValue ? previousValue.Value.ToString(DateFormat) : null; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
if (string.IsNullOrWhiteSpace(Value))
{
rawValue = null;
parsedValue = null;
}
else
{
DateTime valueDateTime;
if (!DateTime.TryParse(Value.Trim(), CultureInfo.CurrentCulture, System.Globalization.DateTimeStyles.AssumeLocal, out valueDateTime))
{
rawValue = Value.Trim();
return Error(string.Format("Cannot parse the value as a Date/Time using {0} culture (system default).", CultureInfo.CurrentCulture.Name));
}
else
{
// Accuracy to the second (remove any milliseconds)
parsedValue = new DateTime((valueDateTime.Ticks / 10000000L) * 10000000L);
}
}
string errorMessage;
if (parsedValue.HasValue && !CanDecommissionDevice(ExistingDevice, Values, out errorMessage))
return Error(errorMessage);
setReason = !Values.ContainsKey(DeviceImportFieldTypes.DeviceDecommissionedReason) ||
(parsedValue.HasValue && string.IsNullOrWhiteSpace(Values[DeviceImportFieldTypes.DeviceDecommissionedReason]));
if (ExistingDevice != null && ExistingDevice.DecommissionedDate != parsedValue)
{
previousValue = ExistingDevice.DecommissionedDate;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Modified)
{
// Decommission or Recommission Device
Device.DecommissionedDate = this.parsedValue;
if (setReason)
Device.DecommissionReason = this.parsedValue.HasValue ? (DecommissionReasons?)DecommissionReasons.EndOfLife : null;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn &&
h.Item1.Item1.IndexOf("decommission date", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("decommissiondate", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("decommissioned date", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("decommissioneddate", System.StringComparison.OrdinalIgnoreCase) >= 0
);
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
public static bool CanDecommissionDevice(Device Device, Dictionary<DeviceImportFieldTypes, string> Values, out string ErrorMessage)
{
if (Device == null)
{
ErrorMessage = "Cannot decommission new devices";
return false;
}
// Check device is assigned (or being removed in this import)
if ((!Values.ContainsKey(DeviceImportFieldTypes.AssignedUserId) && Device.AssignedUserId != null) ||
(Values.ContainsKey(DeviceImportFieldTypes.AssignedUserId) && !string.IsNullOrWhiteSpace(Values[DeviceImportFieldTypes.AssignedUserId])))
{
if (Device.AssignedUserId != null)
ErrorMessage = string.Format("The device is assigned to a user ({0} [{1}]) and cannot be decommissioned", Device.AssignedUser.DisplayName, Device.AssignedUser.UserId);
else
ErrorMessage = string.Format("The device is being assigned to a user ({0}) and cannot be decommissioned", Values[DeviceImportFieldTypes.AssignedUserId]);
return false;
}
// Check device doesn't have any open jobs
var openJobCount = Device.Jobs.Count(j => !j.ClosedDate.HasValue);
if (openJobCount > 0)
{
ErrorMessage = string.Format("The device is associated with {0} open job{1} and cannot be decommissioned", openJobCount, openJobCount == 1 ? null : "s");
return false;
}
ErrorMessage = null;
return true;
}
}
}
@@ -0,0 +1,116 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DeviceDecommissionedReasonImportField : DeviceImportFieldBase
{
private string rawValue;
private DecommissionReasons? parsedValue;
private DecommissionReasons? previousValue;
private bool setDate { get; set; }
private static Lazy<Dictionary<string, DecommissionReasons>> decommissionReasonsMap = new Lazy<Dictionary<string, DecommissionReasons>>(() => BuildDecommissionReasonsMap());
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DeviceDecommissionedReason; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue.HasValue ? parsedValue.Value.ToString() : rawValue; } }
public override string FriendlyPreviousValue { get { return previousValue.HasValue ? previousValue.Value.ToString() : null; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
if (string.IsNullOrWhiteSpace(Value))
{
rawValue = null;
parsedValue = null;
}
else
{
DecommissionReasons valueReason;
if (!decommissionReasonsMap.Value.TryGetValue(Value.Trim(), out valueReason))
{
rawValue = Value.Trim();
return Error("Cannot parse the value as a Decommission Reason");
}
else
{
parsedValue = valueReason;
}
}
if (parsedValue.HasValue && !Values.ContainsKey(DeviceImportFieldTypes.DeviceDecommissionedDate))
{
string errorMessage;
if (!DeviceDecommissionedDateImportField.CanDecommissionDevice(ExistingDevice, Values, out errorMessage))
return Error(errorMessage);
setDate = true;
}
else if (parsedValue.HasValue && string.IsNullOrWhiteSpace(Values[DeviceImportFieldTypes.DeviceDecommissionedDate]))
{
setDate = true;
}
if (ExistingDevice != null && ExistingDevice.DecommissionReason != parsedValue)
{
previousValue = ExistingDevice.DecommissionReason;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Modified)
{
// Decommission or Recommission Device
Device.DecommissionReason = this.parsedValue;
if (setDate)
Device.DecommissionedDate = this.parsedValue.HasValue ? (DateTime?)DateTime.Now : null;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn &&
h.Item1.Item1.IndexOf("decommission reason", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("decommissionreason", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("decommissioned reason", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("decommissionedreason", System.StringComparison.OrdinalIgnoreCase) >= 0
);
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
public static Dictionary<string, DecommissionReasons> BuildDecommissionReasonsMap()
{
return Enum.GetValues(typeof(DecommissionReasons)).Cast<DecommissionReasons>().SelectMany(r =>
{
var rCamelName = r.ToString();
var rName = string.Join(string.Empty, rCamelName.SelectMany(c => char.IsUpper(c) ? new char[] { ' ', c } : new char[] { c })).Trim();
if (rCamelName == rName)
return new Tuple<DecommissionReasons, string>[] { Tuple.Create(r, rCamelName) };
else
return new Tuple<DecommissionReasons, string>[] { Tuple.Create(r, rCamelName), Tuple.Create(r, rName) };
}).ToDictionary(k => k.Item2, v => v.Item1, StringComparer.OrdinalIgnoreCase);
}
}
}
@@ -0,0 +1,68 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DeviceLocationImportField : DeviceImportFieldBase
{
private string parsedValue;
private string previousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DeviceLocation; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue; } }
public override string FriendlyPreviousValue { get { return previousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
if (string.IsNullOrWhiteSpace(Value))
parsedValue = null;
else
{
parsedValue = Value.Trim();
if (parsedValue.Length > 250)
return Error("Cannot be more than 250 characters");
}
if (ExistingDevice == null && parsedValue != null)
return Success(EntityState.Added);
else if (ExistingDevice != null && ExistingDevice.Location != parsedValue)
{
previousValue = ExistingDevice.Location;
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
Device.Location = this.parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && h.Item1.Item1.IndexOf("location", System.StringComparison.OrdinalIgnoreCase) >= 0);
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,98 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class DeviceSerialNumberImportField : DeviceImportFieldBase
{
private const int maxLength = 60;
private string parsedValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.DeviceSerialNumber; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return parsedValue; } }
public override string FriendlyPreviousValue { get { return parsedValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
// Validate
if (string.IsNullOrWhiteSpace(Value))
return Error("The Device Serial Number is required");
else
{
parsedValue = Value.Trim();
if (parsedValue.Length > maxLength)
return Error(string.Format("Cannot be more than {0} characters", maxLength));
}
// Duplicate
var duplicate = Context.RawData
.Take(RecordIndex)
.Select((r, i) => Tuple.Create(i, ParseRawDeviceSerialNumber(r[Context.HeaderDeviceSerialNumberIndex])))
.Where(r => IsDeviceSerialNumberValid(r.Item2))
.FirstOrDefault(r => r.Item2.Equals(parsedValue, StringComparison.OrdinalIgnoreCase));
if (duplicate != null)
return Error(string.Format("This Device Serial Number was already present on Row {0}", duplicate.Item1 + 1));
// No action required
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
// Do nothing for Device Serial Number
return false;
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// 'serial' in column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && h.Item1.Item1.IndexOf("serial", System.StringComparison.OrdinalIgnoreCase) >= 0);
// All Values
possibleColumns = possibleColumns.Where(h =>
{
return Context.RawData.Select(v => v[h.Item2]).All(v => !string.IsNullOrWhiteSpace(v));
}).ToList();
if (possibleColumns.Count() > 1)
{
// Hunt Database
var possibleColumnIndex = possibleColumns.Where(h =>
{
try
{
var top50SerialNumbers = Context.RawData.Select(v => v[h.Item2]).Take(50).ToArray();
return Database.Devices.Count(d => top50SerialNumbers.Contains(d.SerialNumber)) > 0;
}
catch (Exception) { return false; }
}).Select(h => (int?)h.Item2).FirstOrDefault();
if (possibleColumnIndex.HasValue)
return possibleColumnIndex;
}
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
public static string ParseRawDeviceSerialNumber(string DeviceSerialNumber)
{
if (string.IsNullOrWhiteSpace(DeviceSerialNumber))
return null;
else
return DeviceSerialNumber.Trim();
}
public static bool IsDeviceSerialNumberValid(string DeviceSerialNumber)
{
return DeviceSerialNumber != null && DeviceSerialNumber.Length <= maxLength;
}
}
}
@@ -0,0 +1,98 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class ModelIdImportField : DeviceImportFieldBase
{
private int parsedValue;
private string friendlyValue;
private string friendlyPreviousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.ModelId; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return friendlyValue; } }
public override string FriendlyPreviousValue { get { return friendlyPreviousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
friendlyValue = Value;
// Validate
if (string.IsNullOrWhiteSpace(Value))
this.parsedValue = 1; // Default Model
else
if (!int.TryParse(Value, out parsedValue))
return Error("The Model Identifier must be a number");
var m = Cache.DeviceModels.FirstOrDefault(dm => dm.Id == parsedValue);
if (m == null)
return Error(string.Format("The identifier ({0}) does not match any Device Model", Value));
friendlyValue = string.Format("{0} [{1}]", m.Description, m.Id);
if (ExistingDevice == null)
return Success(EntityState.Added);
else if (ExistingDevice != null && ExistingDevice.DeviceModelId != parsedValue)
{
friendlyPreviousValue = null;
if (ExistingDevice.DeviceModelId.HasValue)
{
var previousModel = Cache.DeviceModels.FirstOrDefault(dm => dm.Id == ExistingDevice.DeviceModelId.Value);
friendlyPreviousValue = string.Format("{0} [{1}]", previousModel.Description, previousModel.Id);
}
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
Device.DeviceModelId = this.parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// 'model' in column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && h.Item1.Item1.IndexOf("model", System.StringComparison.OrdinalIgnoreCase) >= 0);
// All Integers Numbers
possibleColumns = possibleColumns.Where(h =>
{
int lastValue;
return Context.RawData.Select(v => v[h.Item2]).Take(100).Where(v => !string.IsNullOrWhiteSpace(v)).All(v => int.TryParse(v, out lastValue));
}).ToList();
// Multiple Columns, tighten column definition
if (possibleColumns.Count() > 1)
{
possibleColumns = possibleColumns
.Where(h =>
h.Item1.Item1.IndexOf("modelid", StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("model id", StringComparison.OrdinalIgnoreCase) >= 0);
}
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,94 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace Disco.Services.Devices.Importing.Fields
{
internal class ProfileIdImportField : DeviceImportFieldBase
{
private int parsedValue;
private string friendlyValue;
private string friendlyPreviousValue;
public override DeviceImportFieldTypes FieldType { get { return DeviceImportFieldTypes.ProfileId; } }
public override object RawParsedValue { get { return parsedValue; } }
public override string FriendlyValue { get { return friendlyValue; } }
public override string FriendlyPreviousValue { get { return friendlyPreviousValue; } }
public override bool Parse(DiscoDataContext Database, IDeviceImportCache Cache, DeviceImportContext Context, int RecordIndex, string DeviceSerialNumber, Device ExistingDevice, Dictionary<DeviceImportFieldTypes, string> Values, string Value)
{
friendlyValue = Value;
// Validate
if (string.IsNullOrWhiteSpace(Value))
this.parsedValue = Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId;
else
if (!int.TryParse(Value, out parsedValue))
return Error("The Profile Identifier must be a number");
var p = Cache.DeviceProfiles.FirstOrDefault(dp => dp.Id == parsedValue);
if (p == null)
return Error(string.Format("The identifier ({0}) does not match any Device Profile", Value));
friendlyValue = string.Format("{0} [{1}]", p.Description, p.Id);
if (ExistingDevice == null)
return Success(EntityState.Added);
else if (ExistingDevice != null && ExistingDevice.DeviceProfileId != parsedValue)
{
var previousProfile = Cache.DeviceProfiles.FirstOrDefault(dp => dp.Id == ExistingDevice.DeviceProfileId);
friendlyPreviousValue = string.Format("{0} [{1}]", previousProfile.Description, previousProfile.Id);
return Success(EntityState.Modified);
}
else
return Success(EntityState.Unchanged);
}
public override bool Apply(DiscoDataContext Database, Device Device)
{
if (this.FieldAction == EntityState.Added ||
this.FieldAction == EntityState.Modified)
{
Device.DeviceProfileId = this.parsedValue;
return true;
}
else
{
return false;
}
}
public override int? GuessHeader(DiscoDataContext Database, DeviceImportContext Context)
{
// column name
var possibleColumns = Context.Header
.Select((h, i) => Tuple.Create(h, i))
.Where(h => h.Item1.Item2 == DeviceImportFieldTypes.IgnoreColumn && h.Item1.Item1.IndexOf("profile", System.StringComparison.OrdinalIgnoreCase) >= 0);
// All Integers Numbers
possibleColumns = possibleColumns.Where(h =>
{
int lastValue;
return Context.RawData.Select(v => v[h.Item2]).Take(100).Where(v => !string.IsNullOrWhiteSpace(v)).All(v => int.TryParse(v, out lastValue));
}).ToList();
// Multiple Columns, tighten column definition
if (possibleColumns.Count() > 1)
{
possibleColumns = possibleColumns
.Where(h =>
h.Item1.Item1.IndexOf("profileid", StringComparison.OrdinalIgnoreCase) >= 0 ||
h.Item1.Item1.IndexOf("profile id", StringComparison.OrdinalIgnoreCase) >= 0);
}
return possibleColumns.Select(h => (int?)h.Item2).FirstOrDefault();
}
}
}
@@ -0,0 +1,15 @@
using Disco.Models.Repository;
using System.Collections.Generic;
namespace Disco.Services.Devices.Importing
{
internal interface IDeviceImportCache
{
Device FindDevice(string DeviceSerialNumber);
IEnumerable<Device> Devices { get; }
IEnumerable<DeviceModel> DeviceModels { get; }
IEnumerable<DeviceProfile> DeviceProfiles { get; }
IEnumerable<DeviceBatch> DeviceBatches { get; }
}
}
+25 -3
View File
@@ -39,6 +39,9 @@
<Reference Include="EntityFramework">
<HintPath>..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="LumenWorks.Framework.IO">
<HintPath>..\packages\LumenWorks.Framework.IO.3.8.0\lib\net20\LumenWorks.Framework.IO.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.1.2\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
@@ -175,6 +178,27 @@
<Compile Include="Devices\Exporting\DeviceExport.cs" />
<Compile Include="Devices\Exporting\DeviceExportTask.cs" />
<Compile Include="Devices\Exporting\DeviceExportTaskContext.cs" />
<Compile Include="Devices\Importing\DeviceImport.cs" />
<Compile Include="Devices\Importing\DeviceImportApplyTask.cs" />
<Compile Include="Devices\Importing\DeviceImportContext.cs" />
<Compile Include="Devices\Importing\DeviceImportDatabaseCache.cs" />
<Compile Include="Devices\Importing\DeviceImportFieldBase.cs" />
<Compile Include="Devices\Importing\DeviceImportInMemoryCache.cs" />
<Compile Include="Devices\Importing\DeviceImportParseTask.cs" />
<Compile Include="Devices\Importing\DeviceImportRecord.cs" />
<Compile Include="Devices\Importing\Fields\BatchIdImportField.cs" />
<Compile Include="Devices\Importing\Fields\AssignedUserIdImportField.cs" />
<Compile Include="Devices\Importing\Fields\DeviceDecommissionedReasonImportField.cs" />
<Compile Include="Devices\Importing\Fields\DeviceDecommissionedDateImportField.cs" />
<Compile Include="Devices\Importing\Fields\ProfileIdImportField.cs" />
<Compile Include="Devices\Importing\Fields\DetailACAdapterImportField.cs" />
<Compile Include="Devices\Importing\Fields\DetailWLanMacAddressImportField.cs" />
<Compile Include="Devices\Importing\Fields\DetailLanMacAddressImportField.cs" />
<Compile Include="Devices\Importing\Fields\DeviceLocationImportField.cs" />
<Compile Include="Devices\Importing\Fields\DeviceAssetNumberImportField.cs" />
<Compile Include="Devices\Importing\Fields\DeviceSerialNumberImportField.cs" />
<Compile Include="Devices\Importing\Fields\ModelIdImportField.cs" />
<Compile Include="Devices\Importing\IDeviceImportCache.cs" />
<Compile Include="Extensions\DateTimeExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Interop\ActiveDirectory\ActiveDirectory.cs" />
@@ -298,9 +322,7 @@
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<Folder Include="Devices\Importing\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
+1
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="5.0.0" targetFramework="net45" />
<package id="LumenWorks.Framework.IO" version="3.8.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.Core" version="1.1.2" targetFramework="net45" />
@@ -1,11 +1,14 @@
using Disco.BI.Extensions;
using Disco.Models.Services.Devices.Importing;
using Disco.Services.Authorization;
using Disco.Services.Devices.Exporting;
using Disco.Services.Devices.Importing;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
using Disco.Services.Web;
using Disco.Web.Models.Device;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
@@ -530,9 +533,26 @@ namespace Disco.Web.Areas.API.Controllers
#endregion
#region Importing
internal const string ImportSessionCacheKey = "DeviceImportContext_{0}";
internal static void Import_StoreContext(DeviceImportContext Context)
{
string key = string.Format(ImportSessionCacheKey, Context.SessionId);
HttpRuntime.Cache.Insert(key, Context, null, DateTime.Now.AddMinutes(60), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);
}
internal static DeviceImportContext Import_RetrieveContext(string SessionId, bool Remove = false)
{
string key = string.Format(ImportSessionCacheKey, SessionId);
DeviceImportContext context = HttpRuntime.Cache.Get(key) as DeviceImportContext;
if (Remove && context != null)
HttpRuntime.Cache.Remove(key);
return context;
}
[DiscoAuthorize(Claims.Device.Actions.Import)]
public virtual ActionResult ImportParse(HttpPostedFileBase ImportFile)
public virtual ActionResult ImportBegin(HttpPostedFileBase ImportFile, bool HasHeader)
{
if (ImportFile == null || ImportFile.ContentLength == 0)
throw new ArgumentNullException("ImportFile");
@@ -541,22 +561,50 @@ namespace Disco.Web.Areas.API.Controllers
if (fileName.Contains(@"\"))
fileName = fileName.Substring(fileName.LastIndexOf('\\') + 1);
var status = Disco.BI.DeviceBI.Importing.ImportParseTask.Run(ImportFile.InputStream, fileName);
var context = DeviceImport.BeginImport(Database, fileName, HasHeader, ImportFile.InputStream);
Import_StoreContext(context);
status.SetFinishedUrl(Url.Action(MVC.Device.ImportReview(status.SessionId)));
return RedirectToAction(MVC.Device.ImportHeaders(context.SessionId));
}
[DiscoAuthorize(Claims.Device.Actions.Import)]
public virtual ActionResult ImportParse(string Id, List<DeviceImportFieldTypes> Headers)
{
if (string.IsNullOrWhiteSpace(Id))
throw new ArgumentNullException("Id");
var context = Import_RetrieveContext(Id);
if (context == null)
throw new ArgumentException("The Import Session Id is invalid or the session timed out (60 minutes), try importing again", "Id");
context.UpdateHeaderTypes(Headers);
var status = DeviceImportParseTask.ScheduleNow(context);
var finishedUrl = MVC.Device.ImportReview(context.SessionId);
status.SetFinishedUrl(Url.Action(finishedUrl));
if (status.WaitUntilFinished(TimeSpan.FromSeconds(2)))
return RedirectToAction(finishedUrl);
else
return RedirectToAction(MVC.Config.Logging.TaskStatus(status.SessionId));
}
[DiscoAuthorize(Claims.Device.Actions.Import)]
public virtual ActionResult ImportProcess(string ParseTaskSessionKey)
public virtual ActionResult ImportApply(string Id)
{
if (string.IsNullOrWhiteSpace(ParseTaskSessionKey))
throw new ArgumentNullException("ParseTaskSessionKey");
if (string.IsNullOrWhiteSpace(Id))
throw new ArgumentNullException("Id");
var status = Disco.BI.DeviceBI.Importing.ImportProcessTask.Run(ParseTaskSessionKey);
var context = Import_RetrieveContext(Id);
status.SetFinishedUrl(Url.Action(MVC.Device.Index()));
if (context == null)
throw new ArgumentException("The Import Session Id is invalid or the session timed out (60 minutes), try importing again", "Id");
var status = DeviceImportApplyTask.ScheduleNow(context);
status.SetFinishedUrl(Url.Action(MVC.Device.Import(context.SessionId)));
return RedirectToAction(MVC.Config.Logging.TaskStatus(status.SessionId));
}
+254 -21
View File
@@ -1838,15 +1838,15 @@ body .ui-tooltip {
border-radius: 8px;
}
/*!
* Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome
* Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
@font-face {
font-family: 'FontAwesome';
src: url('/ClientSource/Style/FontAwesome/fontawesome-webfont.eot?v=4.0.3');
src: url('/ClientSource/Style/FontAwesome/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('/ClientSource/Style/FontAwesome/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('/ClientSource/Style/FontAwesome/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('/ClientSource/Style/FontAwesome/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');
src: url('/ClientSource/Style/FontAwesome//fontawesome-webfont.eot?v=4.1.0');
src: url('/ClientSource/Style/FontAwesome//fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('/ClientSource/Style/FontAwesome//fontawesome-webfont.woff?v=4.1.0') format('woff'), url('/ClientSource/Style/FontAwesome//fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url('/ClientSource/Style/FontAwesome//fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -1861,7 +1861,7 @@ body .ui-tooltip {
}
/* makes the font 33% larger relative to the icon container */
.fa-lg {
font-size: 1.3333333333333333em;
font-size: 1.33333333em;
line-height: 0.75em;
vertical-align: -15%;
}
@@ -1878,12 +1878,12 @@ body .ui-tooltip {
font-size: 5em;
}
.fa-fw {
width: 1.2857142857142858em;
width: 1.28571429em;
text-align: center;
}
.fa-ul {
padding-left: 0;
margin-left: 2.142857142857143em;
margin-left: 2.14285714em;
list-style-type: none;
}
.fa-ul > li {
@@ -1892,8 +1892,8 @@ body .ui-tooltip {
.fa-li {
position: absolute;
left: -2.14285714em;
width: 2.142857142857143em;
top: 0.14285714285714285em;
width: 2.14285714em;
top: 0.14285714em;
text-align: center;
}
.fa-li.fa-lg {
@@ -1946,19 +1946,13 @@ body .ui-tooltip {
-o-transform: rotate(359deg);
}
}
@-ms-keyframes spin {
0% {
-ms-transform: rotate(0deg);
}
100% {
-ms-transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@@ -2208,6 +2202,8 @@ body .ui-tooltip {
.fa-video-camera:before {
content: "\f03d";
}
.fa-photo:before,
.fa-image:before,
.fa-picture-o:before {
content: "\f03e";
}
@@ -2571,6 +2567,8 @@ body .ui-tooltip {
.fa-square:before {
content: "\f0c8";
}
.fa-navicon:before,
.fa-reorder:before,
.fa-bars:before {
content: "\f0c9";
}
@@ -2630,11 +2628,11 @@ body .ui-tooltip {
content: "\f0dc";
}
.fa-sort-down:before,
.fa-sort-asc:before {
.fa-sort-desc:before {
content: "\f0dd";
}
.fa-sort-up:before,
.fa-sort-desc:before {
.fa-sort-asc:before {
content: "\f0de";
}
.fa-envelope:before {
@@ -2824,12 +2822,10 @@ body .ui-tooltip {
.fa-code:before {
content: "\f121";
}
.fa-mail-reply-all:before,
.fa-reply-all:before {
content: "\f122";
}
.fa-mail-reply-all:before {
content: "\f122";
}
.fa-star-half-empty:before,
.fa-star-half-full:before,
.fa-star-half-o:before {
@@ -3175,6 +3171,238 @@ body .ui-tooltip {
.fa-plus-square-o:before {
content: "\f196";
}
.fa-space-shuttle:before {
content: "\f197";
}
.fa-slack:before {
content: "\f198";
}
.fa-envelope-square:before {
content: "\f199";
}
.fa-wordpress:before {
content: "\f19a";
}
.fa-openid:before {
content: "\f19b";
}
.fa-institution:before,
.fa-bank:before,
.fa-university:before {
content: "\f19c";
}
.fa-mortar-board:before,
.fa-graduation-cap:before {
content: "\f19d";
}
.fa-yahoo:before {
content: "\f19e";
}
.fa-google:before {
content: "\f1a0";
}
.fa-reddit:before {
content: "\f1a1";
}
.fa-reddit-square:before {
content: "\f1a2";
}
.fa-stumbleupon-circle:before {
content: "\f1a3";
}
.fa-stumbleupon:before {
content: "\f1a4";
}
.fa-delicious:before {
content: "\f1a5";
}
.fa-digg:before {
content: "\f1a6";
}
.fa-pied-piper-square:before,
.fa-pied-piper:before {
content: "\f1a7";
}
.fa-pied-piper-alt:before {
content: "\f1a8";
}
.fa-drupal:before {
content: "\f1a9";
}
.fa-joomla:before {
content: "\f1aa";
}
.fa-language:before {
content: "\f1ab";
}
.fa-fax:before {
content: "\f1ac";
}
.fa-building:before {
content: "\f1ad";
}
.fa-child:before {
content: "\f1ae";
}
.fa-paw:before {
content: "\f1b0";
}
.fa-spoon:before {
content: "\f1b1";
}
.fa-cube:before {
content: "\f1b2";
}
.fa-cubes:before {
content: "\f1b3";
}
.fa-behance:before {
content: "\f1b4";
}
.fa-behance-square:before {
content: "\f1b5";
}
.fa-steam:before {
content: "\f1b6";
}
.fa-steam-square:before {
content: "\f1b7";
}
.fa-recycle:before {
content: "\f1b8";
}
.fa-automobile:before,
.fa-car:before {
content: "\f1b9";
}
.fa-cab:before,
.fa-taxi:before {
content: "\f1ba";
}
.fa-tree:before {
content: "\f1bb";
}
.fa-spotify:before {
content: "\f1bc";
}
.fa-deviantart:before {
content: "\f1bd";
}
.fa-soundcloud:before {
content: "\f1be";
}
.fa-database:before {
content: "\f1c0";
}
.fa-file-pdf-o:before {
content: "\f1c1";
}
.fa-file-word-o:before {
content: "\f1c2";
}
.fa-file-excel-o:before {
content: "\f1c3";
}
.fa-file-powerpoint-o:before {
content: "\f1c4";
}
.fa-file-photo-o:before,
.fa-file-picture-o:before,
.fa-file-image-o:before {
content: "\f1c5";
}
.fa-file-zip-o:before,
.fa-file-archive-o:before {
content: "\f1c6";
}
.fa-file-sound-o:before,
.fa-file-audio-o:before {
content: "\f1c7";
}
.fa-file-movie-o:before,
.fa-file-video-o:before {
content: "\f1c8";
}
.fa-file-code-o:before {
content: "\f1c9";
}
.fa-vine:before {
content: "\f1ca";
}
.fa-codepen:before {
content: "\f1cb";
}
.fa-jsfiddle:before {
content: "\f1cc";
}
.fa-life-bouy:before,
.fa-life-saver:before,
.fa-support:before,
.fa-life-ring:before {
content: "\f1cd";
}
.fa-circle-o-notch:before {
content: "\f1ce";
}
.fa-ra:before,
.fa-rebel:before {
content: "\f1d0";
}
.fa-ge:before,
.fa-empire:before {
content: "\f1d1";
}
.fa-git-square:before {
content: "\f1d2";
}
.fa-git:before {
content: "\f1d3";
}
.fa-hacker-news:before {
content: "\f1d4";
}
.fa-tencent-weibo:before {
content: "\f1d5";
}
.fa-qq:before {
content: "\f1d6";
}
.fa-wechat:before,
.fa-weixin:before {
content: "\f1d7";
}
.fa-send:before,
.fa-paper-plane:before {
content: "\f1d8";
}
.fa-send-o:before,
.fa-paper-plane-o:before {
content: "\f1d9";
}
.fa-history:before {
content: "\f1da";
}
.fa-circle-thin:before {
content: "\f1db";
}
.fa-header:before {
content: "\f1dc";
}
.fa-paragraph:before {
content: "\f1dd";
}
.fa-sliders:before {
content: "\f1de";
}
.fa-share-alt:before {
content: "\f1e0";
}
.fa-share-alt-square:before {
content: "\f1e1";
}
.fa-bomb:before {
content: "\f1e2";
}
.tableData {
border: solid 1px #f4f4f4;
border-collapse: collapse;
@@ -4692,3 +4920,8 @@ input:-moz-placeholder {
text-transform: uppercase;
padding: .6em 1em;
}
body .ui-tooltip {
border-width: 1px;
-webkit-box-shadow: none;
box-shadow: none;
}
File diff suppressed because one or more lines are too long
+137 -60
View File
@@ -373,95 +373,172 @@
margin-right: 10px;
color: #1e6dab;
}
#deviceImport #ImportFile {
width: 100%;
#Devices_Import #ImportFile {
width: 96%;
margin-bottom: 8px;
}
#deviceImport #documentation {
#Devices_Import #Devices_Import_Documentation {
width: 700px;
margin: 20px auto;
}
#deviceImport #documentation > table > thead > tr > th:first-child {
width: 100px;
#Devices_Import #Devices_Import_Documentation > table > tbody th:first-child {
width: 220px;
}
#deviceImportReview #errorMessage {
font-weight: bold;
color: red;
#Devices_Import_Completed_Dialog {
padding: 50px 0;
text-align: center;
}
#deviceImportReview #devicesNavigation {
#Devices_Import_Completed_Dialog h3 {
margin-bottom: 16px;
}
#Devices_Import_Completed_Dialog i {
margin-right: 10px;
color: #60a917;
}
#Devices_Import_Loading_Dialog {
padding-top: 50px;
text-align: center;
}
#Devices_Import_Loading_Dialog i {
margin-right: 10px;
color: #1e6dab;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer {
margin: 18px 0;
overflow-x: auto;
border: 1px solid #ccc;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer table > thead {
white-space: nowrap;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer table > thead ul.importHeaderType > li > a > span:not(.ui-menu-icon) {
padding-right: 16px;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer table > thead ul.importHeaderType ul {
z-index: 1000;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer table > thead td.headerIgnoreColumn {
background-color: #fa6800;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer table > thead td:not(.headerIgnoreColumn) {
background-color: #1e6dab;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer table > tbody td.headerDeviceSerialNumber {
border-top-color: #d1e6f7;
border-bottom-color: #d1e6f7;
background-color: #e2f0fa;
}
#Devices_Import_Headers #Devices_Import_Headers_TableContainer table > tbody td.headerIgnoreColumn {
max-width: 150px;
white-space: nowrap;
overflow: hidden;
-ms-text-overflow: ellipsis;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
color: #cccccc;
}
#Devices_Import_Parsing_Dialog {
padding-top: 50px;
text-align: center;
}
#Devices_Import_Parsing_Dialog i {
margin-right: 10px;
color: #1e6dab;
}
#Devices_Import_Review #Devices_Import_Review_Navigation {
margin-top: 15px;
text-align: right;
}
#deviceImportReview #devicesNavigation ul {
#Devices_Import_Review #Devices_Import_Review_Navigation ul {
display: inline-block;
padding: 1px;
padding: 0px;
border: 1px solid #bbb;
}
#deviceImportReview #devicesNavigation ul li {
#Devices_Import_Review #Devices_Import_Review_Navigation ul li {
display: inline-block;
padding: 1px 6px;
padding: 3px 10px;
margin: 0;
}
#deviceImportReview #devicesNavigation ul li.statusError {
background-color: #ffd3d3;
#Devices_Import_Review #Devices_Import_Review_Navigation ul li.actionDetached {
background-color: #ffd0cc;
}
#deviceImportReview #devicesNavigation ul li.statusUpdate {
background-color: #d3f3ff;
#Devices_Import_Review #Devices_Import_Review_Navigation ul li.actionModified {
background-color: #e2f0fa;
}
#deviceImportReview #devicesNavigation ul li.statusNew {
background-color: #d9ffb4;
#Devices_Import_Review #Devices_Import_Review_Navigation ul li.actionAdded {
background-color: #e7f9d5;
}
#deviceImportReview #devices {
border: solid 1px #f4f4f4;
border-collapse: collapse;
margin-top: 6px;
}
#deviceImportReview #devices > tbody > tr > td {
border: solid 1px #f4f4f4;
background-color: #ffffff;
}
#deviceImportReview #devices > tbody > tr:nth-child(odd) > td {
#Devices_Import_Review #Devices_Import_Review_Navigation ul li.actionUnchanged {
background-color: #fcfcfc;
}
#deviceImportReview #devices > thead > tr > th,
#deviceImportReview #devices > tbody > tr > th {
background-color: #f4f4f4;
border: solid 1px #f4f4f4;
#Devices_Import_Review #Devices_Import_Review_TableContainer {
margin: 18px 0;
overflow-x: auto;
border: 1px solid #ccc;
}
#deviceImportReview #devices > tbody > tr:hover > td {
background-color: #fefefe;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > thead {
white-space: nowrap;
}
#deviceImportReview #devices > tbody > tr:hover:nth-child(odd) > td {
background-color: #fafafa;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > thead tr:nth-child(2) th {
padding-top: 0;
font-weight: normal;
font-size: 0.9em;
}
#deviceImportReview #devices > tfoot > tr > th,
#deviceImportReview #devices > tfoot > tr > td {
background-color: #f4f4f4;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.action {
text-align: center;
}
#deviceImportReview #devices > tbody td {
vertical-align: middle;
min-height: 32px;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionDetached td.action i:before {
color: #e51400;
content: "\f12a";
}
#deviceImportReview #devices > tbody tr.statusError td {
background-color: #ffd3d3;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionDetached td {
background-color: #ffe7e5;
}
#deviceImportReview #devices > tbody tr.statusUpdate td {
background-color: #d3f3ff;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionUnchanged td.action i:before {
content: "\f068";
}
#deviceImportReview #devices > tbody tr.statusNew td {
background-color: #d9ffb4;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionUnchanged td {
background-color: #fcfcfc;
}
#deviceImportReview #devices > tbody tr td.action {
font-weight: bold;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionUnchanged td:nth-child(n+3) {
color: #cccccc;
}
#deviceImportReview #devices > tbody tr td.serialNumber {
font-family: Consolas, "Courier New", monospace;
font-weight: bold;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionModified td.action i:before {
color: #1e6dab;
content: "\f040";
}
#deviceImportReview #devices > tbody tr td.model img.modelImage {
width: 32px;
height: 32px;
margin-right: 2px;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionModified td {
background-color: #f4f9fd;
}
#deviceImportReview #devices > tbody .error {
font-weight: bold;
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionAdded td.action i:before {
color: #60a917;
content: "\f067";
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionAdded td {
background-color: #e7f9d5;
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr:not(.actionUnchanged) td.actionUnchanged:nth-child(n+3):not(.headerDeviceSerialNumber) {
color: #cccccc;
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.actionError {
color: #e51400;
background-color: #fff1ef;
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.actionError span.errorMessage {
display: none;
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.actionModified {
background-color: #e2f0fa !important;
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.headerDeviceSerialNumber,
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.headerDeviceDecommissionedDate,
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.headerDeviceDecommissionedReason,
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.headerModelId,
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.headerBatchId,
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.headerProfileId,
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody td.headerAssignedUserId {
white-space: nowrap;
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody span.smallMessage {
color: inherit;
}
+198 -66
View File
@@ -357,118 +357,250 @@
}
}
#deviceImport {
#Devices_Import {
#ImportFile {
width: 100%;
width: 96%;
margin-bottom: 8px;
}
#documentation {
#Devices_Import_Documentation {
width: 700px;
margin: 20px auto;
& > table {
& > thead > tr > th:first-child {
width: 100px;
& > table > tbody th:first-child {
width: 220px;
}
}
}
#Devices_Import_Completed_Dialog {
padding: 50px 0;
text-align: center;
h3 {
margin-bottom: 16px;
}
i {
margin-right: 10px;
color: @StatusSuccess;
}
}
#Devices_Import_Loading_Dialog {
padding-top: 50px;
text-align: center;
i {
margin-right: 10px;
color: @StatusInformation;
}
}
#Devices_Import_Headers {
#Devices_Import_Headers_TableContainer {
margin: 18px 0;
overflow-x: auto;
border: 1px solid #ccc;
table {
& > thead {
white-space: nowrap;
ul.importHeaderType {
& > li > a > span:not(.ui-menu-icon) {
padding-right: 16px;
}
ul {
z-index: 1000;
}
}
td.headerIgnoreColumn {
background-color: @StatusAlert;
}
td:not(.headerIgnoreColumn) {
background-color: @StatusInformation;
}
}
& > tbody {
td.headerDeviceSerialNumber {
border-top-color: lighten(@StatusInformation, 50%);
border-bottom-color: lighten(@StatusInformation, 50%);
background-color: lighten(@StatusInformation, 54%);
}
td.headerIgnoreColumn {
max-width: 150px;
white-space: nowrap;
overflow: hidden;
-ms-text-overflow: ellipsis;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
color: @SubtleBorderColour;
}
}
}
}
}
#deviceImportReview {
#Devices_Import_Parsing_Dialog {
padding-top: 50px;
text-align: center;
#errorMessage {
font-weight: bold;
color: red;
i {
margin-right: 10px;
color: @StatusInformation;
}
}
#devicesNavigation {
// Icons used within Devices_Import_Review
@import "FontAwesome\variables.less";
#Devices_Import_Review {
#Devices_Import_Review_Navigation {
margin-top: 15px;
text-align: right;
ul {
display: inline-block;
padding: 1px;
padding: 0px;
border: 1px solid #bbb;
li {
display: inline-block;
padding: 1px 6px;
padding: 3px 10px;
margin: 0;
&.statusError {
background-color: #ffd3d3;
&.actionDetached {
background-color: lighten(@StatusError, 45%);
}
&.statusUpdate {
background-color: #d3f3ff;
&.actionModified {
background-color: lighten(@StatusInformation, 54%);
}
&.statusNew {
background-color: #d9ffb4;
&.actionAdded {
background-color: lighten(@StatusSuccess, 53%);
}
&.actionUnchanged {
background-color: @TableDataRowBackgroundColor;
}
}
}
}
#devices {
.tableData;
margin-top: 6px;
#Devices_Import_Review_TableContainer {
margin: 18px 0;
overflow-x: auto;
border: 1px solid #ccc;
table {
& > thead {
white-space: nowrap;
tr:nth-child(2) th {
padding-top: 0;
font-weight: normal;
font-size: 0.9em;
}
}
& > tbody {
td {
vertical-align: middle;
min-height: 32px;
}
tr.statusError td {
background-color: #ffd3d3;
}
tr.statusUpdate td {
background-color: #d3f3ff;
}
tr.statusNew td {
background-color: #d9ffb4;
}
tr {
td.action {
font-weight: bold;
text-align: center;
}
td.serialNumber {
font-family: @FontFamilyMono;
font-weight: bold;
}
td.model {
img.modelImage {
width: 32px;
height: 32px;
margin-right: 2px;
tr.actionDetached {
td.action {
i:before {
color: @StatusError;
content: @fa-var-exclamation;
}
}
td.profile {
}
td.batch {
}
td.assignedUser {
}
td.location {
}
td.assetNumber {
td {
background-color: lighten(@StatusError, 50%);
}
}
.error {
font-weight: bold;
tr.actionUnchanged {
td.action {
i:before {
content: @fa-var-minus;
}
}
td {
background-color: @TableDataRowBackgroundColor;
}
td:nth-child(n+3) {
color: @SubtleBorderColour;
}
}
tr.actionModified {
td.action {
i:before {
color: @StatusInformation;
content: @fa-var-pencil;
}
}
td {
background-color: lighten(@StatusInformation, 58%);
}
}
tr.actionAdded {
td.action {
i:before {
color: @StatusSuccess;
content: @fa-var-plus;
}
}
td {
background-color: lighten(@StatusSuccess, 53%);
}
}
tr:not(.actionUnchanged) {
td.actionUnchanged:nth-child(n+3):not(.headerDeviceSerialNumber) {
color: @SubtleBorderColour;
}
}
td.actionError {
color: @StatusError;
background-color: lighten(@StatusError, 52%);
span.errorMessage {
display: none;
}
}
td.actionModified {
background-color: lighten(@StatusInformation, 54%) !important;
}
td.headerDeviceSerialNumber, td.headerDeviceDecommissionedDate, td.headerDeviceDecommissionedReason,
td.headerModelId, td.headerBatchId, td.headerProfileId,
td.headerAssignedUserId {
white-space: nowrap;
}
span.smallMessage {
color: inherit;
}
}
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -274,3 +274,8 @@ input:-moz-placeholder {
text-transform: uppercase;
padding: .6em 1em;
}
body .ui-tooltip {
border-width: 1px;
-webkit-box-shadow: none;
box-shadow: none;
}
@@ -158,10 +158,8 @@ input:-moz-placeholder {
.ui-dialog {
animation-name: ui-dialog-show;
-webkit-animation-name: ui-dialog-show;
animation-duration: .2s;
-webkit-animation-duration: .2s;
animation-timing-function: ease-in-out;
-webkit-animation-timing-function: ease-in-out;
}
@@ -171,6 +169,7 @@ input:-moz-placeholder {
transform: translateY(-30px);
opacity: 0.0;
}
100% {
transform: translateY(0);
opacity: 1;
@@ -182,6 +181,7 @@ input:-moz-placeholder {
-webkit-transform: translateY(-30px);
opacity: 0.0;
}
100% {
-webkit-transform: translateY(0);
opacity: 1;
@@ -191,10 +191,8 @@ input:-moz-placeholder {
.ui-widget-overlay.ui-front {
animation-name: ui-dialog-fadeIn;
-webkit-animation-name: ui-dialog-fadeIn;
animation-duration: .2s;
-webkit-animation-duration: .2s;
animation-timing-function: ease-in-out;
-webkit-animation-timing-function: ease-in-out;
}
@@ -203,6 +201,7 @@ input:-moz-placeholder {
0% {
opacity: 0.0;
}
100% {
opacity: 0.5;
}
@@ -212,6 +211,7 @@ input:-moz-placeholder {
0% {
opacity: 0.0;
}
100% {
opacity: 0.5;
}
@@ -243,3 +243,10 @@ input:-moz-placeholder {
text-transform: uppercase;
padding: .6em 1em;
}
// Tooltip
body .ui-tooltip {
border-width: 1px;
-webkit-box-shadow: none;
box-shadow: none;
}
File diff suppressed because one or more lines are too long
+48 -17
View File
@@ -124,36 +124,67 @@ namespace Disco.Web.Controllers
#endregion
#region Import/Export
[DiscoAuthorizeAny(Claims.Device.Actions.Import, Claims.Device.Actions.Export), HttpGet]
public virtual ActionResult ImportExport()
#region Import
[DiscoAuthorize(Claims.Device.Actions.Import), HttpGet]
public virtual ActionResult Import(string Id)
{
Models.Device.ImportModel m = new Models.Device.ImportModel();
var m = new Models.Device.ImportModel()
{
DeviceModels = Database.DeviceModels.ToList(),
DeviceProfiles = Database.DeviceProfiles.ToList(),
DeviceBatches = Database.DeviceBatches.ToList()
};
if (Authorization.Has(Claims.Device.Actions.Import))
{
m.DeviceModels = Database.DeviceModels.ToList();
m.DeviceProfiles = Database.DeviceProfiles.ToList();
m.DeviceBatches = Database.DeviceBatches.ToList();
}
if (!string.IsNullOrWhiteSpace(Id))
m.CompletedImportSessionContext = Areas.API.Controllers.DeviceController.Import_RetrieveContext(Id, Remove: true);
// UI Extensions
UIExtensions.ExecuteExtensions<DeviceImportModel>(this.ControllerContext, m);
return View(m);
}
[DiscoAuthorize(Claims.Device.Actions.Import), HttpGet]
public virtual ActionResult ImportReview(string ImportParseTaskId)
public virtual ActionResult ImportHeaders(string Id)
{
if (string.IsNullOrWhiteSpace(ImportParseTaskId))
throw new ArgumentNullException("ImportParseTaskId");
if (string.IsNullOrWhiteSpace(Id))
throw new ArgumentNullException("Id");
var session = Disco.BI.DeviceBI.Importing.Import.GetSession(ImportParseTaskId);
var context = Areas.API.Controllers.DeviceController.Import_RetrieveContext(Id);
if (session == null)
throw new ArgumentException("The Import Parse Task Id is invalid or the session timed out (60 minutes), try importing again", "ImportParseTaskId");
if (context == null)
throw new ArgumentException("The Import Session Id is invalid or the session timed out (60 minutes), try importing again", "Id");
Models.Device.ImportReviewModel m = Models.Device.ImportReviewModel.FromImportDeviceSession(session);
var m = new Models.Device.ImportHeadersModel()
{
Context = context
};
// UI Extensions
UIExtensions.ExecuteExtensions<DeviceImportHeadersModel>(this.ControllerContext, m);
return View(m);
}
[DiscoAuthorize(Claims.Device.Actions.Import), HttpGet]
public virtual ActionResult ImportReview(string Id)
{
if (string.IsNullOrWhiteSpace(Id))
throw new ArgumentNullException("Id");
var context = Areas.API.Controllers.DeviceController.Import_RetrieveContext(Id);
if (context == null)
throw new ArgumentException("The Import Session Id is invalid or the session timed out (60 minutes), try importing again", "Id");
var m = new Models.Device.ImportReviewModel()
{
Context = context,
StatisticErrorRecords = context.Records.Count(r => r.HasError),
StatisticNewRecords = context.Records.Count(r => r.RecordAction == System.Data.EntityState.Added),
StatisticModifiedRecords = context.Records.Count(r => r.RecordAction == System.Data.EntityState.Modified),
StatisticUnmodifiedRecords = context.Records.Count(r => r.RecordAction == System.Data.EntityState.Unchanged)
};
// UI Extensions
UIExtensions.ExecuteExtensions<DeviceImportReviewModel>(this.ControllerContext, m);
+33 -5
View File
@@ -545,7 +545,13 @@
</Compile>
<Compile Include="Extensions\T4MVCExtensions.cs" />
<Compile Include="Models\Device\ExportModel.cs" />
<Compile Include="Models\Device\ImportHeadersModel.cs" />
<Compile Include="Models\InitialConfig\AdministratorsModel.cs" />
<Compile Include="Views\Device\ImportHeaders.generated.cs">
<DependentUpon>ImportHeaders.cshtml</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="Views\Device\Export.generated.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@@ -663,10 +669,10 @@
<DesignTime>True</DesignTime>
<DependentUpon>_Subject.cshtml</DependentUpon>
</Compile>
<Compile Include="Views\Device\ImportExport.generated.cs">
<Compile Include="Views\Device\Import.generated.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>ImportExport.cshtml</DependentUpon>
<DependentUpon>Import.cshtml</DependentUpon>
</Compile>
<Compile Include="Views\Device\ImportReview.generated.cs">
<AutoGen>True</AutoGen>
@@ -1253,6 +1259,18 @@
<DependentUpon>ExpressionEditor.css</DependentUpon>
</Content>
<None Include="ClientSource\Style\Fancytree\disco.fancytree.less" />
<None Include="ClientSource\Style\FontAwesome\bordered-pulled.less" />
<None Include="ClientSource\Style\FontAwesome\core.less" />
<None Include="ClientSource\Style\FontAwesome\fixed-width.less" />
<None Include="ClientSource\Style\FontAwesome\icons.less" />
<None Include="ClientSource\Style\FontAwesome\larger.less" />
<None Include="ClientSource\Style\FontAwesome\list.less" />
<None Include="ClientSource\Style\FontAwesome\mixins.less" />
<None Include="ClientSource\Style\FontAwesome\path.less" />
<None Include="ClientSource\Style\FontAwesome\rotated-flipped.less" />
<None Include="ClientSource\Style\FontAwesome\spinning.less" />
<None Include="ClientSource\Style\FontAwesome\stacked.less" />
<None Include="ClientSource\Style\FontAwesome\variables.less" />
<None Include="ClientSource\Style\Images\Actions\dataTableFirst.png" />
<None Include="ClientSource\Style\Images\Actions\dataTableFirstDisabled.png" />
<None Include="ClientSource\Style\Images\Actions\dataTableLast.png" />
@@ -1347,6 +1365,12 @@
<None Include="ClientSource\Style\normalize.min.css">
<DependentUpon>normalize.less</DependentUpon>
</None>
<Content Include="ClientSource\Style\jQueryUI\jquery-ui.css">
<DependentUpon>jquery-ui.less</DependentUpon>
</Content>
<Content Include="ClientSource\Style\jQueryUI\jquery-ui.min.css">
<DependentUpon>jquery-ui.less</DependentUpon>
</Content>
<Content Include="ClientSource\Style\Public\HeldDevices.css">
<DependentUpon>HeldDevices.less</DependentUpon>
</Content>
@@ -1526,6 +1550,10 @@
<Generator>RazorGenerator</Generator>
<LastGenOutput>AddOffline.generated.cs</LastGenOutput>
</None>
<None Include="Views\Device\ImportHeaders.cshtml">
<Generator>RazorGenerator</Generator>
<LastGenOutput>ImportHeaders.generated.cs</LastGenOutput>
</None>
<None Include="Views\Device\DeviceParts\_Details.cshtml">
<Generator>RazorGenerator</Generator>
<LastGenOutput>_Details.generated.cs</LastGenOutput>
@@ -1554,9 +1582,9 @@
<Generator>RazorGenerator</Generator>
<LastGenOutput>Export.generated.cs</LastGenOutput>
</None>
<None Include="Views\Device\ImportExport.cshtml">
<None Include="Views\Device\Import.cshtml">
<Generator>RazorGenerator</Generator>
<LastGenOutput>ImportExport.generated.cs</LastGenOutput>
<LastGenOutput>Import.generated.cs</LastGenOutput>
</None>
<None Include="Views\Device\ImportReview.cshtml">
<Generator>RazorGenerator</Generator>
@@ -2063,7 +2091,7 @@
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
<UserProperties BuildVersion_StartDate="2011/7/1" BuildVersion_BuildAction="Both" BuildVersion_UseGlobalSettings="False" BuildVersion_DetectChanges="False" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" />
<UserProperties BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_DetectChanges="False" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildAction="Both" BuildVersion_StartDate="2011/7/1" />
</VisualStudio>
</ProjectExtensions>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
@@ -0,0 +1,25 @@
using Disco.Models.Services.Devices.Importing;
using Disco.Models.UI.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Disco.Web.Models.Device
{
public class ImportHeadersModel : DeviceImportHeadersModel
{
public IDeviceImportContext Context { get; set; }
public IEnumerable<Tuple<DeviceImportFieldTypes, string>> HeaderTypes { get; set; }
public ImportHeadersModel()
{
HeaderTypes = typeof(DeviceImportFieldTypes)
.GetFields()
.Select(p => Tuple.Create(p, (DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault()))
.Where(p => p.Item2 != null)
.Select(p => Tuple.Create((DeviceImportFieldTypes)p.Item1.GetRawConstantValue(), p.Item2.Name)).ToList();
}
}
}
+20 -1
View File
@@ -1,4 +1,5 @@
using Disco.Models.Repository;
using Disco.Models.Services.Devices.Importing;
using Disco.Models.UI.Device;
using System;
using System.Collections.Generic;
@@ -10,11 +11,29 @@ namespace Disco.Web.Models.Device
{
public class ImportModel : DeviceImportModel
{
[Required, Display(Name="CSV Import File")]
[Required, Display(Name = "CSV Import File")]
public HttpPostedFileBase ImportFile { get; set; }
[Required, Display(Name = "Has Header")]
public bool HasHeader { get; set; }
public IDeviceImportContext CompletedImportSessionContext { get; set; }
public List<DeviceModel> DeviceModels { get; set; }
public List<DeviceProfile> DeviceProfiles { get; set; }
public List<DeviceBatch> DeviceBatches { get; set; }
public IEnumerable<Tuple<string, string, string>> HeaderTypes { get; set; }
public ImportModel()
{
HasHeader = true;
HeaderTypes = typeof(DeviceImportFieldTypes)
.GetFields()
.Select(p => Tuple.Create(p, (DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault()))
.Where(p => p.Item2 != null && p.Item1.Name != DeviceImportFieldTypes.IgnoreColumn.ToString())
.Select(p => Tuple.Create(p.Item1.Name, p.Item2.Name, p.Item2.Description)).ToList();
}
}
}
+20 -11
View File
@@ -1,26 +1,35 @@
using Disco.Models.BI.Device;
using Disco.Models.Services.Devices.Importing;
using Disco.Models.UI.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace Disco.Web.Models.Device
{
public class ImportReviewModel : DeviceImportReviewModel
{
public string ImportParseTaskId { get; set; }
public string ImportFilename { get; set; }
public List<ImportDevice> ImportDevices { get; set; }
public IDeviceImportContext Context { get; set; }
public static ImportReviewModel FromImportDeviceSession(ImportDeviceSession session)
public int StatisticErrorRecords { get; set; }
public int StatisticNewRecords { get; set; }
public int StatisticModifiedRecords { get; set; }
public int StatisticUnmodifiedRecords { get; set; }
public int StatisticImportRecords
{
return new ImportReviewModel()
get { return this.StatisticNewRecords + StatisticModifiedRecords; }
}
public IEnumerable<Tuple<DeviceImportFieldTypes, string>> HeaderTypes { get; set; }
public ImportReviewModel()
{
ImportParseTaskId = session.ImportParseTaskId,
ImportFilename = session.ImportFilename,
ImportDevices = session.ImportDevices
};
HeaderTypes = typeof(DeviceImportFieldTypes)
.GetFields()
.Select(p => Tuple.Create(p, (DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault()))
.Where(p => p.Item2 != null)
.Select(p => Tuple.Create((DeviceImportFieldTypes)p.Item1.GetRawConstantValue(), p.Item2.Name)).ToList();
}
}
}
+119 -31
View File
@@ -558,12 +558,24 @@ namespace Links
private const string URLPATH = "~/ClientSource/Style/FontAwesome";
public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); }
public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); }
public static readonly string bordered_pulled_less = Url("bordered-pulled.less");
public static readonly string core_less = Url("core.less");
public static readonly string fixed_width_less = Url("fixed-width.less");
public static readonly string font_awesome_less = Url("font-awesome.less");
public static readonly string fontawesome_webfont_eot = Url("fontawesome-webfont.eot");
public static readonly string fontawesome_webfont_svg = Url("fontawesome-webfont.svg");
public static readonly string fontawesome_webfont_ttf = Url("fontawesome-webfont.ttf");
public static readonly string fontawesome_webfont_woff = Url("fontawesome-webfont.woff");
public static readonly string FontAwesome_otf = Url("FontAwesome.otf");
public static readonly string icons_less = Url("icons.less");
public static readonly string larger_less = Url("larger.less");
public static readonly string list_less = Url("list.less");
public static readonly string mixins_less = Url("mixins.less");
public static readonly string path_less = Url("path.less");
public static readonly string rotated_flipped_less = Url("rotated-flipped.less");
public static readonly string spinning_less = Url("spinning.less");
public static readonly string stacked_less = Url("stacked.less");
public static readonly string variables_less = Url("variables.less");
}
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
@@ -683,6 +695,9 @@ namespace Links
}
public static readonly string jquery_ui_less = Url("jquery-ui.less");
public static readonly string jquery_ui_css = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/jquery-ui.min.css") ? Url("jquery-ui.min.css") : Url("jquery-ui.css");
public static readonly string jquery_ui_min_css = Url("jquery-ui.min.css");
}
public static readonly string jQueryUIExtensions_less = Url("jQueryUIExtensions.less");
@@ -889,6 +904,18 @@ namespace Disco.Web.Controllers
}
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Web.Mvc.ActionResult Import()
{
return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Import);
}
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Web.Mvc.ActionResult ImportHeaders()
{
return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportHeaders);
}
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Web.Mvc.ActionResult ImportReview()
{
return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportReview);
@@ -918,7 +945,8 @@ namespace Disco.Web.Controllers
public readonly string Index = "Index";
public readonly string AddOffline = "AddOffline";
public readonly string Export = "Export";
public readonly string ImportExport = "ImportExport";
public readonly string Import = "Import";
public readonly string ImportHeaders = "ImportHeaders";
public readonly string ImportReview = "ImportReview";
public readonly string Show = "Show";
}
@@ -929,7 +957,8 @@ namespace Disco.Web.Controllers
public const string Index = "Index";
public const string AddOffline = "AddOffline";
public const string Export = "Export";
public const string ImportExport = "ImportExport";
public const string Import = "Import";
public const string ImportHeaders = "ImportHeaders";
public const string ImportReview = "ImportReview";
public const string Show = "Show";
}
@@ -953,13 +982,29 @@ namespace Disco.Web.Controllers
public readonly string ExportType = "ExportType";
public readonly string ExportTypeTargetId = "ExportTypeTargetId";
}
static readonly ActionParamsClass_Import s_params_Import = new ActionParamsClass_Import();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public ActionParamsClass_Import ImportParams { get { return s_params_Import; } }
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionParamsClass_Import
{
public readonly string Id = "Id";
}
static readonly ActionParamsClass_ImportHeaders s_params_ImportHeaders = new ActionParamsClass_ImportHeaders();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public ActionParamsClass_ImportHeaders ImportHeadersParams { get { return s_params_ImportHeaders; } }
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionParamsClass_ImportHeaders
{
public readonly string Id = "Id";
}
static readonly ActionParamsClass_ImportReview s_params_ImportReview = new ActionParamsClass_ImportReview();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public ActionParamsClass_ImportReview ImportReviewParams { get { return s_params_ImportReview; } }
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionParamsClass_ImportReview
{
public readonly string ImportParseTaskId = "ImportParseTaskId";
public readonly string Id = "Id";
}
static readonly ActionParamsClass_Show s_params_Show = new ActionParamsClass_Show();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
@@ -983,7 +1028,8 @@ namespace Disco.Web.Controllers
public readonly string _ViewStart = "_ViewStart";
public readonly string AddOffline = "AddOffline";
public readonly string Export = "Export";
public readonly string ImportExport = "ImportExport";
public readonly string Import = "Import";
public readonly string ImportHeaders = "ImportHeaders";
public readonly string ImportReview = "ImportReview";
public readonly string Index = "Index";
public readonly string Show = "Show";
@@ -992,7 +1038,8 @@ namespace Disco.Web.Controllers
public readonly string _ViewStart = "~/Views/Device/_ViewStart.cshtml";
public readonly string AddOffline = "~/Views/Device/AddOffline.cshtml";
public readonly string Export = "~/Views/Device/Export.cshtml";
public readonly string ImportExport = "~/Views/Device/ImportExport.cshtml";
public readonly string Import = "~/Views/Device/Import.cshtml";
public readonly string ImportHeaders = "~/Views/Device/ImportHeaders.cshtml";
public readonly string ImportReview = "~/Views/Device/ImportReview.cshtml";
public readonly string Index = "~/Views/Device/Index.cshtml";
public readonly string Show = "~/Views/Device/Show.cshtml";
@@ -1067,22 +1114,33 @@ namespace Disco.Web.Controllers
return callInfo;
}
partial void ImportExportOverride(T4MVC_System_Web_Mvc_ActionResult callInfo);
partial void ImportOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id);
public override System.Web.Mvc.ActionResult ImportExport()
public override System.Web.Mvc.ActionResult Import(string Id)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportExport);
ImportExportOverride(callInfo);
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Import);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id);
ImportOverride(callInfo, Id);
return callInfo;
}
partial void ImportReviewOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string ImportParseTaskId);
partial void ImportHeadersOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id);
public override System.Web.Mvc.ActionResult ImportReview(string ImportParseTaskId)
public override System.Web.Mvc.ActionResult ImportHeaders(string Id)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportHeaders);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id);
ImportHeadersOverride(callInfo, Id);
return callInfo;
}
partial void ImportReviewOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id);
public override System.Web.Mvc.ActionResult ImportReview(string Id)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportReview);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "ImportParseTaskId", ImportParseTaskId);
ImportReviewOverride(callInfo, ImportParseTaskId);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id);
ImportReviewOverride(callInfo, Id);
return callInfo;
}
@@ -3551,15 +3609,21 @@ namespace Disco.Web.Areas.API.Controllers
}
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Web.Mvc.ActionResult ImportBegin()
{
return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportBegin);
}
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Web.Mvc.ActionResult ImportParse()
{
return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportParse);
}
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Web.Mvc.ActionResult ImportProcess()
public virtual System.Web.Mvc.ActionResult ImportApply()
{
return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportProcess);
return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportApply);
}
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
@@ -3608,8 +3672,9 @@ namespace Disco.Web.Areas.API.Controllers
public readonly string Attachment = "Attachment";
public readonly string Attachments = "Attachments";
public readonly string AttachmentRemove = "AttachmentRemove";
public readonly string ImportBegin = "ImportBegin";
public readonly string ImportParse = "ImportParse";
public readonly string ImportProcess = "ImportProcess";
public readonly string ImportApply = "ImportApply";
public readonly string Export = "Export";
public readonly string ExportRetrieve = "ExportRetrieve";
public readonly string MigrateDeviceMacAddressesFromLog = "MigrateDeviceMacAddressesFromLog";
@@ -3637,8 +3702,9 @@ namespace Disco.Web.Areas.API.Controllers
public const string Attachment = "Attachment";
public const string Attachments = "Attachments";
public const string AttachmentRemove = "AttachmentRemove";
public const string ImportBegin = "ImportBegin";
public const string ImportParse = "ImportParse";
public const string ImportProcess = "ImportProcess";
public const string ImportApply = "ImportApply";
public const string Export = "Export";
public const string ExportRetrieve = "ExportRetrieve";
public const string MigrateDeviceMacAddressesFromLog = "MigrateDeviceMacAddressesFromLog";
@@ -3821,21 +3887,31 @@ namespace Disco.Web.Areas.API.Controllers
{
public readonly string id = "id";
}
static readonly ActionParamsClass_ImportBegin s_params_ImportBegin = new ActionParamsClass_ImportBegin();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public ActionParamsClass_ImportBegin ImportBeginParams { get { return s_params_ImportBegin; } }
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionParamsClass_ImportBegin
{
public readonly string ImportFile = "ImportFile";
public readonly string HasHeader = "HasHeader";
}
static readonly ActionParamsClass_ImportParse s_params_ImportParse = new ActionParamsClass_ImportParse();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public ActionParamsClass_ImportParse ImportParseParams { get { return s_params_ImportParse; } }
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionParamsClass_ImportParse
{
public readonly string ImportFile = "ImportFile";
public readonly string Id = "Id";
public readonly string Headers = "Headers";
}
static readonly ActionParamsClass_ImportProcess s_params_ImportProcess = new ActionParamsClass_ImportProcess();
static readonly ActionParamsClass_ImportApply s_params_ImportApply = new ActionParamsClass_ImportApply();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public ActionParamsClass_ImportProcess ImportProcessParams { get { return s_params_ImportProcess; } }
public ActionParamsClass_ImportApply ImportApplyParams { get { return s_params_ImportApply; } }
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class ActionParamsClass_ImportProcess
public class ActionParamsClass_ImportApply
{
public readonly string ParseTaskSessionKey = "ParseTaskSessionKey";
public readonly string Id = "Id";
}
static readonly ActionParamsClass_Export s_params_Export = new ActionParamsClass_Export();
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
@@ -4086,23 +4162,35 @@ namespace Disco.Web.Areas.API.Controllers
return callInfo;
}
partial void ImportParseOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, System.Web.HttpPostedFileBase ImportFile);
partial void ImportBeginOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, System.Web.HttpPostedFileBase ImportFile, bool HasHeader);
public override System.Web.Mvc.ActionResult ImportParse(System.Web.HttpPostedFileBase ImportFile)
public override System.Web.Mvc.ActionResult ImportBegin(System.Web.HttpPostedFileBase ImportFile, bool HasHeader)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportParse);
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportBegin);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "ImportFile", ImportFile);
ImportParseOverride(callInfo, ImportFile);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "HasHeader", HasHeader);
ImportBeginOverride(callInfo, ImportFile, HasHeader);
return callInfo;
}
partial void ImportProcessOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string ParseTaskSessionKey);
partial void ImportParseOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id, System.Collections.Generic.List<Disco.Models.Services.Devices.Importing.DeviceImportFieldTypes> Headers);
public override System.Web.Mvc.ActionResult ImportProcess(string ParseTaskSessionKey)
public override System.Web.Mvc.ActionResult ImportParse(string Id, System.Collections.Generic.List<Disco.Models.Services.Devices.Importing.DeviceImportFieldTypes> Headers)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportProcess);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "ParseTaskSessionKey", ParseTaskSessionKey);
ImportProcessOverride(callInfo, ParseTaskSessionKey);
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportParse);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Headers", Headers);
ImportParseOverride(callInfo, Id, Headers);
return callInfo;
}
partial void ImportApplyOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id);
public override System.Web.Mvc.ActionResult ImportApply(string Id)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.ImportApply);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id);
ImportApplyOverride(callInfo, Id);
return callInfo;
}
+1 -1
View File
@@ -184,7 +184,7 @@
</script>
}
<div id="Devices_Export_Exporting" class="dialog" title="Exporting Devices...">
<h4><i class="fa fa-lg fa-cog fa-spin" title="Please Wait"></i>Device Exporting devices...</h4>
<h4><i class="fa fa-lg fa-cog fa-spin" title="Please Wait"></i>Exporting devices...</h4>
</div>
<div class="actionBar">
<a id="Devices_Export_Button" href="#" class="button">Export Devices</a>
+1 -1
View File
@@ -640,7 +640,7 @@ WriteLiteral(" class=\"fa fa-lg fa-cog fa-spin\"");
WriteLiteral(" title=\"Please Wait\"");
WriteLiteral("></i>Device Exporting devices...</h4>\r\n</div>\r\n<div");
WriteLiteral("></i>Exporting devices...</h4>\r\n</div>\r\n<div");
WriteLiteral(" class=\"actionBar\"");
+232
View File
@@ -0,0 +1,232 @@
@model Disco.Web.Models.Device.ImportModel
@using Disco.Models.Services.Devices.Importing;
@{
Authorization.Require(Claims.Device.Actions.Import);
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import Devices");
}
<div id="Devices_Import">
@using (Html.BeginForm(MVC.API.Device.ImportBegin(), FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div id="importDialog" class="form" style="width: 450px">
<h2>Import Devices</h2>
<table>
<tr>
<th>
@Html.LabelFor(m => m.ImportFile)
</th>
<td>
<input id="ImportFile" name="ImportFile" type="file" data-val="true" data-val-required="An Import File is required." /><br />
@Html.ValidationMessageFor(m => m.ImportFile)
<div>
@Html.CheckBoxFor(m => m.HasHeader)
@Html.LabelFor(m => m.HasHeader)
</div>
</td>
</tr>
</table>
<p class="actions">
<input type="submit" class="button" value="Begin Import" />
</p>
</div>
<div id="Devices_Import_Loading_Dialog" class="dialog" title="Loading devices import...">
<h4><i class="fa fa-lg fa-cog fa-spin" title="Please Wait"></i>Loading device import...</h4>
</div>
<script>
$(function () {
var $Devices_Import_Loading_Dialog = null;
$('#ImportFile').closest('form').submit(function () {
if ($Devices_Import_Loading_Dialog == null) {
$Devices_Import_Loading_Dialog = $('#Devices_Import_Loading_Dialog').dialog({
width: 400,
height: 160,
resizable: false,
modal: true,
autoOpen: false
});
}
window.setTimeout(function () {
$Devices_Import_Loading_Dialog.dialog('open');
}, 200);
});
});
</script>
}
<div id="Devices_Import_Documentation">
<h3>CSV Import Specification</h3>
<h4>Format</h4>
<ul>
<li>The import file must be in <strong>comma-separated values format</strong> (<a href="http://en.wikipedia.org/wiki/Comma-separated_values" target="_blank">CSV Reference</a>).</li>
<li>Be conscious of editors removing leading zeros from serial numbers (ie: Microsoft Excel).</li>
</ul>
<h4>Fields</h4>
<div class="smallMessage">The following fields/columns are available for to the import file. The Device Serial Number is the only required field, all other fields are optional. Fields can appear in any order.</div>
<table class="tableData">
<thead>
<tr>
<th style="width: 100px;">Field Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
@foreach (var field in Model.HeaderTypes)
{
<tr>
<th>@field.Item2</th>
<td>
@field.Item3
@if (field.Item1 == DeviceImportFieldTypes.DeviceSerialNumber.ToString())
{
<strong>Required</strong>
}
else if (field.Item1 == DeviceImportFieldTypes.ModelId.ToString())
{
<span>(<a href="#" id="Devices_Import_Documentation_DeviceModels_Button">Show IDs</a>)</span>
}
else if (field.Item1 == DeviceImportFieldTypes.ProfileId.ToString())
{
<span>(<a href="#" id="Devices_Import_Documentation_DeviceProfiles_Button">Show IDs</a>)</span>
}
else if (field.Item1 == DeviceImportFieldTypes.BatchId.ToString())
{
<span>(<a href="#" id="Devices_Import_Documentation_DeviceBatches_Button">Show IDs</a>)</span>
}
</td>
</tr>
}
</tbody>
</table>
<div id="Devices_Import_Documentation_DeviceModels_Dialog" class="dialog" title="Disco Device Model Ids">
<table class="tableData">
<thead>
<tr>
<th>ID</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Model</th>
</tr>
</thead>
<tbody>
@foreach (var dm in Model.DeviceModels)
{
<tr>
<td>@Html.ActionLink(dm.Id.ToString(), MVC.Config.DeviceModel.Index(dm.Id))</td>
<td>@dm.ToString()</td>
<td>@dm.Manufacturer</td>
<td>@dm.Model</td>
</tr>
}
</tbody>
</table>
</div>
<div id="Devices_Import_Documentation_DeviceProfiles_Dialog" class="dialog" title="Disco Device Profile Ids">
<table class="tableData">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Short Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
@foreach (var dp in Model.DeviceProfiles)
{
<tr>
<td>@Html.ActionLink(dp.Id.ToString(), MVC.Config.DeviceProfile.Index(dp.Id))</td>
<td>@dp.Name</td>
<td>@dp.ShortName</td>
<td>@dp.Description</td>
</tr>
}
</tbody>
</table>
</div>
<div id="Devices_Import_Documentation_DeviceBatches_Dialog" class="dialog" title="Disco Device Batch Ids">
<table class="tableData">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Purchase Date</th>
</tr>
</thead>
<tbody>
@foreach (var db in Model.DeviceBatches)
{
<tr>
<td>@Html.ActionLink(db.Id.ToString(), MVC.Config.DeviceBatch.Index(db.Id))</td>
<td>@db.Name</td>
<td>@CommonHelpers.FriendlyDate(db.PurchaseDate)</td>
</tr>
}
</tbody>
</table>
</div>
<script>
$(function () {
var dialogOptions = {
width: 700,
height: 600,
resizable: false,
modal: true,
autoOpen: false
},
$DeviceModelsDialog = null,
$DeviceProfilesDialog = null,
$DeviceBatchesDialog = null;
$('#Devices_Import_Documentation_DeviceModels_Button').click(function (e) {
e.preventDefault();
if (!$DeviceModelsDialog)
$DeviceModelsDialog = $('#Devices_Import_Documentation_DeviceModels_Dialog').dialog(dialogOptions);
$DeviceModelsDialog.dialog('open');
});
$('#Devices_Import_Documentation_DeviceProfiles_Button').click(function (e) {
e.preventDefault();
if (!$DeviceProfilesDialog)
$DeviceProfilesDialog = $('#Devices_Import_Documentation_DeviceProfiles_Dialog').dialog(dialogOptions);
$DeviceProfilesDialog.dialog('open');
});
$('#Devices_Import_Documentation_DeviceBatches_Button').click(function (e) {
e.preventDefault();
if (!$DeviceBatchesDialog)
$DeviceBatchesDialog = $('#Devices_Import_Documentation_DeviceBatches_Dialog').dialog(dialogOptions);
$DeviceBatchesDialog.dialog('open');
});
});
</script>
</div>
</div>
@if (Model.CompletedImportSessionContext != null)
{
<div id="Devices_Import_Completed_Dialog" class="dialog" title="Device Import Completed">
<h3><i class="fa fa-lg fa-check"></i>Successfully imported/updated @Model.CompletedImportSessionContext.AffectedRecords device@(Model.CompletedImportSessionContext.AffectedRecords != 1 ? "s" : null).</h3>
<div>File: <code>@Model.CompletedImportSessionContext.Filename</code></div>
</div>
<script>
$(function () {
$('#Devices_Import_Completed_Dialog')
.dialog({
width: 500,
resizable: false,
modal: true,
autoOpen: true,
buttons: {
Close: function () {
$(this).dialog('destroy');
}
}
});
});
</script>
}
+698
View File
@@ -0,0 +1,698 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34014
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Disco.Web.Views.Device
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Routing;
using System.Web.Security;
using System.Web.UI;
using System.Web.WebPages;
using Disco;
using Disco.BI.Extensions;
using Disco.Models.Repository;
#line 2 "..\..\Views\Device\Import.cshtml"
using Disco.Models.Services.Devices.Importing;
#line default
#line hidden
using Disco.Services;
using Disco.Services.Authorization;
using Disco.Services.Web;
using Disco.Web;
using Disco.Web.Extensions;
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")]
[System.Web.WebPages.PageVirtualPathAttribute("~/Views/Device/Import.cshtml")]
public partial class Import : Disco.Services.Web.WebViewPage<Disco.Web.Models.Device.ImportModel>
{
public Import()
{
}
public override void Execute()
{
#line 3 "..\..\Views\Device\Import.cshtml"
Authorization.Require(Claims.Device.Actions.Import);
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import Devices");
#line default
#line hidden
WriteLiteral("\r\n<div");
WriteLiteral(" id=\"Devices_Import\"");
WriteLiteral(">\r\n");
#line 9 "..\..\Views\Device\Import.cshtml"
#line default
#line hidden
#line 9 "..\..\Views\Device\Import.cshtml"
using (Html.BeginForm(MVC.API.Device.ImportBegin(), FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#line default
#line hidden
WriteLiteral(" <div");
WriteLiteral(" id=\"importDialog\"");
WriteLiteral(" class=\"form\"");
WriteLiteral(" style=\"width: 450px\"");
WriteLiteral(">\r\n <h2>Import Devices</h2>\r\n <table>\r\n <tr>" +
"\r\n <th>\r\n");
WriteLiteral(" ");
#line 16 "..\..\Views\Device\Import.cshtml"
Write(Html.LabelFor(m => m.ImportFile));
#line default
#line hidden
WriteLiteral("\r\n </th>\r\n <td>\r\n <i" +
"nput");
WriteLiteral(" id=\"ImportFile\"");
WriteLiteral(" name=\"ImportFile\"");
WriteLiteral(" type=\"file\"");
WriteLiteral(" data-val=\"true\"");
WriteLiteral(" data-val-required=\"An Import File is required.\"");
WriteLiteral(" /><br />\r\n");
WriteLiteral(" ");
#line 20 "..\..\Views\Device\Import.cshtml"
Write(Html.ValidationMessageFor(m => m.ImportFile));
#line default
#line hidden
WriteLiteral("\r\n <div>\r\n");
WriteLiteral(" ");
#line 22 "..\..\Views\Device\Import.cshtml"
Write(Html.CheckBoxFor(m => m.HasHeader));
#line default
#line hidden
WriteLiteral("\r\n");
WriteLiteral(" ");
#line 23 "..\..\Views\Device\Import.cshtml"
Write(Html.LabelFor(m => m.HasHeader));
#line default
#line hidden
WriteLiteral("\r\n </div>\r\n </td>\r\n </tr" +
">\r\n </table>\r\n <p");
WriteLiteral(" class=\"actions\"");
WriteLiteral(">\r\n <input");
WriteLiteral(" type=\"submit\"");
WriteLiteral(" class=\"button\"");
WriteLiteral(" value=\"Begin Import\"");
WriteLiteral(" />\r\n </p>\r\n </div>\r\n");
WriteLiteral(" <div");
WriteLiteral(" id=\"Devices_Import_Loading_Dialog\"");
WriteLiteral(" class=\"dialog\"");
WriteLiteral(" title=\"Loading devices import...\"");
WriteLiteral(">\r\n <h4><i");
WriteLiteral(" class=\"fa fa-lg fa-cog fa-spin\"");
WriteLiteral(" title=\"Please Wait\"");
WriteLiteral("></i>Loading device import...</h4>\r\n </div>\r\n");
WriteLiteral(@" <script>
$(function () {
var $Devices_Import_Loading_Dialog = null;
$('#ImportFile').closest('form').submit(function () {
if ($Devices_Import_Loading_Dialog == null) {
$Devices_Import_Loading_Dialog = $('#Devices_Import_Loading_Dialog').dialog({
width: 400,
height: 160,
resizable: false,
modal: true,
autoOpen: false
});
}
window.setTimeout(function () {
$Devices_Import_Loading_Dialog.dialog('open');
}, 200);
});
});
</script>
");
#line 56 "..\..\Views\Device\Import.cshtml"
}
#line default
#line hidden
WriteLiteral(" <div");
WriteLiteral(" id=\"Devices_Import_Documentation\"");
WriteLiteral(">\r\n <h3>CSV Import Specification</h3>\r\n <h4>Format</h4>\r\n <u" +
"l>\r\n <li>The import file must be in <strong>comma-separated values fo" +
"rmat</strong> (<a");
WriteLiteral(" href=\"http://en.wikipedia.org/wiki/Comma-separated_values\"");
WriteLiteral(" target=\"_blank\"");
WriteLiteral(">CSV Reference</a>).</li>\r\n <li>Be conscious of editors removing leadi" +
"ng zeros from serial numbers (ie: Microsoft Excel).</li>\r\n </ul>\r\n " +
" <h4>Fields</h4>\r\n <div");
WriteLiteral(" class=\"smallMessage\"");
WriteLiteral(">The following fields/columns are available for to the import file. The Device Se" +
"rial Number is the only required field, all other fields are optional. Fields ca" +
"n appear in any order.</div>\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(">\r\n <thead>\r\n <tr>\r\n <th");
WriteLiteral(" style=\"width: 100px;\"");
WriteLiteral(">Field Name</th>\r\n <th>Description</th>\r\n </tr>" +
"\r\n </thead>\r\n <tbody>\r\n");
#line 74 "..\..\Views\Device\Import.cshtml"
#line default
#line hidden
#line 74 "..\..\Views\Device\Import.cshtml"
foreach (var field in Model.HeaderTypes)
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n <th>");
#line 77 "..\..\Views\Device\Import.cshtml"
Write(field.Item2);
#line default
#line hidden
WriteLiteral("</th>\r\n <td>\r\n");
WriteLiteral(" ");
#line 79 "..\..\Views\Device\Import.cshtml"
Write(field.Item3);
#line default
#line hidden
WriteLiteral("\r\n");
#line 80 "..\..\Views\Device\Import.cshtml"
#line default
#line hidden
#line 80 "..\..\Views\Device\Import.cshtml"
if (field.Item1 == DeviceImportFieldTypes.DeviceSerialNumber.ToString())
{
#line default
#line hidden
WriteLiteral(" <strong>Required</strong>\r\n");
#line 83 "..\..\Views\Device\Import.cshtml"
}
else if (field.Item1 == DeviceImportFieldTypes.ModelId.ToString())
{
#line default
#line hidden
WriteLiteral(" <span>(<a");
WriteLiteral(" href=\"#\"");
WriteLiteral(" id=\"Devices_Import_Documentation_DeviceModels_Button\"");
WriteLiteral(">Show IDs</a>)</span>\r\n");
#line 87 "..\..\Views\Device\Import.cshtml"
}
else if (field.Item1 == DeviceImportFieldTypes.ProfileId.ToString())
{
#line default
#line hidden
WriteLiteral(" <span>(<a");
WriteLiteral(" href=\"#\"");
WriteLiteral(" id=\"Devices_Import_Documentation_DeviceProfiles_Button\"");
WriteLiteral(">Show IDs</a>)</span>\r\n");
#line 91 "..\..\Views\Device\Import.cshtml"
}
else if (field.Item1 == DeviceImportFieldTypes.BatchId.ToString())
{
#line default
#line hidden
WriteLiteral(" <span>(<a");
WriteLiteral(" href=\"#\"");
WriteLiteral(" id=\"Devices_Import_Documentation_DeviceBatches_Button\"");
WriteLiteral(">Show IDs</a>)</span>\r\n");
#line 95 "..\..\Views\Device\Import.cshtml"
}
#line default
#line hidden
WriteLiteral("\r\n </td>\r\n </tr> \r\n");
#line 99 "..\..\Views\Device\Import.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n\r\n\r\n <div");
WriteLiteral(" id=\"Devices_Import_Documentation_DeviceModels_Dialog\"");
WriteLiteral(" class=\"dialog\"");
WriteLiteral(" title=\"Disco Device Model Ids\"");
WriteLiteral(">\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(@">
<thead>
<tr>
<th>ID</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Model</th>
</tr>
</thead>
<tbody>
");
#line 115 "..\..\Views\Device\Import.cshtml"
#line default
#line hidden
#line 115 "..\..\Views\Device\Import.cshtml"
foreach (var dm in Model.DeviceModels)
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n <td>");
#line 118 "..\..\Views\Device\Import.cshtml"
Write(Html.ActionLink(dm.Id.ToString(), MVC.Config.DeviceModel.Index(dm.Id)));
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 119 "..\..\Views\Device\Import.cshtml"
Write(dm.ToString());
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 120 "..\..\Views\Device\Import.cshtml"
Write(dm.Manufacturer);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 121 "..\..\Views\Device\Import.cshtml"
Write(dm.Model);
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 123 "..\..\Views\Device\Import.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n </div>\r\n\r\n <div");
WriteLiteral(" id=\"Devices_Import_Documentation_DeviceProfiles_Dialog\"");
WriteLiteral(" class=\"dialog\"");
WriteLiteral(" title=\"Disco Device Profile Ids\"");
WriteLiteral(">\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(@">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Short Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
");
#line 139 "..\..\Views\Device\Import.cshtml"
#line default
#line hidden
#line 139 "..\..\Views\Device\Import.cshtml"
foreach (var dp in Model.DeviceProfiles)
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n <td>");
#line 142 "..\..\Views\Device\Import.cshtml"
Write(Html.ActionLink(dp.Id.ToString(), MVC.Config.DeviceProfile.Index(dp.Id)));
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 143 "..\..\Views\Device\Import.cshtml"
Write(dp.Name);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 144 "..\..\Views\Device\Import.cshtml"
Write(dp.ShortName);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 145 "..\..\Views\Device\Import.cshtml"
Write(dp.Description);
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 147 "..\..\Views\Device\Import.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n </div>\r\n\r\n <div");
WriteLiteral(" id=\"Devices_Import_Documentation_DeviceBatches_Dialog\"");
WriteLiteral(" class=\"dialog\"");
WriteLiteral(" title=\"Disco Device Batch Ids\"");
WriteLiteral(">\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(@">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Purchase Date</th>
</tr>
</thead>
<tbody>
");
#line 162 "..\..\Views\Device\Import.cshtml"
#line default
#line hidden
#line 162 "..\..\Views\Device\Import.cshtml"
foreach (var db in Model.DeviceBatches)
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n <td>");
#line 165 "..\..\Views\Device\Import.cshtml"
Write(Html.ActionLink(db.Id.ToString(), MVC.Config.DeviceBatch.Index(db.Id)));
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 166 "..\..\Views\Device\Import.cshtml"
Write(db.Name);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 167 "..\..\Views\Device\Import.cshtml"
Write(CommonHelpers.FriendlyDate(db.PurchaseDate));
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 169 "..\..\Views\Device\Import.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n </div>\r\n\r\n <script" +
">\r\n $(function () {\r\n var dialogOptions = {\r\n " +
" width: 700,\r\n height: 600,\r\n re" +
"sizable: false,\r\n modal: true,\r\n autoOpen:" +
" false\r\n },\r\n $DeviceModelsDialog = null,\r\n " +
" $DeviceProfilesDialog = null,\r\n $DeviceBatchesDialog = n" +
"ull;\r\n\r\n $(\'#Devices_Import_Documentation_DeviceModels_Button\').c" +
"lick(function (e) {\r\n e.preventDefault();\r\n " +
" if (!$DeviceModelsDialog)\r\n $DeviceModelsDialog = $(\'#D" +
"evices_Import_Documentation_DeviceModels_Dialog\').dialog(dialogOptions);\r\n " +
" $DeviceModelsDialog.dialog(\'open\');\r\n });\r\n\r\n " +
" $(\'#Devices_Import_Documentation_DeviceProfiles_Button\').click(functio" +
"n (e) {\r\n e.preventDefault();\r\n if (!$Devi" +
"ceProfilesDialog)\r\n $DeviceProfilesDialog = $(\'#Devices_I" +
"mport_Documentation_DeviceProfiles_Dialog\').dialog(dialogOptions);\r\n " +
" $DeviceProfilesDialog.dialog(\'open\');\r\n });\r\n " +
" $(\'#Devices_Import_Documentation_DeviceBatches_Button\').click(function (e) {" +
"\r\n e.preventDefault();\r\n if (!$DeviceBatch" +
"esDialog)\r\n $DeviceBatchesDialog = $(\'#Devices_Import_Doc" +
"umentation_DeviceBatches_Dialog\').dialog(dialogOptions);\r\n $D" +
"eviceBatchesDialog.dialog(\'open\');\r\n });\r\n });\r\n " +
" </script>\r\n </div>\r\n</div>\r\n");
#line 210 "..\..\Views\Device\Import.cshtml"
if (Model.CompletedImportSessionContext != null)
{
#line default
#line hidden
WriteLiteral(" <div");
WriteLiteral(" id=\"Devices_Import_Completed_Dialog\"");
WriteLiteral(" class=\"dialog\"");
WriteLiteral(" title=\"Device Import Completed\"");
WriteLiteral(">\r\n <h3><i");
WriteLiteral(" class=\"fa fa-lg fa-check\"");
WriteLiteral("></i>Successfully imported/updated ");
#line 213 "..\..\Views\Device\Import.cshtml"
Write(Model.CompletedImportSessionContext.AffectedRecords);
#line default
#line hidden
WriteLiteral(" device");
#line 213 "..\..\Views\Device\Import.cshtml"
Write(Model.CompletedImportSessionContext.AffectedRecords != 1 ? "s" : null);
#line default
#line hidden
WriteLiteral(".</h3>\r\n <div>File: <code>");
#line 214 "..\..\Views\Device\Import.cshtml"
Write(Model.CompletedImportSessionContext.Filename);
#line default
#line hidden
WriteLiteral("</code></div>\r\n </div>\r\n");
WriteLiteral(@" <script>
$(function () {
$('#Devices_Import_Completed_Dialog')
.dialog({
width: 500,
resizable: false,
modal: true,
autoOpen: true,
buttons: {
Close: function () {
$(this).dialog('destroy');
}
}
});
});
</script>
");
#line 232 "..\..\Views\Device\Import.cshtml"
}
#line default
#line hidden
}
}
}
#pragma warning restore 1591
-192
View File
@@ -1,192 +0,0 @@
@model Disco.Web.Models.Device.ImportModel
@{
Authorization.RequireAny(Claims.Device.Actions.Import, Claims.Device.Actions.Export);
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import/Export Devices");
}
@if (Authorization.Has(Claims.Device.Actions.Import)){
<div id="deviceImport">
@using (Html.BeginForm(MVC.API.Device.ImportParse(), FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.ValidationSummary()
<div id="importDialog" class="form" style="width: 450px">
<h2>Import Devices</h2>
<table>
<tr>
<th>
@Html.LabelFor(m => m.ImportFile)
</th>
<td>
<input id="ImportFile" name="ImportFile" type="file" data-val="true" data-val-required="An Import File is required." />
</td>
</tr>
</table>
<p class="actions">
<input type="submit" class="button" value="Import" />
</p>
</div>
}
<div id="documentation">
<h3>CSV Import Specification</h3>
<h4>Format</h4>
<ul>
<li>The import file must be in <strong>comma-separated values format</strong> (<a href="http://en.wikipedia.org/wiki/Comma-separated_values" target="_blank">CSV Reference</a>).</li>
<li>The <strong>first line will be ignored</strong> (it is assumed the file includes headers).</li>
<li>Be conscious of editors removing leading zeros from serial numbers (ie: Microsoft Excel).</li>
</ul>
<h4>Fields</h4>
<div class="smallMessage">The following fields/columns are available for to the import file. The <strong>order of the fields</strong> must be as shown below.</div>
<table class="tableData">
<thead>
<tr>
<th style="width: 100px;">Field Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th>Serial Number</th>
<td><strong>Required</strong> - must contain the device serial number (maximum of 60 characters).
</td>
</tr>
<tr>
<th>Device Model</th>
<td>The <span class="code">ID</span> for the Device Model (<a href="#" id="showDeviceModels">Show IDs</a>). <em>Default: <span class="code">1</span> [@Html.ActionLink(Model.DeviceModels[0].ToString(), MVC.Config.DeviceModel.Index(Model.DeviceModels[0].Id))]</em>
</td>
</tr>
<tr>
<th>Device Profile</th>
<td>The <span class="code">ID</span> for the Device Profile (<a href="#" id="showDeviceProfiles">Show IDs</a>). <em>Default: <span class="code">1</span> [@Html.ActionLink(Model.DeviceProfiles[0].ToString(), MVC.Config.DeviceProfile.Index(Model.DeviceProfiles[0].Id))]</em>
</td>
</tr>
<tr>
<th>Device Batch</th>
<td>The <span class="code">ID</span> for the Device Batch (<a href="#" id="showDeviceBatches">Show IDs</a>). <em>Default: <span class="code">&lt;None&gt;</span></em>
</td>
</tr>
<tr>
<th>Assigned User</th>
<td>The <span class="code">ID</span> for the User assigned to the device. <em>Default: <span class="code">&lt;None&gt;</span></em>
</td>
</tr>
<tr>
<th>Location</th>
<td>Updates the Location of the device. Maximum of 250 characters. <em>Default: <span class="code">&lt;None&gt;</span></em>
</td>
</tr>
<tr>
<th>Asset Number</th>
<td>Updates the Asset Number of the device. Maximum of 40 characters. <em>Default: <span class="code">&lt;None&gt;</span></em>
</td>
</tr>
</tbody>
</table>
<div id="showDeviceModelsDialog" class="hiddenDialog" title="Disco Device Model Ids">
<table class="tableData">
<thead>
<tr>
<th>ID</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Model</th>
</tr>
</thead>
<tbody>
@foreach (var dm in Model.DeviceModels)
{
<tr>
<td>@Html.ActionLink(dm.Id.ToString(), MVC.Config.DeviceModel.Index(dm.Id))</td>
<td>@dm.ToString()</td>
<td>@dm.Manufacturer</td>
<td>@dm.Model</td>
</tr>
}
</tbody>
</table>
</div>
<div id="showDeviceProfilesDialog" class="hiddenDialog" title="Disco Device Profile Ids">
<table class="tableData">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Short Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
@foreach (var dp in Model.DeviceProfiles)
{
<tr>
<td>@Html.ActionLink(dp.Id.ToString(), MVC.Config.DeviceProfile.Index(dp.Id))</td>
<td>@dp.Name</td>
<td>@dp.ShortName</td>
<td>@dp.Description</td>
</tr>
}
</tbody>
</table>
</div>
<div id="showDeviceBatchesDialog" class="hiddenDialog" title="Disco Device Batch Ids">
<table class="tableData">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Purchase Date</th>
</tr>
</thead>
<tbody>
@foreach (var db in Model.DeviceBatches)
{
<tr>
<td>@Html.ActionLink(db.Id.ToString(), MVC.Config.DeviceBatch.Index(db.Id))</td>
<td>@db.Name</td>
<td>@CommonHelpers.FriendlyDate(db.PurchaseDate)</td>
</tr>
}
</tbody>
</table>
</div>
<script>
$(function () {
var dialogOptions = {
width: 700,
height: 600,
resizable: false,
modal: true,
autoOpen: false
},
$showDeviceModelsDialog = null,
$showDeviceProfilesDialog = null,
$showDeviceBatchesDialog = null;
$('#showDeviceModels').click(function (e) {
e.preventDefault();
if (!$showDeviceModelsDialog)
$showDeviceModelsDialog = $('#showDeviceModelsDialog').dialog(dialogOptions);
$showDeviceModelsDialog.dialog('open');
});
$('#showDeviceProfiles').click(function (e) {
e.preventDefault();
if (!$showDeviceProfilesDialog)
$showDeviceProfilesDialog = $('#showDeviceProfilesDialog').dialog(dialogOptions);
$showDeviceProfilesDialog.dialog('open');
});
$('#showDeviceBatches').click(function (e) {
e.preventDefault();
if (!$showDeviceBatchesDialog)
$showDeviceBatchesDialog = $('#showDeviceBatchesDialog').dialog(dialogOptions);
$showDeviceBatchesDialog.dialog('open');
});
});
</script>
</div>
</div>
}
@@ -1,553 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34014
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Disco.Web.Views.Device
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Routing;
using System.Web.Security;
using System.Web.UI;
using System.Web.WebPages;
using Disco;
using Disco.BI.Extensions;
using Disco.Models.Repository;
using Disco.Services;
using Disco.Services.Authorization;
using Disco.Services.Web;
using Disco.Web;
using Disco.Web.Extensions;
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")]
[System.Web.WebPages.PageVirtualPathAttribute("~/Views/Device/ImportExport.cshtml")]
public partial class ImportExport : Disco.Services.Web.WebViewPage<Disco.Web.Models.Device.ImportModel>
{
public ImportExport()
{
}
public override void Execute()
{
#line 2 "..\..\Views\Device\ImportExport.cshtml"
Authorization.RequireAny(Claims.Device.Actions.Import, Claims.Device.Actions.Export);
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import/Export Devices");
#line default
#line hidden
WriteLiteral("\r\n");
#line 7 "..\..\Views\Device\ImportExport.cshtml"
if (Authorization.Has(Claims.Device.Actions.Import)){
#line default
#line hidden
WriteLiteral("<div");
WriteLiteral(" id=\"deviceImport\"");
WriteLiteral(">\r\n");
#line 9 "..\..\Views\Device\ImportExport.cshtml"
#line default
#line hidden
#line 9 "..\..\Views\Device\ImportExport.cshtml"
using (Html.BeginForm(MVC.API.Device.ImportParse(), FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#line default
#line hidden
#line 11 "..\..\Views\Device\ImportExport.cshtml"
Write(Html.ValidationSummary());
#line default
#line hidden
#line 11 "..\..\Views\Device\ImportExport.cshtml"
#line default
#line hidden
WriteLiteral(" <div");
WriteLiteral(" id=\"importDialog\"");
WriteLiteral(" class=\"form\"");
WriteLiteral(" style=\"width: 450px\"");
WriteLiteral(">\r\n <h2>Import Devices</h2>\r\n <table>\r\n <tr>" +
"\r\n <th>\r\n");
WriteLiteral(" ");
#line 17 "..\..\Views\Device\ImportExport.cshtml"
Write(Html.LabelFor(m => m.ImportFile));
#line default
#line hidden
WriteLiteral("\r\n </th>\r\n <td>\r\n <i" +
"nput");
WriteLiteral(" id=\"ImportFile\"");
WriteLiteral(" name=\"ImportFile\"");
WriteLiteral(" type=\"file\"");
WriteLiteral(" data-val=\"true\"");
WriteLiteral(" data-val-required=\"An Import File is required.\"");
WriteLiteral(" />\r\n </td>\r\n </tr>\r\n </table>\r\n " +
" <p");
WriteLiteral(" class=\"actions\"");
WriteLiteral(">\r\n <input");
WriteLiteral(" type=\"submit\"");
WriteLiteral(" class=\"button\"");
WriteLiteral(" value=\"Import\"");
WriteLiteral(" />\r\n </p>\r\n </div>\r\n");
#line 28 "..\..\Views\Device\ImportExport.cshtml"
}
#line default
#line hidden
WriteLiteral(" <div");
WriteLiteral(" id=\"documentation\"");
WriteLiteral(">\r\n <h3>CSV Import Specification</h3>\r\n <h4>Format</h4>\r\n <u" +
"l>\r\n <li>The import file must be in <strong>comma-separated values fo" +
"rmat</strong> (<a");
WriteLiteral(" href=\"http://en.wikipedia.org/wiki/Comma-separated_values\"");
WriteLiteral(" target=\"_blank\"");
WriteLiteral(@">CSV Reference</a>).</li>
<li>The <strong>first line will be ignored</strong> (it is assumed the file includes headers).</li>
<li>Be conscious of editors removing leading zeros from serial numbers (ie: Microsoft Excel).</li>
</ul>
<h4>Fields</h4>
<div");
WriteLiteral(" class=\"smallMessage\"");
WriteLiteral(">The following fields/columns are available for to the import file. The <strong>o" +
"rder of the fields</strong> must be as shown below.</div>\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(">\r\n <thead>\r\n <tr>\r\n <th");
WriteLiteral(" style=\"width: 100px;\"");
WriteLiteral(@">Field Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th>Serial Number</th>
<td><strong>Required</strong> - must contain the device serial number (maximum of 60 characters).
</td>
</tr>
<tr>
<th>Device Model</th>
<td>The <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">ID</span> for the Device Model (<a");
WriteLiteral(" href=\"#\"");
WriteLiteral(" id=\"showDeviceModels\"");
WriteLiteral(">Show IDs</a>). <em>Default: <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">1</span> [");
#line 54 "..\..\Views\Device\ImportExport.cshtml"
Write(Html.ActionLink(Model.DeviceModels[0].ToString(), MVC.Config.DeviceModel.Index(Model.DeviceModels[0].Id)));
#line default
#line hidden
WriteLiteral("]</em>\r\n </td>\r\n </tr>\r\n <tr>\r\n " +
" <th>Device Profile</th>\r\n <td>The <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">ID</span> for the Device Profile (<a");
WriteLiteral(" href=\"#\"");
WriteLiteral(" id=\"showDeviceProfiles\"");
WriteLiteral(">Show IDs</a>). <em>Default: <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">1</span> [");
#line 59 "..\..\Views\Device\ImportExport.cshtml"
Write(Html.ActionLink(Model.DeviceProfiles[0].ToString(), MVC.Config.DeviceProfile.Index(Model.DeviceProfiles[0].Id)));
#line default
#line hidden
WriteLiteral("]</em>\r\n </td>\r\n </tr>\r\n <tr>\r\n " +
" <th>Device Batch</th>\r\n <td>The <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">ID</span> for the Device Batch (<a");
WriteLiteral(" href=\"#\"");
WriteLiteral(" id=\"showDeviceBatches\"");
WriteLiteral(">Show IDs</a>). <em>Default: <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">&lt;None&gt;</span></em>\r\n </td>\r\n </tr>\r\n " +
" <tr>\r\n <th>Assigned User</th>\r\n " +
" <td>The <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">ID</span> for the User assigned to the device. <em>Default: <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">&lt;None&gt;</span></em>\r\n </td>\r\n </tr>\r\n " +
" <tr>\r\n <th>Location</th>\r\n <td" +
">Updates the Location of the device. Maximum of 250 characters. <em>Default: <sp" +
"an");
WriteLiteral(" class=\"code\"");
WriteLiteral(">&lt;None&gt;</span></em>\r\n </td>\r\n </tr>\r\n " +
" <tr>\r\n <th>Asset Number</th>\r\n " +
" <td>Updates the Asset Number of the device. Maximum of 40 characters. <em>Defau" +
"lt: <span");
WriteLiteral(" class=\"code\"");
WriteLiteral(">&lt;None&gt;</span></em>\r\n </td>\r\n </tr>\r\n " +
" </tbody>\r\n </table>\r\n\r\n\r\n <div");
WriteLiteral(" id=\"showDeviceModelsDialog\"");
WriteLiteral(" class=\"hiddenDialog\"");
WriteLiteral(" title=\"Disco Device Model Ids\"");
WriteLiteral(">\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(@">
<thead>
<tr>
<th>ID</th>
<th>Description</th>
<th>Manufacturer</th>
<th>Model</th>
</tr>
</thead>
<tbody>
");
#line 97 "..\..\Views\Device\ImportExport.cshtml"
#line default
#line hidden
#line 97 "..\..\Views\Device\ImportExport.cshtml"
foreach (var dm in Model.DeviceModels)
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n <td>");
#line 100 "..\..\Views\Device\ImportExport.cshtml"
Write(Html.ActionLink(dm.Id.ToString(), MVC.Config.DeviceModel.Index(dm.Id)));
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 101 "..\..\Views\Device\ImportExport.cshtml"
Write(dm.ToString());
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 102 "..\..\Views\Device\ImportExport.cshtml"
Write(dm.Manufacturer);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 103 "..\..\Views\Device\ImportExport.cshtml"
Write(dm.Model);
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 105 "..\..\Views\Device\ImportExport.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n </div>\r\n\r\n <div");
WriteLiteral(" id=\"showDeviceProfilesDialog\"");
WriteLiteral(" class=\"hiddenDialog\"");
WriteLiteral(" title=\"Disco Device Profile Ids\"");
WriteLiteral(">\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(@">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Short Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
");
#line 121 "..\..\Views\Device\ImportExport.cshtml"
#line default
#line hidden
#line 121 "..\..\Views\Device\ImportExport.cshtml"
foreach (var dp in Model.DeviceProfiles)
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n <td>");
#line 124 "..\..\Views\Device\ImportExport.cshtml"
Write(Html.ActionLink(dp.Id.ToString(), MVC.Config.DeviceProfile.Index(dp.Id)));
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 125 "..\..\Views\Device\ImportExport.cshtml"
Write(dp.Name);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 126 "..\..\Views\Device\ImportExport.cshtml"
Write(dp.ShortName);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 127 "..\..\Views\Device\ImportExport.cshtml"
Write(dp.Description);
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 129 "..\..\Views\Device\ImportExport.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n </div>\r\n\r\n <div");
WriteLiteral(" id=\"showDeviceBatchesDialog\"");
WriteLiteral(" class=\"hiddenDialog\"");
WriteLiteral(" title=\"Disco Device Batch Ids\"");
WriteLiteral(">\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(@">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Purchase Date</th>
</tr>
</thead>
<tbody>
");
#line 144 "..\..\Views\Device\ImportExport.cshtml"
#line default
#line hidden
#line 144 "..\..\Views\Device\ImportExport.cshtml"
foreach (var db in Model.DeviceBatches)
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n <td>");
#line 147 "..\..\Views\Device\ImportExport.cshtml"
Write(Html.ActionLink(db.Id.ToString(), MVC.Config.DeviceBatch.Index(db.Id)));
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 148 "..\..\Views\Device\ImportExport.cshtml"
Write(db.Name);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 149 "..\..\Views\Device\ImportExport.cshtml"
Write(CommonHelpers.FriendlyDate(db.PurchaseDate));
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 151 "..\..\Views\Device\ImportExport.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n </div>\r\n\r\n <script" +
">\r\n $(function () {\r\n var dialogOptions = {\r\n " +
" width: 700,\r\n height: 600,\r\n re" +
"sizable: false,\r\n modal: true,\r\n autoOpen:" +
" false\r\n },\r\n $showDeviceModelsDialog = null,\r\n " +
" $showDeviceProfilesDialog = null,\r\n $showDeviceBatch" +
"esDialog = null;\r\n\r\n $(\'#showDeviceModels\').click(function (e) {\r" +
"\n e.preventDefault();\r\n if (!$showDeviceMo" +
"delsDialog)\r\n $showDeviceModelsDialog = $(\'#showDeviceMod" +
"elsDialog\').dialog(dialogOptions);\r\n $showDeviceModelsDialog." +
"dialog(\'open\');\r\n });\r\n\r\n $(\'#showDeviceProfiles\')" +
".click(function (e) {\r\n e.preventDefault();\r\n " +
" if (!$showDeviceProfilesDialog)\r\n $showDeviceProfiles" +
"Dialog = $(\'#showDeviceProfilesDialog\').dialog(dialogOptions);\r\n " +
" $showDeviceProfilesDialog.dialog(\'open\');\r\n });\r\n " +
" $(\'#showDeviceBatches\').click(function (e) {\r\n e.preventD" +
"efault();\r\n if (!$showDeviceBatchesDialog)\r\n " +
" $showDeviceBatchesDialog = $(\'#showDeviceBatchesDialog\').dialog(dialogOpt" +
"ions);\r\n $showDeviceBatchesDialog.dialog(\'open\');\r\n " +
" });\r\n });\r\n </script>\r\n </div>\r\n</div>\r\n");
#line 192 "..\..\Views\Device\ImportExport.cshtml"
}
#line default
#line hidden
}
}
}
#pragma warning restore 1591
+183
View File
@@ -0,0 +1,183 @@
@model Disco.Web.Models.Device.ImportHeadersModel
@{
Authorization.Require(Claims.Device.Actions.Import);
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import Devices", MVC.Device.Import(), string.Format("File: {0}", Model.Context.Filename));
}
<div id="Devices_Import_Headers">
<h2>Define Import Columns</h2>
@if (Model.Context.RawData.Count > 10)
{
<h4 class="alert">@Model.Context.RawData.Count records were loaded, only the first 10 are shown here.</h4>
}
<h4 id="Devices_Import_Headers_DeviceSerialNumberRequired" class="error">The Device Serial Number column must be defined.</h4>
<div id="Devices_Import_Headers_TableContainer">
<table class="tableData">
<thead>
<tr>
@foreach (var header in Model.Context.Header.Select((h, i) => Tuple.Create(h, i)))
{
<th data-headerindex="@header.Item2" class="header@(header.Item1.Item2.ToString())">@header.Item1.Item1</th>
}
</tr>
<tr>
@foreach (var header in Model.Context.Header.Select((h, i) => Tuple.Create(h, i)))
{
<td data-headerindex="@header.Item2" class="header@(header.Item1.Item2.ToString())">
<ul class="importHeaderType" data-headerindex="@header.Item2" data-headertype="@header.Item1.Item2.ToString()">
<li><a href="#"><span class="headerTypeTitle">@(Model.HeaderTypes.FirstOrDefault(h => h.Item1 == header.Item1.Item2).Item2)</span></a>
<ul>
@foreach (var headerType in Model.HeaderTypes)
{
<li data-headertype="@headerType.Item1"><a href="#">@headerType.Item2</a></li>
}
</ul>
</li>
</ul>
</td>
}
</tr>
</thead>
<tbody>
@foreach (var record in Model.Context.RawData.Take(10))
{
<tr>
@foreach (var field in record.Select((h, i) => Tuple.Create(h, i)))
{
<td data-headerindex="@field.Item2">@field.Item1</td>
}
</tr>
}
</tbody>
</table>
</div>
<div class="actionBar">
@using (Html.BeginForm(MVC.API.Device.ImportParse(Model.Context.SessionId, null)))
{
<a id="Devices_Import_Headers_Submit" href="#" class="button">Parse Device Import</a>
}
</div>
</div>
<div id="Devices_Import_Parsing_Dialog" class="dialog" title="Parsing devices import...">
<h4><i class="fa fa-lg fa-cog fa-spin" title="Please Wait"></i>Parsing device import...</h4>
</div>
<script>
$(function () {
var headerTypes = {
@foreach (var h in Model.HeaderTypes)
{
<text>'@(h.Item1)': '@(h.Item2)',</text>
}
};
var $Devices_Import_Headers_TableContainer = $('#Devices_Import_Headers_TableContainer');
var $importHeaderTypes = $Devices_Import_Headers_TableContainer.find('thead').find('ul.importHeaderType');
var $Devices_Import_Headers_DeviceSerialNumberRequired = $('#Devices_Import_Headers_DeviceSerialNumberRequired');
var $Devices_Import_Headers_Submit = $('#Devices_Import_Headers_Submit');
var $Devices_Import_Parsing_Dialog = null;
function getUsedHeaders() {
return $importHeaderTypes.map(function () { return $(this).attr('data-headertype'); }).filter(function () { return this != 'IgnoreColumn' }).get();
}
function updateHeaderOptions() {
var usedHeaders = getUsedHeaders();
var deviceSerialNumberPresent = (usedHeaders.indexOf('DeviceSerialNumber') >= 0);
if (deviceSerialNumberPresent) {
$Devices_Import_Headers_Submit.attr('disabled', null);
$Devices_Import_Headers_DeviceSerialNumberRequired.hide();
} else {
$Devices_Import_Headers_DeviceSerialNumberRequired.show();
$Devices_Import_Headers_Submit.attr('disabled', 'disabled');
}
$importHeaderTypes.each(function () {
var $header = $(this);
var $headerType = $header.attr('data-headertype');
$header.find('li[data-headertype]').each(function () {
var $headerOption = $(this);
var $headerOptionType = $headerOption.attr('data-headertype');
if ($headerOptionType === $headerType) {
$headerOption.removeClass('ui-state-disabled');
$headerOption.addClass('ui-state-highlight');
} else if (usedHeaders.indexOf($headerOptionType) < 0) {
$headerOption.removeClass('ui-state-disabled ui-state-highlight');
} else {
$headerOption.removeClass('ui-state-highlight');
$headerOption.addClass('ui-state-disabled');
}
})
});
}
function updateDataStyle(index, headerType) {
$Devices_Import_Headers_TableContainer.find('tbody').find('td[data-headerindex="' + index + '"]').removeClass().addClass('header' + headerType);
$Devices_Import_Headers_TableContainer.find('thead').find('td[data-headerindex="' + index + '"], th[data-headerindex="' + index + '"]').removeClass().addClass('header' + headerType);
}
$Devices_Import_Headers_TableContainer.find('thead').on('menuselect', 'ul.importHeaderType', function (e, ui) {
var headerType = ui.item.attr('data-headertype');
if (headerType !== undefined) {
var $this = $(this).closest('ul.importHeaderType');
var headerIndex = $this.attr('data-headerindex');
var headerTypeName = headerTypes[headerType];
$this.attr('data-headertype', headerType).find('span.headerTypeTitle').text(headerTypeName);
updateDataStyle(headerIndex, headerType);
updateHeaderOptions();
}
});
// Add Type Options
$importHeaderTypes.each(function () {
var $this = $(this);
var thisHeaderIndex = $this.attr('data-headerindex');
var thisHeaderType = $this.attr('data-headertype');
updateDataStyle(thisHeaderIndex, thisHeaderType);
}).menu({ position: { my: "left top", at: "left bottom" } });
updateHeaderOptions();
$('#Devices_Import_Headers_Submit').click(function () {
// Validate Device Serial Number Present
var usedHeaders = getUsedHeaders();
var deviceSerialNumberPresent = (usedHeaders.indexOf('DeviceSerialNumber') >= 0);
if (!deviceSerialNumberPresent) {
updateHeaderOptions();
$Devices_Import_Headers_DeviceSerialNumberRequired.show('highlight');
} else {
var $form = $(this).closest('form');
// Build Form
$importHeaderTypes.each(function () {
var $this = $(this);
var thisHeaderIndex = $this.attr('data-headerindex');
var thisHeaderType = $this.attr('data-headertype');
$(document.createElement('input')).attr({ type: 'hidden', name: 'Headers[' + thisHeaderIndex + ']', value: thisHeaderType }).appendTo($form);
});
// Submit Form
if ($Devices_Import_Parsing_Dialog == null) {
$Devices_Import_Parsing_Dialog = $('#Devices_Import_Parsing_Dialog').dialog({
width: 400,
height: 160,
resizable: false,
modal: true,
autoOpen: false
});
}
$Devices_Import_Parsing_Dialog.dialog('open');
$form.submit();
}
return false;
});
});
</script>
@@ -0,0 +1,557 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34014
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Disco.Web.Views.Device
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Routing;
using System.Web.Security;
using System.Web.UI;
using System.Web.WebPages;
using Disco;
using Disco.BI.Extensions;
using Disco.Models.Repository;
using Disco.Services;
using Disco.Services.Authorization;
using Disco.Services.Web;
using Disco.Web;
using Disco.Web.Extensions;
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")]
[System.Web.WebPages.PageVirtualPathAttribute("~/Views/Device/ImportHeaders.cshtml")]
public partial class ImportHeaders : Disco.Services.Web.WebViewPage<Disco.Web.Models.Device.ImportHeadersModel>
{
public ImportHeaders()
{
}
public override void Execute()
{
#line 2 "..\..\Views\Device\ImportHeaders.cshtml"
Authorization.Require(Claims.Device.Actions.Import);
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import Devices", MVC.Device.Import(), string.Format("File: {0}", Model.Context.Filename));
#line default
#line hidden
WriteLiteral("\r\n<div");
WriteLiteral(" id=\"Devices_Import_Headers\"");
WriteLiteral(">\r\n\r\n <h2>Define Import Columns</h2>\r\n\r\n");
#line 11 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 11 "..\..\Views\Device\ImportHeaders.cshtml"
if (Model.Context.RawData.Count > 10)
{
#line default
#line hidden
WriteLiteral(" <h4");
WriteLiteral(" class=\"alert\"");
WriteLiteral(">");
#line 13 "..\..\Views\Device\ImportHeaders.cshtml"
Write(Model.Context.RawData.Count);
#line default
#line hidden
WriteLiteral(" records were loaded, only the first 10 are shown here.</h4>\r\n");
#line 14 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral("\r\n <h4");
WriteLiteral(" id=\"Devices_Import_Headers_DeviceSerialNumberRequired\"");
WriteLiteral(" class=\"error\"");
WriteLiteral(">The Device Serial Number column must be defined.</h4>\r\n\r\n <div");
WriteLiteral(" id=\"Devices_Import_Headers_TableContainer\"");
WriteLiteral(">\r\n <table");
WriteLiteral(" class=\"tableData\"");
WriteLiteral(">\r\n <thead>\r\n <tr>\r\n");
#line 22 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 22 "..\..\Views\Device\ImportHeaders.cshtml"
foreach (var header in Model.Context.Header.Select((h, i) => Tuple.Create(h, i)))
{
#line default
#line hidden
WriteLiteral(" <th");
WriteLiteral(" data-headerindex=\"");
#line 24 "..\..\Views\Device\ImportHeaders.cshtml"
Write(header.Item2);
#line default
#line hidden
WriteLiteral("\"");
WriteAttribute("class", Tuple.Create(" class=\"", 984), Tuple.Create("\"", 1030)
, Tuple.Create(Tuple.Create("", 992), Tuple.Create("header", 992), true)
#line 24 "..\..\Views\Device\ImportHeaders.cshtml"
, Tuple.Create(Tuple.Create("", 998), Tuple.Create<System.Object, System.Int32>(header.Item1.Item2.ToString()
#line default
#line hidden
, 998), false)
);
WriteLiteral(">");
#line 24 "..\..\Views\Device\ImportHeaders.cshtml"
Write(header.Item1.Item1);
#line default
#line hidden
WriteLiteral("</th>\r\n");
#line 25 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tr>\r\n <tr>\r\n");
#line 28 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 28 "..\..\Views\Device\ImportHeaders.cshtml"
foreach (var header in Model.Context.Header.Select((h, i) => Tuple.Create(h, i)))
{
#line default
#line hidden
WriteLiteral(" <td");
WriteLiteral(" data-headerindex=\"");
#line 30 "..\..\Views\Device\ImportHeaders.cshtml"
Write(header.Item2);
#line default
#line hidden
WriteLiteral("\"");
WriteAttribute("class", Tuple.Create(" class=\"", 1313), Tuple.Create("\"", 1359)
, Tuple.Create(Tuple.Create("", 1321), Tuple.Create("header", 1321), true)
#line 30 "..\..\Views\Device\ImportHeaders.cshtml"
, Tuple.Create(Tuple.Create("", 1327), Tuple.Create<System.Object, System.Int32>(header.Item1.Item2.ToString()
#line default
#line hidden
, 1327), false)
);
WriteLiteral(">\r\n <ul");
WriteLiteral(" class=\"importHeaderType\"");
WriteLiteral(" data-headerindex=\"");
#line 31 "..\..\Views\Device\ImportHeaders.cshtml"
Write(header.Item2);
#line default
#line hidden
WriteLiteral("\"");
WriteLiteral(" data-headertype=\"");
#line 31 "..\..\Views\Device\ImportHeaders.cshtml"
Write(header.Item1.Item2.ToString());
#line default
#line hidden
WriteLiteral("\"");
WriteLiteral(">\r\n <li><a");
WriteLiteral(" href=\"#\"");
WriteLiteral("><span");
WriteLiteral(" class=\"headerTypeTitle\"");
WriteLiteral(">");
#line 32 "..\..\Views\Device\ImportHeaders.cshtml"
Write(Model.HeaderTypes.FirstOrDefault(h => h.Item1 == header.Item1.Item2).Item2);
#line default
#line hidden
WriteLiteral("</span></a>\r\n <ul>\r\n");
#line 34 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 34 "..\..\Views\Device\ImportHeaders.cshtml"
foreach (var headerType in Model.HeaderTypes)
{
#line default
#line hidden
WriteLiteral(" <li");
WriteLiteral(" data-headertype=\"");
#line 36 "..\..\Views\Device\ImportHeaders.cshtml"
Write(headerType.Item1);
#line default
#line hidden
WriteLiteral("\"");
WriteLiteral("><a");
WriteLiteral(" href=\"#\"");
WriteLiteral(">");
#line 36 "..\..\Views\Device\ImportHeaders.cshtml"
Write(headerType.Item2);
#line default
#line hidden
WriteLiteral("</a></li>\r\n");
#line 37 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral(" </ul>\r\n </li>\r" +
"\n </ul>\r\n </td>\r\n");
#line 42 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tr>\r\n </thead>\r\n <tbody>\r\n");
#line 46 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 46 "..\..\Views\Device\ImportHeaders.cshtml"
foreach (var record in Model.Context.RawData.Take(10))
{
#line default
#line hidden
WriteLiteral(" <tr>\r\n");
#line 49 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 49 "..\..\Views\Device\ImportHeaders.cshtml"
foreach (var field in record.Select((h, i) => Tuple.Create(h, i)))
{
#line default
#line hidden
WriteLiteral(" <td");
WriteLiteral(" data-headerindex=\"");
#line 51 "..\..\Views\Device\ImportHeaders.cshtml"
Write(field.Item2);
#line default
#line hidden
WriteLiteral("\"");
WriteLiteral(">");
#line 51 "..\..\Views\Device\ImportHeaders.cshtml"
Write(field.Item1);
#line default
#line hidden
WriteLiteral("</td>\r\n");
#line 52 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tr>\r\n");
#line 54 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n </div>\r\n <div");
WriteLiteral(" class=\"actionBar\"");
WriteLiteral(">\r\n");
#line 59 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 59 "..\..\Views\Device\ImportHeaders.cshtml"
using (Html.BeginForm(MVC.API.Device.ImportParse(Model.Context.SessionId, null)))
{
#line default
#line hidden
WriteLiteral(" <a");
WriteLiteral(" id=\"Devices_Import_Headers_Submit\"");
WriteLiteral(" href=\"#\"");
WriteLiteral(" class=\"button\"");
WriteLiteral(">Parse Device Import</a> \r\n");
#line 62 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral(" </div>\r\n</div>\r\n<div");
WriteLiteral(" id=\"Devices_Import_Parsing_Dialog\"");
WriteLiteral(" class=\"dialog\"");
WriteLiteral(" title=\"Parsing devices import...\"");
WriteLiteral(">\r\n <h4><i");
WriteLiteral(" class=\"fa fa-lg fa-cog fa-spin\"");
WriteLiteral(" title=\"Please Wait\"");
WriteLiteral("></i>Parsing device import...</h4>\r\n</div>\r\n<script>\r\n $(function () {\r\n " +
" var headerTypes = {\r\n");
#line 71 "..\..\Views\Device\ImportHeaders.cshtml"
#line default
#line hidden
#line 71 "..\..\Views\Device\ImportHeaders.cshtml"
foreach (var h in Model.HeaderTypes)
{
#line default
#line hidden
WriteLiteral(" ");
WriteLiteral("\'");
#line 73 "..\..\Views\Device\ImportHeaders.cshtml"
Write(h.Item1);
#line default
#line hidden
WriteLiteral("\': \'");
#line 73 "..\..\Views\Device\ImportHeaders.cshtml"
Write(h.Item2);
#line default
#line hidden
WriteLiteral("\',");
WriteLiteral("\r\n");
#line 74 "..\..\Views\Device\ImportHeaders.cshtml"
}
#line default
#line hidden
WriteLiteral(" };\r\n\r\n var $Devices_Import_Headers_TableContainer = $(\'#Devices_Im" +
"port_Headers_TableContainer\');\r\n var $importHeaderTypes = $Devices_Import" +
"_Headers_TableContainer.find(\'thead\').find(\'ul.importHeaderType\');\r\n var " +
"$Devices_Import_Headers_DeviceSerialNumberRequired = $(\'#Devices_Import_Headers_" +
"DeviceSerialNumberRequired\');\r\n var $Devices_Import_Headers_Submit = $(\'#" +
"Devices_Import_Headers_Submit\');\r\n var $Devices_Import_Parsing_Dialog = n" +
"ull;\r\n\r\n function getUsedHeaders() {\r\n return $importHeaderTyp" +
"es.map(function () { return $(this).attr(\'data-headertype\'); }).filter(function " +
"() { return this != \'IgnoreColumn\' }).get();\r\n }\r\n\r\n function upda" +
"teHeaderOptions() {\r\n var usedHeaders = getUsedHeaders();\r\n " +
" var deviceSerialNumberPresent = (usedHeaders.indexOf(\'DeviceSerialNumber\') >= " +
"0);\r\n\r\n if (deviceSerialNumberPresent) {\r\n $Devices_Im" +
"port_Headers_Submit.attr(\'disabled\', null);\r\n $Devices_Import_Hea" +
"ders_DeviceSerialNumberRequired.hide();\r\n } else {\r\n $" +
"Devices_Import_Headers_DeviceSerialNumberRequired.show();\r\n $Devi" +
"ces_Import_Headers_Submit.attr(\'disabled\', \'disabled\');\r\n }\r\n\r\n " +
" $importHeaderTypes.each(function () {\r\n var $header = $(thi" +
"s);\r\n var $headerType = $header.attr(\'data-headertype\');\r\n " +
" $header.find(\'li[data-headertype]\').each(function () {\r\n " +
" var $headerOption = $(this);\r\n var $headerOptionType = $" +
"headerOption.attr(\'data-headertype\');\r\n if ($headerOptionType" +
" === $headerType) {\r\n $headerOption.removeClass(\'ui-state" +
"-disabled\');\r\n $headerOption.addClass(\'ui-state-highlight" +
"\');\r\n } else if (usedHeaders.indexOf($headerOptionType) < 0) " +
"{\r\n $headerOption.removeClass(\'ui-state-disabled ui-state" +
"-highlight\');\r\n } else {\r\n $headerOpti" +
"on.removeClass(\'ui-state-highlight\');\r\n $headerOption.add" +
"Class(\'ui-state-disabled\');\r\n }\r\n })\r\n " +
" });\r\n }\r\n\r\n function updateDataStyle(index, headerType) {\r\n " +
" $Devices_Import_Headers_TableContainer.find(\'tbody\').find(\'td[data-head" +
"erindex=\"\' + index + \'\"]\').removeClass().addClass(\'header\' + headerType);\r\n " +
" $Devices_Import_Headers_TableContainer.find(\'thead\').find(\'td[data-header" +
"index=\"\' + index + \'\"], th[data-headerindex=\"\' + index + \'\"]\').removeClass().add" +
"Class(\'header\' + headerType);\r\n }\r\n\r\n $Devices_Import_Headers_Tabl" +
"eContainer.find(\'thead\').on(\'menuselect\', \'ul.importHeaderType\', function (e, ui" +
") {\r\n var headerType = ui.item.attr(\'data-headertype\');\r\n\r\n " +
" if (headerType !== undefined) {\r\n var $this = $(this).closest(\'" +
"ul.importHeaderType\');\r\n var headerIndex = $this.attr(\'data-heade" +
"rindex\');\r\n var headerTypeName = headerTypes[headerType];\r\n " +
" $this.attr(\'data-headertype\', headerType).find(\'span.headerTypeTitle\')" +
".text(headerTypeName);\r\n updateDataStyle(headerIndex, headerType)" +
";\r\n updateHeaderOptions();\r\n }\r\n });\r\n\r\n " +
" // Add Type Options\r\n $importHeaderTypes.each(function () {\r\n " +
" var $this = $(this);\r\n var thisHeaderIndex = $this.attr(\'data-header" +
"index\');\r\n var thisHeaderType = $this.attr(\'data-headertype\');\r\n " +
" updateDataStyle(thisHeaderIndex, thisHeaderType);\r\n }).menu({ posi" +
"tion: { my: \"left top\", at: \"left bottom\" } });\r\n\r\n updateHeaderOptions()" +
";\r\n\r\n $(\'#Devices_Import_Headers_Submit\').click(function () {\r\n\r\n " +
" // Validate Device Serial Number Present\r\n var usedHeaders = getU" +
"sedHeaders();\r\n var deviceSerialNumberPresent = (usedHeaders.indexOf(" +
"\'DeviceSerialNumber\') >= 0);\r\n if (!deviceSerialNumberPresent) {\r\n " +
" updateHeaderOptions();\r\n $Devices_Import_Headers_Dev" +
"iceSerialNumberRequired.show(\'highlight\');\r\n } else {\r\n " +
" var $form = $(this).closest(\'form\');\r\n\r\n // Build Form\r\n " +
" $importHeaderTypes.each(function () {\r\n var $this = " +
"$(this);\r\n var thisHeaderIndex = $this.attr(\'data-headerindex" +
"\');\r\n var thisHeaderType = $this.attr(\'data-headertype\');\r\n\r\n" +
" $(document.createElement(\'input\')).attr({ type: \'hidden\', na" +
"me: \'Headers[\' + thisHeaderIndex + \']\', value: thisHeaderType }).appendTo($form)" +
";\r\n });\r\n\r\n // Submit Form\r\n if ($D" +
"evices_Import_Parsing_Dialog == null) {\r\n $Devices_Import_Par" +
"sing_Dialog = $(\'#Devices_Import_Parsing_Dialog\').dialog({\r\n " +
" width: 400,\r\n height: 160,\r\n r" +
"esizable: false,\r\n modal: true,\r\n " +
"autoOpen: false\r\n });\r\n }\r\n $De" +
"vices_Import_Parsing_Dialog.dialog(\'open\');\r\n $form.submit();\r\n " +
" }\r\n\r\n return false;\r\n });\r\n });\r\n</script>\r\n");
}
}
}
#pragma warning restore 1591
+164 -8
View File
@@ -1,15 +1,171 @@
@model Disco.Web.Models.Device.ImportReviewModel
@using Disco.BI.DeviceBI.Importing
@using Disco.Models.Services.Devices.Importing;
@using System.Data;
@{
Authorization.Require(Claims.Device.Actions.Import);
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import Devices", MVC.Device.ImportExport(), string.Format("File: {0}", Model.ImportFilename));
int importDeviceOkCount = Model.ImportDevices.Count(id => id.Errors.Count == 0);
int importDeviceNewCount = Model.ImportDevices.Count(id => id.Errors.Count == 0 && id.Device == null);
int importDeviceUpdateCount = Model.ImportDevices.Count(id => id.Errors.Count == 0 && id.Device != null);
int importDeviceErrorCount = Model.ImportDevices.Count - importDeviceOkCount;
ViewBag.Title = Html.ToBreadcrumb("Devices", MVC.Device.Index(), "Import Devices", MVC.Device.Import(), string.Format("File: {0}", Model.Context.Filename));
}
<div id="Devices_Import_Review">
<h2>Review Pending Changes</h2>
<h3>Parsed @Model.Context.Records.Count Device Record@(Model.Context.Records.Count != 1 ? "s" : null)</h3>
<h4>
@Model.StatisticImportRecords of @Model.Context.Records.Count Device@(Model.Context.Records.Count != 1 ? "s" : null) are ready for import.
</h4>
@if (Model.StatisticErrorRecords > 0)
{
<h4 class="field-validation-error">
@(Model.StatisticErrorRecords) Record@(Model.StatisticErrorRecords != 1 ? "s" : null) will be skipped due to parse errors.
</h4>
}
<div id="Devices_Import_Review_Navigation">
<ul class="none">
@if (Model.StatisticErrorRecords > 0)
{<li class="actionDetached">
<input id="Devices_Import_Review_Navigation_Error" type="checkbox" checked /><label for="Devices_Import_Review_Navigation_Error">Show Errors (@(Model.StatisticErrorRecords))</label>
</li>}@if (Model.StatisticNewRecords > 0)
{<li class="actionAdded">
<input id="Devices_Import_Review_Navigation_New" type="checkbox" checked /><label for="Devices_Import_Review_Navigation_New">Show New Devices (@(Model.StatisticNewRecords))</label>
</li>}@if (Model.StatisticModifiedRecords > 0)
{<li class="actionModified">
<input id="Devices_Import_Review_Navigation_Modified" type="checkbox" checked /><label for="Devices_Import_Review_Navigation_Modified">Show Modified Devices (@(Model.StatisticModifiedRecords))</label>
</li>}@if (Model.StatisticUnmodifiedRecords > 0)
{<li class="actionUnchanged">
<input id="Devices_Import_Review_Navigation_Unchanged" type="checkbox" checked /><label for="Devices_Import_Review_Navigation_Unchanged">Show Unchanged Devices (@(Model.StatisticUnmodifiedRecords))</label>
</li>}
</ul>
<script>
$(function () {
$navigationContainer = $('#Devices_Import_Review_Navigation');
$tableBody = $('#Devices_Import_Review_TableContainer').find('tbody');
$navigationContainer.on('change', 'input', function () {
$tableBody.find('tr').show();
$navigationContainer.find('input').each(function () {
var $this = $(this);
var action = $this.closest('li').attr('class');
var records = $tableBody.find('tr.' + action);
if ($this.is(':checked')) {
records.show();
} else {
records.hide();
}
});
});
});
</script>
</div>
<div id="Devices_Import_Review_TableContainer">
<table class="tableData">
<thead>
<tr>
<th>Action</th>
<th>Row</th>
@foreach (var header in Model.Context.ParsedHeaders)
{
<th>@(Model.HeaderTypes.FirstOrDefault(h => h.Item1 == header.Item2).Item2)</th>
}
</tr>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
@foreach (var header in Model.Context.ParsedHeaders)
{
<th>@header.Item1</th>
}
</tr>
</thead>
<tbody>
@foreach (var recordEntry in Model.Context.Records.Select((r, i) => Tuple.Create(r, i)))
{
var record = recordEntry.Item1;
<tr class="action@(record.RecordAction)">
<td class="action"><i class="fa fa-fw"></i></td>
<td>@(recordEntry.Item2 + 1)</td>
@foreach (var field in record.Fields)
{
var friendlyValue = field.FriendlyValue;
<td class="header@(field.FieldType) action@(field.FieldAction.HasValue ? field.FieldAction.ToString() : "Error")" data-previousvalue="@(field.FieldAction.HasValue && field.FieldAction.Value == System.Data.EntityState.Modified ? field.FriendlyPreviousValue : null)">
@if (!field.FieldAction.HasValue)
{
<span class="errorMessage"><strong>Error:</strong> @field.ErrorMessage</span>
<i class="fa fa-exclamation-triangle fa-fw"></i>
}
@if (string.IsNullOrEmpty(friendlyValue))
{<span class="smallMessage">&lt;None&gt;</span>}
else if (field.FieldType == DeviceImportFieldTypes.DeviceSerialNumber && field.FieldAction.HasValue && field.FieldAction.Value != EntityState.Added)
{
@Html.ActionLink(friendlyValue, MVC.Device.Show((string)field.RawParsedValue), new { target="_blank" })}
else if (field.FieldType == DeviceImportFieldTypes.AssignedUserId && field.FieldAction.HasValue && field.FieldAction.Value != EntityState.Unchanged)
{
@Html.ActionLink(friendlyValue, MVC.User.Show((string)field.RawParsedValue), new { target="_blank" })}
else
{@friendlyValue}
</td>
}
</tr>
}
</tbody>
</table>
</div>
<div class="actionBar">
<a id="Devices_Import_Review_ChangeHeaders" href="@Url.Action(MVC.Device.ImportHeaders(Model.Context.SessionId))" class="button"><i class="fa fa-caret-left"></i>Change Import Columns</a>
@if (Model.StatisticImportRecords == 0)
{
<a id="Devices_Import_Review_Apply" href="#" class="button disabled" disabled><i class="fa fa-wrench"></i>Apply Device Import</a>
}
else
{
<a id="Devices_Import_Review_Apply" href="@Url.Action(MVC.API.Device.ImportApply(Model.Context.SessionId))" class="button"><i class="fa fa-wrench"></i>Apply Device Import</a>
}
</div>
</div>
<script>
$(function () {
$Devices_Import_Review_TableContainer = $('#Devices_Import_Review_TableContainer');
$Devices_Import_Review_TableContainer.find('tbody').tooltip({
items: 'td.action, td.actionError, td.actionModified',
content: function () {
var $this = $(this);
if ($this.hasClass('action')) {
var record = $(this).closest('tr');
if (record.hasClass('actionDetached')) {
return '<strong>Error Parsing Record</strong><div>Review the record fields for details about any errors.</div><div class="smallMessage">This record will be skipped.</div>';
} else if (record.hasClass('actionUnchanged')) {
return '<strong>No Changes</strong><div>No changes were found while parsing this record.</div><div class="smallMessage">This record will be skipped.</div>';
} else if (record.hasClass('actionModified')) {
return '<strong>Pending Changes</strong><div>This record contains changes which will be applied.</div>';
} else if (record.hasClass('actionAdded')) {
return '<strong>New Record</strong><div>This record will be imported.</div>';
}
} else if ($this.hasClass('actionError')) {
return $(this).find('span.errorMessage').html();
} else if ($this.hasClass('actionModified')) {
var v = $(this).attr('data-previousvalue');
if (v) {
return '<strong>Previous Value:</strong><br />' + v;
} else {
return '<strong>Previous Value:</strong><br /><em>&lt;None&gt;</em>';
}
}
},
position: {
my: "left top",
at: "left bottom",
collision: "flipfit flip"
}
});
});
</script>
@*
<div id="deviceImportReview">
@if (Model.ImportDevices.Count > 0)
{
@@ -248,4 +404,4 @@
{
<h2>No Devices were found in this file</h2>
}
</div>
</div>*@
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -11,7 +11,7 @@
<div class="actionBar">
@if (Authorization.Has(Claims.Device.Actions.Import))
{
@Html.ActionLinkButton("Import Devices", MVC.Device.ImportExport())
@Html.ActionLinkButton("Import Devices", MVC.Device.Import())
}
@if (Authorization.Has(Claims.Device.Actions.Export))
{
+1 -1
View File
@@ -115,7 +115,7 @@ WriteLiteral(">\r\n");
#line hidden
#line 14 "..\..\Views\Device\Index.cshtml"
Write(Html.ActionLinkButton("Import Devices", MVC.Device.ImportExport()));
Write(Html.ActionLinkButton("Import Devices", MVC.Device.Import()));
#line default
+5
View File
@@ -33,6 +33,11 @@
</appSettings>
<system.web>
<compilation>
<assemblies>
<add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
<httpHandlers>
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>