From 3a001d0e555258cddb3261c5508f55840e25fe96 Mon Sep 17 00:00:00 2001
From: "Drechsler, Meik"
Date: Thu, 14 Aug 2025 16:20:42 +0200
Subject: [PATCH] go
---
C4IT.API.Contracts/C4IT.API.Contracts.csproj | 57 +
C4IT.API.Contracts/Contracts.cs | 100 ++
C4IT.API.Contracts/Properties/AssemblyInfo.cs | 11 +
.../C4IT.API.CustomerPanelTests.csproj | 52 +
.../CustomerPanelControllerTest.cs | 41 +
C4IT.API.Helper/C4IT.API.Helper.csproj | 85 ++
C4IT.API.Helper/HelperController.cs | 178 +++
C4IT.API.Helper/Properties/AssemblyInfo.cs | 11 +
C4IT.API.Helper/app.config | 15 +
C4IT.API.Helper/packages.config | 7 +
C4IT.API.sln | 57 +
C4IT.API/ApiCache.cs | 60 +
C4IT.API/C4IT.API.CustomerPanel.csproj | 170 +++
C4IT.API/ConverterHelper.cs | 70 ++
C4IT.API/CustomerPanelController.cs | 156 +++
C4IT.API/CustomerPanelHelper.cs | 1009 +++++++++++++++++
C4IT.API/CustomerPanelHub.cs | 57 +
C4IT.API/CustomerPanelSecurePassword.cs | 22 +
C4IT.API/DeepLinkBuilder.cs | 93 ++
C4IT.API/PostBuildCopy.bat | 51 +
.../PrivateCustomerPanelSecurePassword.cs | 22 +
C4IT.API/Properties/AssemblyInfo.cs | 8 +
C4IT.API/SignFiles.cmd | 11 +
C4IT.API/app.config | 27 +
C4IT.API/packages.config | 20 +
CommonAssembyInfo.cs | 15 +
SB_DLLs/Matrix42.Common.dll | Bin 0 -> 124416 bytes
SB_DLLs/Matrix42.Contracts.Common.dll | Bin 0 -> 34304 bytes
SB_DLLs/Matrix42.DataLayer.Common.dll | Bin 0 -> 39424 bytes
SB_DLLs/Matrix42.Pandora.Contracts.dll | Bin 0 -> 377856 bytes
SB_DLLs/System.Web.Http.dll | Bin 0 -> 456168 bytes
SB_DLLs/update4u.SPS.Config.dll | Bin 0 -> 53248 bytes
SB_DLLs/update4u.SPS.DataLayer.dll | Bin 0 -> 1959424 bytes
33 files changed, 2405 insertions(+)
create mode 100644 C4IT.API.Contracts/C4IT.API.Contracts.csproj
create mode 100644 C4IT.API.Contracts/Contracts.cs
create mode 100644 C4IT.API.Contracts/Properties/AssemblyInfo.cs
create mode 100644 C4IT.API.CustomerPanelTests/C4IT.API.CustomerPanelTests.csproj
create mode 100644 C4IT.API.CustomerPanelTests/CustomerPanelControllerTest.cs
create mode 100644 C4IT.API.Helper/C4IT.API.Helper.csproj
create mode 100644 C4IT.API.Helper/HelperController.cs
create mode 100644 C4IT.API.Helper/Properties/AssemblyInfo.cs
create mode 100644 C4IT.API.Helper/app.config
create mode 100644 C4IT.API.Helper/packages.config
create mode 100644 C4IT.API.sln
create mode 100644 C4IT.API/ApiCache.cs
create mode 100644 C4IT.API/C4IT.API.CustomerPanel.csproj
create mode 100644 C4IT.API/ConverterHelper.cs
create mode 100644 C4IT.API/CustomerPanelController.cs
create mode 100644 C4IT.API/CustomerPanelHelper.cs
create mode 100644 C4IT.API/CustomerPanelHub.cs
create mode 100644 C4IT.API/CustomerPanelSecurePassword.cs
create mode 100644 C4IT.API/DeepLinkBuilder.cs
create mode 100644 C4IT.API/PostBuildCopy.bat
create mode 100644 C4IT.API/PrivateCustomerPanelSecurePassword.cs
create mode 100644 C4IT.API/Properties/AssemblyInfo.cs
create mode 100644 C4IT.API/SignFiles.cmd
create mode 100644 C4IT.API/app.config
create mode 100644 C4IT.API/packages.config
create mode 100644 CommonAssembyInfo.cs
create mode 100644 SB_DLLs/Matrix42.Common.dll
create mode 100644 SB_DLLs/Matrix42.Contracts.Common.dll
create mode 100644 SB_DLLs/Matrix42.DataLayer.Common.dll
create mode 100644 SB_DLLs/Matrix42.Pandora.Contracts.dll
create mode 100644 SB_DLLs/System.Web.Http.dll
create mode 100644 SB_DLLs/update4u.SPS.Config.dll
create mode 100644 SB_DLLs/update4u.SPS.DataLayer.dll
diff --git a/C4IT.API.Contracts/C4IT.API.Contracts.csproj b/C4IT.API.Contracts/C4IT.API.Contracts.csproj
new file mode 100644
index 0000000..370aacc
--- /dev/null
+++ b/C4IT.API.Contracts/C4IT.API.Contracts.csproj
@@ -0,0 +1,57 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {6CF1365B-C5BC-479C-B7BF-9229DEC98988}
+ Library
+ Properties
+ C4IT.API.Contracts
+ C4IT.API.Contracts
+ v4.7.2
+ 512
+ true
+
+ SAK
+ SAK
+ SAK
+ SAK
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties\CommonAssembyInfo.cs
+
+
+
+
+
+
\ No newline at end of file
diff --git a/C4IT.API.Contracts/Contracts.cs b/C4IT.API.Contracts/Contracts.cs
new file mode 100644
index 0000000..8a0df67
--- /dev/null
+++ b/C4IT.API.Contracts/Contracts.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Media;
+
+namespace C4IT.API.Contracts
+{
+ public class Announcement
+ {
+ public string _message = String.Empty;
+ public string _subject = String.Empty;
+ public int Type = 0;
+ public DateTime _createdDate;
+ public DateTime? _visibleFrom;
+ public Guid _objectID;
+ public string _prioColor;
+ public int _priority = 0;
+ public bool _isAdhoc = false;
+
+ public SolidColorBrush getPrioColorBrush()
+ {
+ if (!string.IsNullOrEmpty(_prioColor))
+ {
+ if (_prioColor.StartsWith("0x"))
+ {
+ return new SolidColorBrush((Color)ColorConverter.ConvertFromString(_prioColor.Replace("0xFF", "#")));
+ }
+ return new SolidColorBrush((Color)ColorConverter.ConvertFromString(_prioColor));
+ }
+
+ return null;
+ }
+ }
+
+ public class Ticket
+ {
+ public string _ticketNumber;
+ public string _subject;
+ public string _state;
+ public DateTime _createdDate;
+ public Int32 _lastJournalEntryAction;
+ public DateTime _lastJournalEntryDate;
+ public Guid _objectID;
+ public string _sysEntity;
+ public Int32 _stateValue;
+ public bool _newInformation;
+ public string _lastJournalEntryActionText;
+ public string _ticketType;
+ public Ticket()
+ {
+ _lastJournalEntryAction = 0;
+ _stateValue = 0;
+ }
+ }
+
+ public enum enumMainFunctions { Announcement = 0, Information, Ssp, Incident, CustomLinks };
+ public class CustomerPanelConfig
+ {
+ public string _remoteAppPath = String.Empty;
+ public bool _disableClosing = false;
+ public int _iconColor = 10;
+ public int _mainIconTextColor = 10;
+ public Version _ServerVersion;
+ public bool _isStartApplicationMinimized;
+ public string _logoUrl = null;
+ public string _trayIconUrl = null;
+ public Dictionary _uiColors = new Dictionary();
+ public bool _isDraggable = false;
+ public Dictionary _linkList = new Dictionary();
+ public int _timerIntervalTicket = 10;
+ public int _timerIntervalAdHocAnnouncements = 2;
+ public int _timerIntervalRegularAnnouncements = 10;
+ public string _createNewTicketDirectLink = String.Empty;
+ public bool _isUUX = true;
+ public string _encryptedApiToken = String.Empty;
+ public int _reloginIntervalDays = 14;
+ public Dictionary MainFunctionActivation = new Dictionary()
+ {
+ {enumMainFunctions.Announcement, false },
+ {enumMainFunctions.Incident, false },
+ {enumMainFunctions.Ssp, false },
+ {enumMainFunctions.Information, true },
+ {enumMainFunctions.CustomLinks, false }
+ };
+
+ public CustomerPanelConfig()
+ {
+ _uiColors["activeButtonColor"] = "#186292";
+ _uiColors["inactiveButtonColor"] = "#186292";
+ _uiColors["backgroundColor"] = "#FFCDC9C9";
+ _uiColors["headerColor"] = "#FFCDC9C9";
+ }
+ }
+
+ public enum announcementType
+ {
+ Adhoc = 1,
+ Regular = 2
+ }
+
+}
diff --git a/C4IT.API.Contracts/Properties/AssemblyInfo.cs b/C4IT.API.Contracts/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..d26e83a
--- /dev/null
+++ b/C4IT.API.Contracts/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("C4IT.API.Contracts")]
+[assembly: ComVisible(false)]
+[assembly: Guid("6cf1365b-c5bc-479c-b7bf-9229dec98988")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/C4IT.API.CustomerPanelTests/C4IT.API.CustomerPanelTests.csproj b/C4IT.API.CustomerPanelTests/C4IT.API.CustomerPanelTests.csproj
new file mode 100644
index 0000000..16c654f
--- /dev/null
+++ b/C4IT.API.CustomerPanelTests/C4IT.API.CustomerPanelTests.csproj
@@ -0,0 +1,52 @@
+
+
+
+ SAK
+ SAK
+ SAK
+ SAK
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\lib\Matrix42.Contracts.Common.dll
+
+
+ ..\lib\Matrix42.Pandora.Contracts.dll
+
+
+ ..\..\..\Users\OT202\source\repos\FleetMarketTestWebService\FleetMarketTestWebService\lib\System.Web.Http.dll
+
+
+ ..\lib\update4u.SPS.DataLayer.dll
+
+
+
+
+
+
+
+
diff --git a/C4IT.API.CustomerPanelTests/CustomerPanelControllerTest.cs b/C4IT.API.CustomerPanelTests/CustomerPanelControllerTest.cs
new file mode 100644
index 0000000..0473fea
--- /dev/null
+++ b/C4IT.API.CustomerPanelTests/CustomerPanelControllerTest.cs
@@ -0,0 +1,41 @@
+using C4IT.API.Contracts;
+using Matrix42.Pandora.Contracts;
+using Matrix42.Pandora.Contracts.Internationalization;
+using Moq;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using update4u.SPS.DataLayer;
+using System.Net.Http.Formatting;
+
+namespace C4IT.API.CustomerPanelTests
+{
+ public class CustomerPanelControllerTest
+ {
+
+ [Test]
+ public void GetVersion_ShouldReturnDefaultVersion_WhenDataIsInvalid()
+ {
+ // Arrange
+ var expectedDefaultVersion = new Version(0, 0, 0, 0);
+
+ // Act
+ CustomerPanelHelper customerPanelHelper = new CustomerPanelHelper();
+ customerPanelHelper.getVersion();
+
+ // Assert
+ Assert.That(customerPanelHelper.getVersion(), Is.EqualTo(expectedDefaultVersion));
+ }
+
+ // Tests
+
+ // GetVersion_HandlesExceptions
+ // GetVersion_ReturnsDefaultVersion_WhenDataTableIsNull
+ // GetVersion_ReturnsDefaultVersion_WhenDataTableIsEmpty
+ // GetVersion_ReturnsDefaultVersion_WhenDatabaseQueryFails
+ // GetVersion_ReturnsCorrectVersion_WhenDatabaseQuerySucceeds
+ }
+}
diff --git a/C4IT.API.Helper/C4IT.API.Helper.csproj b/C4IT.API.Helper/C4IT.API.Helper.csproj
new file mode 100644
index 0000000..0283821
--- /dev/null
+++ b/C4IT.API.Helper/C4IT.API.Helper.csproj
@@ -0,0 +1,85 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {68000E96-161F-42E4-81C1-552EC2B00BD9}
+ Library
+ Properties
+ C4IT.API.Helper
+ C4IT.API.Helper
+ v4.7.2
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\lib\log4net.dll
+
+
+ ..\..\..\Users\OT202\source\repos\FleetMarketTestWebService\FleetMarketTestWebService\lib\Matrix42.Common.dll
+
+
+
+ ..\lib\Microsoft.Net.Http.Headers.dll
+
+
+
+
+ ..\lib\QRCoder.dll
+
+
+
+
+
+
+
+
+
+ ..\..\..\Users\OT202\source\repos\FleetMarketTestWebService\FleetMarketTestWebService\lib\System.Web.Http.dll
+
+
+
+
+
+
+
+
+ ..\lib\update4u.SPS.DatabaseFileStorage.dll
+
+
+ ..\..\..\Users\OT202\source\repos\FleetMarketTestWebService\FleetMarketTestWebService\lib\update4u.SPS.DataLayer.dll
+
+
+
+
+
+ Properties\CommonAssembyInfo.cs
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/C4IT.API.Helper/HelperController.cs b/C4IT.API.Helper/HelperController.cs
new file mode 100644
index 0000000..ff8d34e
--- /dev/null
+++ b/C4IT.API.Helper/HelperController.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web.Http;
+using update4u.SPS.DataLayer;
+using update4u.SPS.DatabaseFileStorage;
+using update4u.SPS.DataLayer.Schema.Internal;
+using QRCoder;
+using System.Drawing;
+using System.IO;
+using System.Net.Http;
+using System.Net;
+
+namespace C4IT.API.Helper
+{
+ [RoutePrefix("api/c4it/helper")]
+ public class HelperController : ApiController
+ {
+
+
+ [Route("copyFiles"), HttpGet]
+ public string copyFiles(Guid sourceEOID, Guid targetEOID)
+ {
+ StorageService storageService = new StorageService();
+ if (!sourceEOID.Equals(Guid.Empty) && !targetEOID.Equals(Guid.Empty))
+ {
+
+ try
+ {
+ Guid sourceTypeID = new Guid(FragmentRequestBase.SimpleLoad(SPSDataEngineSchemaReader.ClassGetIDFromName("SPSCommonClassBase"), "typeid", "[expression-objectid]='" + sourceEOID.ToString() + "'").Rows[0]["typeid"].ToString());
+ Guid targetTypeID = new Guid(FragmentRequestBase.SimpleLoad(SPSDataEngineSchemaReader.ClassGetIDFromName("SPSCommonClassBase"), "typeid", "[expression-objectid]='" + targetEOID.ToString() + "'").Rows[0]["typeid"].ToString());
+
+ string targetFolder = StorageService.GetFolderName(targetTypeID, targetEOID);
+ string sourceFolder = StorageService.GetFolderName(sourceTypeID, sourceEOID);
+ List dbFiles = storageService.ListFiles(sourceFolder);
+
+ List copyFileGuids = new List();
+ foreach (DatabaseFile dbFile in dbFiles)
+ {
+ copyFileGuids.Add(dbFile.FileGuid);
+
+ }
+ storageService.CopyFiles(copyFileGuids, targetFolder);
+
+
+ }
+ catch (Exception e)
+ {
+ return e.Message;
+ }
+ }
+ return "ok";
+
+ }
+
+
+ [Route("moveFiles"), HttpGet]
+ public string moveFiles(Guid sourceEOID, Guid targetEOID)
+ {
+ StorageService storageService = new StorageService();
+ if (!sourceEOID.Equals(Guid.Empty) && !targetEOID.Equals(Guid.Empty)) {
+
+ try{
+ Guid sourceTypeID = new Guid(FragmentRequestBase.SimpleLoad(SPSDataEngineSchemaReader.ClassGetIDFromName("SPSCommonClassBase"), "typeid", "[expression-objectid]='" + sourceEOID.ToString() + "'").Rows[0]["typeid"].ToString());
+ Guid targetTypeID = new Guid(FragmentRequestBase.SimpleLoad(SPSDataEngineSchemaReader.ClassGetIDFromName("SPSCommonClassBase"), "typeid", "[expression-objectid]='" + targetEOID.ToString() + "'").Rows[0]["typeid"].ToString());
+
+ string targetFolder = StorageService.GetFolderName(targetTypeID, targetEOID);
+ string sourceFolder = StorageService.GetFolderName(sourceTypeID, sourceEOID);
+
+
+ storageService.RenameFolder(sourceFolder, targetFolder);
+
+
+ }
+ catch (Exception e)
+ {
+ return e.Message;
+ }
+ }
+ return "ok";
+ }
+
+ [Route("unreadActivity"), HttpPost]
+ public string unreadActivity(Guid[] activiesEOID)
+ {
+ string log = activiesEOID.Length.ToString();
+ if (activiesEOID.Length > 0)
+ {
+ try
+ {
+ foreach (Guid activityEOID in activiesEOID)
+ {
+
+ Guid typeId = new Guid(FragmentRequestBase.SimpleLoad(SPSDataEngineSchemaReader.ClassGetIDFromName("SPSCommonClassBase"), "typeid", "[expression-objectid]='" + activityEOID.ToString() + "'").Rows[0]["typeid"].ToString());
+ SPSObject activity = ObjectRequest.GetSPSObject(typeId, activityEOID);
+ log += " / " + activity.GetAttribute("SPSActivityClassBase", "TicketNumber");
+ activity.UpdateAttribute("SPSActivityClassBase","NewInformationReceived",DBNull.Value);
+ activity.Update();
+
+ }
+ }catch(Exception e){
+ return log + " / " + e.Message;
+ }
+ }
+ return log;
+ }
+
+ [Route("genQrCode"),HttpGet]
+ public HttpResponseMessage genQRCode(string qrText)
+ {
+ QRCodeGenerator qrGenerator = new QRCodeGenerator();
+ QRCodeData qrCodeData = qrGenerator.CreateQrCode(qrText,
+ QRCodeGenerator.ECCLevel.Q);
+ QRCode qrCode = new QRCode(qrCodeData);
+ Bitmap qrCodeImage = qrCode.GetGraphic(20);
+ var dataStream = new MemoryStream(BitmapToBytes(qrCodeImage));
+ var fileName = Path.GetInvalidFileNameChars().Aggregate(qrText.Substring(0, 10), (current, c) => current.Replace(c.ToString(), string.Empty));
+ if (String.IsNullOrEmpty(fileName))
+ {
+ fileName = "dummy";
+ }
+ HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);
+ httpResponseMessage.Content = new StreamContent(dataStream);
+ httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
+ httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName+".png";
+ httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
+
+ return httpResponseMessage;
+ }
+ // This method is for converting bitmap into a byte array
+ private static byte[] BitmapToBytes(Bitmap img)
+ {
+ using (MemoryStream stream = new MemoryStream())
+ {
+ img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
+ return stream.ToArray();
+ }
+ }
+
+
+ [Route("copyObject"), HttpGet]
+
+ public string copyObject(Guid objectToCopy)
+ {
+ string log = "";
+ try
+ {
+ Guid sourceTypeID = new Guid(FragmentRequestBase.SimpleLoad(SPSDataEngineSchemaReader.ClassGetIDFromName("SPSCommonClassBase"), "typeid", "[expression-objectid]='" + objectToCopy.ToString() + "'").Rows[0]["typeid"].ToString());
+ SPSObject oldObject = ObjectRequest.GetSPSObject(sourceTypeID, objectToCopy);
+ SPSObject newObject = (SPSObject)oldObject.CreateCopy(true);
+
+ IAttribute k = newObject.ConfigurationItem.AutoNumberAttribute;
+ string attrib = k.DbColumnName;
+ IDataDefinition dd = k.BelongsToSchemaObjectClass;
+ newObject.UpdateAttribute(dd.Name, attrib, DBNull.Value);
+ log = attrib + " / " + dd.Name;
+
+
+ newObject.Update();
+ }catch(Exception e)
+ {
+ return e.Message;
+ }
+
+ return log;
+ }
+
+
+
+ [Route("createUninstall"), HttpPost]
+ public bool createUninstall(Guid[] installBookings)
+ {
+ return true;
+ }
+ }
+}
diff --git a/C4IT.API.Helper/Properties/AssemblyInfo.cs b/C4IT.API.Helper/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6f642ff
--- /dev/null
+++ b/C4IT.API.Helper/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("C4IT.API.Helper")]
+[assembly: ComVisible(false)]
+[assembly: Guid("68000e96-161f-42e4-81c1-552ec2b00bd9")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/C4IT.API.Helper/app.config b/C4IT.API.Helper/app.config
new file mode 100644
index 0000000..996f4bc
--- /dev/null
+++ b/C4IT.API.Helper/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/C4IT.API.Helper/packages.config b/C4IT.API.Helper/packages.config
new file mode 100644
index 0000000..7eae6bf
--- /dev/null
+++ b/C4IT.API.Helper/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/C4IT.API.sln b/C4IT.API.sln
new file mode 100644
index 0000000..8476e7a
--- /dev/null
+++ b/C4IT.API.sln
@@ -0,0 +1,57 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.10.35122.118
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C4IT.API.CustomerPanel", "C4IT.API\C4IT.API.CustomerPanel.csproj", "{C965495D-D326-4521-9B6A-668227ED0651}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C4IT.API.Contracts", "C4IT.API.Contracts\C4IT.API.Contracts.csproj", "{6CF1365B-C5BC-479C-B7BF-9229DEC98988}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Common", "_Common", "{2C8DF0C0-0FB7-4E67-8AC4-7C0D368410CE}"
+ ProjectSection(SolutionItems) = preProject
+ CommonAssembyInfo.cs = CommonAssembyInfo.cs
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C4IT.API.CustomerPanelTests", "C4IT.API.CustomerPanelTests\C4IT.API.CustomerPanelTests.csproj", "{ECE98C21-EF8F-442B-BA98-8DB8E848D906}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C965495D-D326-4521-9B6A-668227ED0651}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C965495D-D326-4521-9B6A-668227ED0651}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C965495D-D326-4521-9B6A-668227ED0651}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C965495D-D326-4521-9B6A-668227ED0651}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6CF1365B-C5BC-479C-B7BF-9229DEC98988}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6CF1365B-C5BC-479C-B7BF-9229DEC98988}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6CF1365B-C5BC-479C-B7BF-9229DEC98988}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6CF1365B-C5BC-479C-B7BF-9229DEC98988}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ECE98C21-EF8F-442B-BA98-8DB8E848D906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ECE98C21-EF8F-442B-BA98-8DB8E848D906}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ECE98C21-EF8F-442B-BA98-8DB8E848D906}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ECE98C21-EF8F-442B-BA98-8DB8E848D906}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4E72DDDF-0E9A-4BA8-B27B-3C86DBE20A7F}
+ EndGlobalSection
+ GlobalSection(TeamFoundationVersionControl) = preSolution
+ SccNumberOfProjects = 4
+ SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
+ SccTeamFoundationServer = https://consulting4it.visualstudio.com/
+ SccLocalPath0 = .
+ SccProjectUniqueName1 = C4IT.API.Contracts\\C4IT.API.Contracts.csproj
+ SccProjectName1 = C4IT.API.Contracts
+ SccLocalPath1 = C4IT.API.Contracts
+ SccProjectUniqueName2 = C4IT.API\\C4IT.API.CustomerPanel.csproj
+ SccProjectName2 = C4IT.API
+ SccLocalPath2 = C4IT.API
+ SccProjectUniqueName3 = C4IT.API.CustomerPanelTests\\C4IT.API.CustomerPanelTests.csproj
+ SccProjectName3 = C4IT.API.CustomerPanelTests
+ SccLocalPath3 = C4IT.API.CustomerPanelTests
+ EndGlobalSection
+EndGlobal
diff --git a/C4IT.API/ApiCache.cs b/C4IT.API/ApiCache.cs
new file mode 100644
index 0000000..385c434
--- /dev/null
+++ b/C4IT.API/ApiCache.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Runtime.Caching;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace C4IT.API
+{
+ class ApiCache
+ {
+ public static object GetValue(string key)
+ {
+ MemoryCache memoryCache = MemoryCache.Default;
+
+ return memoryCache.Get(key);
+ }
+
+ public static object AddOrGet(string key, object value, DateTimeOffset absExpiration)
+ {
+ MemoryCache memoryCache = MemoryCache.Default;
+ lock (memoryCache)
+ {
+ return memoryCache.AddOrGetExisting(key, value, absExpiration);
+ }
+ }
+
+ public static bool Add(string key, object value, DateTimeOffset absExpiration)
+ {
+
+ MemoryCache memoryCache = MemoryCache.Default;
+ lock (memoryCache)
+ {
+ return memoryCache.Add(key, value, absExpiration);
+ }
+
+ }
+
+ public static void Delete(string key)
+ {
+ MemoryCache memoryCache = MemoryCache.Default;
+ if (memoryCache.Contains(key))
+ {
+ memoryCache.Remove(key);
+ }
+ }
+
+ public static List GetAllKeysInCache()
+ {
+ MemoryCache memoryCache = MemoryCache.Default;
+ List cacheKeys = new List();
+ var keys= memoryCache.Where(o => true).Select(o => o.Key);
+
+ foreach (var key in keys)
+ {
+ cacheKeys.Add(key);
+ }
+
+ return cacheKeys;
+ }
+ }
+}
diff --git a/C4IT.API/C4IT.API.CustomerPanel.csproj b/C4IT.API/C4IT.API.CustomerPanel.csproj
new file mode 100644
index 0000000..8ecada6
--- /dev/null
+++ b/C4IT.API/C4IT.API.CustomerPanel.csproj
@@ -0,0 +1,170 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C965495D-D326-4521-9B6A-668227ED0651}
+ Library
+ Properties
+ C4IT.API.CustomerPanel
+ C4IT.API.CustomerPanel
+ v4.7.2
+ 512
+ true
+
+ SAK
+ SAK
+ SAK
+ SAK
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\SB_DLLs\Matrix42.Common.dll
+
+
+ ..\SB_DLLs\Matrix42.Contracts.Common.dll
+
+
+ ..\SB_DLLs\Matrix42.DataLayer.Common.dll
+
+
+ ..\SB_DLLs\Matrix42.Pandora.Contracts.dll
+
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
+ False
+
+
+ ..\packages\Microsoft.Extensions.Caching.Abstractions.8.0.0\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
+ False
+
+
+ ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.8.0.1\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+ False
+
+
+ ..\packages\Microsoft.Extensions.Logging.Abstractions.8.0.1\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
+ False
+
+
+ ..\packages\Microsoft.Extensions.Options.8.0.2\lib\net462\Microsoft.Extensions.Options.dll
+ False
+
+
+ ..\packages\Microsoft.Extensions.Primitives.8.0.0\lib\net462\Microsoft.Extensions.Primitives.dll
+ False
+
+
+ ..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll
+ False
+
+
+ ..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
+ False
+
+
+ ..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll
+ False
+
+
+ ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll
+
+
+
+
+
+
+ ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll
+ False
+
+
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+ False
+
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+ False
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
+ False
+
+
+
+ ..\SB_DLLs\System.Web.Http.dll
+
+
+
+
+
+
+
+
+ ..\SB_DLLs\update4u.SPS.Config.dll
+
+
+ ..\SB_DLLs\update4u.SPS.DataLayer.dll
+
+
+
+
+ Common\C4IT.Logging.LogManager.cs
+
+
+ Common\C4IT.Security.SecurePassword.cs
+
+
+ Properties\CommonAssembyInfo.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {6cf1365b-c5bc-479c-b7bf-9229dec98988}
+ C4IT.API.Contracts
+ True
+
+
+
+
+ call "$(ProjectDir)PostBuildCopy.bat" "$(Configuration)"
+
+
\ No newline at end of file
diff --git a/C4IT.API/ConverterHelper.cs b/C4IT.API/ConverterHelper.cs
new file mode 100644
index 0000000..cc26ce5
--- /dev/null
+++ b/C4IT.API/ConverterHelper.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Linq;
+using System.Net;
+using System.Text.RegularExpressions;
+
+namespace C4IT.API
+{
+ public static class ConverterHelper
+ {
+ public static string Html2Plaintext(string html)
+ {
+ if (string.IsNullOrWhiteSpace(html))
+ return string.Empty;
+
+ // 1. HTML‐Entities dekodieren (z.B. ü → ü)
+ string decoded = WebUtility.HtmlDecode(html);
+
+ // 2.
und
durch Zeilenumbruch ersetzen
+ string withBreaks = Regex.Replace(
+ decoded,
+ @"(?:p|div|li|blockquote|pre|h[1-6]|tr)\s*> # schließende Block-Tags
+ |<(?:br|hr)\b[^>]*> #
,
+ |<(?:ul|ol|table|thead|tbody|tfoot|article # öffnende Block-Container
+ |section|nav|aside|header|footer
+ |figure|figcaption|details|summary)[^>]*>",
+ " ",
+ RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
+
+ // 3. Alle übrigen Tags entfernen
+ string noTags = Regex.Replace(withBreaks, @"<[^>]+>", string.Empty);
+
+ return noTags.Trim();
+ }
+
+ public static bool ObjectToBoolConverter(object source)
+ {
+ try
+ {
+ if (source.Equals("1"))
+ {
+ source = 1;
+ }
+ return Convert.ToBoolean(source);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ public static int ObjectToIntConverter(object source)
+ {
+ try
+ {
+ return Convert.ToInt32(source);
+ }
+ catch (Exception)
+ {
+ return 0;
+ }
+ }
+
+ public static string ObjectToStringConverter(this object value)
+ {
+ return (value ?? string.Empty).ToString();
+ }
+
+
+ }
+}
diff --git a/C4IT.API/CustomerPanelController.cs b/C4IT.API/CustomerPanelController.cs
new file mode 100644
index 0000000..55a38bb
--- /dev/null
+++ b/C4IT.API/CustomerPanelController.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Web.Http;
+using C4IT.API.Contracts;
+using C4IT.Logging;
+using System.Reflection;
+using System.Web.Http.Controllers;
+using Matrix42.Pandora.Contracts;
+using static C4IT.Logging.cLogManager;
+using Matrix42.Pandora.Contracts.Internationalization;
+using System.Security.Principal;
+using update4u.SPS.DataLayer.Query.FunctionExpression;
+using System.Net.Http;
+using System.Net;
+
+namespace C4IT.API
+{
+ [RoutePrefix("api/c4it/customerpanel")]
+
+ public class CustomerPanelController : ApiController
+ {
+ private readonly CustomerPanelHelper _CPanelHelper = new CustomerPanelHelper();
+
+ public static bool IsInitialized { get; private set; } = false;
+
+ public IPandoraUserProfile UserProfile => _userProfile;
+
+ public ICultureConfuguration GlobalSettings => _globalSettings;
+
+ public LanguageInfo[] Languages => _languages ?? (_languages = GlobalSettings.GetLanguages());
+
+ private static Object initLock = new object();
+ internal static CustomerPanelController controller = null;
+
+ private readonly IPandoraUserProfile _userProfile;
+ private readonly ICultureConfuguration _globalSettings;
+ private LanguageInfo[] _languages = null;
+ public CustomerPanelController(IPandoraUserProfile pandoraUserProfile, ICultureConfuguration globalSettings)
+ {
+ controller = this;
+ _userProfile = pandoraUserProfile;
+ _globalSettings = globalSettings;
+ }
+
+ protected override void Initialize(HttpControllerContext controllerContext)
+ {
+ base.Initialize(controllerContext);
+ try
+ {
+ lock (initLock)
+ {
+ if (IsInitialized)
+ return;
+
+ var Ass = Assembly.GetExecutingAssembly();
+ var LM = cLogManagerFile.CreateInstance(LocalMachine: true, A: Ass);
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+ cLogManager.DefaultLogger.LogAssemblyInfo(Ass);
+
+ CustomerPanelSecurePassword.Init();
+ PrivateCustomerPanelSecurePassword.Init();
+
+ IsInitialized = true;
+ LogMethodEnd(CM);
+ }
+ }
+ catch { };
+ }
+
+ [Route("version"), HttpGet]
+ public Version GetVersion()
+ {
+ return _CPanelHelper.getVersion();
+ }
+
+ [Route("closeallclients"), HttpGet]
+ public bool GetCloseAllClients()
+ {
+ return _CPanelHelper.GetCloseAllClients();
+ }
+
+ [Route("config"), HttpGet]
+ public CustomerPanelConfig GetConfig(bool noCache = false)
+ {
+ return _CPanelHelper.GetConfig(noCache);
+ }
+
+ [Route("tickets/{userid:guid}"), HttpGet]
+ public List GetTicketsByUserId(Guid userId)
+ {
+ return _CPanelHelper.GetTickets(userId);
+ }
+ [Route("tickets"), HttpGet]
+ public List GetTickets()
+ {
+ return _CPanelHelper.GetTickets(controller.UserProfile.UserId);
+ }
+
+ [Route("resetCache"), HttpGet]
+ public bool ResetCache()
+ {
+ return this._CPanelHelper.ResetCache();
+ }
+
+ [Route("announcements/{userid:guid}"), HttpGet]
+ public List GetAnnouncementsByUserId(announcementType type, Guid userId, bool noCache = false)
+ {
+ return GetAnnouncementsInternal(type, userId, noCache);
+ }
+
+ [Route("announcements"), HttpGet]
+ public List GetAnnouncements(announcementType type, bool noCache = false)
+ {
+ return GetAnnouncementsInternal(type, null, noCache);
+ }
+
+ [Route("isalive"), HttpGet]
+ public HttpResponseMessage isAlive()
+ {
+ return new HttpResponseMessage(HttpStatusCode.NoContent);
+ }
+
+ private List GetAnnouncementsInternal(announcementType type, Guid? userId, bool noCache)
+ {
+ switch (type)
+ {
+ case announcementType.Regular:
+ return userId.HasValue
+ ? this._CPanelHelper.GetRegularAnnouncements(userId.Value)
+ : this._CPanelHelper.GetRegularAnnouncements(controller.UserProfile.UserId);
+ case announcementType.Adhoc:
+ return this._CPanelHelper.GetAdHocAnnouncements(noCache);
+ default:
+ return null;
+ }
+ }
+
+ [Route("Encode"), HttpPost]
+ public string Encode(string str)
+ {
+ return this._CPanelHelper.Encode(str);
+ }
+
+ [Route("ApiCache"), HttpGet]
+ public Object GetApiCache()
+ {
+ return ApiCache.GetAllKeysInCache();
+ }
+
+ //[Route("JoinPool"), HttpGet]
+ //public void JoinHubPool()
+ //{
+ //}
+ }
+}
diff --git a/C4IT.API/CustomerPanelHelper.cs b/C4IT.API/CustomerPanelHelper.cs
new file mode 100644
index 0000000..6d63372
--- /dev/null
+++ b/C4IT.API/CustomerPanelHelper.cs
@@ -0,0 +1,1009 @@
+using C4IT.API.Contracts;
+using C4IT.API.CustomerPanel;
+using C4IT.Logging;
+using Matrix42.Pandora.Contracts.Internationalization;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.SqlClient;
+using System.Globalization;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Web;
+using System.Web.Caching;
+using update4u.SPS.DataLayer;
+using update4u.SPS.DataLayer.Command;
+using update4u.SPS.DataLayer.DataReader;
+using update4u.SPS.DataLayer.Transaction;
+using static C4IT.Logging.cLogManager;
+
+namespace C4IT.API
+{
+ public class CustomerPanelHelper
+ {
+ #region SQLStatements
+ private const string ticketSQLStatement = @"
+ --DECLARE @user_id UNIQUEIDENTIFIER = '7edaa9c7-7b33-e511-80f6-0050562f9516',
+ --@LCID_long INT = 1031,
+ --@LCID_short INT = 7;
+
+ SELECT
+ acb.NewInformationReceived,
+ acb.[Expression-ObjectID] AS eoid,
+ acb.Subject,
+ ccb.State AS stateValue,
+ ISNULL(
+ (SELECT TOP 1 cpo.DisplayString
+ FROM [SPSCommonPickupObjectStatus-CI] AS cpo WITH(NOLOCK)
+ WHERE scpo.ID = cpo.[Owner] AND cpo.LCID IN (@LCID_long, @LCID_short, 127)
+ ORDER BY CASE cpo.LCID
+ WHEN @LCID_long THEN 1
+ WHEN @LCID_short THEN 2
+ WHEN 127 THEN 3
+ END ASC),
+ scpo.DisplayString) AS [state],
+ acb.CreatedDate,
+ lj2.CreatedDate as maxJournalDate,
+ lj2.ActivityAction AS journalAction,
+ sot.name AS sysentity,
+ acb.TicketNumber,
+ ISNULL(
+ (SELECT TOP 1 bso.DisplayName
+ FROM [BasicSchemaObjectType-CI] AS bso WITH(NOLOCK)
+ WHERE bso.Owner = sot.[ID] AND bso.LCID IN (@LCID_long, @LCID_short, 127)
+ ORDER BY CASE bso.LCID
+ WHEN @LCID_long THEN 1
+ WHEN @LCID_short THEN 2
+ WHEN 127 THEN 3
+ END ASC),
+ sot.DisplayName) AS [TicketType]
+ FROM SPSActivityClassBase acb WITH(NOLOCK)
+ LEFT JOIN C4IT_CustomerPanelActivityClassAdditionalRelations a WITH(NOLOCK) on a.[Expression-ObjectID] = acb.[Expression-ObjectID]
+ LEFT JOIN SPSActivityClassUnitOfWork lj2 WITH(NOLOCK) on lj2.ID = a.LastJournalEntry
+ JOIN SPSCommonClassBase ccb WITH(NOLOCK)
+ ON ccb.[expression-objectid] = acb.[expression-objectid]
+ LEFT JOIN SPSCommonPickupObjectStatus scpo WITH(NOLOCK)
+ ON scpo.Value = ccb.State
+ LEFT JOIN SchemaObjectType sot WITH(NOLOCK)
+ ON sot.id = ccb.TypeID
+ LEFT JOIN [BasicSchemaObjectType-CI] AS bso
+ ON bso.Owner = sot.[ID] AND bso.LCID IN (@LCID_long, @LCID_short, 127)
+ LEFT JOIN [SPSCommonPickupObjectStatus-CI] AS cpo
+ ON scpo.ID = cpo.[Owner] AND cpo.LCID IN (@LCID_long, @LCID_short, 127)
+ WHERE
+ (ccb.[State] <> 204 OR GETUTCDATE() < DATEADD(WEEK, 1, acb.ClosedDate))
+ AND (
+ acb.[UsedInTypeSPSActivityTypeTicket] IS NOT NULL
+ OR acb.[UsedInTypeSPSActivityTypeServiceRequest] IS NOT NULL
+ OR acb.[UsedInTypeSPSActivityTypeIncident] IS NOT NULL
+ )
+ AND acb.Initiator = @user_id
+ ORDER BY lj2.CreatedDate DESC, acb.CreatedDate DESC;
+ ";
+ private const string configSQLStatement = @"
+ SELECT
+ name,
+ version,
+ announcementEnabled,
+ incidentEnabled,
+ informationEnabled,
+ updateInterval,
+ UpdateIntervallRegularAnnouncements,
+ UpdateIntervalAdHocAnnouncements,
+ logoUrl,
+ startMinimized,
+ remoteApp,
+ trayIcon,
+ sspEnabled,
+ customLinks,
+ isDraggable,
+ activeButtonColor,
+ inactiveButtonColor,
+ backgroundColor,
+ headerColor,
+ iconColor,
+ mainIconTextColor,
+ disableClosing,
+ encryptedApiToken,
+ reloginIntervalDays
+ FROM
+ [C4IT_CustomerPanel_SetupClassBase]
+ WITH(NOLOCK)";
+ private const string adHocAnnouncementsSQLStatement = @"
+ -- Get announcements (AdHoc)
+ DECLARE
+ @CurrentDate DATETIME = GETUTCDATE();
+
+ SELECT
+ sab.[Expression-ObjectID] as eoid,
+ COALESCE(als.Subject, sab.Subject) AS AnnouncementSubject,
+ sab.CreatedDate AS AnnouncementCreatedDate,
+ sab.VisibleFrom AS AnnouncementVisibleFrom,
+ sab.Visible AS AnnouncementVisibility,
+ COALESCE(amh.MessageHTML, sab.MessageHTML) AS [Message],
+ col.Color AS prioColor,
+ col.Priority as priority
+ FROM SVMAnnouncementClassBase AS sab WITH(NOLOCK)
+ LEFT JOIN C4it_CP_AnnouncementClassBase AS cpa WITH(NOLOCK)
+ ON sab.[Expression-ObjectID] = cpa.[Expression-ObjectID]
+ LEFT JOIN C4IT_CP_AnnouncementPriortyPickupType AS col WITH(NOLOCK)
+ ON cpa.[priority] = col.[Value]
+ OUTER APPLY (
+ SELECT TOP 1 als.Subject
+ FROM [SVMAnnouncementClassBase-CI] AS als WITH(NOLOCK)
+ WHERE als.Owner = sab.ID
+ AND als.LCID IN (@LCID_long, @LCID_short, 127)
+ ORDER BY CASE als.LCID
+ WHEN @LCID_long THEN 1
+ WHEN @LCID_short THEN 2
+ WHEN 127 THEN 3
+ END ASC
+ ) als
+ OUTER APPLY (
+ SELECT TOP 1 amh.MessageHTML
+ FROM [SVMAnnouncementClassBase-CI] AS amh WITH(NOLOCK)
+ WHERE amh.Owner = sab.ID
+ AND amh.LCID IN (@LCID_long, @LCID_short, 127)
+ ORDER BY CASE amh.LCID
+ WHEN @LCID_long THEN 1
+ WHEN @LCID_short THEN 2
+ WHEN 127 THEN 3
+ END ASC
+ ) amh
+ WHERE
+ (sab.Visible = 1
+ OR (sab.Visible = 2
+ AND (sab.VisibleFrom IS NULL OR sab.VisibleFrom < @CurrentDate)
+ AND (sab.VisibleUntil IS NULL OR sab.VisibleUntil > @CurrentDate))
+ )
+ AND (sab.Restricted IS NULL
+ OR sab.Restricted = 0 )
+
+ OPTION(OPTIMIZE FOR (@CurrentDate UNKNOWN, @LCID_long UNKNOWN,@LCID_short UNKNOWN))
+ ";
+ private const string regularAnnouncementsSQLStatement = @"
+ --DECLARE @CurrentUserId UNIQUEIDENTIFIER = '7edaa9c7-7b33-e511-80f6-0050562f9516',
+ --@LCID_long INT = 1031,
+ --@LCID_short INT = 7,
+ --@RecursiveOU TINYINT = 1;
+ DECLARE @CurrentDate DATETIME = GETUTCDATE();
+
+ WITH RoleCTE AS (
+ SELECT
+ ar.Roles
+ FROM [SchemaRelation-SVMAnnouncementClassBase2SPSSecurityClassRole] AS ar WITH(NOLOCK)
+ INNER JOIN [SchemaRelation-SPSSecurityClassRole2SPSUserClassBase] AS rr WITH(NOLOCK)
+ ON ar.RelatedAnnouncements = rr.Members
+ WHERE
+ rr.MemberOf = @CurrentUserId
+ ),
+ ParentOrgUnits AS (
+ SELECT
+ IIF( @RecursiveOU = 1 , op.ID , uou.ID ) AS OrgUnitID
+ FROM SPSUserClassBase AS u WITH(NOLOCK)
+ INNER JOIN SPSCommonClassBase AS uc WITH(NOLOCK)
+ ON u.[Expression-ObjectID] = uc.[Expression-ObjectID]
+ INNER JOIN SPSOrgUnitClassBase AS uou WITH(NOLOCK)
+ ON uc.OU = uou.ID
+ LEFT JOIN [Schema-PC-SPSOrgUnitType-SPSCommonClassBase-OU] AS pcou WITH(NOLOCK)
+ ON uou.ID = pcou.Child
+ LEFT JOIN SPSOrgUnitClassBase AS op WITH(NOLOCK)
+ ON op.ID = pcou.Parent
+ WHERE
+ u.ID = @CurrentUserId
+ )
+ -- Get announcements
+ SELECT
+ sab.[Expression-ObjectID] as eoid,
+ COALESCE(als.Subject, sab.Subject) AS AnnouncementSubject,
+ sab.CreatedDate AS AnnouncementCreatedDate,
+ sab.VisibleFrom AS AnnouncementVisibleFrom,
+ sab.Visible AS AnnouncementVisibility,
+ COALESCE(amh.MessageHTML, sab.MessageHTML) AS [Message],
+ col.Color AS prioColor,
+ col.Priority AS priority
+ FROM SVMAnnouncementClassBase AS sab WITH(NOLOCK)
+ LEFT JOIN C4it_CP_AnnouncementClassBase AS cpa WITH(NOLOCK)
+ ON sab.[Expression-ObjectID] = cpa.[Expression-ObjectID]
+ LEFT JOIN C4IT_CP_AnnouncementPriortyPickupType AS col WITH(NOLOCK)
+ ON cpa.[priority] = col.[Value]
+ LEFT JOIN [SchemaRelation-SVMAnnouncementClassBase2SPSUserClassBase] AS rel WITH(NOLOCK)
+ ON sab.ID = rel.UserS AND rel.Announcements = @CurrentUserId
+ LEFT JOIN RoleCTE AS rc ON sab.ID = rc.Roles
+ LEFT JOIN [SchemaRelation-SVMAnnouncementClassBase2SPSOrgUnitClassBase] AS ao WITH(NOLOCK)
+ ON ao.OrgUnits = sab.ID
+ LEFT JOIN SPSOrgUnitClassBase AS ou WITH(NOLOCK)
+ ON ou.ID = ao.Announcements
+ OUTER APPLY (
+ SELECT TOP 1 als.Subject
+ FROM [SVMAnnouncementClassBase-CI] AS als WITH(NOLOCK)
+ WHERE als.Owner = sab.ID
+ AND als.LCID IN (@LCID_long, @LCID_short, 127)
+ ORDER BY CASE als.LCID
+ WHEN @LCID_long THEN 1
+ WHEN @LCID_short THEN 2
+ WHEN 127 THEN 3
+ END ASC
+ ) als
+ OUTER APPLY (
+ SELECT TOP 1 amh.MessageHTML
+ FROM [SVMAnnouncementClassBase-CI] AS amh WITH(NOLOCK)
+ WHERE amh.Owner = sab.ID
+ AND amh.LCID IN (@LCID_long, @LCID_short, 127)
+ ORDER BY CASE amh.LCID
+ WHEN @LCID_long THEN 1
+ WHEN @LCID_short THEN 2
+ WHEN 127 THEN 3
+ END ASC
+ ) amh
+ WHERE
+ (sab.Visible = 1
+ OR (sab.Visible = 2
+ AND (sab.VisibleFrom IS NULL OR sab.VisibleFrom < @CurrentDate)
+ AND (sab.VisibleUntil IS NULL OR sab.VisibleUntil > @CurrentDate))
+ )
+ AND (sab.Restricted = 1
+ AND (rel.Users IS NOT NULL
+ OR rc.Roles IS NOT NULL
+ OR EXISTS (SELECT 1 FROM ParentOrgUnits AS pou WITH(NOLOCK) WHERE ou.ID = pou.OrgUnitID)
+ )
+ ) OPTION(OPTIMIZE FOR (@CurrentDate UNKNOWN, @CurrentUserId UNKNOWN, @LCID_long UNKNOWN,@LCID_short UNKNOWN,@RecursiveOU UNKNOWN ))
+
+ ";
+
+ #endregion
+
+
+ private readonly Guid CustomerPanelConfigID = new Guid("83F7BBCA-32FD-E811-CC82-000C29A7A20A");
+
+ public Version getVersion()
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ try
+ {
+ var classId = SPSDataEngineSchemaReader.ClassGetIDFromName("C4IT_CustomerPanel_SetupClassBase");
+ DataTable dtCustomerPanelConfig = FragmentRequestBase.SimpleLoad(classId, "version", "id='" + CustomerPanelConfigID.ToString("D") + "'");
+
+ if (dtCustomerPanelConfig == null || dtCustomerPanelConfig.Rows.Count == 0)
+ {
+ LogEntry("No configuration found for CustomerPanelConfigID: " + CustomerPanelConfigID, LogLevels.Warning);
+ return new Version(0, 0, 0, 0);
+ }
+
+ DataRow drCustomerPanelConfig = dtCustomerPanelConfig.Rows[0];
+ string versionString = drCustomerPanelConfig["Version"]?.ToString() ?? "0.0.0.0";
+
+ if (Version.TryParse(versionString, out Version parsedVersion))
+ {
+ return parsedVersion;
+ }
+
+ return new Version(0, 0, 0, 0);
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ return new Version(0, 0, 0, 0);
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+ }
+
+ internal CustomerPanelConfig GetConfig(bool noCache)
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ CustomerPanelConfig cPconfig = new CustomerPanelConfig();
+
+ try
+ {
+ // Cache-Handling wie bisher
+ var _cacheConfig = ApiCache.GetValue("config") as CustomerPanelConfig;
+ if (noCache && _cacheConfig != null)
+ {
+ ApiCache.Delete("config");
+ _cacheConfig = null;
+ }
+
+ if (_cacheConfig != null)
+ {
+ return _cacheConfig;
+ }
+
+ // Transaktion + Reader via neues Framework
+ using (var sPSTransactionScope = new SPSTransactionScope())
+ using (var dataReader = DirectDbCompat.ExecuteReader(
+ dbCmd =>
+ {
+ dbCmd.CommandText = configSQLStatement;
+ dbCmd.CommandType = CommandType.Text;
+ },
+ SPSTransaction.Current,
+ CommandBehavior.SingleRow))
+ {
+ if (dataReader.Read())
+ {
+ // Spalten-Indizes
+ int versionColumnIndex = dataReader.GetOrdinal("Version");
+ int timerIntervalTicketColumnIndex = dataReader.GetOrdinal("updateInterval");
+ int timerIntervalAdHocColumnIndex = dataReader.GetOrdinal("UpdateIntervalAdHocAnnouncements");
+ int timerIntervalRegularColumnIndex = dataReader.GetOrdinal("UpdateIntervallRegularAnnouncements");
+ int isStartApplicationMinimizedColumnIndex = dataReader.GetOrdinal("startMinimized");
+ int iconColorColumnIndex = dataReader.GetOrdinal("iconColor");
+ int mainIconTextColorColumnIndex = dataReader.GetOrdinal("mainIconTextColor");
+ int logoUrlColumnIndex = dataReader.GetOrdinal("logoUrl");
+ int remoteAppColumnIndex = dataReader.GetOrdinal("remoteApp");
+ int trayIconColumnIndex = dataReader.GetOrdinal("trayIcon");
+ int isDraggableColumnIndex = dataReader.GetOrdinal("isDraggable");
+ int disableClosingColumnIndex = dataReader.GetOrdinal("disableClosing");
+ int activeButtonColorColumnIndex = dataReader.GetOrdinal("activeButtonColor");
+ int inactiveButtonColorColumnIndex = dataReader.GetOrdinal("inactiveButtonColor");
+ int backgroundColorColumnIndex = dataReader.GetOrdinal("backgroundColor");
+ int headerColorColumnIndex = dataReader.GetOrdinal("headerColor");
+ int announcementEnabledColumnIndex = dataReader.GetOrdinal("announcementEnabled");
+ int incidentEnabledColumnIndex = dataReader.GetOrdinal("incidentEnabled");
+ int sspEnabledColumnIndex = dataReader.GetOrdinal("sspEnabled");
+ int informationEnabledColumnIndex = dataReader.GetOrdinal("informationEnabled");
+ int customLinksColumnIndex = dataReader.GetOrdinal("customLinks");
+ int encryptedApiTokenColumnIndex = dataReader.GetOrdinal("encryptedApiToken");
+ int reloginIntervalDaysColumnIndex = dataReader.GetOrdinal("reloginIntervalDays");
+
+ // Version sicher parsen
+ string versionString = dataReader.IsDBNull(versionColumnIndex) ? "0.0.0.0" : dataReader.GetString(versionColumnIndex);
+ Version serverVersion;
+ if (!Version.TryParse(versionString, out serverVersion))
+ {
+ serverVersion = new Version(0, 0, 0, 0);
+ }
+
+ // Grundobjekt befüllen
+ cPconfig = new CustomerPanelConfig
+ {
+ _ServerVersion = serverVersion,
+ _timerIntervalTicket = dataReader.IsDBNull(timerIntervalTicketColumnIndex) ? 10 : dataReader.GetInt32(timerIntervalTicketColumnIndex),
+ _timerIntervalAdHocAnnouncements = dataReader.IsDBNull(timerIntervalAdHocColumnIndex) ? 2 : dataReader.GetInt32(timerIntervalAdHocColumnIndex),
+ _timerIntervalRegularAnnouncements = dataReader.IsDBNull(timerIntervalRegularColumnIndex) ? 10 : dataReader.GetInt32(timerIntervalRegularColumnIndex),
+ _isStartApplicationMinimized = ConverterHelper.ObjectToBoolConverter(dataReader[isStartApplicationMinimizedColumnIndex]?.ToString()),
+ _iconColor = dataReader.GetInt32(iconColorColumnIndex),
+ _mainIconTextColor = dataReader.GetInt32(mainIconTextColorColumnIndex),
+ _logoUrl = dataReader.IsDBNull(logoUrlColumnIndex) ? string.Empty : dataReader.GetString(logoUrlColumnIndex),
+ _trayIconUrl = dataReader.IsDBNull(trayIconColumnIndex) ? string.Empty : dataReader.GetString(trayIconColumnIndex),
+ _isDraggable = ConverterHelper.ObjectToBoolConverter(dataReader[isDraggableColumnIndex]?.ToString()),
+ _disableClosing = ConverterHelper.ObjectToBoolConverter(dataReader[disableClosingColumnIndex]?.ToString()),
+ _encryptedApiToken = dataReader.IsDBNull(encryptedApiTokenColumnIndex) ? string.Empty : dataReader.GetString(encryptedApiTokenColumnIndex),
+ _reloginIntervalDays = dataReader.IsDBNull(reloginIntervalDaysColumnIndex) ? 14 : dataReader.GetInt32(reloginIntervalDaysColumnIndex),
+ _remoteAppPath = dataReader.IsDBNull(remoteAppColumnIndex) ? string.Empty : dataReader.GetString(remoteAppColumnIndex)
+ };
+
+ // UI-Farben
+ cPconfig._uiColors["activeButtonColor"] = dataReader.IsDBNull(activeButtonColorColumnIndex) ? string.Empty : dataReader.GetString(activeButtonColorColumnIndex);
+ cPconfig._uiColors["inactiveButtonColor"] = dataReader.IsDBNull(inactiveButtonColorColumnIndex) ? string.Empty : dataReader.GetString(inactiveButtonColorColumnIndex);
+ cPconfig._uiColors["backgroundColor"] = dataReader.IsDBNull(backgroundColorColumnIndex) ? string.Empty : dataReader.GetString(backgroundColorColumnIndex);
+ cPconfig._uiColors["headerColor"] = dataReader.IsDBNull(headerColorColumnIndex) ? string.Empty : dataReader.GetString(headerColorColumnIndex);
+
+ // Token neu kodieren
+ var encryptedToken = PrivateCustomerPanelSecurePassword.Instance.Decode(cPconfig._encryptedApiToken);
+ cPconfig._encryptedApiToken = CustomerPanelSecurePassword.Instance.Encode(encryptedToken);
+
+ // Custom Links
+ var linkList = dataReader.IsDBNull(customLinksColumnIndex) ? string.Empty : dataReader.GetString(customLinksColumnIndex);
+ if (!string.IsNullOrWhiteSpace(linkList))
+ {
+ foreach (string s in linkList.Split(';'))
+ {
+ var temp = s.Split(',');
+ if (temp.Length == 2)
+ {
+ cPconfig._linkList[temp[1].Trim()] = temp[0].Trim();
+ }
+ }
+ }
+
+ cPconfig.MainFunctionActivation = new Dictionary
+ {
+ [enumMainFunctions.Announcement] = ConverterHelper.ObjectToBoolConverter(dataReader[announcementEnabledColumnIndex]?.ToString()),
+ [enumMainFunctions.Incident] = ConverterHelper.ObjectToBoolConverter(dataReader[incidentEnabledColumnIndex]?.ToString()),
+ [enumMainFunctions.Ssp] = ConverterHelper.ObjectToBoolConverter(dataReader[sspEnabledColumnIndex]?.ToString()),
+ [enumMainFunctions.Information] = ConverterHelper.ObjectToBoolConverter(dataReader[informationEnabledColumnIndex]?.ToString()),
+ [enumMainFunctions.CustomLinks] = !string.IsNullOrEmpty(dataReader[customLinksColumnIndex]?.ToString())
+ };
+
+ sPSTransactionScope.Complete();
+ }
+ }
+ string suiteUuxUrl = GetSuiteUuxUrl();
+ cPconfig._createNewTicketDirectLink = GetCreateTicketDeeplink();
+ // LogoUrl korrigieren
+ if (!string.IsNullOrEmpty(cPconfig._logoUrl) && !Uri.IsWellFormedUriString(cPconfig._logoUrl, UriKind.Absolute))
+ {
+ if (!cPconfig._logoUrl.StartsWith("m42Services/", StringComparison.InvariantCultureIgnoreCase))
+ cPconfig._logoUrl = $"m42Services/{cPconfig._logoUrl}";
+
+ if (Uri.TryCreate(suiteUuxUrl, UriKind.Absolute, out Uri baseUri))
+ {
+ var logoUri = new Uri(baseUri, cPconfig._logoUrl);
+ cPconfig._logoUrl = logoUri.ToString();
+ }
+ else
+ {
+ LogException(new UriFormatException($"Ungültige SuiteUuxUrl: {suiteUuxUrl}"));
+ }
+ }
+
+ // TrayIconUrl korrigieren
+ if (!string.IsNullOrEmpty(cPconfig._trayIconUrl) && !Uri.IsWellFormedUriString(cPconfig._trayIconUrl, UriKind.Absolute))
+ {
+ if (!cPconfig._trayIconUrl.StartsWith("m42Services/", StringComparison.InvariantCultureIgnoreCase))
+ cPconfig._trayIconUrl = $"m42Services/{cPconfig._trayIconUrl}";
+
+ if (Uri.TryCreate(suiteUuxUrl, UriKind.Absolute, out Uri baseTrayUri))
+ {
+ var trayIconUri = new Uri(baseTrayUri, cPconfig._trayIconUrl);
+ cPconfig._trayIconUrl = trayIconUri.ToString();
+ }
+ else
+ {
+ LogException(new UriFormatException($"Ungültige SuiteUuxUrl: {suiteUuxUrl}"));
+ }
+
+
+ }
+
+ ApiCache.Add("config", cPconfig, DateTime.Now.AddMinutes(5));
+ return cPconfig;
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ return cPconfig;
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+ }
+ internal string GetCreateTicketDeeplink(string suiteUuxUrl = null)
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ string deeplink = string.Empty;
+
+ try
+ {
+ // suiteUuxUrl einmalig ermitteln (oder vom Aufrufer übernehmen)
+ if (string.IsNullOrWhiteSpace(suiteUuxUrl))
+ suiteUuxUrl = GetSuiteUuxUrl();
+
+ // Flag aus SPSGlobalConfigurationClassServiceDesk via SimpleLoad
+ bool ticketAndServiceRequestEnabled = false;
+
+ var sdClassId = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSGlobalConfigurationClassServiceDesk");
+ DataTable sdCfg = FragmentRequestBase.SimpleLoad(sdClassId, "TicketAndServiceRequestEnabled", "1=1");
+
+ if (sdCfg != null && sdCfg.Rows.Count > 0)
+ {
+ string colName = sdCfg.Columns.Contains("TicketAndServiceRequestEnabled")
+ ? "TicketAndServiceRequestEnabled"
+ : (sdCfg.Columns.Contains("ticketAndServiceRequestEnabled") ? "ticketAndServiceRequestEnabled" : null);
+
+ if (colName != null)
+ {
+ string val = sdCfg.Rows[0][colName]?.ToString();
+ ticketAndServiceRequestEnabled = ConverterHelper.ObjectToBoolConverter(val);
+ }
+ }
+
+ // DeepLink bauen
+ if (!string.IsNullOrEmpty(suiteUuxUrl) && Uri.TryCreate(suiteUuxUrl, UriKind.Absolute, out Uri uri))
+ {
+ string host = $"{uri.Scheme}://{uri.Host}";
+ string type = ticketAndServiceRequestEnabled ? "SPSActivityTypeTicket" : "SPSActivityTypeIncident";
+ const string appName = "SelfServicePortal";
+ const ViewType viewType = ViewType.New;
+
+ deeplink = DeepLinkBuilder.CreateDeepLink(host, appName, viewType, type);
+ }
+ else
+ {
+ LogException(new UriFormatException($"Ungültige SuiteUuxUrl: {suiteUuxUrl}"));
+ }
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+
+ return deeplink;
+ }
+
+ private string GetSuiteUuxUrl()
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ const string cacheKey = "SuiteUuxUrl";
+
+ if (ApiCache.GetValue(cacheKey) is string cached && !string.IsNullOrEmpty(cached))
+ {
+ LogMethodEnd(CM);
+ return cached;
+ }
+
+ string suiteUuxUrl = string.Empty;
+
+ try
+ {
+ var classId = SPSDataEngineSchemaReader.ClassGetIDFromName("SPSAlertingConfiguration");
+ DataTable result = FragmentRequestBase.SimpleLoad(classId, "suiteUuxUrl", "1=1");
+
+ if (result != null && result.Rows.Count > 0)
+ {
+ string colName = result.Columns.Contains("suiteUuxUrl")
+ ? "suiteUuxUrl"
+ : (result.Columns.Contains("SuiteUuxUrl") ? "SuiteUuxUrl" : null);
+
+ if (colName != null)
+ {
+ suiteUuxUrl = result.Rows[0][colName]?.ToString();
+ if (!string.IsNullOrWhiteSpace(suiteUuxUrl))
+ suiteUuxUrl = suiteUuxUrl.Trim().TrimEnd('/');
+ else
+ suiteUuxUrl = string.Empty;
+ }
+ }
+
+ ApiCache.Add(cacheKey, suiteUuxUrl, DateTime.Now.AddMinutes(60));
+ return suiteUuxUrl;
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ return string.Empty;
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+ }
+
+
+
+
+
+ internal bool ResetCache()
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ try
+ {
+ ApiCache.Delete("AdHocMessage");
+ ApiCache.Delete("config");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ return false;
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+ }
+
+ internal List GetTickets(Guid? userId = null)
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ var tickets = new List();
+
+ if (userId == null || !userId.HasValue)
+ {
+ LogEntry("UserId is null", LogLevels.Error);
+ return tickets;
+ }
+
+ try
+ {
+ var _globalSettings = CustomerPanelController.controller.GlobalSettings;
+
+ // Client-Sprache aus dem Header bestimmen (fallback: en)
+ string defaultClientLanguageTwoLetterCode = "en";
+ string acceptLanguageHeader = HttpContext.Current?.Request?.Headers["Accept-Language"];
+
+ if (!string.IsNullOrWhiteSpace(acceptLanguageHeader))
+ {
+ try
+ {
+ var cultureInfo = new CultureInfo(acceptLanguageHeader.Split(',')[0]);
+ defaultClientLanguageTwoLetterCode = cultureInfo.TwoLetterISOLanguageName;
+ }
+ catch
+ {
+ LogEntry("Identifying the client user culture based on 'Accept-Language' parameter '"
+ + acceptLanguageHeader + "' failed", LogLevels.Debug);
+ }
+ }
+
+ // Sprache aus Settings (fallback: en; falls beides nicht vorhanden, harte Defaults)
+ var languageInfo =
+ _globalSettings.GetLanguages().FirstOrDefault(lang => lang.TwoLetterCode == defaultClientLanguageTwoLetterCode)
+ ?? _globalSettings.GetLanguages().FirstOrDefault(lang => lang.TwoLetterCode == "en");
+
+ int lcidLong = languageInfo?.LCID ?? 1033; // en-US
+ int lcidShort = languageInfo?.ParentLCID ?? 9; // EN
+
+ using (var sPSTransactionScope = new SPSTransactionScope())
+ using (var dataReader = DirectDbCompat.ExecuteReader(
+ dbCmd =>
+ {
+ dbCmd.CommandText = ticketSQLStatement;
+ dbCmd.CommandType = CommandType.Text;
+
+ var pLong = dbCmd.CreateParameter();
+ pLong.ParameterName = "@LCID_long";
+ pLong.DbType = DbType.Int32;
+ pLong.Value = lcidLong;
+ dbCmd.Parameters.Add(pLong);
+
+ var pShort = dbCmd.CreateParameter();
+ pShort.ParameterName = "@LCID_short";
+ pShort.DbType = DbType.Int32;
+ pShort.Value = lcidShort;
+ dbCmd.Parameters.Add(pShort);
+
+ var pUser = dbCmd.CreateParameter();
+ pUser.ParameterName = "@user_id";
+ pUser.DbType = DbType.Guid;
+ pUser.Value = userId.Value;
+ dbCmd.Parameters.Add(pUser);
+ },
+ SPSTransaction.Current,
+ CommandBehavior.Default))
+ {
+ // Spalten-Indizes einmalig ermitteln
+ int subjectColumnIndex = dataReader.GetOrdinal("Subject");
+ int expressionObjectIdColumnIndex = dataReader.GetOrdinal("eoid");
+ int createdDateColumnIndex = dataReader.GetOrdinal("CreatedDate");
+ int stateColumnIndex = dataReader.GetOrdinal("state");
+ int newInformationReceivedColumnIdx = dataReader.GetOrdinal("NewInformationReceived");
+ int sysEntityColumnIndex = dataReader.GetOrdinal("sysentity");
+ int ticketTypeColumnIndex = dataReader.GetOrdinal("TicketType");
+ int ticketNumberColumnIndex = dataReader.GetOrdinal("TicketNumber");
+ int lastJournalActionColumnIndex = dataReader.GetOrdinal("journalAction");
+ int stateValueColumnIndex = dataReader.GetOrdinal("stateValue");
+ int lastJournalEntryDateColumnIndex = dataReader.GetOrdinal("maxJournalDate");
+
+ while (dataReader.Read())
+ {
+ var ticket = new Ticket
+ {
+ _subject = dataReader.IsDBNull(subjectColumnIndex) ? string.Empty : dataReader.GetString(subjectColumnIndex),
+ _objectID = dataReader.GetGuid(expressionObjectIdColumnIndex),
+ _createdDate = dataReader.GetDateTime(createdDateColumnIndex),
+ _state = dataReader.IsDBNull(stateColumnIndex) ? string.Empty : dataReader.GetString(stateColumnIndex),
+ _newInformation = ConverterHelper.ObjectToBoolConverter(dataReader[newInformationReceivedColumnIdx]?.ToString()),
+ _sysEntity = dataReader.IsDBNull(sysEntityColumnIndex) ? string.Empty : dataReader.GetString(sysEntityColumnIndex),
+ _ticketType = dataReader.IsDBNull(ticketTypeColumnIndex) ? string.Empty : dataReader.GetString(ticketTypeColumnIndex),
+ _ticketNumber = dataReader.IsDBNull(ticketNumberColumnIndex) ? string.Empty : dataReader.GetString(ticketNumberColumnIndex),
+ _lastJournalEntryAction = dataReader.IsDBNull(lastJournalActionColumnIndex) ? -1 : dataReader.GetInt32(lastJournalActionColumnIndex),
+ _stateValue = dataReader.GetInt32(stateValueColumnIndex),
+ _lastJournalEntryDate = dataReader.IsDBNull(lastJournalEntryDateColumnIndex) ? DateTime.MinValue : dataReader.GetDateTime(lastJournalEntryDateColumnIndex)
+ };
+
+ tickets.Add(ticket);
+ }
+
+ sPSTransactionScope.Complete();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+
+ return tickets;
+ }
+
+ internal bool GetCloseAllClients()
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ try
+ {
+ var classId = SPSDataEngineSchemaReader.ClassGetIDFromName("C4IT_CustomerPanel_SetupClassBase");
+ DataTable result = FragmentRequestBase.SimpleLoad(classId, "closeAllClients", $"id='{CustomerPanelConfigID:D}'");
+
+ if (result == null || result.Rows.Count == 0)
+ {
+ return false;
+ }
+
+ string closeAllClients = result.Rows[0]["closeAllClients"]?.ToString();
+ return ConverterHelper.ObjectToBoolConverter(closeAllClients);
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ return false;
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+ }
+
+ internal List GetAdHocAnnouncements(bool noCache)
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ try
+ {
+ var _globalSettings = CustomerPanelController.controller.GlobalSettings;
+
+ // Client-Sprache ermitteln (Fallback: en)
+ string defaultClientLanguageTwoLetterCode = "en";
+ string acceptLanguageHeader = HttpContext.Current?.Request?.Headers["Accept-Language"];
+
+ if (!string.IsNullOrWhiteSpace(acceptLanguageHeader))
+ {
+ try
+ {
+ var cultureInfo = new CultureInfo(acceptLanguageHeader.Split(',')[0]);
+ defaultClientLanguageTwoLetterCode = cultureInfo.TwoLetterISOLanguageName;
+ }
+ catch
+ {
+ LogEntry("Identifying the client user culture based on 'Accept-Language' parameter '"
+ + acceptLanguageHeader + "' failed", LogLevels.Debug);
+ }
+ }
+
+ // Sprache aus Settings (Fallbacks, falls nicht vorhanden)
+ var languageInfo =
+ _globalSettings.GetLanguages().FirstOrDefault(lang => lang.TwoLetterCode == defaultClientLanguageTwoLetterCode)
+ ?? _globalSettings.GetLanguages().FirstOrDefault(lang => lang.TwoLetterCode == "en");
+
+ int lcidLong = languageInfo?.LCID ?? 1033; // en-US
+ int lcidShort = languageInfo?.ParentLCID ?? 9; // EN
+ string langCode = languageInfo?.TwoLetterCode ?? "en";
+
+ // Cache-Key pro Sprache
+ string cacheKey = "AdHocMessages_" + langCode;
+ var _cachedAnnouncements = ApiCache.GetValue(cacheKey) as List;
+
+ if (noCache && _cachedAnnouncements != null)
+ {
+ ApiCache.Delete(cacheKey);
+ _cachedAnnouncements = null;
+ }
+
+ if (_cachedAnnouncements == null)
+ {
+ // Cache-Dauer aus Konfiguration (Fallback 2 Minuten wie in GetConfig-Defaults)
+ int timerIntervalAdHoc = 2;
+ try { timerIntervalAdHoc = GetConfig(noCache)._timerIntervalAdHocAnnouncements; } catch { /* defensiv */ }
+
+ using (var sPSTransactionScope = new SPSTransactionScope())
+ using (var dataReader = DirectDbCompat.ExecuteReader(
+ dbCmd =>
+ {
+ dbCmd.CommandText = adHocAnnouncementsSQLStatement;
+ dbCmd.CommandType = CommandType.Text;
+
+ var pLong = dbCmd.CreateParameter();
+ pLong.ParameterName = "@LCID_long";
+ pLong.DbType = DbType.Int32;
+ pLong.Value = lcidLong;
+ dbCmd.Parameters.Add(pLong);
+
+ var pShort = dbCmd.CreateParameter();
+ pShort.ParameterName = "@LCID_short";
+ pShort.DbType = DbType.Int32;
+ pShort.Value = lcidShort;
+ dbCmd.Parameters.Add(pShort);
+ },
+ SPSTransaction.Current,
+ CommandBehavior.SingleResult))
+ {
+ // Spalten-Indizes einmalig ermitteln
+ int announcementSubjectColumnIndex = dataReader.GetOrdinal("AnnouncementSubject");
+ int eoidColumnIndex = dataReader.GetOrdinal("eoid");
+ int announcemntCreatedDateColumnIndex = dataReader.GetOrdinal("AnnouncementCreatedDate");
+ int announcementVisibleFromColumnIndex = dataReader.GetOrdinal("AnnouncementVisibleFrom");
+ int messageColumnIndex = dataReader.GetOrdinal("Message");
+ int prioColorColumnIndex = dataReader.GetOrdinal("prioColor");
+ int priorityColumnIndex = dataReader.GetOrdinal("priority");
+
+ _cachedAnnouncements = new List();
+
+ while (dataReader.Read())
+ {
+ var announcement = new Announcement
+ {
+ _subject = dataReader.IsDBNull(announcementSubjectColumnIndex) ? string.Empty : dataReader.GetString(announcementSubjectColumnIndex),
+ _objectID = dataReader.GetGuid(eoidColumnIndex),
+ _createdDate = dataReader.IsDBNull(announcemntCreatedDateColumnIndex) ? DateTime.MinValue : dataReader.GetDateTime(announcemntCreatedDateColumnIndex),
+ _visibleFrom = dataReader.IsDBNull(announcementVisibleFromColumnIndex) ? (DateTime?)null : dataReader.GetDateTime(announcementVisibleFromColumnIndex),
+ _message = dataReader.IsDBNull(messageColumnIndex) ? string.Empty : dataReader.GetString(messageColumnIndex),
+ _prioColor = dataReader.IsDBNull(prioColorColumnIndex) ? string.Empty : dataReader.GetString(prioColorColumnIndex),
+ _priority = dataReader.IsDBNull(priorityColumnIndex) ? 0 : dataReader.GetInt32(priorityColumnIndex),
+ _isAdhoc = true
+ };
+
+ // Tags entfernen und HTML decodieren
+ announcement._message = ConverterHelper.Html2Plaintext(announcement._message);
+
+ _cachedAnnouncements.Add(announcement);
+ }
+
+ // Cache setzen nach erfolgreichem Read
+ ApiCache.Add(cacheKey, _cachedAnnouncements, DateTime.Now.AddMinutes(timerIntervalAdHoc));
+
+ sPSTransactionScope.Complete();
+ }
+ }
+
+ return _cachedAnnouncements;
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ return null;
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+ }
+
+ internal List GetRegularAnnouncements(Guid? userId = null)
+ {
+ var CM = MethodBase.GetCurrentMethod();
+ LogMethodBegin(CM);
+
+ var announcements = new List();
+
+ if (userId == null || !userId.HasValue)
+ {
+ LogEntry("UserId is null", LogLevels.Error);
+ return announcements;
+ }
+
+ try
+ {
+ var _globalSettings = CustomerPanelController.controller.GlobalSettings;
+
+ // Client-Sprache bestimmen (Fallback: en)
+ string defaultClientLanguageTwoLetterCode = "en";
+ string acceptLanguageHeader = HttpContext.Current?.Request?.Headers["Accept-Language"];
+
+ if (!string.IsNullOrWhiteSpace(acceptLanguageHeader))
+ {
+ try
+ {
+ var cultureInfo = new CultureInfo(acceptLanguageHeader.Split(',')[0]);
+ defaultClientLanguageTwoLetterCode = cultureInfo.TwoLetterISOLanguageName;
+ }
+ catch
+ {
+ LogEntry("Identifying the client user culture based on 'Accept-Language' parameter '"
+ + acceptLanguageHeader + "' failed", LogLevels.Debug);
+ }
+ }
+
+ // Sprache aus Settings (Fallbacks, falls keine passende Sprache konfiguriert ist)
+ var languageInfo =
+ _globalSettings.GetLanguages().FirstOrDefault(lang => lang.TwoLetterCode == defaultClientLanguageTwoLetterCode)
+ ?? _globalSettings.GetLanguages().FirstOrDefault(lang => lang.TwoLetterCode == "en");
+
+ int lcidLong = languageInfo?.LCID ?? 1033; // en-US
+ int lcidShort = languageInfo?.ParentLCID ?? 9; // EN
+ int recursiveOU = 0;
+
+ using (var sPSTransactionScope = new SPSTransactionScope())
+ using (var dataReader = DirectDbCompat.ExecuteReader(
+ dbCmd =>
+ {
+ dbCmd.CommandText = regularAnnouncementsSQLStatement;
+ dbCmd.CommandType = CommandType.Text;
+
+ // @LCID_long
+ var pLong = dbCmd.CreateParameter();
+ pLong.ParameterName = "@LCID_long";
+ pLong.DbType = DbType.Int32;
+ pLong.Value = lcidLong;
+ dbCmd.Parameters.Add(pLong);
+
+ // @LCID_short
+ var pShort = dbCmd.CreateParameter();
+ pShort.ParameterName = "@LCID_short";
+ pShort.DbType = DbType.Int32;
+ pShort.Value = lcidShort;
+ dbCmd.Parameters.Add(pShort);
+
+ // @CurrentUserId
+ var pUser = dbCmd.CreateParameter();
+ pUser.ParameterName = "@CurrentUserId";
+ pUser.DbType = DbType.Guid;
+ pUser.Value = userId.Value;
+ dbCmd.Parameters.Add(pUser);
+
+ // @RecursiveOU
+ var pRec = dbCmd.CreateParameter();
+ pRec.ParameterName = "@RecursiveOU";
+ pRec.DbType = DbType.Int32;
+ pRec.Value = recursiveOU;
+ dbCmd.Parameters.Add(pRec);
+ },
+ SPSTransaction.Current,
+ CommandBehavior.SingleResult))
+ {
+ // Spalten-Indizes nur einmal bestimmen
+ int announcementSubjectColumnIndex = dataReader.GetOrdinal("AnnouncementSubject");
+ int eoidColumnIndex = dataReader.GetOrdinal("eoid");
+ int announcemntCreatedDateColumnIndex = dataReader.GetOrdinal("AnnouncementCreatedDate");
+ int messageColumnIndex = dataReader.GetOrdinal("Message");
+ int prioColorColumnIndex = dataReader.GetOrdinal("prioColor");
+ int priorityColumnIndex = dataReader.GetOrdinal("priority");
+ int announcementVisibleFromColumnIndex = dataReader.GetOrdinal("AnnouncementVisibleFrom");
+
+ while (dataReader.Read())
+ {
+ var announcement = new Announcement
+ {
+ _subject = dataReader.IsDBNull(announcementSubjectColumnIndex) ? string.Empty : dataReader.GetString(announcementSubjectColumnIndex),
+ _objectID = dataReader.GetGuid(eoidColumnIndex),
+ _createdDate = dataReader.IsDBNull(announcemntCreatedDateColumnIndex) ? DateTime.MinValue : dataReader.GetDateTime(announcemntCreatedDateColumnIndex),
+ _visibleFrom = dataReader.IsDBNull(announcementVisibleFromColumnIndex) ? (DateTime?)null : dataReader.GetDateTime(announcementVisibleFromColumnIndex),
+ _message = dataReader.IsDBNull(messageColumnIndex) ? string.Empty : dataReader.GetString(messageColumnIndex),
+ _prioColor = dataReader.IsDBNull(prioColorColumnIndex) ? string.Empty : dataReader.GetString(prioColorColumnIndex),
+ _priority = dataReader.IsDBNull(priorityColumnIndex) ? 0 : dataReader.GetInt32(priorityColumnIndex)
+ };
+
+ // Tags entfernen & HTML decodieren
+ announcement._message = ConverterHelper.Html2Plaintext(announcement._message);
+
+ announcements.Add(announcement);
+ }
+
+ sPSTransactionScope.Complete();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogException(ex);
+ }
+ finally
+ {
+ LogMethodEnd(CM);
+ }
+
+ return announcements;
+ }
+
+ internal string Encode(string str)
+ {
+ return PrivateCustomerPanelSecurePassword.Instance.Encode(str);
+ }
+ }
+}
\ No newline at end of file
diff --git a/C4IT.API/CustomerPanelHub.cs b/C4IT.API/CustomerPanelHub.cs
new file mode 100644
index 0000000..c46ffcf
--- /dev/null
+++ b/C4IT.API/CustomerPanelHub.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Threading.Tasks;
+using System.Collections.Concurrent;
+
+namespace C4IT.API
+{
+ /*
+ public delegate void ClientConnectionEventHandler(string clientId);
+ public delegate void ClientNameChangedEventHandler(string clientId, string newName);
+ public delegate void ClientGroupEventHandler(string clientId, string groupName);
+
+ public delegate void MessageReceivedEventHandler(string senderClientId, string message);
+ class CustomerPanelHub : Hub
+ {
+ static ConcurrentDictionary _users = new ConcurrentDictionary();
+
+ public static event ClientConnectionEventHandler ClientConnected;
+ public static event ClientConnectionEventHandler ClientDisconnected;
+ public static event ClientNameChangedEventHandler ClientNameChanged;
+
+ public static event ClientGroupEventHandler ClientJoinedToGroup;
+ public static event ClientGroupEventHandler ClientLeftGroup;
+
+ public static event MessageReceivedEventHandler MessageReceived;
+
+ public static void ClearState()
+ {
+ _users.Clear();
+ }
+
+ //Called when a client is connected
+ public override Task OnConnected()
+ {
+ _users.TryAdd(Context.ConnectionId, Context.ConnectionId);
+
+ ClientConnected?.Invoke(Context.ConnectionId);
+
+ return base.OnConnected();
+ }
+
+ //Called when a client is disconnected
+ public override Task OnDisconnected(bool stopCalled)
+ {
+ string userName;
+ _users.TryRemove(Context.ConnectionId, out userName);
+
+ ClientDisconnected?.Invoke(Context.ConnectionId);
+
+ return base.OnDisconnected(stopCalled);
+ }
+ }
+ */
+ class CustomerPanelHub
+ {
+
+ }
+}
diff --git a/C4IT.API/CustomerPanelSecurePassword.cs b/C4IT.API/CustomerPanelSecurePassword.cs
new file mode 100644
index 0000000..3730003
--- /dev/null
+++ b/C4IT.API/CustomerPanelSecurePassword.cs
@@ -0,0 +1,22 @@
+using C4IT.Security;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace C4IT.API
+{
+ static public class CustomerPanelSecurePassword
+ {
+ public readonly static string FFH = "RL5?IX%E1fE^37cUWDY~u+|NA";
+ public readonly static string FFK = "PyTLMoZo0Ece/tbEA+nmiUOcn14cjg1KPG9185EoHD5EVWUWPm2iUDwXX+8Vne4saDsihtj7CeKi0aeOFqKEL05lmrmEkVFrYOQ1yhVaLdNzz+yw2KGZ9cF9nS0g+le5PqKg67vfiueoQWwvUQYkbLjjARPm3eoaUcjqFTZqVZ7vcW4C6eushQZ4NrOAzd5WcVENU55l6ORnstRW+SoT+SV2t4MO3ObXWlOK495Gerf44juHDeVGfPL+MjnfcIyUvEVe0AcC+Uddvn7atlbcjBF2m317wVqsogKoAPDpaGHhWAW6D6j8ezP2DSRvDmYXm7EuRXqbb6XatwO1TC/JWnPBk2sHR3/suiURx0ui34oD5e+/QbqapG++caQhba4jf+ailaBrKCdauMPP+BJ6d1Zulu+6NXTWknpbQo9qgPQfWk6B9WQJfzyzUxQ8uey4TaRqS6TDgdqObSddXnzk5pjBhvwNSWOtfvaoX4m4h2iHCTWDp+O3g94MR/StOjIK/eX/D0bIbVxXQD3jgjqyiFoPYbZM6QrNT+CaXD3eEpiBKRojxe1jd5fUOj2gq64HYAvD1DQhvP/6MgR87BOeUGWwrXm8PIwyc4wMoj8RnEEnkH8+yjx9SvHfv7Gb2tW4JDKXOaCZF7+NOrDe3L9ECJh3XyyE6a6kI7eW+XEbjFh/8ugPgqbP2rGAFlKES5AzaPN3ePl2Dv/9s00gYyuGhnM02iWTkj7wOzOTpTaIrhS4uZdBxqlIxnbw/UCDkajitaqq8zeGXWNTAAEc8G4FELXruw0QbwIIuszOS770gAGsmSbpLeOtDyYNFWnnuYlZfhuCyofrk02mnB2e+nZ6p4O9G+MvZkgimeST23MnhLdLNFEIASlwFxDqD9X0GfKN0zD4S2LrwT0yp8mKfgJPyKxG9f3Dwr7yuWIIodplWcIQa/NYVEn/0lga43+F6fSv73uhAKBKqlaivMnZXRqzrX9YsRxgzMOuVJm9WXs0iWhV/Q183vrUVyQnx4H1cZq7p+RriIwEn6Y/01RgE5TzpLILDGdT56if4BGDVoKa4RcAFq4kamdAj8VnLtNTZSt+QA2IOrISFcoiFpn6Z5ZPpjp3ktP5EcqFi/jPNiLn1aQi4c4xhgsggd+S+bpEr2Q31p6WKDv40w2wXmClNz2Ck8BcYtN7z040WGqGtfu4gDPiYpQedytPwzc65ipxKJQ8/sv4At0kVd5U+u/Gsc2lYcDU5va/t338ioKOj1nfB9YmIUuxjvd5hzCmWjHizf/rjMV7SF7PbCsq3RZtPa2Hy4BcXkV5YaoZACN3wgOHadkiz1T3rAGC8KhoiJ94dlQvkwwtoNDblMrpnKoHedBDgsl1Gg09fAzlyXQ6ofNonzOsMERdSwNDyDuOQCoDK809bhxsyGeGvLsWDS7srI7ODedjevx6Obdvpgb95YXKPBIzSXgZRMKMNwTJNMUA4qWrNPZOiW0tkYRhZujN50oMHX2ZJfRKllknJk5SgiijKxlSPd4Abk62n9eGG42DXC1dAEpNoDkiP6lWbtaxVOVL6Kr+LsgD0G6N0Y21Q8YpEH1MTW+462AHaZkcnsVAEGWH";
+ public static cSecurePassword Instance { get; private set; } = null;
+
+ static public void Init()
+ {
+ Instance = new cSecurePassword(FFH);
+ Instance.SetRsaKey(FFK);
+ }
+ }
+}
diff --git a/C4IT.API/DeepLinkBuilder.cs b/C4IT.API/DeepLinkBuilder.cs
new file mode 100644
index 0000000..65adb9d
--- /dev/null
+++ b/C4IT.API/DeepLinkBuilder.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Web;
+using Newtonsoft.Json;
+
+namespace C4IT.API
+{
+ public enum ViewType
+ {
+ New, // open create dialog
+ Preview, // open preview
+ Action, // run action/wizard
+ Edit // open edit dialog
+ }
+
+ public class DeepLinkBuilder
+ {
+ public string Host { get; set; }
+ public string AppName { get; set; }
+
+ // Parameter fr view-options
+ public bool? Embedded { get; set; }
+ public Guid? DialogId { get; set; }
+ public Guid? ObjectId { get; set; }
+ public int? Archived { get; set; }
+ public string Type { get; set; }
+ public ViewType? ViewType { get; set; }
+ public Guid? ActionId { get; set; }
+ public Guid? ViewId { get; set; }
+
+ public object PresetParams { get; set; }
+
+ ///
+ /// Baut die URL basierend auf den gesetzten Eigenschaften.
+ ///
+ /// Die erstellte DeepLink-URL als string.
+ public string BuildUrl()
+ {
+ var viewOptions = new Dictionary();
+
+ if (Embedded.HasValue)
+ viewOptions["embedded"] = Embedded.Value;
+ if (DialogId.HasValue)
+ viewOptions["dialogId"] = DialogId.Value;
+ if (ObjectId.HasValue)
+ viewOptions["objectId"] = ObjectId.Value;
+ if (Archived.HasValue)
+ viewOptions["archived"] = Archived.Value;
+ if (!string.IsNullOrEmpty(Type))
+ viewOptions["type"] = Type;
+ if (ViewType.HasValue)
+ viewOptions["viewType"] = ViewType.Value.ToString().ToLower();
+ if (ActionId.HasValue)
+ viewOptions["actionId"] = ActionId.Value;
+ if (ViewId.HasValue)
+ viewOptions["viewId"] = ViewId.Value;
+
+ string viewOptionsJson = JsonConvert.SerializeObject(viewOptions);
+ string encodedViewOptions = HttpUtility.UrlEncode(viewOptionsJson);
+ string url = $"{Host.TrimEnd('/')}/wm/app-{AppName}/?view-options={encodedViewOptions}";
+
+ if (PresetParams != null)
+ {
+ string presetParamsJson = JsonConvert.SerializeObject(PresetParams);
+ string encodedPresetParams = HttpUtility.UrlEncode(presetParamsJson);
+ url += $"&presetParams={encodedPresetParams}";
+ }
+
+ return url;
+ }
+
+ ///
+ /// Statische Methode zur Erstellung eines DeepLinks basierend auf den angegebenen Parametern.
+ ///
+ /// Der Hostname der Anwendung.
+ /// Der Name der Anwendung.
+ /// Der Typ der Ansicht.
+ /// Der Type des Objekts.
+ /// Die erstellte DeepLink-URL als string.
+ public static string CreateDeepLink(string host, string appName, ViewType viewType, string type)
+ {
+ var builder = new DeepLinkBuilder
+ {
+ Host = host,
+ AppName = appName,
+ ViewType = viewType,
+ Type = type,
+ };
+
+ return builder.BuildUrl();
+ }
+ }
+}
diff --git a/C4IT.API/PostBuildCopy.bat b/C4IT.API/PostBuildCopy.bat
new file mode 100644
index 0000000..232faa6
--- /dev/null
+++ b/C4IT.API/PostBuildCopy.bat
@@ -0,0 +1,51 @@
+@echo off
+SETLOCAL
+
+REM Überprüfe, ob die Konfiguration als Parameter übergeben wurde
+IF "%1"=="" (
+ echo ERROR: Keine Konfiguration übergeben. Verwenden Sie z. B. "Debug" oder "Release".
+ EXIT 1
+)
+
+REM Setze die Quelldateien und das Zielverzeichnis
+SET SourceDir=%~dp0bin\%1
+SET TargetDir=\\srvwsm001.imagoverum.com\c$\Program Files (x86)\Matrix42\Matrix42 Workplace Management\svc\bin
+
+REM Debugging-Ausgabe
+echo Starting Post-Build Copy Script...
+echo Source Directory: %SourceDir%
+echo Target Directory: %TargetDir%
+
+REM Prüfe, ob die Quelldateien existieren
+IF NOT EXIST "%SourceDir%\C4IT.API.Contracts.dll" (
+ echo ERROR: C4IT.API.Contracts.dll nicht gefunden.
+ EXIT 1
+)
+
+IF NOT EXIST "%SourceDir%\C4IT.API.CustomerPanel.dll" (
+ echo ERROR: C4IT.API.CustomerPanel.dll nicht gefunden.
+ EXIT 1
+)
+
+REM Kopiere die Dateien
+echo Kopiere C4IT.API.Contracts.dll...
+xcopy "%SourceDir%\C4IT.API.Contracts.dll" "%SourceDir%\..\..\..\..\Matrix42\C4IT_CustomerPanel\Matrix42Libs" /Y
+IF ERRORLEVEL 1 (
+ echo ERROR: Kopiervorgang von C4IT.API.Contracts.dll fehlgeschlagen.
+ EXIT 1
+)
+xcopy "%SourceDir%\C4IT.API.Contracts.dll" "%TargetDir%" /Y
+IF ERRORLEVEL 1 (
+ echo ERROR: Kopiervorgang von C4IT.API.Contracts.dll fehlgeschlagen.
+ EXIT 1
+)
+
+echo Kopiere C4IT.API.CustomerPanel.dll...
+xcopy "%SourceDir%\C4IT.API.CustomerPanel.dll" "%TargetDir%" /Y
+IF ERRORLEVEL 1 (
+ echo ERROR: Kopiervorgang von C4IT.API.CustomerPanel.dll fehlgeschlagen.
+ EXIT 1
+)
+
+echo Dateien erfolgreich kopiert.
+ENDLOCAL
diff --git a/C4IT.API/PrivateCustomerPanelSecurePassword.cs b/C4IT.API/PrivateCustomerPanelSecurePassword.cs
new file mode 100644
index 0000000..b38c52a
--- /dev/null
+++ b/C4IT.API/PrivateCustomerPanelSecurePassword.cs
@@ -0,0 +1,22 @@
+using C4IT.Security;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace C4IT.API
+{
+ static public class PrivateCustomerPanelSecurePassword
+ {
+ public readonly static string FFH = "RL5?IX%E1fE^37cUWDY~u+|NA";
+ public readonly static string FFK = "PyTLMoZo0Ece/tbEA+nmiUOcn14cjg1KPG9185EoHD5EVWUWPm2iUDwXX+8Vne4saDsihtj7CeKi0aeOFqKEL05lmrmEkVFrYOQ1yhVaLdNzz+yw2KGZ9cF9nS0g+le5PqKg67vfiueoQWwvUQYkbLjjARPm3eoaUcjqFTZqVZ7vcW4C6eushQZ4NrOAzd5WcVENU55l6ORnstRW+SoT+SV2t4MO3ObXWlOK495Gerf44juHDeVGfPL+MjnfcIyUvEVe0AcC+Uddvn7atlbcjBF2m317wVqsogKoAPDpaGHhWAW6D6j8ezP2DSRvDmYXm7EuRXqbb6XatwO1TC/JWnPBk2sHR3/suiURx0ui34oD5e+/QbqapG++caQhba4jf+ailaBrKCdauMPP+BJ6d1Zulu+6NXTWknpbQo9qgPQfWk6B9WQJfzyzUxQ8uey4TaRqS6TDgdqObSddXnzk5pjBhvwNSWOtfvaoX4m4h2iHCTWDp+O3g94MR/StOjIK/eX/D0bIbVxXQD3jgjqyiFoPYbZM6QrNT+CaXD3eEpiBKRojxe1jd5fUOj2gq64HYAvD1DQhvP/6MgR87BOeUGWwrXm8PIwyc4wMoj8RnEEnkH8+yjx9SvHfv7Gb2tW4JDKXOaCZF7+NOrDe3L9ECJh3XyyE6a6kI7eW+XEbjFh/8ugPgqbP2rGAFlKES5AzaPN3ePl2Dv/9s00gYyuGhnM02iWTkj7wOzOTpTaIrhS4uZdBxqlIxnbw/UCDkajitaqq8zeGXWNTAAEc8G4FELXruw0QbwIIuszOS770gAGsmSbpLeOtDyYNFWnnuYlZfhuCyofrk02mnB2e+nZ6p4O9G+MvZkgimeST23MnhLdLNFEIASlwFxDqD9X0GfKN0zD4S2LrwT0yp8mKfgJPyKxG9f3Dwr7yuWIIodplWcIQa/NYVEn/0lga43+F6fSv73uhAKBKqlaivMnZXRqzrX9YsRxgzMOuVJm9WXs0iWhV/Q183vrUVyQnx4H1cZq7p+RriIwEn6Y/01RgE5TzpLILDGdT56if4BGDVoKa4RcAFq4kamdAj8VnLtNTZSt+QA2IOrISFcoiFpn6Z5ZPpjp3ktP5EcqFi/jPNiLn1aQi4c4xhgsggd+S+bpEr2Q31p6WKDv40w2wXmClNz2Ck8BcYtN7z040WGqGtfu4gDPiYpQedytPwzc65ipxKJQ8/sv4At0kVd5U+u/Gsc2lYcDU5va/t338ioKOj1nfB9YmIUuxjvd5hzCmWjHizf/rjMV7SF7PbCsq3RZtPa2Hy4BcXkV5YaoZACN3wgOHadkiz1T3rAGC8KhoiJ94dlQvkwwtoNDblMrpnKoHedBDgsl1Gg09fAzlyXQ6ofNonzOsMERdSwNDyDuOQCoDK809bhxsyGeGvLsWDS7srI7ODedjevx6Obdvpgb95YXKPBIzSXgZRMKMNwTJNMUA4qWrNPZOiW0tkYRhZujN50oMHX2ZJfRKllknJk5SgiijKxlSPd4Abk62n9eGG42DXC1dAEpNoDkiP6lWbtaxVOVL6Kr+LsgD0G6N0Y21Q8YpEH1MTW+462AHaZkcnsVAEGWH";
+ public static cSecurePassword Instance { get; private set; } = null;
+
+ static public void Init()
+ {
+ Instance = new cSecurePassword(FFH);
+ Instance.SetRsaKey(FFK, "C4itF4sdCustomerPanelWebApi");
+ }
+ }
+}
diff --git a/C4IT.API/Properties/AssemblyInfo.cs b/C4IT.API/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..2d7b30f
--- /dev/null
+++ b/C4IT.API/Properties/AssemblyInfo.cs
@@ -0,0 +1,8 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("C4IT.API")]
+[assembly: ComVisible(false)]
+[assembly: Guid("c965495d-d326-4521-9b6a-668227ed0651")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/C4IT.API/SignFiles.cmd b/C4IT.API/SignFiles.cmd
new file mode 100644
index 0000000..6e5a1da
--- /dev/null
+++ b/C4IT.API/SignFiles.cmd
@@ -0,0 +1,11 @@
+set ProductName="C4IT Customer Panel API"
+
+set SignTool=..\..\Common Code\Tools\signtool.exe
+set TimeStamp=http://rfc3161timestamp.globalsign.com/advanced
+
+set Src=.\bin\Release
+"%SignTool%" sign /a /tr %TimeStamp% /td SHA256 /fd SHA256 /d %ProductName% "%Src%\C4IT.API.Contracts.dll" "%Src%\C4IT.API.CustomerPanel.dll" "%Src%\C4IT.API.Contracts.dll"
+
+pause
+
+
diff --git a/C4IT.API/app.config b/C4IT.API/app.config
new file mode 100644
index 0000000..31a1d44
--- /dev/null
+++ b/C4IT.API/app.config
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/C4IT.API/packages.config b/C4IT.API/packages.config
new file mode 100644
index 0000000..42588ec
--- /dev/null
+++ b/C4IT.API/packages.config
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CommonAssembyInfo.cs b/CommonAssembyInfo.cs
new file mode 100644
index 0000000..a7e3614
--- /dev/null
+++ b/CommonAssembyInfo.cs
@@ -0,0 +1,15 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyProduct("C4IT Customer Panel API")]
+
+[assembly: AssemblyCompany("Consulting4IT GmbH, Germany")]
+[assembly: AssemblyCopyright("Copyright © 2025, Consulting4IT GmbH, Germany")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: AssemblyInformationalVersion("1.2.*.*")]
+[assembly: AssemblyVersion("1.2.0.0")]
\ No newline at end of file
diff --git a/SB_DLLs/Matrix42.Common.dll b/SB_DLLs/Matrix42.Common.dll
new file mode 100644
index 0000000000000000000000000000000000000000..32a28f6163561f16f38a6e89995ae56c277ecf6f
GIT binary patch
literal 124416
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