diff --git a/.gitignore b/.gitignore
index 39a91ef..c776f7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,6 @@ F4SDHelper/bin/
F4SDHelper/obj/
F4SDM42WebApi/bin/
F4SDM42WebApi/obj/
-artifacts/
+artifacts/
+Legacy/BasePackage/Assemblies/
+Legacy/PackageTemplate/Assemblies/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..0cc3083
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,32 @@
+# Repository Instructions
+
+## Git Completion
+
+- After completing and verifying requested repository changes, create a Git commit and push the current branch to `origin` unless the user explicitly asks not to commit or push.
+- Stage only files that belong to the completed task. Leave unrelated local changes, reference material, exports, and generated artifacts untouched.
+- Report the commit hash and pushed branch to the user.
+- This Git rule does not authorize any TFS check-in or upload.
+
+## TFS and Git
+
+- TFS remains active for the Matrix42 ESM 12.1.3-25.x legacy implementation.
+- Import TFS changes into Git with `tools/Sync-TfsLegacy.ps1` and review them on a dedicated `sync/tfs-legacy` branch.
+- Never copy a complete TFS workspace over the sandbox projects.
+- Never push, check in, or otherwise publish changes to TFS automatically.
+- The final transfer from Git into TFS is always performed and reviewed manually by the user in Visual Studio.
+- Do not run `tf checkin`, `tf reconcile`, scripted TFS uploads, or equivalent commands.
+
+## Platform Ownership
+
+- `Legacy/` contains the .NET Framework 4.7.2 host for Matrix42 ESM 12.1.3-25.x.
+- `F4SDM42WebApi/` and `PackageTemplate/` contain the sandboxed .NET 8 host for Matrix42 ESM 26.1 and newer.
+- `F4SDHelper/Common/C4IT.F4SD.WebApi.Contracts.cs` contains contracts shared only by both Web API variants.
+- The external `_Common/C4IT.F4SD.Base.Ticket.cs` is shared with other F4SD products and must not be changed merely to simplify this Web API.
+- Keep platform-specific dependency resolution, controllers, package metadata, and journal access in their respective host.
+
+## Builds
+
+- Use `C4IT - F4SD - M42WebApi.sln` for the sandbox build.
+- Use `C4IT - F4SD - M42WebApi.Legacy.sln` for the legacy build.
+- `Release_signed` is the optional signed configuration for both variants.
+- Package versions are derived from `SharedAssemblyInfo.cs`.
diff --git a/C4IT - F4SD - M42WebApi.Legacy.sln b/C4IT - F4SD - M42WebApi.Legacy.sln
new file mode 100644
index 0000000..f692028
--- /dev/null
+++ b/C4IT - F4SD - M42WebApi.Legacy.sln
@@ -0,0 +1,32 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "F4SD - Helper (Legacy)", "Legacy\F4SDHelper\F4SD - Helper.Legacy.csproj", "{27A63A98-232D-4D54-A781-E9D493417D59}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "F4SD - M42WebApi (Legacy)", "Legacy\F4SDM42WebApi\F4SD - M42WebApi.Legacy.csproj", "{B6316CC4-4827-4D16-A005-DDB8055695BC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ Release_signed|Any CPU = Release_signed|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {27A63A98-232D-4D54-A781-E9D493417D59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {27A63A98-232D-4D54-A781-E9D493417D59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {27A63A98-232D-4D54-A781-E9D493417D59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {27A63A98-232D-4D54-A781-E9D493417D59}.Release|Any CPU.Build.0 = Release|Any CPU
+ {27A63A98-232D-4D54-A781-E9D493417D59}.Release_signed|Any CPU.ActiveCfg = Release_signed|Any CPU
+ {27A63A98-232D-4D54-A781-E9D493417D59}.Release_signed|Any CPU.Build.0 = Release_signed|Any CPU
+ {B6316CC4-4827-4D16-A005-DDB8055695BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B6316CC4-4827-4D16-A005-DDB8055695BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B6316CC4-4827-4D16-A005-DDB8055695BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B6316CC4-4827-4D16-A005-DDB8055695BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B6316CC4-4827-4D16-A005-DDB8055695BC}.Release_signed|Any CPU.ActiveCfg = Release_signed|Any CPU
+ {B6316CC4-4827-4D16-A005-DDB8055695BC}.Release_signed|Any CPU.Build.0 = Release_signed|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/F4SDHelper/Common/C4IT.F4SD.WebApi.Contracts.cs b/F4SDHelper/Common/C4IT.F4SD.WebApi.Contracts.cs
new file mode 100644
index 0000000..b1c9d29
--- /dev/null
+++ b/F4SDHelper/Common/C4IT.F4SD.WebApi.Contracts.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace C4IT.FASD.Base
+{
+ public class cF4SDTicket : cF4SDTicketSummary
+ {
+ public enum enumTicketCreationSource
+ {
+ Unknown = 0,
+ Mail = 1,
+ Phone = 2,
+ F4SD = 3
+ }
+
+ public class cTicketJournalItem
+ {
+ public DateTime CreationDate { get; set; }
+ public string Header { get; set; }
+ public string CreatedBy { get; set; }
+ public string DescriptionHtml { get; set; }
+ public string Description { get; set; }
+ public bool IsVisibleForUser { get; set; }
+ public Guid ActivityObjectId { get; set; }
+ public Guid JournalId { get; set; }
+ }
+
+ public Guid AffectedUserId { get; set; }
+ public Guid AssetId { get; set; }
+ public DateTime CreationDate { get; set; }
+ public DateTime? ClosingDate { get; set; }
+ public int CreationSourceId { get; set; }
+ public string CreationSource { get; set; }
+ public string Description { get; set; }
+ public string DescriptionHtml { get; set; }
+ public int PriorityId { get; set; }
+ public string Priority { get; set; }
+ public Guid CategoryId { get; set; }
+ public string Category { get; set; }
+ public string CategoryHierarchical { get; set; }
+ public string CIName { get; set; }
+ public string DirectLinkEdit { get; set; }
+ public Guid AssetCIId { get; set; }
+ public int AssetSKUAssetGroupId { get; set; }
+ public string AssetSKUAssetGroup { get; set; }
+ public int AssetSKUTypeId { get; set; }
+ public string AssetSKUType { get; set; }
+ public string DirectLinkPreview { get; set; }
+ public string DirectLinkClose { get; set; }
+ public string AffectedUser { get; set; }
+ public string SolutionHtml { get; set; }
+ public string Solution { get; set; }
+ public string AssetDomain { get; set; }
+ public string Urgency { get; set; }
+ public int UrgencyId { get; set; }
+ public string Impact { get; set; }
+ public int ImpactId { get; set; }
+ }
+}
diff --git a/F4SDHelper/F4SD - Helper.csproj b/F4SDHelper/F4SD - Helper.csproj
index 76827c8..38daf0e 100644
--- a/F4SDHelper/F4SD - Helper.csproj
+++ b/F4SDHelper/F4SD - Helper.csproj
@@ -1,5 +1,5 @@
-
+
net8.0
Library
C4IT.F4SDM
@@ -9,7 +9,8 @@
latestmajor
false
false
- Debug;Release;Debug_and_copy;Release_and_copy;Release_signed
+ Debug;Release;Debug_and_copy;Release_and_copy;Release_signed
+ $(MSBuildProjectDirectory)\..\..\_Common
@@ -28,10 +29,18 @@
Common\C4IT.Logging.LogManager.cs
-
+
+ Common\C4IT.F4SD.Base.Ticket.cs
+
+
Properties\SharedAssemblyInfo.cs
-
-
-
+
+
+
+
+
+
+
diff --git a/F4SDM42WebApi/BuildM42Package.ps1 b/F4SDM42WebApi/BuildM42Package.ps1
index 8d617c1..4b189e0 100644
--- a/F4SDM42WebApi/BuildM42Package.ps1
+++ b/F4SDM42WebApi/BuildM42Package.ps1
@@ -40,7 +40,7 @@ if (-not (Test-Path -LiteralPath (Join-Path $templateDirFull 'package.json'))) {
throw "Package template not found: $templateDirFull"
}
-if (-not (Test-Path -LiteralPath (Join-Path $assembliesDirFull 'C4ITF4SDM42WebApi.dll'))) {
+if (-not (Get-ChildItem -LiteralPath $assembliesDirFull -Filter 'C4ITF4SDM42WebApi.dll' -File -Recurse | Select-Object -First 1)) {
throw "Package assemblies not found: $assembliesDirFull"
}
@@ -53,8 +53,12 @@ if (Test-Path -LiteralPath $zipPath) {
}
New-Item -ItemType Directory -Path $packageDir -Force | Out-Null
-Copy-Item -Path (Join-Path $templateDirFull '*') -Destination $packageDir -Recurse -Force
-Copy-Item -Path $assembliesDirFull -Destination (Join-Path $packageDir 'Assemblies') -Recurse -Force
+Copy-Item -Path (Join-Path $templateDirFull '*') -Destination $packageDir -Recurse -Force
+$packageAssembliesPath = Join-Path $packageDir 'Assemblies'
+if (Test-Path -LiteralPath $packageAssembliesPath) {
+ Remove-Item -LiteralPath $packageAssembliesPath -Recurse -Force
+}
+Copy-Item -Path $assembliesDirFull -Destination $packageAssembliesPath -Recurse -Force
$packageJsonPath = Join-Path $packageDir 'package.json'
$packageJson = [System.IO.File]::ReadAllText($packageJsonPath)
@@ -62,24 +66,22 @@ $packageJson = [regex]::Replace($packageJson, '"Version"\s*:\s*"[^"]+"', "`"Vers
$packageJson = [regex]::Replace($packageJson, '"LastUpdatedDate"\s*:\s*"[^"]+"', "`"LastUpdatedDate`": `"$(Get-Date -Format 'yyyy-MM-ddTHH:mm:ss')`"")
[System.IO.File]::WriteAllText($packageJsonPath, $packageJson, [System.Text.UTF8Encoding]::new($true))
-$configDataPath = Join-Path $packageDir 'install\1010_Config_Data\02-01-0080 C4IT_F4SDConfigurationType.dat'
-if (-not (Test-Path -LiteralPath $configDataPath)) {
- throw "Configuration data file not found: $configDataPath"
-}
-
-$configData = [System.IO.File]::ReadAllText($configDataPath)
-$configData = [regex]::Replace(
- $configData,
- '([\s\S]*?)[^<]*()',
- "`${1}$PackageVersion`${2}",
- [System.Text.RegularExpressions.RegexOptions]::Singleline
-)
-
-if ($configData -notmatch "$([regex]::Escape($PackageVersion))") {
- throw "Could not update C4IT_F4SDConfigurationType Version to $PackageVersion in $configDataPath"
-}
-
-[System.IO.File]::WriteAllText($configDataPath, $configData, [System.Text.UTF8Encoding]::new($true))
+$configDataPath = Join-Path $packageDir 'install\1010_Config_Data\02-01-0080 C4IT_F4SDConfigurationType.dat'
+if (Test-Path -LiteralPath $configDataPath) {
+ $configData = [System.IO.File]::ReadAllText($configDataPath)
+ $configData = [regex]::Replace(
+ $configData,
+ '([\s\S]*?)[^<]*()',
+ "`${1}$PackageVersion`${2}",
+ [System.Text.RegularExpressions.RegexOptions]::Singleline
+ )
+
+ if ($configData -notmatch "$([regex]::Escape($PackageVersion))") {
+ throw "Could not update C4IT_F4SDConfigurationType Version to $PackageVersion in $configDataPath"
+ }
+
+ [System.IO.File]::WriteAllText($configDataPath, $configData, [System.Text.UTF8Encoding]::new($true))
+}
$textFileExtensions = @('.json', '.xml', '.dat', '.type', '.class', '.config', '.host')
Get-ChildItem -LiteralPath $packageDir -Recurse -File |
diff --git a/F4SDM42WebApi/F4SD - M42WebApi.csproj b/F4SDM42WebApi/F4SD - M42WebApi.csproj
index ac58675..f9f36b2 100644
--- a/F4SDM42WebApi/F4SD - M42WebApi.csproj
+++ b/F4SDM42WebApi/F4SD - M42WebApi.csproj
@@ -92,7 +92,8 @@
-
+
+
diff --git a/F4SDM42WebApi/F4SDHelperService.UserId.cs b/F4SDM42WebApi/F4SDHelperService.UserId.cs
new file mode 100644
index 0000000..0749414
--- /dev/null
+++ b/F4SDM42WebApi/F4SDHelperService.UserId.cs
@@ -0,0 +1,298 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+
+using C4IT.FASD.Base;
+using C4IT.Logging;
+
+using Matrix42.Common;
+
+using update4u.SPS.DataLayer;
+
+using static C4IT.Logging.cLogManager;
+
+namespace C4IT.F4SD
+{
+ public partial class F4SDHelperService
+ {
+ internal async Task> getTicketListByUser(
+ Guid userId,
+ int hours,
+ int queueoption,
+ List queues)
+ {
+ var method = MethodBase.GetCurrentMethod();
+ LogMethodBegin(method);
+ try
+ {
+ await Task.Delay(0);
+ if (userId == Guid.Empty)
+ return new List();
+
+ var activityFilter = GetActivityFilter(userId, hours, ticketAndServiceRequestEnabled(), queueoption, queues);
+ LogEntry($"Generating ticket list for userId '{userId}'. ASQL filter: {activityFilter}", LogLevels.Debug);
+
+ var activityTable = FragmentRequestBase.SimpleLoad(
+ SPSActivityClassBaseID,
+ "[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",
+ activityFilter);
+
+ if (activityTable?.Rows == null || activityTable.Rows.Count == 0)
+ return new List();
+
+ var tickets = new List(activityTable.Rows.Count);
+ foreach (DataRow entry in activityTable.Rows)
+ {
+ var activityId = getGuidFromObject(entry["EOID"]);
+ var ticketNumber = getStringFromObject(entry["TicketNumber"]);
+ var subject = getStringFromObject(entry["Subject"]);
+ if (string.IsNullOrEmpty(ticketNumber) || string.IsNullOrEmpty(subject))
+ continue;
+
+ var state = GetActivityState(activityId);
+ tickets.Add(new cF4SDTicketSummary
+ {
+ TicketObjectId = activityId,
+ Name = ticketNumber,
+ ActivityType = getStringFromObject(entry["ActivityType"]),
+ Summary = subject,
+ StatusId = state.Item1,
+ Status = state.Item2,
+ AssetCIName = getStringFromObject(entry["AssetCIName"]),
+ AssetName = getStringFromObject(entry["AssetName"]),
+ ServiceId = getGuidFromObject(entry["ServiceId"]),
+ ServiceName = getStringFromObject(entry["ServiceName"]),
+ UserId = userId,
+ IsPrimaryAccount = true
+ });
+ }
+
+ return tickets.OrderByDescending(ticket => ticket.Name).ToList();
+ }
+ catch (Exception exception)
+ {
+ LogException(exception);
+ return new List();
+ }
+ finally
+ {
+ LogMethodEnd(method);
+ }
+ }
+
+ internal async Task getTicketOverviewCounts(
+ Guid userId,
+ string scope,
+ IEnumerable keys,
+ int queueoption,
+ List queues)
+ {
+ var method = MethodBase.GetCurrentMethod();
+ LogMethodBegin(method);
+ try
+ {
+ var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase);
+ var normalizedKeys = (keys ?? Array.Empty())
+ .Where(key => !string.IsNullOrWhiteSpace(key))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+
+ if (normalizedKeys.Count == 0)
+ normalizedKeys.AddRange(TicketOverviewKeys);
+
+ var entries = await LoadTicketOverviewEntries(userId, useRoleScope, queueoption, queues);
+ List unassignedEntries = null;
+ if (!useRoleScope && normalizedKeys.Any(IsUnassignedOverviewKey))
+ unassignedEntries = await LoadTicketOverviewUnassignedEntriesForPersonalScope(userId, queueoption, queues);
+
+ var counts = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var key in normalizedKeys)
+ {
+ var source = !useRoleScope && IsUnassignedOverviewKey(key)
+ ? unassignedEntries ?? new List()
+ : entries;
+ counts[key] = source.Count(entry => MatchesTicketOverviewKey(entry, key));
+ }
+
+ return new TicketOverviewCountsResult { Counts = counts };
+ }
+ catch (Exception exception)
+ {
+ LogException(exception);
+ return new TicketOverviewCountsResult();
+ }
+ finally
+ {
+ LogMethodEnd(method);
+ }
+ }
+
+ internal async Task getTicketOverviewCountsByRoles(
+ Guid userId,
+ IEnumerable roleGuids,
+ IEnumerable keys,
+ int queueoption,
+ List queues)
+ {
+ var roleIds = await ResolveTicketOverviewRoleIdsAsync(userId, roleGuids);
+ return await getTicketOverviewCountsByRoles(null, roleIds, keys, queueoption, queues);
+ }
+
+ internal async Task> getTicketOverviewRelations(
+ Guid userId,
+ string scope,
+ string key,
+ int count,
+ int queueoption,
+ List queues)
+ {
+ var method = MethodBase.GetCurrentMethod();
+ LogMethodBegin(method);
+ try
+ {
+ if (userId == Guid.Empty || string.IsNullOrWhiteSpace(key))
+ return new List();
+
+ var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase);
+ var entries = !useRoleScope && IsUnassignedOverviewKey(key)
+ ? await LoadTicketOverviewUnassignedEntriesForPersonalScope(userId, queueoption, queues)
+ : await LoadTicketOverviewEntries(userId, useRoleScope, queueoption, queues);
+
+ var filtered = entries
+ .Where(entry => MatchesTicketOverviewKey(entry, key))
+ .OrderByDescending(entry => entry.CreatedDate);
+ if (count > 0)
+ filtered = filtered.Take(count).OrderByDescending(entry => entry.CreatedDate);
+
+ return filtered.Select(entry => new TicketOverviewRelationDto
+ {
+ Type = enumF4sdSearchResultClass.Ticket,
+ Name = entry.TicketNumber ?? string.Empty,
+ DisplayName = entry.TicketNumber ?? string.Empty,
+ id = entry.TicketId,
+ Status = enumF4sdSearchResultStatus.Active,
+ Infos = new Dictionary
+ {
+ ["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 ? string.Empty : entry.InitiatorId.ToString(),
+ ["UserGuid"] = entry.InitiatorId == Guid.Empty ? string.Empty : entry.InitiatorId.ToString()
+ },
+ Identities = new List
+ {
+ new cF4sdIdentityEntry { Class = enumFasdInformationClass.Ticket, Id = entry.TicketId },
+ new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = entry.InitiatorId }
+ }
+ }).ToList();
+ }
+ catch (Exception exception)
+ {
+ LogException(exception);
+ return new List();
+ }
+ finally
+ {
+ LogMethodEnd(method);
+ }
+ }
+
+ private async Task> LoadTicketOverviewEntries(
+ Guid userId,
+ bool useRoleScope,
+ int queueoption,
+ List queues)
+ {
+ if (userId == Guid.Empty)
+ return new List();
+
+ var filter = await BuildTicketOverviewFilterAsync(userId, useRoleScope, queueoption, queues);
+ return await LoadTicketOverviewEntriesByFilter(filter);
+ }
+
+ private async Task> LoadTicketOverviewUnassignedEntriesForPersonalScope(
+ Guid userId,
+ int queueoption,
+ List queues)
+ {
+ if (userId == Guid.Empty)
+ return new List();
+
+ var roleIds = await ResolveTicketOverviewRoleIdsAsync(userId, null);
+ var filter = BuildTicketOverviewFilterForRoleIds(roleIds, null, queueoption, queues);
+ if (string.IsNullOrWhiteSpace(filter))
+ return new List();
+
+ return await LoadTicketOverviewEntriesByFilter(filter + " AND Recipient IS NULL");
+ }
+
+ private async Task BuildTicketOverviewFilterAsync(
+ Guid userId,
+ bool useRoleScope,
+ int queueoption,
+ List queues)
+ {
+ var filter = BuildTicketOverviewBaseFilter(queueoption, queues);
+ if (!useRoleScope)
+ return filter + $" AND (Recipient = '{Escape(userId.ToString("D"))}')";
+
+ var roleIds = await ResolveTicketOverviewRoleIdsAsync(userId, null);
+ return BuildTicketOverviewFilterForRoleIds(roleIds, filter);
+ }
+
+ private async Task> ResolveTicketOverviewRoleIdsAsync(Guid userId, IEnumerable roleGuids)
+ {
+ var roleIds = (roleGuids ?? Enumerable.Empty())
+ .Where(id => id != Guid.Empty)
+ .Distinct()
+ .ToList();
+ if (roleIds.Count > 0 || userId == Guid.Empty)
+ return roleIds;
+
+ var roles = await getRoleMembershipById(userId) ?? new List();
+ return roles
+ .Where(role => role != null && role.Id != Guid.Empty)
+ .Select(role => role.Id)
+ .Distinct()
+ .ToList();
+ }
+
+ private string GetActivityFilter(
+ Guid userId,
+ int hours,
+ bool ticketAndServiceRequestEnabled,
+ int queueoption,
+ List queues)
+ {
+ var filter = $"Initiator = '{Escape(userId.ToString("D"))}'";
+ 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";
+ }
+
+ var startDate = AsqlHelper.PrepareDateTime(DateTime.UtcNow.AddHours(-hours), true);
+ var endDate = AsqlHelper.PrepareDateTime(DateTime.UtcNow, true);
+ filter += " AND (T(SPSCommonClassBase).State <> 204" +
+ $" OR (ClosedDate > {startDate} AND ClosedDate < {endDate}))";
+
+ return AppendQueueFilter(filter, queueoption, queues);
+ }
+ }
+}
diff --git a/F4SDM42WebApi/F4SDHelperService.cs b/F4SDM42WebApi/F4SDHelperService.cs
index 7dd0206..da035bd 100644
--- a/F4SDM42WebApi/F4SDHelperService.cs
+++ b/F4SDM42WebApi/F4SDHelperService.cs
@@ -27,7 +27,7 @@ using static C4IT.Logging.cLogManager;
namespace C4IT.F4SD
{
- public class F4SDHelperService
+ public partial class F4SDHelperService
{
private static Guid SPSUserClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSUserClassBase");
private static Guid SPSAccountClassBaseID = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSAccountClassBase");
diff --git a/F4SDM42WebApi/F4SDM42WebApiController.cs b/F4SDM42WebApi/F4SDM42WebApiController.cs
index 424b586..7a7a25f 100644
--- a/F4SDM42WebApi/F4SDM42WebApiController.cs
+++ b/F4SDM42WebApi/F4SDM42WebApiController.cs
@@ -7,7 +7,7 @@ using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
-
+
using Matrix42.Common;
using Matrix42.Contracts.Common.Security;
using Matrix42.Contracts.ServiceManagement.ServiceContracts;
@@ -18,14 +18,14 @@ using Matrix42.Services.Description.Contracts;
using Matrix42.WebApi.Contracts;
using Matrix42.WebApi.Contracts.OData;
using update4u.SPS.Utility.GlobalConfiguration;
-
+
using C4IT.F4SDM;
using C4IT.FASD.Base;
using C4IT.Logging;
-
+
using static C4IT.FASD.Base.cF4SDTicket;
using static C4IT.Logging.cLogManager;
-
+
namespace C4IT.F4SD
{
[RoutePrefix("api/C4ITF4SDWebApi")]
@@ -39,12 +39,12 @@ namespace C4IT.F4SD
//public readonly IFragmentService _fragmentService;
private readonly IDependencyResolver _resolver;
private readonly IEnumerationProvider _enumerationProvider;
-
+
private readonly F4SDHelperService _f4stHelperService;
public string BaseUrl => Request?.RequestUri == null ? string.Empty : $"{Request.RequestUri.Scheme}://{Request.RequestUri.Host}";
public string EndpointBaseUrl => $"{BaseUrl}/m42Services/api/c4itf4sdwebapi";
-
+
public F4SDM42WebApiController(IDependencyResolver resolver, IEnumerationProvider enumerationProvider)
{
_resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
@@ -53,12 +53,12 @@ namespace C4IT.F4SD
//_objectService = objectService;
//_fragmentService = fragmentService;
//_incidentService = Guard.NullArgument(incidentService, "incidentService");
-
+
_globalConfigurationProvider = GlobalConfigurationProvider.Instance;
_f4stHelperService = new F4SDHelperService();
EnsureInitialized();
- }
-
+ }
+
private static readonly object initLock = new object();
private static void EnsureInitialized()
{
@@ -78,22 +78,22 @@ namespace C4IT.F4SD
}
}
catch { };
- }
-
+ }
+
private T GetRequiredService() where T : class
{
var service = _resolver.TryGet();
if (service != null)
return service;
-
+
throw new InvalidOperationException($"Required Matrix42 service is not registered: {typeof(T).FullName}");
}
-
+
internal IJournalService GetJournalService()
{
return GetRequiredService();
}
-
+
[Route("getDirectLinkCreateTicket"), HttpGet]
public async Task getDirectLinkCreateTicket([FromUri] string sid = "", [FromUri] string assetname = "")
{
@@ -110,8 +110,9 @@ namespace C4IT.F4SD
type = QueryValue(type, nameof(type));
return await _f4stHelperService.getDirectLinkF4SD(eoid, type) ?? string.Empty;
}
-
- [Route("getTicketList"), HttpGet]
+
+ [Obsolete("Use getTicketListForUser with a Matrix42 user ID.")]
+ [Route("getTicketList"), HttpGet]
public async Task> getTicketList(
[FromUri] string sid,
[FromUri] int hours,
@@ -124,7 +125,7 @@ namespace C4IT.F4SD
queueoption = QueryValue(queueoption, nameof(queueoption));
queues = QueryValue(queues, nameof(queues));
var decodedPairs = ParseQueues(queues);
-
+
// Nun weiterreichen an Service
return await _f4stHelperService.getTicketListByUser(
sid,
@@ -132,8 +133,27 @@ namespace C4IT.F4SD
queueoption,
decodedPairs
) ?? new List();
- }
-
+ }
+
+ [Route("getTicketListForUser"), HttpGet]
+ public async Task> getTicketListForUser(
+ [FromUri] Guid userId,
+ [FromUri] int hours,
+ [FromUri] int queueoption = 0,
+ [FromUri] string queues = "")
+ {
+ userId = QueryValue(userId, nameof(userId));
+ hours = QueryValue(hours, nameof(hours));
+ queueoption = QueryValue(queueoption, nameof(queueoption));
+ queues = QueryValue(queues, nameof(queues));
+
+ return await _f4stHelperService.getTicketListByUser(
+ userId,
+ hours,
+ queueoption,
+ ParseQueues(queues)) ?? new List();
+ }
+
[Route("getTicketDetails"), HttpGet]
public async Task getTicketDetails([FromUri] Guid objectId)
@@ -142,18 +162,19 @@ namespace C4IT.F4SD
var tickets = await _f4stHelperService.getTicketDetails(new List() { objectId });
if (tickets?.Count > 0)
return tickets[0];
-
+
return new cF4SDTicket { TicketObjectId = objectId };
}
-
+
[Route("getTicketHistory"), HttpGet]
public async Task> getTicketHistory([FromUri] Guid objectId)
{
objectId = QueryValue(objectId, nameof(objectId));
return await _f4stHelperService.GetJournalEntries(objectId) ?? new List();
}
-
- [Route("getTicketOverviewCounts"), HttpGet]
+
+ [Obsolete("Use getTicketOverviewCountsForUser with a Matrix42 user ID.")]
+ [Route("getTicketOverviewCounts"), HttpGet]
public async Task getTicketOverviewCounts(
[FromUri] string sid,
[FromUri] string scope = "personal",
@@ -172,13 +193,42 @@ namespace C4IT.F4SD
.Select(key => key.Trim())
.Where(key => !string.IsNullOrWhiteSpace(key))
.ToList();
-
+
var decodedQueues = ParseQueues(queues);
return await _f4stHelperService.getTicketOverviewCounts(sid, scope, parsedKeys, queueoption, decodedQueues)
?? new F4SDHelperService.TicketOverviewCountsResult();
- }
-
- [Route("getTicketOverviewCountsByRoles"), HttpPost]
+ }
+
+ [Route("getTicketOverviewCountsForUser"), HttpGet]
+ public async Task getTicketOverviewCountsForUser(
+ [FromUri] Guid userId,
+ [FromUri] string scope = "personal",
+ [FromUri] string keys = "",
+ [FromUri] int queueoption = 0,
+ [FromUri] string queues = "")
+ {
+ userId = QueryValue(userId, nameof(userId));
+ scope = QueryValue(scope, nameof(scope));
+ keys = QueryValue(keys, nameof(keys));
+ queueoption = QueryValue(queueoption, nameof(queueoption));
+ queues = QueryValue(queues, nameof(queues));
+
+ var parsedKeys = (keys ?? string.Empty)
+ .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(key => key.Trim())
+ .Where(key => !string.IsNullOrWhiteSpace(key))
+ .ToList();
+
+ return await _f4stHelperService.getTicketOverviewCounts(
+ userId,
+ scope,
+ parsedKeys,
+ queueoption,
+ ParseQueues(queues)) ?? new F4SDHelperService.TicketOverviewCountsResult();
+ }
+
+ [Obsolete("Use getTicketOverviewCountsByRolesForUser with a Matrix42 user ID.")]
+ [Route("getTicketOverviewCountsByRoles"), HttpPost]
public async Task getTicketOverviewCountsByRoles([FromBody] TicketOverviewCountsByRolesRequest request)
{
var parsedKeys = (request?.Keys ?? new List())
@@ -186,12 +236,12 @@ namespace C4IT.F4SD
.Select(key => key.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
-
+
var roleGuids = (request?.RoleGuids ?? new List())
.Where(roleId => roleId != Guid.Empty)
.Distinct()
.ToList();
-
+
var decodedQueues = ParseQueues(request?.Queues ?? string.Empty);
return await _f4stHelperService.getTicketOverviewCountsByRoles(
request?.Sid,
@@ -200,9 +250,34 @@ namespace C4IT.F4SD
request?.QueueOption ?? 0,
decodedQueues
) ?? new F4SDHelperService.TicketOverviewCountsByRoleResult();
- }
-
- [Route("getTicketOverviewRelations"), HttpGet]
+ }
+
+ [Route("getTicketOverviewCountsByRolesForUser"), HttpPost]
+ public async Task getTicketOverviewCountsByRolesForUser(
+ [FromBody] TicketOverviewCountsByRolesForUserRequest 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?.userId ?? Guid.Empty,
+ roleGuids,
+ parsedKeys,
+ request?.QueueOption ?? 0,
+ ParseQueues(request?.Queues ?? string.Empty))
+ ?? new F4SDHelperService.TicketOverviewCountsByRoleResult();
+ }
+
+ [Obsolete("Use getTicketOverviewRelationsForUser with a Matrix42 user ID.")]
+ [Route("getTicketOverviewRelations"), HttpGet]
public async Task> getTicketOverviewRelations(
[FromUri] string sid,
[FromUri] string scope = "personal",
@@ -221,7 +296,32 @@ namespace C4IT.F4SD
var decodedQueues = ParseQueues(queues);
return await _f4stHelperService.getTicketOverviewRelations(sid, scope, key, count, queueoption, decodedQueues)
?? new List();
- }
+ }
+
+ [Route("getTicketOverviewRelationsForUser"), HttpGet]
+ public async Task> getTicketOverviewRelationsForUser(
+ [FromUri] Guid userId,
+ [FromUri] string scope = "personal",
+ [FromUri] string key = "",
+ [FromUri] int count = 0,
+ [FromUri] int queueoption = 0,
+ [FromUri] string queues = "")
+ {
+ userId = QueryValue(userId, nameof(userId));
+ scope = QueryValue(scope, nameof(scope));
+ key = QueryValue(key, nameof(key));
+ count = QueryValue(count, nameof(count));
+ queueoption = QueryValue(queueoption, nameof(queueoption));
+ queues = QueryValue(queues, nameof(queues));
+
+ return await _f4stHelperService.getTicketOverviewRelations(
+ userId,
+ scope,
+ key,
+ count,
+ queueoption,
+ ParseQueues(queues)) ?? new List();
+ }
/*
[Route("updateActivitySolution/{objectId}"), HttpPost]
public async Task updateActivitySolution(Guid objectId, [FromBody] string SolutionHtml)
@@ -242,18 +342,18 @@ namespace C4IT.F4SD
group = QueryValue(group, nameof(group));
EntityEnumeration enumerationTemp = GetEnumeration(name, mode);
var vals = enumerationTemp.Values;
-
+
if (group > -1)
{
vals = vals.Where(row => !row.Extentions.TryGetValue("StateGroup", out var stateGroup) || (ConvertHelper.ParseInt(stateGroup, 0) == group)).ToArray();
}
-
+
string[] columns = new string[] { "position" };
foreach (var item in vals)
{
item.Extentions = item.Extentions.Where(x => columns.Contains(x.Key.ToLower())).ToDictionary(x => x.Key, x => x.Value);
}
-
+
EntityEnumeration enumeration = new EntityEnumeration
{
Name = enumerationTemp.Name,
@@ -262,26 +362,26 @@ namespace C4IT.F4SD
//CacheOutputAttribute.RegisterResponseEtag($"enum_{enumeration.Name}_{(int)mode}", $"{enumeration.Name}_{(int)mode}", cultureInvariant: false, userInvariant: true, val);
return enumeration;
}
-
+
[Route("getMyRoleMemberships"), HttpGet]
public async Task