Initial commit: JDE Scoping Tool migration project

Set up repository with legacy .NET Framework 4.8 source (OLD/),
new .NET 10 Blazor solution (NEW/), OpenSpec specifications,
documentation, and project configuration.
This commit is contained in:
Joseph Doherty
2026-01-02 07:43:29 -05:00
commit 26ff8d9b4f
1761 changed files with 596509 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using OfficeOpenXml;
using OfficeOpenXml.Style;
namespace WebInterface.Helpers
{
/// <summary>
/// Lot # / work order # template generator
/// </summary>
public class ExcelTemplateGenerator
{
/// <summary>
/// Generates Excel spreadsheet data entry template
/// </summary>
/// <typeparam name="T">Data type of source data</typeparam>
/// <param name="sourceData">Source data to display in template</param>
/// <param name="headerText">Column header text</param>
/// <returns>Generated spreadsheet</returns>
public static byte[] Generate<T>(List<T> sourceData, string headerText)
{
//Create worksheet to hold data
ExcelPackage package = new ExcelPackage();
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Data Entry Template");
//Write header
worksheet.Cells[1, 1].Value = headerText;
worksheet.Cells[1, 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
worksheet.Cells[1, 1].Style.Font.Bold = true;
worksheet.Cells[1, 1].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[1, 1].Style.Fill.BackgroundColor.SetColor(Color.Gainsboro);
worksheet.Column(1).Width = 45;
//Write uploaded data
if (sourceData != null)
{
int row = 2;
foreach (T element in sourceData)
{
worksheet.Cells[row++, 1].Value = element;
}
}
//Write result
MemoryStream fileStream = new MemoryStream();
package.SaveAs(fileStream);
fileStream.Position = 0;
byte[] data = fileStream.ToArray();
return data;
}
/// <summary>
/// Generates Excel spreadsheet data entry template
/// </summary>
/// <param name="sourceData">Source data to display in template</param>
/// <param name="headerText">Column header text</param>
/// <returns>Generated spreadsheet</returns>
public static byte[] Generate(object[][] sourceData, string[] headerText)
{
//Create worksheet to hold data
ExcelPackage package = new ExcelPackage();
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Data Entry Template");
int numColumns = headerText.Length;
//Write header
ExcelRange header = worksheet.Cells[1, 1, 1, numColumns];
header.LoadFromArrays(new List<object[]> { headerText });
header.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
header.Style.Font.Bold = true;
header.Style.Fill.PatternType = ExcelFillStyle.Solid;
header.Style.Fill.BackgroundColor.SetColor(Color.Gainsboro);
//Write uploaded data
if (sourceData != null && sourceData.Length > 0)
{
ExcelRange dataRange = worksheet.Cells[2, 1, sourceData.Length + 2, numColumns];
dataRange.LoadFromArrays(sourceData);
}
//Size columns
for (int col = 1; col <= numColumns; col++)
{
worksheet.Column(col).Width = 65;
worksheet.Column(col).Style.Numberformat.Format = "@";
}
//Write result
MemoryStream fileStream = new MemoryStream();
package.SaveAs(fileStream);
fileStream.Position = 0;
byte[] data = fileStream.ToArray();
return data;
}
}
}
+65
View File
@@ -0,0 +1,65 @@
using System.Security.Claims;
using System.Text;
using System.Web.Mvc;
using DataModel.Models;
using WebInterface.Security;
namespace WebInterface.Helpers
{
/// <summary>
/// HTML helper methods
/// </summary>
public static class HtmlHelpers
{
/// <summary>
/// Gets current user for request
/// </summary>
/// <param name="html">HTML helper context</param>
/// <returns>LDAP entry for current session</returns>
public static LDAPEntry CurrentUser(this HtmlHelper html)
{
//Verify user is authenticated before continuing
if (!html.ViewContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
return null;
}
LDAPEntry currentUser = null;
try
{
UserIdentity identity = new UserIdentity(html.ViewContext.RequestContext.HttpContext.User.Identity as ClaimsIdentity);
currentUser = identity.ToLDAPEntry();
}
catch
{
//Do nothing
}
return currentUser;
}
/// <summary>
/// Generates logon control for user/session
/// </summary>
/// <param name="html">HTML helper context</param>
/// <returns>Raw HTML output for logon control</returns>
public static MvcHtmlString LogonControl(this HtmlHelper html)
{
LDAPEntry currentUser = html.CurrentUser();
UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext);
StringBuilder builder = new StringBuilder();
if (currentUser == null || html.ViewContext.RequestContext.HttpContext.User.Identity==null|| !html.ViewContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
builder.AppendFormat("<a href='{0}' class='btn btn-primary'>Login</a>", urlHelper.Action("Login", "Account", null, null));
}
else
{
builder.AppendFormat("<span style='color: #9d9d9d;'>{0}</span> <a href='{1}' class='btn btn-primary'>Logout</a>", currentUser.DisplayName, urlHelper.Action("Logout", "Account", null, null));
}
return new MvcHtmlString(builder.ToString());
}
}
}
+66
View File
@@ -0,0 +1,66 @@
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;
namespace WebInterface.Helpers
{
/// <summary>
/// Custom JSON action result using JSON.NET library
/// </summary>
public class JsonNetResult : JsonResult
{
/// <summary>
/// Serialization settings
/// </summary>
public JsonSerializerSettings Settings { get; }
/// <summary>
/// Constructor
/// </summary>
public JsonNetResult()
{
Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Error
};
}
/// <summary>
/// Generates the resulting JSON output
/// </summary>
/// <param name="context">Controller context for outputting</param>
public override void ExecuteResult(ControllerContext context)
{
//Verify context is valid before continuing
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
//Verify HTTP method is valid before continuing
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("JSON GET is not allowed");
}
//Write response
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(ContentType) ? "application/json" : ContentType;
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data == null)
return;
var scriptSerializer = JsonSerializer.Create(Settings);
using (var sw = new StringWriter())
{
scriptSerializer.Serialize(sw, Data);
response.Write(sw.ToString());
}
}
}
}
+162
View File
@@ -0,0 +1,162 @@
using System;
using System.DirectoryServices;
using System.Linq;
using DataModel.Models;
using NLog;
using SearchResult = System.DirectoryServices.SearchResult;
namespace WebInterface.Helpers
{
/// <summary>
/// LDAP server interface helper methods
/// </summary>
public class LDAPHelper
{
/// <summary>
/// LDAP user lookup format
/// </summary>
private const string LDAP_LOOKUP_FORMAT = "(sAMAccountName={0})";
/// <summary>
/// Shared logger instance
/// </summary>
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// Authenticates the user's credentials against the given LDAP server
/// </summary>
/// <param name="username">LDAP username</param>
/// <param name="password">LDAP password</param>
/// <param name="serverURL">LDAP server URL</param>
/// <param name="ldapGroup">LDAP group to filter for</param>
/// <returns>Whether or not user's credentials are valid</returns>
public static bool Authenticate(string username, string password, string serverURL, string ldapGroup = null)
{
bool result = false;
try
{
//Form full LDAP URL
string ldapURL = $"LDAP://{serverURL}";
//Attempt to find entry
DirectoryEntry entry = new DirectoryEntry(ldapURL, username, password);
object val = entry.NativeObject;
result = true;
}
catch (Exception error)
{
//Log but do not forward error
logger.Error("Authenticate: failed to authenticate user '{0}' against LDAP server '{1}': {2}.", username, serverURL, error.Message);
}
return result;
}
/// <summary>
/// Checks if user is in given group
/// </summary>
/// <param name="username">LDAP username</param>
/// <param name="password">LDAP password</param>
/// <param name="serverURL">LDAP server URL</param>
/// <param name="ldapGroup">LDAP group to filter for</param>
/// <param name="ldapFilter">LDAP search filter</param>
/// <returns>Whether or not user belongs to given group</returns>
public static bool IsInGroup(string username, string password, string serverURL, string ldapGroup, string ldapFilter = LDAP_LOOKUP_FORMAT)
{
try
{
//Form full LDAP URL
string ldapURL = $"LDAP://{serverURL}";
//Create a LDAP searcher
DirectorySearcher searcher = new DirectorySearcher
{
SearchRoot = new DirectoryEntry(ldapURL, username, password),
Filter = string.Format(ldapFilter, username)
};
foreach (SearchResult searchResult in searcher.FindAll())
{
DirectoryEntry directoryEntry = searchResult.GetDirectoryEntry();
foreach (var groupName in directoryEntry.Properties["memberOf"])
{
if (string.Equals(groupName.ToString(), ldapGroup, StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
}
}
}
catch (Exception error)
{
//Log but do not forward error
logger.Error("Authenticate: failed to authenticate user '{0}' against LDAP server '{1}': {2}.", username, serverURL, error.Message);
}
return false;
}
/// <summary>
/// Searches LDAP server for matching entry
/// </summary>
/// <param name="serverURL">URL for LDAP server to search</param>
/// <param name="username">LDAP server binding username</param>
/// <param name="password">LDAP server binding password</param>
/// <param name="ldapFilter">LDAP search filter</param>
/// <returns>Collection of matching LDAP entries</returns>
public static LDAPEntry LookupUser(string username, string password, string serverURL, string ldapFilter = LDAP_LOOKUP_FORMAT)
{
LDAPEntry result = null;
//Form full LDAP URL
string ldapURL = $"LDAP://{serverURL}";
//Create a LDAP searcher
DirectorySearcher searcher = new DirectorySearcher
{
SearchRoot = new DirectoryEntry(ldapURL, username, password),
Filter = string.Format(ldapFilter, username)
};
//Loop through search results
foreach (SearchResult searchResult in searcher.FindAll())
{
//Parse the entry's details
result = new LDAPEntry()
{
DN = ExtractProperty(searchResult, "distinguishedName"),
Username = username.ToLower(),
FirstName = ExtractProperty(searchResult, "givenName"),
LastName = ExtractProperty(searchResult, "sn"),
EmailAddress = ExtractProperty(searchResult, "mail"),
Title = ExtractProperty(searchResult, "title")
};
if (string.IsNullOrEmpty(result.FirstName) && !string.IsNullOrEmpty(result.DisplayName))
{
result.FirstName = result.DisplayName;
}
if (string.IsNullOrEmpty(result.LastName) && !string.IsNullOrEmpty(result.DisplayName))
{
result.LastName = result.DisplayName;
}
}
return result;
}
/// <summary>
/// Extracts the specified property from the LDAP search result
/// </summary>
/// <param name="result">LDAP search result to parse property from</param>
/// <param name="key">Lookup key for property</param>
/// <returns>Property value if it exists, empty string if it does not exist</returns>
private static string ExtractProperty(SearchResult result, string key)
{
return result.Properties[key].Count > 0 ? (string)result.Properties[key][0] : string.Empty;
}
}
}