feat: expand cockpit integrations and support workflows

Add Phoenix remote desktop, action connector, documentation engine, and gamification support. Refactor support-case processing and search/action UI, and update installer assets and dependencies.
This commit is contained in:
Meik
2026-07-13 11:16:53 +02:00
parent bbedbf71c8
commit 0b52999b1d
303 changed files with 11235 additions and 3580 deletions

View File

@@ -1,13 +1,17 @@
using System;
using System.Collections.Generic;
using C4IT.FASD.Base;
using C4IT.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using C4IT.FASD.Base;
using C4IT.Logging;
using FasdDesktopUi.Basics;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Helper
{
internal static class TicketDeepLinkHelper
internal static class TicketExternalLinkHelper
{
internal static bool TryOpenTicketRelationExternally(cF4sdApiSearchResultRelation relation)
{
@@ -16,127 +20,51 @@ namespace FasdDesktopUi.Basics.Helper
if (relation == null || relation.Type != enumF4sdSearchResultClass.Ticket)
return false;
var ticketConfig = cFasdCockpitConfig.Instance?.Global?.TicketConfiguration;
if (ticketConfig == null)
// check how we should open this ticket (intern, extern or both)
var ticketType = GetTicketType(relation);
var processing = ShouldOpenExternally(ticketType);
// check if we have a valid user id in the id list => if not we could open this ticket only extern.
var hasUser = relation.Identities.Any(e => (e.Class == enumFasdInformationClass.User && e.Id != null && e.Id != Guid.Empty));
if (!hasUser)
processing = enumTicketProcessing.Extern;
if (processing == enumTicketProcessing.Intern)
return false;
var activityType = GetActivityType(relation);
var openExternally = ShouldOpenExternally(ticketConfig, activityType);
if (!openExternally)
return false;
var url = BuildTicketDeepLink(relation.id, activityType);
if (string.IsNullOrWhiteSpace(url))
return false;
if (relation?.Infos?.TryGetValue("TicketLink", out var ticketLink) == true && !string.IsNullOrWhiteSpace(ticketLink))
new cBrowsers().Start("default", ticketLink);
new cBrowsers().Start("default", url);
return true;
return processing == enumTicketProcessing.Extern;
}
catch (Exception ex)
{
LogException(ex);
}
return false;
}
private static string GetActivityType(cF4sdApiSearchResultRelation relation)
{
if (relation?.Infos != null && relation.Infos.TryGetValue("ActivityType", out var activityTypeValue))
return activityTypeValue;
return null;
}
private static bool ShouldOpenExternally(cF4sdTicketConfig ticketConfig, string activityType)
{
if (ticketConfig == null)
return false;
if (TryGetOverride(ticketConfig.OpenActivitiesExternallyOverrides, activityType, out var overrideValue))
return overrideValue;
return ticketConfig.OpenActivitiesExternally;
}
private static bool TryGetOverride(IEnumerable<string> overrides, string activityType, out bool value)
{
value = false;
if (string.IsNullOrWhiteSpace(activityType) || overrides == null)
return false;
foreach (var entry in overrides)
{
if (string.IsNullOrWhiteSpace(entry))
continue;
var parts = entry.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
continue;
var typeName = parts[0].Trim();
if (!string.Equals(typeName, activityType, StringComparison.OrdinalIgnoreCase))
continue;
if (!TryParseBool(parts[1], out value))
return false;
return true;
}
return false;
}
private static bool TryParseBool(string value, out bool result)
{
result = false;
if (string.IsNullOrWhiteSpace(value))
return false;
switch (value.Trim().ToLowerInvariant())
{
case "true":
case "1":
case "yes":
result = true;
return true;
case "false":
case "0":
case "no":
result = false;
return true;
default:
return bool.TryParse(value, out result);
}
}
return false;
}
internal static string BuildTicketDeepLink(Guid ticketId, string activityType)
private static enumTicketType GetTicketType(cF4sdApiSearchResultRelation relation)
{
if (ticketId == Guid.Empty)
return null;
var server = cCockpitConfiguration.Instance?.m42ServerConfiguration?.Server;
if (string.IsNullOrWhiteSpace(server))
return null;
var baseUrl = server.TrimEnd('/');
if (!baseUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
!baseUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
if (relation?.Infos != null && relation.Infos.TryGetValue("TicketType", out var ticketTypeValue))
{
baseUrl = "https://" + baseUrl;
if (Enum.TryParse<enumTicketType>(ticketTypeValue, true, out var ticketType))
return ticketType;
}
if (!baseUrl.EndsWith("/wm", StringComparison.OrdinalIgnoreCase))
baseUrl += "/wm";
return enumTicketType.Ticket;
}
if (string.IsNullOrWhiteSpace(activityType))
return null;
private static enumTicketProcessing ShouldOpenExternally(enumTicketType ticketType)
{
var ticketConfig = cFasdCockpitConfig.Instance?.Global?.TicketConfiguration;
if (ticketConfig == null)
return enumTicketProcessing.Extern;
var viewOptionsJson = $"{{\"embedded\":false,\"objectId\":\"{ticketId}\",\"type\":\"{activityType}\",\"viewType\":\"preview\",\"archived\":0}}";
var viewOptionsEncoded = Uri.EscapeDataString(viewOptionsJson);
if (ticketConfig.TicketProcessing?.TryGetValue(ticketType, out var processing) == true)
return processing;
return $"{baseUrl}/app-ServiceDesk/?view-options={viewOptionsEncoded}";
return ticketConfig.DefaultProcessing;
}
}
}