using C4IT.F4SDM; using C4IT.FASD.Base; using C4IT.Logging; using Matrix42.Common; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Dynamic; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using update4u.SPS.DataLayer; using update4u.SPS.DataLayer.Transaction; using static C4IT.FASD.Base.cF4SDTicket; using static C4IT.Logging.cLogManager; namespace C4IT.F4SD { public class F4SDHelperService { private static Guid SPSUserClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSUserClassBase"); private static Guid SPSAccountClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSAccountClassBase"); private static Guid SPSCommonClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSCommonClassBase"); private static Guid SPSAccountClassADID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSAccountClassAD"); private static Guid SPSActivityClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSActivityClassBase"); private static Guid SPSActivityClassIncidentID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSActivityClassIncident"); private static Guid SPSActivityClassUnitOfWorkID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSActivityClassUnitOfWork"); private static Guid SPSScCategoryClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSScCategoryClassBase"); private static Guid SPSAssetClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSAssetClassBase"); private static Guid SPSSecurityClassRole = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSSecurityClassRole"); public static readonly Guid Administrators = new Guid("{A5D7B682-B211-4D94-A96D-8C57EEDAFDEA}"); private const string TicketCloseActionId = "51bb3283-7bd1-e511-9a82-60e327035d31"; private const string TicketDirectLinkCreateTemplate = @"{0}/wm/app-ServiceDesk/notSet/create-object/{1}?view-options=%7B%22embedded%22:false%7D&presetParams={2}"; private const string IncidentDirectLinkCloseTemplate = @"{0}/wm/app-ServiceDesk/notSet/preview-object/SPSActivityTypeIncident/{1}/0/?view-options={2}"; private const string TicketDirectLinkPreviewTemplate = @"{0}/wm/app-ServiceDesk/notSet/preview-object/{1}/{2}/0/"; private const string TicketDirectLinkEditTemplate = @"{0}/wm/app-ServiceDesk/notSet/edit-object/{1}/{2}"; private const string placeHolderSubject = "PARAM_SUBJECT"; private const string placeHolderDescription = "PARAM_DESCRIPTION"; private const string c4itf4sdmonLinkBase = "f4sdsend://localhost"; private const string F4SDTicketTableName = "M42WPM-TICKETS-INFOS"; private const string F4SDTicketStatusColumnName = "STATUS"; public static IEnumerable ReadLines(Func streamProvider, Encoding encoding) { using (var stream = streamProvider()) using (var reader = new StreamReader(stream, encoding)) { string line; while ((line = reader.ReadLine()) != null) { yield return line; } } } internal async Task getDirectLinkF4SD(Guid EOID, string type) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { LogEntry($"Generating F4SD URI for type: '{type}', GUID: '{EOID}'", LogLevels.Debug); var builder = new UriBuilder(c4itf4sdmonLinkBase); var queryString = HttpUtility.ParseQueryString(builder.Query); switch (type.ToLowerInvariant().Split('.').Last()) { case "person": case "user": var user = getUsersByAsql($"[Expression-ObjectId]='{EOID}'")?.FirstOrDefault(); string Sids = string.Empty; if (user != null) { var accounts = getAccountsByAsql($"Owner='{user.Id}'"); Sids = string.Join(",", accounts?.Where(x => !string.IsNullOrEmpty(x.Sid)).Select(x => x.Sid) ?? Array.Empty()); } if (!string.IsNullOrEmpty(user?.Name)) { queryString.Add("command", "UserSidSearch"); queryString.Add("name", user.Name); queryString.Add("sids", Sids); } break; case "asset": var asset = getAssetsByAsql($"UsedInTypeSPSComputerType is not null AND [Expression-ObjectId]='{EOID}'")?.FirstOrDefault(); if (asset != null) { queryString.Add("command", "ComputerDomainSearch"); queryString.Add("name", asset.Name); queryString.Add("domain", asset.DomainName); } break; case "incident": case "servicerequest": case "ticket": var ticket = (await getTicketDetails(new List { EOID }))?.FirstOrDefault(); if (ticket != null) { queryString.Add("command", "TicketSearch"); queryString.Add("tname", ticket.Name); queryString.Add("tid", ticket.TicketObjectId.ToString()); if (ticket.AffectedUserId != Guid.Empty) { var initiator = getUsersByAsql($"ID = '{ticket.AffectedUserId}'")?.FirstOrDefault(); string userName = initiator?.Name; Sids = string.Join(",", getAccountsByAsql($"Owner='{ticket.AffectedUserId}'")?.Where(x => !string.IsNullOrEmpty(x.Sid)).Select(x => x.Sid) ?? Array.Empty()); queryString.Add("uname", userName); queryString.Add("sids", Sids); } } break; default: break; } builder.Query = queryString.ToString(); return string.IsNullOrEmpty(builder.Query) ? null : builder.ToString(); } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } internal async Task> getTicketListByUser(string userSid, int hours, int queueoption, List queues) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { LogEntry($"Generating ticket list for userSid: '{userSid}', {hours} hours", LogLevels.Debug); await Task.Delay(0); var tickets = new List(); var activityFilter = GetActivityFilter(userSid, hours, ticketAndServiceRequestEnabled(), queueoption, queues); LogEntry($"ASql Filter: {activityFilter}"); var activityTable = FragmentRequestBase.SimpleLoad(SPSActivityClassBaseID, string.Format("[Expression-ObjectID] as EOID, TicketNumber, Subject, Service.ID as ServiceId, Service.Name as ServiceName, SUBQUERY(BasicSchemaObjectType as bso, bso.Name, bso.id = base.T(SPSCommonClassBase).TypeID) as ActivityType" + ", COALESCE(T(SPSActivityClassIncident).Asset.T(SPSComputerClassBase).Name, T(SPSActivityClassIncident).Asset.T(SPSAssetClassSIMCard).PhoneNumber, T(SPSActivityClassIncident).Asset.Name, T(SPSActivityClassIncident).Asset.objectid) as AssetName" + ", SUBQUERY(BasicSchemaObjectType AS t, t.Name, t.ID=base.T(SPSActivityClassIncident).Asset.T(SPSCommonClassBase).TypeID) as AssetCIName" + ", CASE WHEN Initiator.PrimaryAccount.T(SPSAccountClassAd).Sid = '{0}' THEN 1 ELSE 0 END AS IsPrimaryAccount", userSid), activityFilter); if (activityTable?.Rows == null || activityTable.Rows.Count == 0) { LogEntry($"No activity entries found for userSid: '{userSid}'", LogLevels.Warning); return null; } for (int i = 0; i < activityTable.Rows.Count; i++) { DataRow entry = activityTable.Rows[i]; var activityEOId = getGuidFromObject(entry["EOID"]); var ticketNumber = getStringFromObject(entry["TicketNumber"]); var activityType = getStringFromObject(entry["ActivityType"]); var assetName = getStringFromObject(entry["AssetName"]); var ServiceName = getStringFromObject(entry["ServiceName"]); var assetCIName = getStringFromObject(entry["AssetCIName"]); var subject = getStringFromObject(entry["Subject"]); var stateData = GetActivityState(activityEOId); var state = stateData.Item1; var stateDisp = stateData.Item2; LogEntry($"Activity found {i + 1}/{activityTable.Rows.Count}: ObjectID={activityEOId}, TicketNumber={ticketNumber}, Subject={subject}, State={state}", LogLevels.Debug); if (string.IsNullOrEmpty(ticketNumber)) { LogEntry($"No TicketNumber found for activity entry", LogLevels.Warning); continue; } if (string.IsNullOrEmpty(assetName)) { LogEntry($"No AssetName found for activity entry", LogLevels.Debug); } if (string.IsNullOrEmpty(assetCIName)) { LogEntry($"No AssetCIName found for activity entry", LogLevels.Debug); } var ServiceId = getGuidFromObject(entry["ServiceId"]); if (ServiceId == Guid.Empty) { LogEntry($"no ServiceId found for activity entry", LogLevels.Debug); } var AssetCIName = getStringFromObject(entry["AssetCIName"]); if (AssetCIName == string.Empty) { LogEntry($"no AssetCIName found for activity entry", LogLevels.Debug); } var Subject = getStringFromObject(entry["Subject"]); if (Subject == string.Empty) { LogEntry($"no Subject found for activity entry", LogLevels.Warning); continue; } var IsPrimaryAccount = getIntFromObject(entry["IsPrimaryAccount"]); if (string.IsNullOrEmpty(subject)) { LogEntry($"No Subject found for activity entry", LogLevels.Warning); continue; } tickets.Add(new cF4SDTicketSummary() { TicketObjectId = activityEOId, Name = ticketNumber, ActivityType = activityType, AssetCIName = assetCIName, AssetName = assetName, ServiceId = ServiceId, ServiceName = ServiceName, StatusId = state, Status = stateDisp, Summary = subject, IsPrimaryAccount = IsPrimaryAccount == 1 }); } tickets = tickets.OrderByDescending(x => x.Name).ToList(); return tickets; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private static readonly string[] TicketOverviewKeys = new[] { "TicketsNew", "TicketsActive", "TicketsCritical", "TicketsNewInfo", "IncidentNew", "IncidentActive", "IncidentCritical", "IncidentNewInfo", "UnassignedTickets", "UnassignedTicketsCritical" }; public class TicketOverviewCountsResult { [JsonProperty("counts")] public Dictionary Counts { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } public class TicketOverviewCountsByRoleResult { [JsonProperty("generatedAtUtc")] public DateTime GeneratedAtUtc { get; set; } = DateTime.UtcNow; [JsonProperty("countsByRole")] public Dictionary> CountsByRole { get; set; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); } public class TicketOverviewRelationDto { public enumF4sdSearchResultClass Type { get; set; } public string Name { get; set; } public string DisplayName { get; set; } public Guid id { get; set; } public enumF4sdSearchResultStatus Status { get; set; } = enumF4sdSearchResultStatus.Unknown; public Dictionary Infos { get; set; } = null; public List Identities { get; set; } = null; } private sealed class TicketOverviewEntry { public Guid TicketId { get; set; } public string TicketNumber { get; set; } public string Summary { get; set; } public Guid InitiatorId { get; set; } public string InitiatorDisplayName { get; set; } public string InitiatorAccount { get; set; } public string InitiatorDomain { get; set; } public string InitiatorSid { get; set; } public Guid RecipientId { get; set; } public Guid RecipientRoleId { get; set; } public int State { get; set; } public DateTime CreatedDate { get; set; } public bool NewInformationReceived { get; set; } public bool ReactionTimeEscalated { get; set; } public bool SolutionTimeEscalated { get; set; } public bool IsIncident { get; set; } } internal async Task getTicketOverviewCounts(string sid, string scope, IEnumerable keys) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase); var normalizedKeys = (keys ?? Array.Empty()) .Where(k => !string.IsNullOrWhiteSpace(k)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); if (normalizedKeys.Count == 0) { normalizedKeys.AddRange(TicketOverviewKeys); } var entries = await LoadTicketOverviewEntries(sid, useRoleScope); var counts = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var key in normalizedKeys) { counts[key] = entries.Count(entry => MatchesTicketOverviewKey(entry, key)); } return new TicketOverviewCountsResult { Counts = counts }; } catch (Exception E) { LogException(E); return new TicketOverviewCountsResult(); } finally { LogMethodEnd(CM); } } internal async Task getTicketOverviewCountsByRoles(string sid, IEnumerable roleGuids, IEnumerable keys) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var normalizedKeys = (keys ?? Array.Empty()) .Where(k => !string.IsNullOrWhiteSpace(k)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); if (normalizedKeys.Count == 0) { normalizedKeys.AddRange(TicketOverviewKeys); } var roleIds = await ResolveTicketOverviewRoleIdsAsync(sid, roleGuids); if (roleIds.Count == 0) return new TicketOverviewCountsByRoleResult(); var entries = await LoadTicketOverviewEntriesByRoleIds(roleIds); var entriesByRole = entries .GroupBy(entry => entry.RecipientRoleId) .ToDictionary(group => group.Key, group => group.ToList()); var countsByRole = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var roleId in roleIds) { var roleEntries = entriesByRole.TryGetValue(roleId, out var list) ? list : new List(); var counts = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var key in normalizedKeys) { counts[key] = roleEntries.Count(entry => MatchesTicketOverviewKey(entry, key)); } countsByRole[roleId.ToString()] = counts; } return new TicketOverviewCountsByRoleResult { GeneratedAtUtc = DateTime.UtcNow, CountsByRole = countsByRole }; } catch (Exception E) { LogException(E); return new TicketOverviewCountsByRoleResult(); } finally { LogMethodEnd(CM); } } internal async Task> getTicketOverviewRelations(string sid, string scope, string key, int count) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (string.IsNullOrWhiteSpace(key)) return new List(); var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase); var entries = await LoadTicketOverviewEntries(sid, useRoleScope); var filtered = entries .Where(entry => MatchesTicketOverviewKey(entry, key)) .OrderByDescending(entry => entry.CreatedDate) .ToList(); if (count > 0) filtered = filtered.Take(count).ToList(); var relations = new List(filtered.Count); foreach (var entry in filtered) { var relation = new TicketOverviewRelationDto { Type = enumF4sdSearchResultClass.Ticket, DisplayName = entry.TicketNumber ?? string.Empty, Name = entry.TicketNumber ?? string.Empty, id = entry.TicketId, Status = enumF4sdSearchResultStatus.Active, Infos = new Dictionary { ["Summary"] = entry.Summary ?? string.Empty, ["StatusId"] = ConvertM42State(entry.State), ["UserDisplayName"] = entry.InitiatorDisplayName ?? string.Empty, ["UserAccount"] = entry.InitiatorAccount ?? string.Empty, ["UserDomain"] = entry.InitiatorDomain ?? string.Empty, ["UserSid"] = entry.InitiatorSid ?? string.Empty, ["Sids"] = entry.InitiatorSid ?? string.Empty, ["UserId"] = entry.InitiatorId != Guid.Empty ? entry.InitiatorId.ToString() : string.Empty, ["UserGuid"] = entry.InitiatorId != Guid.Empty ? entry.InitiatorId.ToString() : string.Empty }, Identities = new List { new cF4sdIdentityEntry { Class = enumFasdInformationClass.Ticket, Id = entry.TicketId }, new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = entry.InitiatorId } } }; relations.Add(relation); } return relations; } catch (Exception E) { LogException(E); return new List(); } finally { LogMethodEnd(CM); } } private async Task> LoadTicketOverviewEntries(string sid, bool useRoleScope) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); if (string.IsNullOrWhiteSpace(sid)) return new List(); var filter = await BuildTicketOverviewFilterAsync(sid, useRoleScope); return await LoadTicketOverviewEntriesByFilter(filter); } catch (Exception E) { LogException(E); return new List(); } finally { LogMethodEnd(CM); } } private async Task> LoadTicketOverviewEntriesByRoleIds(IEnumerable roleIds) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var filter = BuildTicketOverviewFilterForRoleIds(roleIds); return await LoadTicketOverviewEntriesByFilter(filter); } catch (Exception E) { LogException(E); return new List(); } finally { LogMethodEnd(CM); } } private async Task> LoadTicketOverviewEntriesByFilter(string filter) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); if (string.IsNullOrWhiteSpace(filter)) return new List(); var tbl = FragmentRequestBase.SimpleLoad(SPSActivityClassBaseID, "[Expression-ObjectID] as EOID" + ", TicketNumber" + ", Subject" + ", Initiator as InitiatorId" + ", Initiator.LastName + ISNULL(', ' + Initiator.FirstName, '') as Initiator" + ", Initiator.PrimaryAccount.T(SPSAccountClassAD).NBAccountName as InitiatorAccount" + ", Initiator.PrimaryAccount.T(SPSAccountClassAD).Domain.NT4Name as InitiatorDomain" + ", Initiator.PrimaryAccount.T(SPSAccountClassAD).Sid as InitiatorSid" + ", Recipient as RecipientId" + ", RecipientRole.T(SPSSecurityClassRole).ID as RecipientRoleId" + ", T(SPSCommonClassBase).State as State" + ", CreatedDate" + ", NewInformationReceived" + ", ReactionTimeEscalated" + ", SolutionTimeEscalated" + ", UsedInTypeSPSActivityTypeIncident as IsIncident", filter); if (tbl?.Rows == null || tbl.Rows.Count == 0) return new List(); var entries = new List(tbl.Rows.Count); foreach (DataRow row in tbl.Rows) { var ticketId = getGuidFromObject(row["EOID"]); if (ticketId == Guid.Empty) continue; entries.Add(new TicketOverviewEntry { TicketId = ticketId, TicketNumber = getStringFromObject(row["TicketNumber"]), Summary = getStringFromObject(row["Subject"]), InitiatorId = getGuidFromObject(row["InitiatorId"]), InitiatorDisplayName = getStringFromObject(row["Initiator"]), InitiatorAccount = getStringFromObject(row["InitiatorAccount"]), InitiatorDomain = getStringFromObject(row["InitiatorDomain"]), InitiatorSid = getStringFromObject(row["InitiatorSid"]), RecipientId = getGuidFromObject(row["RecipientId"]), RecipientRoleId = getGuidFromObject(row["RecipientRoleId"]), State = getIntFromObject(row["State"]), CreatedDate = getDateTimeFromObject(row["CreatedDate"]), NewInformationReceived = GetBoolValue(row["NewInformationReceived"]), ReactionTimeEscalated = GetBoolValue(row["ReactionTimeEscalated"]), SolutionTimeEscalated = GetBoolValue(row["SolutionTimeEscalated"]), IsIncident = row["IsIncident"] != DBNull.Value }); } return entries; } catch (Exception E) { LogException(E); return new List(); } finally { LogMethodEnd(CM); } } private async Task BuildTicketOverviewFilterAsync(string sid, bool useRoleScope) { var filter = BuildTicketOverviewBaseFilter(); if (!useRoleScope) { filter += $" AND Initiator.Accounts.T(SPSAccountClassAd).Sid = '{Escape(sid)}'"; return filter; } var roleIds = await ResolveTicketOverviewRoleIdsAsync(sid, null); return BuildTicketOverviewFilterForRoleIds(roleIds, filter); } private string BuildTicketOverviewBaseFilter() { var filter = "T(SPSCommonClassBase).State <> 204"; if (ticketAndServiceRequestEnabled()) { filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL" + " OR UsedInTypeSPSActivityTypeTicket IS NOT NULL" + " OR UsedInTypeSPSActivityTypeServiceRequest IS NOT NULL)"; } else { filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL)"; } return filter; } private string BuildTicketOverviewFilterForRoleIds(IEnumerable roleIds, string baseFilter = null) { var filter = string.IsNullOrWhiteSpace(baseFilter) ? BuildTicketOverviewBaseFilter() : baseFilter; var roleIdList = (roleIds ?? Enumerable.Empty()) .Where(id => id != Guid.Empty) .Distinct() .Select(id => $"'{id}'") .ToList(); if (roleIdList.Count == 0) return null; filter += $" AND RecipientRole.T(SPSSecurityClassRole).ID IN ({string.Join(", ", roleIdList)})"; return filter; } private async Task> ResolveTicketOverviewRoleIdsAsync(string sid, IEnumerable roleGuids) { var roleIds = (roleGuids ?? Enumerable.Empty()) .Where(id => id != Guid.Empty) .Distinct() .ToList(); if (roleIds.Count > 0) return roleIds; if (string.IsNullOrWhiteSpace(sid)) return new List(); var userId = getUserBySid(sid); if (userId == Guid.Empty) return new List(); var roles = await getRoleMembershipById(userId) ?? new List(); return roles .Where(role => role != null && role.Id != Guid.Empty) .Select(role => role.Id) .Distinct() .ToList(); } private static bool MatchesTicketOverviewKey(TicketOverviewEntry entry, string key) { if (entry == null || string.IsNullOrWhiteSpace(key)) return false; var isTicket = !entry.IsIncident; var isAssigned = entry.RecipientId != Guid.Empty || entry.RecipientRoleId != Guid.Empty; var isCritical = entry.ReactionTimeEscalated || entry.SolutionTimeEscalated; var isNew = entry.State == 200; var isActive = entry.State == 201 || entry.State == 202 || entry.State == 203; switch (key.Trim()) { case "TicketsNew": return isTicket && isAssigned && isNew; case "TicketsActive": return isTicket && isAssigned && isActive; case "TicketsCritical": return isTicket && isAssigned && isCritical; case "TicketsNewInfo": return isTicket && isAssigned && entry.NewInformationReceived; case "IncidentNew": return entry.IsIncident && isNew; case "IncidentActive": return entry.IsIncident && isActive; case "IncidentCritical": return entry.IsIncident && isCritical; case "IncidentNewInfo": return entry.IsIncident && entry.NewInformationReceived; case "UnassignedTickets": return isTicket && !isAssigned && isNew; case "UnassignedTicketsCritical": return isTicket && !isAssigned && isCritical; default: return false; } } private static bool GetBoolValue(object value) { if (value == null || value is DBNull) return false; if (value is bool boolValue) return boolValue; if (value is int intValue) return intValue != 0; if (value is long longValue) return longValue != 0; if (bool.TryParse(value.ToString(), out var parsedBool)) return parsedBool; if (int.TryParse(value.ToString(), out var parsedInt)) return parsedInt != 0; return false; } private static string ConvertM42State(int state) { switch (state) { case 200: return "New"; case 201: case 202: return "InProgress"; case 203: return "OnHold"; case 204: return "Closed"; default: return "Unknown"; } } private (int, string) GetActivityState(Guid activityEOId) { var tbl3 = FragmentRequestBase.SimpleLoad(SPSCommonClassBaseID, "State.Value as State, State.DisplayString as StateDisp", $"[Expression-ObjectID] = '{activityEOId}'"); if (tbl3?.Rows == null || tbl3.Rows.Count <= 0) { LogEntry($"SPSCommonClassBase fragment not found: ClassId={SPSCommonClassBaseID}, ObjectId={activityEOId}", LogLevels.Debug); return (0, ""); } var state = getIntFromObject(tbl3.Rows[0]["State"]); var stateDisp = getStringFromObject(tbl3.Rows[0]["StateDisp"]); return (state, stateDisp); } private string Escape(string input) { return input?.Replace("'", "''"); } private string GetActivityFilter( string userSid, int hours, bool ticketAndServiceRequestEnabled, int queueoption, List queues ) { // Baseline-Filter auf User und Datum var filter = $"Initiator.Accounts.T(SPSAccountClassAd).Sid = '{Escape(userSid)}'"; string fStartDate = AsqlHelper.PrepareDateTime(DateTime.UtcNow.AddHours(-hours), true); string fEndDate = AsqlHelper.PrepareDateTime(DateTime.UtcNow, true); // Incident vs. Ticket/ServiceRequest if (ticketAndServiceRequestEnabled) { filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL" + " OR UsedInTypeSPSActivityTypeTicket IS NOT NULL" + " OR UsedInTypeSPSActivityTypeServiceRequest IS NOT NULL)"; } else { filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL)"; } // Offene bzw. kürzlich geschlossene Objekte filter += " AND (T(SPSCommonClassBase).State <> 204" + $" OR (ClosedDate > {fStartDate} AND ClosedDate < {fEndDate}))"; // Queue-Filter nur, wenn tatsächlich Queues übergeben wurden if (queues != null && queues.Count > 0) { // URL-escaping für Name und ID var escapedNames = queues .Select(q => $"'{Escape(q.QueueName)}'") .ToList(); var escapedIds = queues .Select(q => $"'{Escape(q.QueueID.ToString())}'") .ToList(); string nameList = string.Join(", ", escapedNames); string idList = string.Join(", ", escapedIds); switch (queueoption) { // 1 = entweder keine Queue oder eine der übergebenen Queues (Name oder ID) case 1: filter += $" AND (" + "Queue IS NULL" + $" OR Queue.Name IN ({nameList})" + $" OR Queue.ID IN ({idList})" + ")"; break; // 2 = nur die übergebenen Queues (Name oder ID) case 2: filter += $" AND (" + "Queue IS NOT NULL" + $" AND (Queue.Name IN ({nameList})" + $" OR Queue.ID IN ({idList}))" + ")"; break; // 3 = nur Objekte ohne Queue case 3: filter += " AND Queue IS NULL"; break; // 0 oder andere = keine zusätzliche Einschränkung default: break; } } else if (queueoption == 3) { // Ausnahme: wenn keine Queues übergeben, aber Option 3 = nur ohne Queue filter += " AND Queue IS NULL"; } return filter; } internal async Task getDirectLinkCreateTicket(string sid, string assetname) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); // get global settings bool TicketAndServiceRequestEnabled = ticketAndServiceRequestEnabled(); var user = getUserBySid(sid); var asset = getAssetByName(assetname); dynamic presetParamsDyn = new ExpandoObject(); presetParamsDyn.SPSActivityClassBase = new ExpandoObject(); presetParamsDyn.SPSActivityClassBase.Subject = placeHolderSubject; presetParamsDyn.SPSActivityClassBase.DescriptionHTML = placeHolderDescription; if (user != Guid.Empty) presetParamsDyn.SPSActivityClassBase.Initiator = user; if (asset != null) { presetParamsDyn.SPSActivityClassIncident = new ExpandoObject(); presetParamsDyn.SPSActivityClassIncident.Asset = asset.Id; } var type = TicketAndServiceRequestEnabled ? "SPSActivityTypeTicket" : "SPSActivityTypeIncident"; var presetParams = HttpUtility.UrlEncode(JsonConvert.SerializeObject(presetParamsDyn)); var directLink = string.Format(TicketDirectLinkCreateTemplate, F4SDM42WebApiController.defaultInstance.BaseUrl, type, presetParams); return new DirectLink() { Link = directLink, DescriptionParameter = placeHolderDescription, SubjectParameter = placeHolderSubject }; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private static bool ticketAndServiceRequestEnabled() { var configProvider = F4SDM42WebApiController.defaultInstance._globalConfigurationProvider; // using reflection because signature of "ReloadSettings" changed in ESM v12 var method = configProvider.GetType().GetMethod("ReloadSettings"); if (method != null) { var defaultParameters = method.GetParameters().Select(p => p.HasDefaultValue ? p.DefaultValue : null).ToArray(); method.Invoke(configProvider, defaultParameters); } return configProvider.ServiceDeskConfiguration.TicketAndServiceRequestEnabled; } internal async Task> getTicketDetails(List ticketObjectIds) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var Filter = AsqlHelper.BuildInCondition("[Expression-ObjectId]", ticketObjectIds); LogEntry($"ASql Filter: {Filter}"); var tbl = FragmentRequestBase.SimpleLoad(SPSActivityClassBaseID, "[Expression-ObjectId] as EOID" + ", TicketNumber" + ", Subject" + ", Category.Name as Category" + ", Category as CategoryId" + ", CreatedDate, ClosedDate" + ", Initiator as InitiatorId" + ", Initiator.LastName + ISNULL(', ' + Initiator.FirstName, '') as Initiator" + ", DescriptionHTML" + ", Impact" + ", Impact.DisplayString as ImpactDisp" + ", Urgency" + ", Urgency.DisplayString as UrgencyDisp" + ", Priority" + ", Priority.Description as PrioDisp" + ", SolutionHTML", Filter); if (tbl == null || tbl.Rows == null) { LogEntry($"No activity entry list found with [Expression-ObjectId]='{string.Join(", ", ticketObjectIds)}'", LogLevels.Warning); return null; } var tickets = new List(); foreach (DataRow Entry in tbl.Rows) { var ActivityEOID = getGuidFromObject(Entry["EOID"]); if (ActivityEOID == Guid.Empty) { LogEntry($"No expression object id found for activity entry", LogLevels.Warning); continue; } var InitiatorId = getGuidFromObject(Entry["InitiatorId"]); var Initiator = getStringFromObject(Entry["Initiator"]); if (InitiatorId == Guid.Empty) { LogEntry($"No Initiator id found for activity entry", LogLevels.Warning); } var TicketNumber = getStringFromObject(Entry["TicketNumber"]); if (string.IsNullOrEmpty(TicketNumber)) { LogEntry($"No TicketNumber found for activity entry", LogLevels.Warning); continue; } var CategoryId = getGuidFromObject(Entry["CategoryId"]); if (CategoryId == Guid.Empty) { LogEntry($"No Initiator object id found for activity entry", LogLevels.Warning); continue; } var Category = getStringFromObject(Entry["Category"]); if (string.IsNullOrEmpty(Category)) { LogEntry($"No Category found for activity entry", LogLevels.Warning); continue; } var CreatedDate = getDateTimeFromObject(Entry["CreatedDate"]); if (CreatedDate == DateTime.MinValue) { LogEntry($"No CreationDate found for activity entry", LogLevels.Warning); continue; } var ClosedDate = getDateTimeFromObject(Entry["ClosedDate"]); var DescriptionHtml = getStringFromObject(Entry["DescriptionHTML"]); var Description = DescriptionHtml != string.Empty ? Matrix42.Common.Html.HtmlConverter.ConvertHtmlToPlainText(DescriptionHtml) : string.Empty; var SolutionHtml = getStringFromObject(Entry["SolutionHTML"]); var Solution = SolutionHtml != string.Empty ? Matrix42.Common.Html.HtmlConverter.ConvertHtmlToPlainText(SolutionHtml) : string.Empty; var Subject = getStringFromObject(Entry["Subject"]); if (string.IsNullOrEmpty(Subject)) { LogEntry($"No Subject found for activity entry", LogLevels.Warning); continue; } var PriorityId = getIntFromObject(Entry["Priority"]); var Priority = getStringFromObject(Entry["PrioDisp"]); var UrgencyId = getIntFromObject(Entry["Urgency"]); var Urgency = getStringFromObject(Entry["UrgencyDisp"]); var ImpactId = getIntFromObject(Entry["Impact"]); var Impact = getStringFromObject(Entry["ImpactDisp"]); LogEntry($"get category information: {Filter}"); Filter = string.Format("Recursive(Children).T(SPSScCategoryClassBase).Id='{0}'", CategoryId); var tbl1 = FragmentRequestBase.SimpleLoad(SPSScCategoryClassBaseID, "Name", Filter); if (tbl1?.Rows == null || tbl1.Rows.Count <= 0) { LogEntry($"No Category entry list found with Initiator='{CategoryId}'", LogLevels.Warning); return null; } var CategoryHierarchical = string.Join(" > ", tbl1.Rows.Cast().Reverse().Select(row => getStringFromObject(row["Name"]))); var tbl2 = FragmentRequestBase.SimpleLoad(SPSActivityClassIncidentID, "EntryBy as EntryBy, EntryBy.DisplayString as EntryByDisp, COALESCE(AssetAffected, Asset) as Asset, COALESCE(AssetAffected.T(SPSComputerClassAD).Domain.NT4Name, Asset.T(SPSComputerClassAD).Domain.NT4Name) as AssetDomain, COALESCE(COALESCE(AssetAffected.T(SPSComputerClassBase).Name, AssetAffected.T(SPSAssetClassSIMCard).PhoneNumber, AssetAffected.Name, AssetAffected.objectid), COALESCE(Asset.T(SPSComputerClassBase).Name, Asset.T(SPSAssetClassSIMCard).PhoneNumber, Asset.Name, Asset.objectid)) as AssetName", $"[Expression-ObjectID] = '{ActivityEOID}'"); if (tbl2?.Rows == null || tbl2.Rows.Count <= 0) { LogEntry($"SPSActivityClassIncident fragment not found: ClassId={SPSActivityClassIncidentID}, ObjectId={ActivityEOID}", LogLevels.Debug); continue; } var AssetId = getGuidFromObject(tbl2.Rows[0]["Asset"]); var Asset = getStringFromObject(tbl2.Rows[0]["AssetName"]); var AssetDomain = getStringFromObject(tbl2.Rows[0]["AssetDomain"]); var EntryBy = getIntFromObject(tbl2.Rows[0]["EntryBy"]); var EntryByDisp = getStringFromObject(tbl2.Rows[0]["EntryByDisp"]); M42Asset AssetObject = null; if (AssetId != Guid.Empty) AssetObject = getAssetsByAsql($"ID = '{AssetId}'").FirstOrDefault(); var tbl3 = FragmentRequestBase.SimpleLoad(SPSCommonClassBaseID, "State, State.DisplayString as StateDisp, SUBQUERY(BasicSchemaObjectType AS t, t.Name, t.ID=base.TypeID) as CIName", $"[Expression-ObjectID] = '{ActivityEOID}'"); if (tbl3?.Rows == null || tbl3.Rows.Count <= 0) { LogEntry($"SPSCommonClassBase fragment not found: ClassId={SPSCommonClassBaseID}, ObjectId={ActivityEOID}", LogLevels.Debug); continue; } var State = getIntFromObject(tbl3.Rows[0]["State"]); var StateDisp = getStringFromObject(tbl3.Rows[0]["StateDisp"]); var CIName = getStringFromObject(tbl3.Rows[0]["CIName"]); dynamic viewOptionsDyn = new ExpandoObject(); viewOptionsDyn.objectId = ActivityEOID; viewOptionsDyn.type = CIName; viewOptionsDyn.viewType = "action"; viewOptionsDyn.actionId = TicketCloseActionId; var viewOptions = HttpUtility.UrlEncode(JsonConvert.SerializeObject(viewOptionsDyn)); var ticketDirectLinkPreview = string.Format(TicketDirectLinkPreviewTemplate, F4SDM42WebApiController.defaultInstance.BaseUrl, CIName, ActivityEOID); var ticketDirectLinkEdit = string.Format(TicketDirectLinkEditTemplate, F4SDM42WebApiController.defaultInstance.BaseUrl, CIName, ActivityEOID); var ticketDirectLinkClose = string.Format(IncidentDirectLinkCloseTemplate, F4SDM42WebApiController.defaultInstance.BaseUrl, ActivityEOID, viewOptions); LogEntry($"Activity found: ObjectID={ActivityEOID}, TicketNumber={TicketNumber}, Subject={Subject}, State={State}", LogLevels.Debug); var ticket = new cF4SDTicket() { TicketObjectId = ActivityEOID, Name = TicketNumber, CIName = CIName, StatusId = State, Status = StateDisp, Summary = Subject, AffectedUserId = InitiatorId, AffectedUser = Initiator, CreationDate = CreatedDate, ClosingDate = ClosedDate, DescriptionHtml = DescriptionHtml, Description = Description, SolutionHtml = SolutionHtml, Solution = Solution, Priority = Priority, PriorityId = PriorityId, CreationSourceId = EntryBy, CreationSource = EntryByDisp, CategoryHierarchical = CategoryHierarchical, CategoryId = CategoryId, Category = Category, DirectLinkPreview = ticketDirectLinkPreview, Urgency = Urgency, UrgencyId = UrgencyId, Impact = Impact, ImpactId = ImpactId //JournalItems = GetJournalEntriesByEOID(ActivityEOID) }; if (AssetObject != null) { ticket.AssetId = AssetId; ticket.AssetName = Asset; ticket.AssetDomain = AssetDomain; ticket.AssetCIId = AssetObject.CIId; ticket.AssetCIName = AssetObject.CIName; ticket.AssetSKUAssetGroupId = AssetObject.SKUAssetGroupId; ticket.AssetSKUAssetGroup = AssetObject.SKUAssetGroup; ticket.AssetSKUTypeId = AssetObject.SKUTypeId; ticket.AssetSKUType = AssetObject.SKUType; } if (ticket.StatusId != 204) ticket.DirectLinkEdit = ticketDirectLinkEdit; if ((CIName.ToUpper() == "SPSACTIVITYTYPEINCIDENT" || CIName.ToUpper() == "SPSACTIVITYTYPESERVICEREQUEST") && ticket.StatusId != 204) ticket.DirectLinkClose = ticketDirectLinkClose; tickets.Add(ticket); } tickets = tickets.OrderBy(x => x.CreationDate).ToList(); tickets.Reverse(); return tickets; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } internal async Task> GetJournalEntries(Guid activityEOID) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); List journalEntries = new List(); var entries = F4SDM42WebApiController.defaultInstance._journalService.GetJournalList(activityEOID, false, 0, 50, "{2}", 120); LogEntry($"{entries.Length} Journal entries found for ObjectID={activityEOID}", LogLevels.Debug); for (int i = 0; i < entries.Length; i++) { Matrix42.Contracts.Platform.Data.JournalEntryInfo item = entries[i]; LogEntry($"Journal entry {i + 1}/{entries.Length}: ID={item.Id}, CreatedDate={item.CreatedDate}, CreatedBy={item.Creator}, Header={item.Header}", LogLevels.Debug); journalEntries.Add(new cTicketJournalItem() { JournalId = item.Id, ActivityObjectId = activityEOID, CreatedBy = item.Creator, CreationDate = (DateTime)item.CreatedDate, DescriptionHtml = item.Text, Description = Matrix42.Common.Html.HtmlConverter.ConvertHtmlToPlainText(item.Text), Header = item.Header, IsVisibleForUser = item.VisibleInPortal }); } return journalEntries; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } internal async Task updateActivitySolution(Guid objectId, string solutionHtml) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); if (objectId == Guid.Empty) return false; var tbl = FragmentRequestBase.SimpleLoad(SPSActivityClassBaseID, "ID", $"[Expression-ObjectID] = '{objectId}'"); if (tbl?.Rows == null || tbl.Rows.Count <= 0) { LogEntry($"SPSActivityClassBase fragment not found: ClassId={SPSActivityClassBaseID}, ObjectId={objectId}", LogLevels.Debug); return false; } var fragmentId = getGuidFromObject(tbl.Rows[0]["ID"]); SPSFragment activityFragment = ObjectBroker.GetFragment(SPSActivityClassBaseID, fragmentId); activityFragment["SolutionHTML"] = solutionHtml; ObjectBroker.UpdateFragment(SPSActivityClassBaseID, activityFragment); return true; } catch (Exception E) { LogException(E); return false; } finally { LogMethodEnd(CM); } } private List getAssetsByAsql(string Filter) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { // get SPSAssetClassBase if (string.IsNullOrEmpty(Filter)) return null; List assets = new List(); LogEntry($"ASql Filter: {Filter}"); var tbl = FragmentRequestBase.SimpleLoad(SPSAssetClassBaseID , "Id" + ", COALESCE(T(SPSComputerClassBase).Name, T(SPSAssetClassSIMCard).PhoneNumber, Name) as AssetName" + ", SUBQUERY(BasicSchemaObjectType AS t, t.Name, t.ID=base.T(SPSCommonClassBase).TypeID) as CIName" + ", SUBQUERY(BasicSchemaObjectType AS t, t.Id, t.ID=base.T(SPSCommonClassBase).TypeID) as CIId" + ", SKU.Type as SKUTypeId" + ", SKU.Type.DisplayString as SKUType" + ", SKU.Type.AssetGroup as SKUAssetGroupId" + ", SUBQUERY(SPSAssetPickupTypeCategory AS ag, ag.DisplayString, ag.Value=base.SKU.Type.AssetGroup) as SKUAssetGroup" + ", T(SPSComputerClassAD).Domain.NT4Name as DomainName" + ", T(SPSComputerClassAD).Sid as Sid" , Filter); if (tbl?.Rows == null || tbl.Rows.Count <= 0) { LogEntry($"SPSAssetClassBase fragment not found: ClassId={SPSActivityClassIncidentID}, Filter={Filter}", LogLevels.Debug); return null; } else { foreach (DataRow row in tbl.Rows) { var asset = new M42Asset() { Id = getGuidFromObject(row["Id"]), Name = getStringFromObject(row["AssetName"]), CIName = getStringFromObject(row["CIName"]), CIId = getGuidFromObject(row["CIId"]), SKUTypeId = getIntFromObject(row["SKUTypeId"]), SKUType = getStringFromObject(row["SKUType"]), SKUAssetGroupId = getIntFromObject(row["SKUAssetGroupId"]), SKUAssetGroup = getStringFromObject(row["SKUAssetGroup"]), DomainName = getStringFromObject(row["DomainName"]), Sid = getStringFromObject(row["Sid"]), }; assets.Add(asset); LogEntry($"Asset found: Id={asset.Id}, Name={asset.Name}, CIName={asset.CIName}", LogLevels.Debug); } } return assets; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private M42Asset getAssetByName(string assetname) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { // get SPSAssetClassBase if (string.IsNullOrEmpty(assetname)) return null; var Filter = string.Format("T(SPSComputerClassBase).Name = '{0}' OR T(SPSAssetClassSIMCard).PhoneNumber = '{0}' OR Name = '{0}'", assetname); return getAssetsByAsql(Filter)?.First(); } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private Guid getUserBySid(string sid) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { string sidPattern = @"^S-\d-\d+-(\d+-){1,14}\d+$"; if (string.IsNullOrEmpty(sid) || !Regex.IsMatch(sid, sidPattern)) { LogEntry($"Invalid or empty SID: '{sid}'", LogLevels.Warning); return Guid.Empty; } var users = getUsersByAsql(string.Format("Accounts.T(SPSAccountClassAD).Sid = '{0}' OR PrimaryAccount.T(SPSAccountClassAD).Sid = '{0}'", sid)); return users != null ? users.First().Id : Guid.Empty; } catch (Exception E) { LogException(E); return Guid.Empty; } finally { LogMethodEnd(CM); } } private List getUsersByAsql(string asqlFilter) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var Persons = new List(); LogEntry($"ASql Filter: {asqlFilter}"); var tbl = FragmentRequestBase.SimpleLoad(SPSUserClassBaseID, "Id as Id" + ", [Expression-ObjectID] as UserEOID" + ", T(SPSCommonClassBase).State as State" + ", LastName + ISNULL(', ' + FirstName,'') as Name" + ", PreferredMailCulture.Locale as Locale" + ", T(SPSCommonClassBase).State.DisplayString as StateDisp", asqlFilter); if (tbl?.Rows == null || tbl.Rows.Count <= 0) { LogEntry($"no user entry list found with asql='{asqlFilter}'", LogLevels.Warning); return null; } foreach (DataRow Entry in tbl.Rows) { // get the id var UserId = getGuidFromObject(Entry["ID"]); if (UserId == Guid.Empty) { LogEntry($"no id found for user entry", LogLevels.Warning); continue; } var UserEOID = getGuidFromObject(Entry["UserEOID"]); if (UserEOID == Guid.Empty) { LogEntry($"no expression object id found for user entry", LogLevels.Warning); continue; } var Name = getStringFromObject(Entry["Name"]); if (string.IsNullOrEmpty(Name)) { LogEntry($"no Name found for entry", LogLevels.Debug); } var Locale = getStringFromObject(Entry["Locale"]); if (string.IsNullOrEmpty(Locale)) { LogEntry($"no locale found for entry", LogLevels.Debug); } var StateDisp = getStringFromObject(Entry["StateDisp"]); if (string.IsNullOrEmpty(StateDisp)) { LogEntry($"no statedisp found for entry", LogLevels.Debug); } var State = getIntFromObject(Entry["State"]); LogEntry($"User found: ID={UserId}, ObjectID={UserEOID}, State={State}", LogLevels.Debug); if (State != 2023) LogEntry($"User is not active", LogLevels.Debug); Persons.Add(new M42User() { Id = UserId, ObjectId = UserEOID, Name = Name, State = State, Locale = Locale, LanguageId = new CultureInfo(Locale).LCID % 1024, StateDisp = StateDisp }); } return Persons; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private List getAccountsByAsql(string asqlFilter) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var result = new List(); LogEntry($"ASql Filter: {asqlFilter}"); var tbl = FragmentRequestBase.SimpleLoad(SPSAccountClassBaseID, "Id as Id" + ", [Expression-ObjectID] as EOID" + ", T(SPSCommonClassBase).State as State" + ", T(SPSAccountClassAD).Sid as Sid" + ", T(SPSAccountClassAD).UserPrincipalName as UserPrincipalName" + ", Owner ", asqlFilter); if (tbl?.Rows == null || tbl.Rows.Count <= 0) { LogEntry($"no account entry list found with asql='{asqlFilter}'", LogLevels.Warning); return null; } foreach (DataRow Entry in tbl.Rows) { // get the id var Id = getGuidFromObject(Entry["ID"]); if (Id == Guid.Empty) { LogEntry($"no id found for entry", LogLevels.Warning); continue; } var EOID = getGuidFromObject(Entry["EOID"]); if (EOID == Guid.Empty) { LogEntry($"no expression object id found for entry", LogLevels.Warning); continue; } var Owner = getGuidFromObject(Entry["Owner"]); if (Owner == Guid.Empty) { LogEntry($"no owner found for entry", LogLevels.Warning); } var Sid = getStringFromObject(Entry["Sid"]); if (string.IsNullOrEmpty(Sid)) { LogEntry($"no Sid found for entry", LogLevels.Debug); } var UserPrincipalName = getStringFromObject(Entry["UserPrincipalName"]); if (string.IsNullOrEmpty(UserPrincipalName)) { LogEntry($"no UserPrincipalName found for entry", LogLevels.Debug); } var State = getIntFromObject(Entry["State"]); LogEntry($"Account found: ID={Id}, ObjectID={EOID}, State={State}", LogLevels.Debug); result.Add(new M42Account() { Id = Id, EOID = EOID, State = State, UserPrincipalName = UserPrincipalName, Sid = Sid, Owner = Owner }); } return result; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } internal HttpResponseMessage privGetLog(string download, int maxLines, HttpRequestMessage request, string filter) { var response = new HttpResponseMessage(); if (download == "1") { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var logger = (DefaultLogger.Manager as cLogManagerFile); var logFileName = logger.GetLogFileName(); MemoryStream memoryStream = new MemoryStream(); ZipArchive zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true); string[] files = Directory.GetFiles(Path.GetDirectoryName(logFileName), $"*.log"); foreach (string fileName in files) { using (var tmpInStream = new FileStream(logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { ZipArchiveEntry zipArchiveEntry = zipArchive.CreateEntry(Path.GetFileName(fileName)); using (Stream destination = zipArchiveEntry.Open()) { tmpInStream.CopyTo(destination); } } } zipArchive.Dispose(); memoryStream.Position = 0L; HttpResponseMessage httpResponseMessage = request.CreateResponse(HttpStatusCode.OK); httpResponseMessage.Content = new StreamContent(memoryStream); httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = Path.GetFileNameWithoutExtension(logFileName) + ".zip" }; httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); return httpResponseMessage; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } else { var logger = (DefaultLogger.Manager as cLogManagerFile); var logFileName = logger.GetLogFileName(); using (var inStream = new FileStream(logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var lines = ReadLines(() => inStream, Encoding.UTF8) .ToList(); var sb = new StringBuilder(); sb.AppendLine(""); var htmlLines = new List(); foreach (var line in lines) { var lineSplit = line.Split('\t'); if (string.IsNullOrEmpty(filter) || !string.IsNullOrEmpty(filter) && lineSplit.Length > 4 && filter.ToLowerInvariant().Split(',').Contains(lineSplit[3].ToLowerInvariant())) htmlLines.Add(""); } if (maxLines > 0) htmlLines = htmlLines.Skip(Math.Max(0, htmlLines.Count() - maxLines)).ToList(); foreach (var line in htmlLines) { sb.AppendLine(line); } sb.AppendLine("
" + string.Join("", lineSplit) + "
"); response.Content = new StringContent(sb.ToString()); response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html") { CharSet = Encoding.UTF8.HeaderName }; response.Content.Headers.Add("CodePage", Encoding.UTF8.CodePage.ToString()); return response; } } } internal List privGetLog2() { var response = new HttpResponseMessage(); { var logger = (DefaultLogger.Manager as cLogManagerFile); var logFileName = logger.GetLogFileName(); using (var inStream = new FileStream(logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var lines = ReadLines(() => inStream, Encoding.UTF8) .ToList(); var entries = new List(); DateTime dateValue; for (int i = 0; i < lines.Count; i++) { string line = lines[i]; try { var lineSplit = line.Split('\t'); if (lineSplit.Length >= 5) { if (string.IsNullOrEmpty(lineSplit[0])) { entries.Last().Message += Environment.NewLine + lineSplit[4]; continue; } DateTime.TryParseExact(lineSplit[0], "yyyy-MM-dd HH:mm:ss:fffff", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateValue); entries.Add(new cM42LogEntry() { LineNumber = i + 1, date = dateValue, ProcessId = lineSplit[1], logLvl = lineSplit[3], Theme = lineSplit[2], Message = lineSplit[4], }); } } catch (Exception) { } } return entries; } } } private int getIntFromObject(object o, int Default = 0) { try { if (o == null) return Default; if (o is DBNull) return Default; if (o is int @int) return @int; if (o is long int1) return (int)int1; if (int.TryParse(o.ToString(), out var r)) return r; } catch { } return Default; } private DateTime getDateTimeFromObject(object o) { try { if (o == null) return DateTime.MinValue; if (o is DBNull) return DateTime.MinValue; if (o is DateTime @dateTime) return @dateTime; if (o is string dstr && DateTime.TryParse(o.ToString(), out var r)) return r; } catch { } return DateTime.MinValue; } private string getStringFromObject(object o, string Default = "") { try { if (o == null) return Default; if (o is DBNull) return Default; return o.ToString(); } catch { } return Default; } private Guid getGuidFromObject(object o) { try { if (o == null) return Guid.Empty; if (o is DBNull) return Guid.Empty; if (o is Guid G) return G; if (Guid.TryParse(o.ToString(), out var r)) return r; } catch { } return Guid.Empty; } internal cM42LogEntry privGetLog2(int id) { return privGetLog2().Find(x => x.LineNumber == id); } internal async Task> getRoleMembershipById(Guid UserId) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); var RoleMemberships = new List(); try { await Task.Delay(0); var asqlFilter = string.Format("Members.Id = '{0}'", UserId.ToString()); LogEntry($"ASql Filter: {asqlFilter}"); FragmentRequestBase fragmentRequestBase = new FragmentRequestBase(SPSSecurityClassRole, ColumnSelectOption.List, "Id as Id" + ", Name as Name"); fragmentRequestBase.Where = asqlFilter; var langExt = new FragmentRequestExtensionLanguage(); langExt.AddCultureRequest(new CultureInfo("en")); fragmentRequestBase.AddExtension(langExt); using (SPSTransactionScope sPSTransactionScope = new SPSTransactionScope(SPSTransactionScopeOption.Required, new SPSTransactionOptions(IsolationLevel.ReadUncommitted))) { fragmentRequestBase.Load(); sPSTransactionScope.Complete(); } // if requested language is already "en" use the english table var tbl = fragmentRequestBase.DataSet.Tables[1]; if (tbl?.Rows == null || tbl.Rows.Count <= 0) tbl = fragmentRequestBase.DataSet.Tables[0]; if (tbl?.Rows == null || tbl.Rows.Count <= 0) { LogEntry($"no role membership found with asql='{asqlFilter}'", LogLevels.Debug); return RoleMemberships; } foreach (DataRow Entry in tbl.Rows) { if (Entry.Table.Columns.Contains("LCID") && (int)Entry["LCID"] != 9) continue; // get the id var IDColumn = Entry.Table.Columns.Contains("Owner") ? "Owner" : "ID"; var RoleId = getGuidFromObject(Entry[IDColumn]); if (RoleId == Guid.Empty) { LogEntry($"no id found for role entry", LogLevels.Warning); continue; } var Name = getStringFromObject(Entry["Name"]); if (string.IsNullOrEmpty(Name)) { LogEntry($"no Name found for entry", LogLevels.Debug); } LogEntry($"Role Membership found: ID={RoleId}, Name={Name}", LogLevels.Debug); RoleMemberships.Add(new M42Role() { Id = RoleId, Name = Name }); } return RoleMemberships; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } /* internal async Task> getRoleMembershipById(Guid UserId) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); var RoleMemberships = new List(); try { await Task.Delay(0); var asqlFilter = string.Format("Members.Id = '{0}'", UserId.ToString()); LogEntry($"ASql Filter: {asqlFilter}"); var tbl = FragmentRequestBase.SimpleLoad(SPSSecurityClassRole, "Id as Id" + ", Name as Name", asqlFilter); if (tbl?.Rows == null || tbl.Rows.Count <= 0) { LogEntry($"no role membership found with asql='{asqlFilter}'", LogLevels.Debug); return RoleMemberships; } foreach (DataRow Entry in tbl.Rows) { // get the id var RoleId = getGuidFromObject(Entry["ID"]); if (RoleId == Guid.Empty) { LogEntry($"no id found for role entry", LogLevels.Warning); continue; } var Name = getStringFromObject(Entry["Name"]); if (string.IsNullOrEmpty(Name)) { LogEntry($"no Name found for entry", LogLevels.Debug); } LogEntry($"Role Membership found: ID={RoleId}, Name={Name}", LogLevels.Debug); RoleMemberships.Add(new M42Role() { Id = RoleId, Name = Name }); } return RoleMemberships; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } */ internal async Task UserPermissionsInfo(string filter) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); var RoleMemberships = new List(); try { await Task.Delay(0); var retVal = new UserPermissionsInfo(); List m42Users = getUsersByAsql(filter); if (m42Users != null && m42Users.Count == 1) { retVal.User = m42Users[0]; var user = F4SDM42WebApiController.defaultInstance._userProfile.GetInteractiveUserInfo(retVal.User.Id); if (user != null) { retVal.User.Id = user.Id; retVal.User.Currency = user.Currency; retVal.User.Email = user.Email; retVal.User.FirstName = user.FirstName; retVal.User.LastName = user.LastName; retVal.User.Phone = user.Phone; retVal.User.Photo = user.Photo; } retVal.Roles = await getRoleMembershipById(retVal.User.Id); retVal.User.IsAdmin = retVal.Roles?.Where(i => i.Id == Administrators).FirstOrDefault() != null; return retVal; } return null; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private enum eM42EntryBy { Phone = 0, Email = 1, Portal = 2, CatalogOrder = 3, EventTrigger = 4, Feedback = 5, VisualSupportAgent = 6, ComplianceAlert = 7, F4SD = 20010, } public class DirectLink { public string Link { get; set; } public string SubjectParameter { get; set; } public string DescriptionParameter { get; set; } } public class M42Asset { public Guid Id { get; set; } public string Name { get; set; } public string CIName { get; set; } public string SKUType { get; set; } public string SKUAssetGroup { get; set; } public Guid CIId { get; set; } public int SKUTypeId { get; internal set; } public int SKUAssetGroupId { get; internal set; } public string DomainName { get; internal set; } public string Sid { get; internal set; } } } public class M42User { public Guid Id { get; internal set; } public Guid ObjectId { get; internal set; } public string Name { get; internal set; } public string FirstName { get; internal set; } public string LastName { get; internal set; } public string Email { get; internal set; } public string Photo { get; internal set; } public string Phone { get; internal set; } public string Currency { get; internal set; } public string Locale { get; internal set; } public int LanguageId { get; internal set; } public bool IsAdmin { get; internal set; } public int State { get; internal set; } public string StateDisp { get; internal set; } } internal class M42Account { public Guid Id { get; internal set; } public Guid EOID { get; internal set; } public Guid Owner { get; internal set; } public int State { get; internal set; } public string UserPrincipalName { get; internal set; } public string Sid { get; internal set; } } public class M42Role { public Guid Id { get; internal set; } public string Name { get; internal set; } } public class UserPermissionsInfo { public M42User User { get; internal set; } public List Roles { get; internal set; } public UserPermissionsInfo() { User = new M42User(); Roles = new List(); } } }