inital
This commit is contained in:
@@ -0,0 +1,563 @@
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.FASD.Cockpit.Communication;
|
||||
using C4IT.Logging;
|
||||
using C4IT.MultiLanguage;
|
||||
using FasdDesktopUi.Basics;
|
||||
using FasdDesktopUi.Basics.CustomEvents;
|
||||
using FasdDesktopUi.Basics.Enums;
|
||||
using FasdDesktopUi.Basics.Helper;
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
using FasdDesktopUi.Basics.UiActions;
|
||||
using FasdDesktopUi.Basics.UserControls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using FasdDesktopUi.Basics.Services.SupportCaseSearchService;
|
||||
using FasdDesktopUi.Basics.Services.RelationService;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
using static FasdDesktopUi.Basics.UserControls.SearchBar;
|
||||
|
||||
|
||||
namespace FasdDesktopUi.Pages.AdvancedSearchPage
|
||||
{
|
||||
public partial class AdvancedSearchPage : Window, ISearchUiProvider, INotifyPropertyChanged
|
||||
{
|
||||
public SupportCaseSearchService SearchService { get; } = new SupportCaseSearchService(new RelationService());
|
||||
// New Advanced-Search-Page IN PROGRESS
|
||||
// Next steps:
|
||||
// 1. Open F4SD Cockpit when user is clicked
|
||||
// 2. Display filter name next to filter icon when filter is selected
|
||||
// 3. When F4SD Tray-Icon is clicked, open the new advanced search page
|
||||
|
||||
|
||||
SearchBar.ChangedSearchValueDelegate ChangedSearchValue { get; set; }
|
||||
private cF4sdApiSearchResultRelation preSelectedRelation = null;
|
||||
|
||||
private static readonly object _currentSearchTaskLock = new object();
|
||||
private static Guid? CurrentSearchTaskId = null;
|
||||
private static CancellationTokenSource CurrentSearchTaskToken = null;
|
||||
|
||||
private readonly DispatcherTimer _searchTimer = new DispatcherTimer();
|
||||
private string searchValue = null;
|
||||
|
||||
public static AdvancedSearchPage advancedSearchPage;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
|
||||
// Mapping zwischen den Enum-Classes, da Integer-Values nicht alle identisch sind. Evtl. im nachgang anpassen, aber vorher alle Abhängigkeiten prüfen
|
||||
private enumFasdInformationClass MapSearchResultClassToInformationClass(enumF4sdSearchResultClass searchResultClass)
|
||||
{
|
||||
switch (searchResultClass)
|
||||
{
|
||||
case enumF4sdSearchResultClass.Computer: return enumFasdInformationClass.Computer;
|
||||
case enumF4sdSearchResultClass.User: return enumFasdInformationClass.User;
|
||||
case enumF4sdSearchResultClass.Ticket: return enumFasdInformationClass.Ticket;
|
||||
case enumF4sdSearchResultClass.VirtualSession: return enumFasdInformationClass.VirtualSession;
|
||||
case enumF4sdSearchResultClass.MobileDevice: return enumFasdInformationClass.MobileDevice;
|
||||
default: return enumFasdInformationClass.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Alle Suchergebnisse (ungefilterte Sammlung)
|
||||
public cFasdApiSearchResultCollection SearchResults { get; set; }
|
||||
|
||||
// Gefilterte Suchergebnisse (für die Anzeige in der UI)
|
||||
private cFasdApiSearchResultCollection _filteredSearchResults;
|
||||
public cFasdApiSearchResultCollection FilteredSearchResults
|
||||
{
|
||||
get => _filteredSearchResults;
|
||||
set
|
||||
{
|
||||
if (_filteredSearchResults != value)
|
||||
{
|
||||
_filteredSearchResults = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aktuell gesetzte Filterklassen
|
||||
public HashSet<enumFasdInformationClass> StoredFilterInformationClasses { get; set; }
|
||||
|
||||
public static readonly DependencyProperty FilterInformationClassesProperty =
|
||||
DependencyProperty.Register("FilterInformationClasses", typeof(HashSet<enumFasdInformationClass>), typeof(AdvancedSearchPage), new PropertyMetadata(new HashSet<enumFasdInformationClass>()));
|
||||
|
||||
// Ermittelte Filter bei der Suche
|
||||
public HashSet<enumFasdInformationClass> FilterInformationClasses
|
||||
{
|
||||
get { return (HashSet<enumFasdInformationClass>)GetValue(FilterInformationClassesProperty); }
|
||||
set { SetValue(FilterInformationClassesProperty, value); }
|
||||
}
|
||||
|
||||
public eSearchStatus searchStatus { get; private set; } = eSearchStatus.message;
|
||||
|
||||
public AdvancedSearchPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
AddCustomEventHandlers();
|
||||
SearchBarUc.ActivateManualSearch();
|
||||
|
||||
advancedSearchPage = this;
|
||||
}
|
||||
|
||||
private void AddCustomEventHandlers()
|
||||
{
|
||||
AddHandler(cUiActionBase.UiActionClickedEvent, new cUiActionBase.UiActionEventHandlerDelegate(UiActionWasTriggered));
|
||||
SearchBarUc.ChangedSearchValue = ChangedSearchValueActionAsync;
|
||||
SearchBarUc.CancledSearchAction = CancledSearchAction;
|
||||
}
|
||||
|
||||
|
||||
private Task ChangedSearchValueActionAsync(cFilteredResults filteredResults)
|
||||
{
|
||||
this.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (filteredResults?.Results is null)
|
||||
{
|
||||
SetSearchResultVisibility(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Speichern der Kompletten Suchergebnisse
|
||||
SearchResults = filteredResults.Results;
|
||||
|
||||
// Verfügbare FilterIcons dynamisch ermitteln
|
||||
UpdateFilterInformationClasses();
|
||||
|
||||
// Filter anwenden
|
||||
ApplyFilters();
|
||||
|
||||
// Menü mit den gefilterten Ergebnissen anzeigen
|
||||
string menuHeaderText = filteredResults?.PreSelectedRelation?.DisplayName;
|
||||
ResultMenu.ShowSearchResults(filteredResults, menuHeaderText, this);
|
||||
|
||||
preSelectedRelation = filteredResults.PreSelectedRelation;
|
||||
SetSearchResultVisibility(true);
|
||||
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.LogException(E);
|
||||
}
|
||||
}));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void UpdateFilterInformationClasses()
|
||||
{
|
||||
if (SearchResults == null)
|
||||
{
|
||||
FilterInformationClasses = new HashSet<enumFasdInformationClass>();
|
||||
return;
|
||||
}
|
||||
|
||||
var filter = new HashSet<enumFasdInformationClass>();
|
||||
|
||||
foreach (var group in SearchResults)
|
||||
{
|
||||
foreach (var entry in group.Value)
|
||||
{
|
||||
var mappedClass = MapSearchResultClassToInformationClass(entry.Type);
|
||||
filter.Add(mappedClass);
|
||||
}
|
||||
}
|
||||
FilterInformationClasses = filter;
|
||||
}
|
||||
|
||||
private void SearchFilterBar_FilterChanged(object sender, SearchFilterChangedEventArgs e)
|
||||
{
|
||||
// Filter speichern
|
||||
StoredFilterInformationClasses = new HashSet<enumFasdInformationClass>(e.InformationClassesToShow);
|
||||
|
||||
// Filter anwenden
|
||||
ApplyFilters();
|
||||
|
||||
// cFilteredResults erzeugen
|
||||
var filteredResultWrapper = new cFilteredResults()
|
||||
{
|
||||
Results = FilteredSearchResults,
|
||||
PreSelectedRelation = preSelectedRelation
|
||||
};
|
||||
|
||||
// Ergebnisse im Menü neu anzeigen
|
||||
string menuHeaderText = preSelectedRelation?.DisplayName;
|
||||
ResultMenu.ShowSearchResults(filteredResultWrapper, menuHeaderText, this);
|
||||
}
|
||||
|
||||
private void ApplyFilters()
|
||||
{
|
||||
if (SearchResults == null)
|
||||
return;
|
||||
|
||||
// Wenn keine Filter ausgewählt wurden, alles übernehmen
|
||||
if (StoredFilterInformationClasses == null || StoredFilterInformationClasses.Count == 0)
|
||||
{
|
||||
FilteredSearchResults = SearchResults;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wenn ein oder mehrere Filter gesetzt, dann Ergebnisse entsprechend gefiltert anzeigen
|
||||
cFasdApiSearchResultCollection filtered = new cFasdApiSearchResultCollection();
|
||||
|
||||
foreach (var group in SearchResults)
|
||||
{
|
||||
var filteredEntries = group.Value
|
||||
.Where(entry =>
|
||||
StoredFilterInformationClasses.Contains(MapSearchResultClassToInformationClass(entry.Type))
|
||||
)
|
||||
.ToList();
|
||||
|
||||
if (filteredEntries.Any())
|
||||
{
|
||||
filtered.Add(group.Key, filteredEntries);
|
||||
}
|
||||
}
|
||||
|
||||
FilteredSearchResults = filtered;
|
||||
}
|
||||
|
||||
public void ShowSearchRelations(cSearchHistorySearchResultEntry searchHistoryEntry, IRelationService relationService, ISearchUiProvider searchUiProvider)
|
||||
{
|
||||
try
|
||||
{
|
||||
ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: false);
|
||||
|
||||
cSearchManager.ResolveRelations(searchHistoryEntry.Relations);
|
||||
|
||||
ILookup<enumFasdInformationClass, cMenuDataBase> relationsLookup = searchHistoryEntry.Relations
|
||||
.OrderBy(r => r.UsingLevel)
|
||||
.ThenBy(r => r.LastUsed)
|
||||
.ToLookup(GetInformationClass, GetMenuData);
|
||||
|
||||
ResultMenu.UpdateSearchRelations(relationsLookup);
|
||||
ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
|
||||
cMenuDataBase GetMenuData(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
var requiredInformationClasses = new List<enumFasdInformationClass>() { cF4sdIdentityEntry.GetFromSearchResult(relation.Type) };
|
||||
bool isEnabled = cHealthCardDataHelper.HasAvailableHealthCard(requiredInformationClasses);
|
||||
string disabledReason = null;
|
||||
|
||||
if (!isEnabled)
|
||||
{
|
||||
disabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
|
||||
}
|
||||
|
||||
string trailingText = null;
|
||||
|
||||
return new cMenuDataSearchRelation(relation)
|
||||
{
|
||||
MenuText = relation.DisplayName,
|
||||
TrailingText = trailingText,
|
||||
UiAction = new cUiProcessSearchRelationAction(searchHistoryEntry, relation, relationService, searchUiProvider)
|
||||
{
|
||||
DisplayType = isEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
|
||||
Description = isEnabled ? string.Empty : disabledReason,
|
||||
AlternativeDescription = isEnabled ? string.Empty : disabledReason
|
||||
}
|
||||
};
|
||||
}
|
||||
enumFasdInformationClass GetInformationClass(cF4sdApiSearchResultRelation relation) => cF4sdIdentityEntry.GetFromSearchResult(relation.Type);
|
||||
|
||||
}
|
||||
|
||||
public void SetPendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => ResultMenu.SetPendingInformationClasses(informationClasses);
|
||||
|
||||
public void UpdatePendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => ResultMenu.UpdatePendingInformationClasses(informationClasses);
|
||||
|
||||
private async void UiActionWasTriggered(object sender, UiActionEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.UiAction == null)
|
||||
return;
|
||||
|
||||
Mouse.OverrideCursor = Cursors.Wait;
|
||||
|
||||
switch (e.UiAction)
|
||||
{
|
||||
case cUiProcessSearchResultAction searchResultAction:
|
||||
searchResultAction.PreSelectedSearchRelation = preSelectedRelation;
|
||||
break;
|
||||
case cUiProcessSearchRelationAction _:
|
||||
case cUiProcessSearchHistoryEntry _:
|
||||
break;
|
||||
default:
|
||||
var _t = e.UiAction?.GetType().Name;
|
||||
Debug.Assert(true, $"The UI action '{_t}' is not supported in detailed page");
|
||||
CancledSearchAction();
|
||||
return;
|
||||
}
|
||||
|
||||
SetSearchResultVisibility(true);
|
||||
if (!await e.UiAction.RunUiActionAsync(sender, null, false, null))
|
||||
CancledSearchAction();
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSearchResultVisibility(bool isVisible)
|
||||
{
|
||||
SearchResultBorder.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
|
||||
BodyStack.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void CancledSearchAction()
|
||||
{
|
||||
SearchBarUc.Clear();
|
||||
ResultMenu.ShowSearchResults(new cFilteredResults(), null, this);
|
||||
SetSearchResultVisibility(false);
|
||||
SetSearchHistoryVisibility(false);
|
||||
Visibility = Visibility.Hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region Cleanup when implementing search
|
||||
|
||||
public void SetSearchHistoryVisibility(bool isVisible)
|
||||
{
|
||||
SearchHistoryBorder.Visibility = isVisible && !SearchHistory.IsEmpty() ? Visibility.Visible : Visibility.Collapsed;
|
||||
BodyStack.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ComputerDomainSearch(cComputerDomainSearchParameters searchInfo)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
var _h = Task.Run(async () =>
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
var _result = await cFasdCockpitCommunicationBase.Instance.GetComputerSearchResults(searchInfo.name, searchInfo.domain);
|
||||
var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.ComputerSearch.Info"), searchInfo.name);
|
||||
ShowExternalSearchInfo(strInfo, _result, enumF4sdSearchResultClass.Computer);
|
||||
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(CM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ShowExternalSearchInfo(string strInfo, cFasdApiSearchResultCollection resultEntry, enumF4sdSearchResultClass Class)
|
||||
{
|
||||
var filteredResults = new cFilteredResults(resultEntry) { AutoContinue = true };
|
||||
var _t = ShowExternalSearchInfoAsync(strInfo, filteredResults, Class);
|
||||
}
|
||||
|
||||
private async Task ShowExternalSearchInfoAsync(string strInfo, cFilteredResults result, enumF4sdSearchResultClass Class)
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await SearchBarUc.SetFixedSearchResultAsync(Class, strInfo, result);
|
||||
|
||||
if (result.AutoContinue && result.Results?.Count == 1)
|
||||
{
|
||||
ResultMenu.IndexOfSelectedResultItem = 0;
|
||||
ResultMenu.SelectCurrentResultItem();
|
||||
}
|
||||
|
||||
Show();
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
}
|
||||
|
||||
public string SearchValue
|
||||
{
|
||||
get => searchValue;
|
||||
set
|
||||
{
|
||||
if (value != searchValue)
|
||||
{
|
||||
searchValue = value;
|
||||
if (searchStatus == eSearchStatus.active)
|
||||
UpdateSearchResults();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateSearchResultsAsync(string searchValue, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchValue))
|
||||
{
|
||||
await ChangedSearchValue.Invoke(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cFasdApiSearchResultCollection filteredResultDictionary = null;
|
||||
|
||||
try
|
||||
{
|
||||
var taskId = await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStart(searchValue, CancellationToken.None);
|
||||
lock (_currentSearchTaskLock)
|
||||
{
|
||||
CurrentSearchTaskId = taskId;
|
||||
}
|
||||
var _canceled = false;
|
||||
if (!cancellationToken.IsCancellationRequested && taskId != null)
|
||||
try
|
||||
{
|
||||
filteredResultDictionary = await cFasdCockpitCommunicationBase.Instance.GetSearchResultsResult((Guid)taskId, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
_canceled = true;
|
||||
LogEntry("UpdateSearchResultsAsync was cancelled by token (4).", LogLevels.Debug);
|
||||
}
|
||||
_canceled |= cancellationToken.IsCancellationRequested;
|
||||
taskId = null;
|
||||
if (_canceled)
|
||||
lock (_currentSearchTaskLock)
|
||||
{
|
||||
taskId = CurrentSearchTaskId;
|
||||
CurrentSearchTaskId = null;
|
||||
}
|
||||
if (taskId != null)
|
||||
await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStop((Guid)taskId, CancellationToken.None);
|
||||
}
|
||||
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
LogEntry("UpdateSearchResultsAsync was cancelled by token (3).", LogLevels.Debug);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
LogEntry("UpdateSearchResultsAsync was cancelled by token (2).", LogLevels.Debug);
|
||||
return;
|
||||
}
|
||||
|
||||
cFilteredResults filteredResult = new cFilteredResults(filteredResultDictionary);
|
||||
await ChangedSearchValue.Invoke(filteredResult);
|
||||
}
|
||||
LogEntry("UpdateSearchResultsAsync finished successful.", LogLevels.Warning);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
LogEntry("UpdateSearchResultsAsync was cancelled by token (1).", LogLevels.Warning);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSearchResults()
|
||||
{
|
||||
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
||||
|
||||
try
|
||||
{
|
||||
if (ChangedSearchValue == null)
|
||||
return;
|
||||
|
||||
if (_searchTimer.IsEnabled)
|
||||
_searchTimer.Stop();
|
||||
|
||||
_searchTimer.Start();
|
||||
|
||||
var stopTask = Task.Run(() =>
|
||||
{
|
||||
CancelRunningSearchTaskAsync();
|
||||
});
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (CM != null) LogMethodEnd(CM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void CancelRunningSearchTaskAsync()
|
||||
{
|
||||
// cancel the search task, in case of a running search task
|
||||
lock (_currentSearchTaskLock)
|
||||
{
|
||||
if (CurrentSearchTaskToken != null)
|
||||
{
|
||||
LogEntry("Canceling current running search...");
|
||||
CurrentSearchTaskToken.Cancel(true);
|
||||
CurrentSearchTaskToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void ShowLoadingTextItem(string itemText)
|
||||
{
|
||||
SetSearchHistoryVisibility(false);
|
||||
ResultMenu.ShowLoadingTextItem(itemText);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user