diff --git a/F4SDM42WebApi/F4SDHelperService.cs b/F4SDM42WebApi/F4SDHelperService.cs index c6fa61b..c8e1169 100644 --- a/F4SDM42WebApi/F4SDHelperService.cs +++ b/F4SDM42WebApi/F4SDHelperService.cs @@ -1,256 +1,256 @@ -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); - } - } - - +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 - }); - } - + { + 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; } @@ -285,6 +285,16 @@ namespace C4IT.F4SD 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; } @@ -354,6 +364,64 @@ namespace C4IT.F4SD } } + 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(); @@ -430,6 +498,49 @@ namespace C4IT.F4SD 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(); @@ -443,7 +554,7 @@ namespace C4IT.F4SD ", Initiator.PrimaryAccount.T(SPSAccountClassAD).Domain.NT4Name as InitiatorDomain" + ", Initiator.PrimaryAccount.T(SPSAccountClassAD).Sid as InitiatorSid" + ", Recipient as RecipientId" + - ", RecipientRole as RecipientRoleId" + + ", RecipientRole.T(SPSSecurityClassRole).ID as RecipientRoleId" + ", T(SPSCommonClassBase).State as State" + ", CreatedDate" + ", NewInformationReceived" + @@ -497,6 +608,20 @@ namespace C4IT.F4SD } 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"; @@ -511,31 +636,49 @@ namespace C4IT.F4SD filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL)"; } - if (!useRoleScope) - { - filter += $" AND Initiator.Accounts.T(SPSAccountClassAd).Sid = '{Escape(sid)}'"; - return filter; - } + 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 null; + return new List(); var roles = await getRoleMembershipById(userId) ?? new List(); - if (roles.Count == 0) - return null; - - var roleIDs = roles - .Select(role => role?.Id) - .Where(Id => !Guid.Empty.Equals(Id)) - .Select(Id => $"'{(Id)}'") + return roles + .Where(role => role != null && role.Id != Guid.Empty) + .Select(role => role.Id) + .Distinct() .ToList(); - - if (roleIDs.Count == 0) - return null; - - filter += $" AND RecipientRole.T(SPSSecurityClassRole).ID IN ({string.Join(", ", roleIDs)})"; - return filter; } private static bool MatchesTicketOverviewKey(TicketOverviewEntry entry, string key) @@ -612,1199 +755,1199 @@ namespace C4IT.F4SD 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(); - } - } -} + 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(); + } + } +} diff --git a/F4SDM42WebApi/F4SDM42WebApiController.cs b/F4SDM42WebApi/F4SDM42WebApiController.cs index 7a021c7..d77b687 100644 --- a/F4SDM42WebApi/F4SDM42WebApiController.cs +++ b/F4SDM42WebApi/F4SDM42WebApiController.cs @@ -162,6 +162,23 @@ namespace C4IT.F4SD return await _f4stHelperService.getTicketOverviewCounts(sid, scope, parsedKeys); } + [Route("getTicketOverviewCountsByRoles"), HttpPost] + public async Task getTicketOverviewCountsByRoles([FromBody] TicketOverviewCountsByRolesRequest request) + { + var parsedKeys = (request?.Keys ?? new List()) + .Where(key => !string.IsNullOrWhiteSpace(key)) + .Select(key => key.Trim()) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + var roleGuids = (request?.RoleGuids ?? new List()) + .Where(roleId => roleId != Guid.Empty) + .Distinct() + .ToList(); + + return await _f4stHelperService.getTicketOverviewCountsByRoles(request?.Sid, roleGuids, parsedKeys); + } + [Route("getTicketOverviewRelations"), HttpGet] public async Task> getTicketOverviewRelations(string sid, string scope = "personal", string key = "", int count = 0) { @@ -241,13 +258,20 @@ namespace C4IT.F4SD } } - public class GetRoleMembershipsRequest - { - public Guid? Id { get; set; } - public string Sid { get; set; } - public string Upn { get; set; } - public GetRoleMembershipsRequest() { } - } + public class GetRoleMembershipsRequest + { + public Guid? Id { get; set; } + public string Sid { get; set; } + public string Upn { get; set; } + public GetRoleMembershipsRequest() { } + } + + public class TicketOverviewCountsByRolesRequest + { + public string Sid { get; set; } + public List RoleGuids { get; set; } = new List(); + public List Keys { get; set; } = new List(); + } [Route("isAlive"), HttpGet] public HttpResponseMessage isAlive()