Files
C4IT-F4SD-M42WebApi/F4SDM42WebApi/F4SDHelperService.cs
2026-02-05 11:21:29 +01:00

2082 lines
87 KiB
C#

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<string> ReadLines(Func<Stream> 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<string> 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<string>());
}
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<Guid> { 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<string>());
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<List<cF4SDTicketSummary>> getTicketListByUser(string userSid, int hours, int queueoption, List<cApiM42TicketQueueInfo> 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<cF4SDTicketSummary>();
var activityFilter = GetActivityFilter(userSid, hours, ticketAndServiceRequestEnabled(), queueoption, queues);
LogEntry($"ASql Filter: {activityFilter}");
var activityTable = FragmentRequestBase.SimpleLoad(SPSActivityClassBaseID,
string.Format("[Expression-ObjectID] as EOID, TicketNumber, Subject, Service.ID as ServiceId, Service.Name as ServiceName, SUBQUERY(BasicSchemaObjectType as bso, bso.Name, bso.id = base.T(SPSCommonClassBase).TypeID) as ActivityType" +
", COALESCE(T(SPSActivityClassIncident).Asset.T(SPSComputerClassBase).Name, T(SPSActivityClassIncident).Asset.T(SPSAssetClassSIMCard).PhoneNumber, T(SPSActivityClassIncident).Asset.Name, T(SPSActivityClassIncident).Asset.objectid) as AssetName" +
", SUBQUERY(BasicSchemaObjectType AS t, t.Name, t.ID=base.T(SPSActivityClassIncident).Asset.T(SPSCommonClassBase).TypeID) as AssetCIName" +
", CASE WHEN Initiator.PrimaryAccount.T(SPSAccountClassAd).Sid = '{0}' THEN 1 ELSE 0 END AS IsPrimaryAccount",
userSid),
activityFilter);
if (activityTable?.Rows == null || activityTable.Rows.Count == 0)
{
LogEntry($"No activity entries found for userSid: '{userSid}'", LogLevels.Warning);
return null;
}
for (int i = 0; i < activityTable.Rows.Count; i++)
{
DataRow entry = activityTable.Rows[i];
var activityEOId = getGuidFromObject(entry["EOID"]);
var ticketNumber = getStringFromObject(entry["TicketNumber"]);
var activityType = getStringFromObject(entry["ActivityType"]);
var assetName = getStringFromObject(entry["AssetName"]);
var ServiceName = getStringFromObject(entry["ServiceName"]);
var assetCIName = getStringFromObject(entry["AssetCIName"]);
var subject = getStringFromObject(entry["Subject"]);
var stateData = GetActivityState(activityEOId);
var state = stateData.Item1;
var stateDisp = stateData.Item2;
LogEntry($"Activity found {i + 1}/{activityTable.Rows.Count}: ObjectID={activityEOId}, TicketNumber={ticketNumber}, Subject={subject}, State={state}", LogLevels.Debug);
if (string.IsNullOrEmpty(ticketNumber))
{
LogEntry($"No TicketNumber found for activity entry", LogLevels.Warning);
continue;
}
if (string.IsNullOrEmpty(assetName))
{
LogEntry($"No AssetName found for activity entry", LogLevels.Debug);
}
if (string.IsNullOrEmpty(assetCIName))
{
LogEntry($"No AssetCIName found for activity entry", LogLevels.Debug);
}
var ServiceId = getGuidFromObject(entry["ServiceId"]);
if (ServiceId == Guid.Empty)
{
LogEntry($"no ServiceId found for activity entry", LogLevels.Debug);
}
var AssetCIName = getStringFromObject(entry["AssetCIName"]);
if (AssetCIName == string.Empty)
{
LogEntry($"no AssetCIName found for activity entry", LogLevels.Debug);
}
var Subject = getStringFromObject(entry["Subject"]);
if (Subject == string.Empty)
{
LogEntry($"no Subject found for activity entry", LogLevels.Warning);
continue;
}
var IsPrimaryAccount = getIntFromObject(entry["IsPrimaryAccount"]);
if (string.IsNullOrEmpty(subject))
{
LogEntry($"No Subject found for activity entry", LogLevels.Warning);
continue;
}
tickets.Add(new cF4SDTicketSummary()
{
TicketObjectId = activityEOId,
Name = ticketNumber,
ActivityType = activityType,
AssetCIName = assetCIName,
AssetName = assetName,
ServiceId = ServiceId,
ServiceName = ServiceName,
StatusId = state,
Status = stateDisp,
Summary = subject,
IsPrimaryAccount = IsPrimaryAccount == 1
});
}
tickets = tickets.OrderByDescending(x => x.Name).ToList();
return tickets;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
private static readonly string[] TicketOverviewKeys = new[]
{
"TicketsNew",
"TicketsActive",
"TicketsCritical",
"TicketsNewInfo",
"IncidentNew",
"IncidentActive",
"IncidentCritical",
"IncidentNewInfo"
};
public class TicketOverviewCountsResult
{
[JsonProperty("counts")]
public Dictionary<string, int> Counts { get; set; } = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
public class TicketOverviewCountsByRoleResult
{
[JsonProperty("generatedAtUtc")]
public DateTime GeneratedAtUtc { get; set; } = DateTime.UtcNow;
[JsonProperty("countsByRole")]
public Dictionary<string, Dictionary<string, int>> CountsByRole { get; set; }
= new Dictionary<string, Dictionary<string, int>>(StringComparer.OrdinalIgnoreCase);
}
public class TicketOverviewRelationDto
{
public enumF4sdSearchResultClass Type { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public Guid id { get; set; }
public enumF4sdSearchResultStatus Status { get; set; } = enumF4sdSearchResultStatus.Unknown;
public Dictionary<string, string> Infos { get; set; } = null;
public List<cF4sdIdentityEntry> Identities { get; set; } = null;
}
private sealed class TicketOverviewEntry
{
public Guid TicketId { get; set; }
public string TicketNumber { get; set; }
public string Summary { get; set; }
public Guid InitiatorId { get; set; }
public string InitiatorDisplayName { get; set; }
public string InitiatorAccount { get; set; }
public string InitiatorDomain { get; set; }
public string InitiatorSid { get; set; }
public Guid RecipientId { get; set; }
public Guid RecipientRoleId { get; set; }
public int State { get; set; }
public DateTime CreatedDate { get; set; }
public bool NewInformationReceived { get; set; }
public bool ReactionTimeEscalated { get; set; }
public bool SolutionTimeEscalated { get; set; }
public bool IsIncident { get; set; }
public string ActivityType { get; set; }
}
internal async Task<TicketOverviewCountsResult> getTicketOverviewCounts(
string sid,
string scope,
IEnumerable<string> keys,
int queueoption,
List<cApiM42TicketQueueInfo> queues)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase);
var normalizedKeys = (keys ?? Array.Empty<string>())
.Where(k => !string.IsNullOrWhiteSpace(k))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (normalizedKeys.Count == 0)
{
normalizedKeys.AddRange(TicketOverviewKeys);
}
var entries = await LoadTicketOverviewEntries(sid, useRoleScope, queueoption, queues);
var counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
foreach (var key in normalizedKeys)
{
counts[key] = entries.Count(entry => MatchesTicketOverviewKey(entry, key));
}
return new TicketOverviewCountsResult { Counts = counts };
}
catch (Exception E)
{
LogException(E);
return new TicketOverviewCountsResult();
}
finally
{
LogMethodEnd(CM);
}
}
internal async Task<TicketOverviewCountsByRoleResult> getTicketOverviewCountsByRoles(
string sid,
IEnumerable<Guid> roleGuids,
IEnumerable<string> keys,
int queueoption,
List<cApiM42TicketQueueInfo> queues)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var normalizedKeys = (keys ?? Array.Empty<string>())
.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, queueoption, queues);
var entriesByRole = entries
.GroupBy(entry => entry.RecipientRoleId)
.ToDictionary(group => group.Key, group => group.ToList());
var countsByRole = new Dictionary<string, Dictionary<string, int>>(StringComparer.OrdinalIgnoreCase);
foreach (var roleId in roleIds)
{
var roleEntries = entriesByRole.TryGetValue(roleId, out var list)
? list
: new List<TicketOverviewEntry>();
var counts = new Dictionary<string, int>(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<List<TicketOverviewRelationDto>> getTicketOverviewRelations(
string sid,
string scope,
string key,
int count,
int queueoption,
List<cApiM42TicketQueueInfo> queues)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (string.IsNullOrWhiteSpace(key))
return new List<TicketOverviewRelationDto>();
var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase);
var entries = await LoadTicketOverviewEntries(sid, useRoleScope, queueoption, queues);
var filtered = entries
.Where(entry => MatchesTicketOverviewKey(entry, key))
.OrderByDescending(entry => entry.CreatedDate)
.ToList();
if (count > 0)
filtered = filtered.Take(count).ToList();
var relations = new List<TicketOverviewRelationDto>(filtered.Count);
foreach (var entry in filtered)
{
var relation = new TicketOverviewRelationDto
{
Type = enumF4sdSearchResultClass.Ticket,
DisplayName = entry.TicketNumber ?? string.Empty,
Name = entry.TicketNumber ?? string.Empty,
id = entry.TicketId,
Status = enumF4sdSearchResultStatus.Active,
Infos = new Dictionary<string, string>
{
["Summary"] = entry.Summary ?? string.Empty,
["StatusId"] = ConvertM42State(entry.State),
["ActivityType"] = entry.ActivityType ?? string.Empty,
["UserDisplayName"] = entry.InitiatorDisplayName ?? string.Empty,
["UserAccount"] = entry.InitiatorAccount ?? string.Empty,
["UserDomain"] = entry.InitiatorDomain ?? string.Empty,
["UserSid"] = entry.InitiatorSid ?? string.Empty,
["Sids"] = entry.InitiatorSid ?? string.Empty,
["UserId"] = entry.InitiatorId != Guid.Empty ? entry.InitiatorId.ToString() : string.Empty,
["UserGuid"] = entry.InitiatorId != Guid.Empty ? entry.InitiatorId.ToString() : string.Empty
},
Identities = new List<cF4sdIdentityEntry>
{
new cF4sdIdentityEntry { Class = enumFasdInformationClass.Ticket, Id = entry.TicketId },
new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = entry.InitiatorId }
}
};
relations.Add(relation);
}
return relations;
}
catch (Exception E)
{
LogException(E);
return new List<TicketOverviewRelationDto>();
}
finally
{
LogMethodEnd(CM);
}
}
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewEntries(
string sid,
bool useRoleScope,
int queueoption,
List<cApiM42TicketQueueInfo> queues)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
await Task.Delay(0);
if (string.IsNullOrWhiteSpace(sid))
return new List<TicketOverviewEntry>();
var filter = await BuildTicketOverviewFilterAsync(sid, useRoleScope, queueoption, queues);
return await LoadTicketOverviewEntriesByFilter(filter);
}
catch (Exception E)
{
LogException(E);
return new List<TicketOverviewEntry>();
}
finally
{
LogMethodEnd(CM);
}
}
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewEntriesByRoleIds(
IEnumerable<Guid> roleIds,
int queueoption,
List<cApiM42TicketQueueInfo> queues)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
await Task.Delay(0);
var filter = BuildTicketOverviewFilterForRoleIds(roleIds, null, queueoption, queues);
return await LoadTicketOverviewEntriesByFilter(filter);
}
catch (Exception E)
{
LogException(E);
return new List<TicketOverviewEntry>();
}
finally
{
LogMethodEnd(CM);
}
}
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewEntriesByFilter(string filter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
await Task.Delay(0);
if (string.IsNullOrWhiteSpace(filter))
return new List<TicketOverviewEntry>();
var tbl = FragmentRequestBase.SimpleLoad(SPSActivityClassBaseID,
"[Expression-ObjectID] as EOID" +
", TicketNumber" +
", Subject" +
", Initiator as InitiatorId" +
", Initiator.LastName + ISNULL(', ' + Initiator.FirstName, '') as Initiator" +
", Initiator.PrimaryAccount.T(SPSAccountClassAD).NBAccountName as InitiatorAccount" +
", Initiator.PrimaryAccount.T(SPSAccountClassAD).Domain.NT4Name as InitiatorDomain" +
", Initiator.PrimaryAccount.T(SPSAccountClassAD).Sid as InitiatorSid" +
", Recipient as RecipientId" +
", RecipientRole.T(SPSSecurityClassRole).ID as RecipientRoleId" +
", T(SPSCommonClassBase).State as State" +
", CreatedDate" +
", NewInformationReceived" +
", ReactionTimeEscalated" +
", SolutionTimeEscalated" +
", UsedInTypeSPSActivityTypeTicket as TicketEoid" +
", UsedInTypeSPSActivityTypeIncident as IncidentEoid" +
", UsedInTypeSPSActivityTypeAlert as AlertEoid" +
", UsedInTypeSPSActivityTypeGroupTicket as ProblemEoid" +
", UsedInTypeSPSActivityTypeServiceRequest as ServiceRequestEoid" +
", UsedInTypeSPSActivityTypeBase as TaskEoid" +
", UsedInTypeSVMChangeRequestType as ChangeEoid",
filter);
if (tbl?.Rows == null || tbl.Rows.Count == 0)
return new List<TicketOverviewEntry>();
var entries = new List<TicketOverviewEntry>(tbl.Rows.Count);
foreach (DataRow row in tbl.Rows)
{
var ticketId = getGuidFromObject(row["EOID"]);
if (ticketId == Guid.Empty)
continue;
var ticketEoid = getGuidFromObject(row["TicketEoid"]);
var incidentEoid = getGuidFromObject(row["IncidentEoid"]);
var alertEoid = getGuidFromObject(row["AlertEoid"]);
var problemEoid = getGuidFromObject(row["ProblemEoid"]);
var serviceRequestEoid = getGuidFromObject(row["ServiceRequestEoid"]);
var taskEoid = getGuidFromObject(row["TaskEoid"]);
var changeEoid = getGuidFromObject(row["ChangeEoid"]);
var isIncident = incidentEoid != Guid.Empty;
var activityType = ResolveTicketOverviewActivityType(
ticketEoid,
incidentEoid,
alertEoid,
problemEoid,
serviceRequestEoid,
taskEoid,
changeEoid);
entries.Add(new TicketOverviewEntry
{
TicketId = ticketId,
TicketNumber = getStringFromObject(row["TicketNumber"]),
Summary = getStringFromObject(row["Subject"]),
InitiatorId = getGuidFromObject(row["InitiatorId"]),
InitiatorDisplayName = getStringFromObject(row["Initiator"]),
InitiatorAccount = getStringFromObject(row["InitiatorAccount"]),
InitiatorDomain = getStringFromObject(row["InitiatorDomain"]),
InitiatorSid = getStringFromObject(row["InitiatorSid"]),
RecipientId = getGuidFromObject(row["RecipientId"]),
RecipientRoleId = getGuidFromObject(row["RecipientRoleId"]),
State = getIntFromObject(row["State"]),
CreatedDate = getDateTimeFromObject(row["CreatedDate"]),
NewInformationReceived = GetBoolValue(row["NewInformationReceived"]),
ReactionTimeEscalated = GetBoolValue(row["ReactionTimeEscalated"]),
SolutionTimeEscalated = GetBoolValue(row["SolutionTimeEscalated"]),
IsIncident = isIncident,
ActivityType = activityType
});
}
return entries;
}
catch (Exception E)
{
LogException(E);
return new List<TicketOverviewEntry>();
}
finally
{
LogMethodEnd(CM);
}
}
private static string ResolveTicketOverviewActivityType(
Guid ticketEoid,
Guid incidentEoid,
Guid alertEoid,
Guid problemEoid,
Guid serviceRequestEoid,
Guid taskEoid,
Guid changeEoid)
{
if (incidentEoid != Guid.Empty)
return "SPSActivityTypeIncident";
if (ticketEoid != Guid.Empty)
return "SPSActivityTypeTicket";
if (alertEoid != Guid.Empty)
return "SPSActivityTypeAlert";
if (problemEoid != Guid.Empty)
return "SPSActivityTypeGroupTicket";
if (serviceRequestEoid != Guid.Empty)
return "SPSActivityTypeServiceRequest";
if (taskEoid != Guid.Empty)
return "SPSActivityTypeBase";
if (changeEoid != Guid.Empty)
return "SVMChangeRequestType";
return null;
}
private async Task<string> BuildTicketOverviewFilterAsync(
string sid,
bool useRoleScope,
int queueoption,
List<cApiM42TicketQueueInfo> queues)
{
var filter = BuildTicketOverviewBaseFilter(queueoption, queues);
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(int queueoption, List<cApiM42TicketQueueInfo> queues)
{
var filter = "T(SPSCommonClassBase).State <> 204";
if (ticketAndServiceRequestEnabled())
{
filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL" +
" OR UsedInTypeSPSActivityTypeTicket IS NOT NULL" +
" OR UsedInTypeSPSActivityTypeServiceRequest IS NOT NULL)";
}
else
{
filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL)";
}
filter = AppendQueueFilter(filter, queueoption, queues);
return filter;
}
private string BuildTicketOverviewFilterForRoleIds(
IEnumerable<Guid> roleIds,
string baseFilter = null,
int queueoption = 0,
List<cApiM42TicketQueueInfo> queues = null)
{
var filter = string.IsNullOrWhiteSpace(baseFilter)
? BuildTicketOverviewBaseFilter(queueoption, queues)
: baseFilter;
var roleIdList = (roleIds ?? Enumerable.Empty<Guid>())
.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 string AppendQueueFilter(string filter, int queueoption, List<cApiM42TicketQueueInfo> queues)
{
if (queues != null && queues.Count > 0)
{
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)
{
case 1:
filter +=
$" AND (" +
"Queue IS NULL" +
$" OR Queue.Name IN ({nameList})" +
$" OR Queue.ID IN ({idList})" +
")";
break;
case 2:
filter +=
$" AND (" +
"Queue IS NOT NULL" +
$" AND (Queue.Name IN ({nameList})" +
$" OR Queue.ID IN ({idList}))" +
")";
break;
case 3:
filter += " AND Queue IS NULL";
break;
default:
break;
}
}
else if (queueoption == 3)
{
filter += " AND Queue IS NULL";
}
return filter;
}
private async Task<List<Guid>> ResolveTicketOverviewRoleIdsAsync(string sid, IEnumerable<Guid> roleGuids)
{
var roleIds = (roleGuids ?? Enumerable.Empty<Guid>())
.Where(id => id != Guid.Empty)
.Distinct()
.ToList();
if (roleIds.Count > 0)
return roleIds;
if (string.IsNullOrWhiteSpace(sid))
return new List<Guid>();
var userId = getUserBySid(sid);
if (userId == Guid.Empty)
return new List<Guid>();
var roles = await getRoleMembershipById(userId) ?? new List<M42Role>();
return roles
.Where(role => role != null && role.Id != Guid.Empty)
.Select(role => role.Id)
.Distinct()
.ToList();
}
private static bool MatchesTicketOverviewKey(TicketOverviewEntry entry, string key)
{
if (entry == null || string.IsNullOrWhiteSpace(key))
return false;
var isTicket = !entry.IsIncident;
var isAssigned = entry.RecipientId != Guid.Empty || entry.RecipientRoleId != Guid.Empty;
var isCritical = entry.ReactionTimeEscalated || entry.SolutionTimeEscalated;
var isNew = entry.State == 200;
var isActive = entry.State == 201 || entry.State == 202 || entry.State == 203;
switch (key.Trim())
{
case "TicketsNew":
return isTicket && isAssigned && isNew;
case "TicketsActive":
return isTicket && isAssigned && isActive;
case "TicketsCritical":
return isTicket && isAssigned && isCritical;
case "TicketsNewInfo":
return isTicket && isAssigned && entry.NewInformationReceived;
case "IncidentNew":
return entry.IsIncident && isNew;
case "IncidentActive":
return entry.IsIncident && isActive;
case "IncidentCritical":
return entry.IsIncident && isCritical;
case "IncidentNewInfo":
return entry.IsIncident && entry.NewInformationReceived;
default:
return false;
}
}
private static bool GetBoolValue(object value)
{
if (value == null || value is DBNull)
return false;
if (value is bool boolValue)
return boolValue;
if (value is int intValue)
return intValue != 0;
if (value is long longValue)
return longValue != 0;
if (bool.TryParse(value.ToString(), out var parsedBool))
return parsedBool;
if (int.TryParse(value.ToString(), out var parsedInt))
return parsedInt != 0;
return false;
}
private static string ConvertM42State(int state)
{
switch (state)
{
case 200:
return "New";
case 201:
case 202:
return "InProgress";
case 203:
return "OnHold";
case 204:
return "Closed";
default:
return "Unknown";
}
}
private (int, string) GetActivityState(Guid activityEOId)
{
var tbl3 = FragmentRequestBase.SimpleLoad(SPSCommonClassBaseID,
"State.Value as State, State.DisplayString as StateDisp", $"[Expression-ObjectID] = '{activityEOId}'");
if (tbl3?.Rows == null || tbl3.Rows.Count <= 0)
{
LogEntry($"SPSCommonClassBase fragment not found: ClassId={SPSCommonClassBaseID}, ObjectId={activityEOId}", LogLevels.Debug);
return (0, "");
}
var state = getIntFromObject(tbl3.Rows[0]["State"]);
var stateDisp = getStringFromObject(tbl3.Rows[0]["StateDisp"]);
return (state, stateDisp);
}
private string Escape(string input)
{
return input?.Replace("'", "''");
}
private string GetActivityFilter(
string userSid,
int hours,
bool ticketAndServiceRequestEnabled,
int queueoption,
List<cApiM42TicketQueueInfo> 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<DirectLink> 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<List<cF4SDTicket>> getTicketDetails(List<Guid> 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<cF4SDTicket>();
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<DataRow>().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<List<cTicketJournalItem>> GetJournalEntries(Guid activityEOID)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
await Task.Delay(0);
List<cTicketJournalItem> journalEntries = new List<cTicketJournalItem>();
var entries = F4SDM42WebApiController.defaultInstance._journalService.GetJournalList(activityEOID, false, 0, 50, "<a href=\"#\" class=\"mx-object-journal--link {0} {1}\">{2}</a>", 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<bool> 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<M42Asset> getAssetsByAsql(string Filter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
// get SPSAssetClassBase
if (string.IsNullOrEmpty(Filter))
return null;
List<M42Asset> assets = new List<M42Asset>();
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<M42User> getUsersByAsql(string asqlFilter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var Persons = new List<M42User>();
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<M42Account> getAccountsByAsql(string asqlFilter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var result = new List<M42Account>();
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("<html><body><table>");
var htmlLines = new List<string>();
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("<tr><td><nobr>" + string.Join("</nobr></td><td><nobr>", lineSplit) + "</nobr></td></tr>");
}
if (maxLines > 0)
htmlLines = htmlLines.Skip(Math.Max(0, htmlLines.Count() - maxLines)).ToList();
foreach (var line in htmlLines)
{
sb.AppendLine(line);
}
sb.AppendLine("</table></body></html>");
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<cM42LogEntry> 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<cM42LogEntry>();
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<List<M42Role>> getRoleMembershipById(Guid UserId)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var RoleMemberships = new List<M42Role>();
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<List<M42Role>> getRoleMembershipById(Guid UserId)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var RoleMemberships = new List<M42Role>();
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<object> UserPermissionsInfo(string filter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var RoleMemberships = new List<M42Role>();
try
{
await Task.Delay(0);
var retVal = new UserPermissionsInfo();
List<M42User> 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<M42Role> Roles { get; internal set; }
public UserPermissionsInfo()
{
User = new M42User();
Roles = new List<M42Role>();
}
}
}