zooflow
This commit is contained in:
@@ -1,483 +0,0 @@
|
||||
Imports System.Drawing
|
||||
Imports System.Windows.Forms
|
||||
Imports System.Text.RegularExpressions
|
||||
Imports DigitalData.Modules.Logging
|
||||
Imports DigitalData.Modules.Windows
|
||||
Imports DigitalData.Modules.Language.Utils
|
||||
Imports DigitalData.Modules.ZooFlow.Params
|
||||
|
||||
Public Class ClassProfileFilter
|
||||
Private _ProfileTable As DataTable
|
||||
Private _ProcessTable As DataTable
|
||||
Private _WindowTable As DataTable
|
||||
Private _ControlTable As DataTable
|
||||
Private _Profiles As List(Of ProfileData)
|
||||
Private _Logger As Logger
|
||||
Private _TreeView As TreeView
|
||||
Private _Window As Window
|
||||
|
||||
Public ReadOnly Property Profiles As List(Of ProfileData)
|
||||
Get
|
||||
Return _Profiles
|
||||
End Get
|
||||
End Property
|
||||
|
||||
Public Sub New(LogConfig As LogConfig, ProfileDatatable As DataTable, ProcessTable As DataTable, WindowDatatable As DataTable, ControlDatatable As DataTable, ImageList As ImageList)
|
||||
Try
|
||||
_Logger = LogConfig.GetLogger()
|
||||
_ProfileTable = ProfileDatatable
|
||||
_ProcessTable = ProcessTable
|
||||
_WindowTable = WindowDatatable
|
||||
_ControlTable = ControlDatatable
|
||||
_Profiles = TransformProfiles()
|
||||
_Window = New Window(LogConfig)
|
||||
|
||||
_TreeView.Nodes.Clear()
|
||||
_TreeView.ImageList = ImageList
|
||||
_TreeView.SelectedImageIndex = 0
|
||||
Catch ex As Exception
|
||||
_Logger.Error(ex)
|
||||
Throw ex
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Public Function ToList() As List(Of ProfileData)
|
||||
Return _Profiles
|
||||
End Function
|
||||
|
||||
Private Function FindNode(ByVal Node As TreeNode, SearchTerm As String)
|
||||
Dim oNode As TreeNode
|
||||
For Each oNode In Node.Nodes
|
||||
If oNode.Text = SearchTerm Then
|
||||
Return oNode
|
||||
End If
|
||||
Next
|
||||
Return Node
|
||||
End Function
|
||||
|
||||
Private Function GetLowestNode(ByVal Node As TreeNode) As TreeNode
|
||||
If Node.GetNodeCount(False) = 1 Then
|
||||
Return GetLowestNode(Node.Nodes.Item(0))
|
||||
Else
|
||||
Return Node
|
||||
End If
|
||||
End Function
|
||||
|
||||
Public Function FilterProfilesByClipboardRegex(Profiles As List(Of ProfileData), ClipboardContents As String) As List(Of ProfileData)
|
||||
Dim oFilteredProfiles As New List(Of ProfileData)
|
||||
For Each oProfile In Profiles
|
||||
_Logger.Debug("Current Profile: {0}", oProfile.Name)
|
||||
|
||||
Try
|
||||
Dim oRegex As New Regex(oProfile.Regex)
|
||||
Dim oMatch = oRegex.Match(ClipboardContents)
|
||||
If oMatch.Success Then
|
||||
_Logger.Debug("FilterProfilesByClipboardRegex: Clipboard Regex Matched: {0}", ClipboardContents)
|
||||
'TODO: Add Debug Data
|
||||
oFilteredProfiles.Add(oProfile)
|
||||
oProfile.IsMatched = True
|
||||
Dim oNode As New TreeNode($"Profile: {oProfile.Name}")
|
||||
oNode.ImageIndex = 0
|
||||
Dim f = New Font("Tahoma", 9, FontStyle.Bold)
|
||||
oNode.NodeFont = f
|
||||
_TreeView.Nodes.Add(oNode)
|
||||
Dim oSubnode As New TreeNode($"MATCH on Global Clipboard Regex: {oProfile.Regex}")
|
||||
oSubnode.ImageIndex = 1
|
||||
oSubnode.Tag = oProfile.Name & "-REGEX"
|
||||
oNode.Nodes.Add(oSubnode)
|
||||
|
||||
End If
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("Regex '{0}' could not be processed for input '{1}'", oProfile.Regex, ClipboardContents)
|
||||
_Logger.Error(ex)
|
||||
End Try
|
||||
Next
|
||||
|
||||
Return oFilteredProfiles
|
||||
End Function
|
||||
Public Function FilterProfilesByProcess(Profiles As List(Of ProfileData), CurrentProcessName As String) As List(Of ProfileData)
|
||||
Dim oFilteredProfiles As New List(Of ProfileData)
|
||||
Try
|
||||
For Each oProfile In Profiles
|
||||
Dim oGuid = oProfile.Guid
|
||||
|
||||
If oProfile.IsMatched = False Then
|
||||
Continue For
|
||||
End If
|
||||
|
||||
|
||||
Dim oProcesses As New List(Of ProfileData.ProcessData)
|
||||
For Each oProcessDef As ProfileData.ProcessData In oProfile.Processes
|
||||
If oProcessDef.ProfileId <> oGuid Then
|
||||
Continue For
|
||||
End If
|
||||
_Logger.Debug($"FilterProfilesByProcess: Checking Profile: {oProfile.Name} ...")
|
||||
If oProcessDef.ProcessName.ToLower = CurrentProcessName.ToLower Then
|
||||
_Logger.Debug($"Yes...Processname Matched: {oProcessDef.ProcessName}")
|
||||
'oProfile.MATCH_PROCESSNAME = $"Processname Matched: {oProfile.ProcessName}"
|
||||
'TODO: Add Debug Data
|
||||
oFilteredProfiles.Add(oProfile)
|
||||
oProfile.MatchedProcessID = oProcessDef.Guid
|
||||
oProcessDef.IsMatched = True
|
||||
oProcesses.Add(oProcessDef)
|
||||
oProfile.IsMatched = True
|
||||
oProfile.MatchedProcessID = oProcessDef.Guid
|
||||
Dim oParentNode As TreeNode
|
||||
Dim oExit = False
|
||||
For Each oTreeNode As TreeNode In _TreeView.Nodes
|
||||
For Each oNodes As TreeNode In oTreeNode.Nodes
|
||||
If oExit = True Then Exit For
|
||||
If oNodes.Tag = oProfile.Name & "-REGEX" Then
|
||||
oParentNode = oNodes
|
||||
oExit = True
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
Next
|
||||
|
||||
If Not IsNothing(oParentNode) Then
|
||||
Dim oNode As New TreeNode($"MATCH on Process: {oProcessDef.ProcessName}")
|
||||
oNode.ImageIndex = 4
|
||||
oNode.Tag = oProfile.Name & "-PROCESS"
|
||||
oParentNode.Nodes.Add(oNode)
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
If oFilteredProfiles.Count > 0 Then
|
||||
oProfile.Processes = oProcesses
|
||||
End If
|
||||
Next
|
||||
|
||||
Return oFilteredProfiles
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("Unexpected error in FilterProfilesByProcess...")
|
||||
_Logger.Error(ex)
|
||||
End Try
|
||||
|
||||
End Function
|
||||
Public Function FilterWindowsByWindowTitleRegex(Profiles As List(Of ProfileData), WindowTitle As String) As List(Of ProfileData)
|
||||
Dim oProfiles As New List(Of ProfileData)
|
||||
|
||||
For Each oProfile As ProfileData In Profiles
|
||||
_Logger.Debug("Checking WindowDefinition for profile: {0}...", oProfile.Name)
|
||||
If oProfile.IsMatched = False Then Continue For
|
||||
Dim oWindows As New List(Of ProfileData.WindowData)
|
||||
|
||||
For Each oWindowDef As ProfileData.WindowData In oProfile.Windows
|
||||
If oWindowDef.WindowProcessID <> oProfile.MatchedProcessID Then Continue For
|
||||
Try
|
||||
If oWindowDef.Regex = String.Empty Then
|
||||
oProfile.MatchedWindowID = oWindowDef.Guid
|
||||
oWindowDef.IsMatched = True
|
||||
oWindows.Add(oWindowDef)
|
||||
Exit For
|
||||
End If
|
||||
|
||||
Dim oRegex As New Regex(oWindowDef.Regex)
|
||||
Dim oMatch = oRegex.Match(WindowTitle)
|
||||
|
||||
If oMatch.Success Then
|
||||
_Logger.Debug("MATCH on WindowTitle: {0}", WindowTitle)
|
||||
'TODO: Add Debug Data
|
||||
oProfile.MatchedWindowID = oWindowDef.Guid
|
||||
oWindowDef.IsMatched = True
|
||||
oWindows.Add(oWindowDef)
|
||||
Dim olowestNode As TreeNode = Node_Get_Lowest_Node(oProfile.Name & "-REGEX")
|
||||
If Not IsNothing(olowestNode) Then
|
||||
Dim oNode As New TreeNode($"MATCH on WindowTitle: [{WindowTitle}]")
|
||||
oNode.ImageIndex = 3
|
||||
oNode.Tag = oProfile.Name & "-WINDOW"
|
||||
olowestNode.Nodes.Add(oNode)
|
||||
End If
|
||||
Exit For
|
||||
End If
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("Regex '{0}' could not be processed for input '{1}'", oWindowDef.Regex, WindowTitle)
|
||||
_Logger.Error(ex)
|
||||
End Try
|
||||
Next
|
||||
|
||||
If oWindows.Count > 0 Then
|
||||
oProfile.Windows = oWindows
|
||||
oProfile.IsMatched = True
|
||||
oProfiles.Add(oProfile)
|
||||
End If
|
||||
Next
|
||||
|
||||
Return oProfiles
|
||||
End Function
|
||||
|
||||
Public Function FilterWindowsByWindowClipboardRegex(Profiles As List(Of ProfileData), ClipboardContents As String) As List(Of ProfileData)
|
||||
Dim oProfiles As New List(Of ProfileData)
|
||||
|
||||
For Each oProfile As ProfileData In Profiles
|
||||
_Logger.Debug("Current Profile: {0}", oProfile.Name)
|
||||
|
||||
Dim oWindows As New List(Of ProfileData.WindowData)
|
||||
|
||||
For Each w As ProfileData.WindowData In oProfile.Windows
|
||||
Try
|
||||
If w.Regex = String.Empty Then
|
||||
oWindows.Add(w)
|
||||
End If
|
||||
|
||||
Dim oRegex As New Regex(w.Regex)
|
||||
Dim oMatch = oRegex.Match(ClipboardContents)
|
||||
|
||||
If oMatch.Success Then
|
||||
_Logger.Debug("Window Clipboard Regex Matched: {0}", ClipboardContents)
|
||||
Dim oResult As TreeNode
|
||||
For Each oTreeNode In _TreeView.Nodes
|
||||
If Not IsNothing(oResult) Then Exit For
|
||||
If oTreeNode.Tag = oProfile.Name & "-REGEX" Then
|
||||
oResult = oTreeNode
|
||||
End If
|
||||
|
||||
|
||||
Next
|
||||
If Not IsNothing(oResult) Then
|
||||
Dim oNode As New TreeNode($"MATCH on WINDOW Clipboard Regex: [{w.Regex}]")
|
||||
oNode.ImageIndex = 2
|
||||
oNode.Tag = oProfile.Name & "-WINDOW_REGEX"
|
||||
Dim olowestNode As TreeNode = GetLowestNode(oResult)
|
||||
olowestNode.Nodes.Add(oNode)
|
||||
End If
|
||||
|
||||
oWindows.Add(w)
|
||||
|
||||
|
||||
End If
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("Regex '{0}' could not be processed for input '{1}'", w.Regex, ClipboardContents)
|
||||
_Logger.Error(ex)
|
||||
End Try
|
||||
Next
|
||||
|
||||
If oWindows.Count > 0 Then
|
||||
oProfile.Windows = oWindows
|
||||
oProfiles.Add(oProfile)
|
||||
End If
|
||||
Next
|
||||
|
||||
Return oProfiles
|
||||
End Function
|
||||
|
||||
Public Function FilterProfilesByFocusedControl(Profiles As List(Of ProfileData), ClipboardContents As String, ControlFocusresult As String) As List(Of ProfileData)
|
||||
Dim oWindow As Window.WindowInfo
|
||||
Dim oFocusedControl As Window.WindowInfo
|
||||
Dim oFocusedControlName As String = String.Empty
|
||||
|
||||
Try
|
||||
oWindow = _Window.GetWindowInfo()
|
||||
oFocusedControl = _Window.GetFocusedControl(oWindow.hWnd)
|
||||
|
||||
If oFocusedControl Is Nothing Then
|
||||
_Logger.Info("Could not get FocusedControl in Window (Old method) {0}", oWindow.WindowTitle)
|
||||
oFocusedControlName = String.Empty
|
||||
Else
|
||||
oFocusedControlName = oFocusedControl.ControlName
|
||||
End If
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("Error while getting Focused control (Old method)")
|
||||
_Logger.Error(ex)
|
||||
oFocusedControlName = String.Empty
|
||||
End Try
|
||||
|
||||
Dim oFilteredProfiles As New List(Of ProfileData)
|
||||
|
||||
For Each oProfileMatchedSofar In Profiles
|
||||
If oProfileMatchedSofar.IsMatched = False Then Continue For
|
||||
|
||||
_Logger.Debug("Checking ControlDefiniotion on profile: {0}", oProfileMatchedSofar.Name)
|
||||
If oProfileMatchedSofar.Controls.Count = 0 Then
|
||||
oFilteredProfiles.Add(oProfileMatchedSofar)
|
||||
|
||||
Dim oNode As New TreeNode($"No Controls configured!")
|
||||
oNode.ImageIndex = 2
|
||||
oNode.ForeColor = Color.Blue
|
||||
oNode.Tag = oProfileMatchedSofar.Name & "-NOCONTROLCONFIG"
|
||||
Dim f = New Font("Tahoma", 10, FontStyle.Bold)
|
||||
oNode.NodeFont = f
|
||||
_TreeView.Nodes.Add(oNode)
|
||||
Continue For
|
||||
End If
|
||||
|
||||
Dim oControls As New List(Of ProfileData.ControlData)
|
||||
|
||||
For Each oControlDefinition In oProfileMatchedSofar.Controls
|
||||
Try
|
||||
If oControlDefinition.WindowId <> oProfileMatchedSofar.MatchedWindowID Then Continue For
|
||||
_Logger.Debug($"Working on ControlDefinition: {oControlDefinition.Guid}-{oControlDefinition.ControlName}...")
|
||||
If oControlDefinition.Regex = String.Empty Then
|
||||
oProfileMatchedSofar.MatchedControlID = oControlDefinition.Guid
|
||||
oControlDefinition.IsMatched = True
|
||||
oControls.Add(oControlDefinition)
|
||||
Exit For
|
||||
End If
|
||||
'Dim oResult As TreeNode
|
||||
'For Each oTreeNode In CurrMatchTreeView.Nodes
|
||||
' oResult = NodeFind(oTreeNode, $"Global Clipboard Regex Matched [{oProfile.Regex}]")
|
||||
'Next
|
||||
'Dim oNode As TreeNode
|
||||
Dim oNodeCaption As String
|
||||
'Dim oAddNode As Boolean = False
|
||||
Dim oRegex As New Regex(oControlDefinition.Regex)
|
||||
|
||||
Dim oFocusedControlResult As String = ""
|
||||
|
||||
If oControlDefinition.AutomationId <> String.Empty And oControlDefinition.ControlName = String.Empty Then
|
||||
_Logger.Debug($"AutomationID should be used...")
|
||||
If Not IsNothing(ControlFocusresult) Then
|
||||
If ControlFocusresult <> String.Empty Then
|
||||
_Logger.Debug($"AutomationID will be used...")
|
||||
oFocusedControlResult = ControlFocusresult
|
||||
End If
|
||||
End If
|
||||
ElseIf oControlDefinition.AutomationId = String.Empty And oControlDefinition.ControlName <> String.Empty Then
|
||||
_Logger.Debug($"ControlName should be used...")
|
||||
If Not IsNothing(oFocusedControlName) Then
|
||||
If oFocusedControlName <> String.Empty Then
|
||||
_Logger.Debug($"ControlName will be used...")
|
||||
oFocusedControlResult = oFocusedControlName
|
||||
End If
|
||||
End If
|
||||
|
||||
End If
|
||||
If oFocusedControlResult <> String.Empty Then
|
||||
Dim oControlRegex As New Regex(oControlDefinition.Regex)
|
||||
Dim oControlMatch = oRegex.Match(oFocusedControlResult)
|
||||
|
||||
If oControlMatch.Success Then
|
||||
_Logger.Debug($"MATCH on Focused Control [{oFocusedControlResult}] with Regex [{oControlDefinition.Regex}]")
|
||||
oProfileMatchedSofar.IsMatched = True
|
||||
oProfileMatchedSofar.MatchedControlID = oControlDefinition.Guid
|
||||
oControlDefinition.IsMatched = True
|
||||
oControls.Add(oControlDefinition)
|
||||
Dim olowestNode As TreeNode = Node_Get_Lowest_Node(oProfileMatchedSofar.Name & "-REGEX")
|
||||
If Not IsNothing(olowestNode) Then
|
||||
Dim oNode As New TreeNode($"MATCH on Focused Control [{oFocusedControlResult}] with Regex [{oControlDefinition.Regex}]")
|
||||
oNode.ImageIndex = 2
|
||||
oNode.Tag = oProfileMatchedSofar.Name & "-CONTROL"
|
||||
olowestNode.Nodes.Add(oNode)
|
||||
End If
|
||||
Exit For
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("Regex '{0}' could not be processed for input '{1}'", oControlDefinition.Regex, oFocusedControlName)
|
||||
_Logger.Error(ex)
|
||||
End Try
|
||||
Next
|
||||
|
||||
If oControls.Count > 0 Then
|
||||
oProfileMatchedSofar.Controls = oControls
|
||||
oFilteredProfiles.Add(oProfileMatchedSofar)
|
||||
Else
|
||||
Dim olowestNode As TreeNode = Node_Get_Lowest_Node(oProfileMatchedSofar.Name & "-REGEX")
|
||||
If Not IsNothing(olowestNode) Then
|
||||
Dim oNode As New TreeNode($"NO MATCHES on Focused Control, Please check the Config")
|
||||
oNode.ImageIndex = 2
|
||||
oNode.Tag = oProfileMatchedSofar.Name & "-CONTROLNoMatch"
|
||||
olowestNode.Nodes.Add(oNode)
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
|
||||
Return oFilteredProfiles
|
||||
End Function
|
||||
Private Function Node_Get_Lowest_Node(NodeTag As String) As TreeNode
|
||||
Dim oExit = False
|
||||
Dim oParentNode As TreeNode
|
||||
For Each oTreeNode As TreeNode In _TreeView.Nodes
|
||||
For Each oNodes As TreeNode In oTreeNode.Nodes
|
||||
If oExit = True Then Exit For
|
||||
If oNodes.Tag = NodeTag Then
|
||||
oParentNode = oNodes
|
||||
oExit = True
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
Next
|
||||
Dim olowestNode As TreeNode = GetLowestNode(oParentNode)
|
||||
|
||||
Return olowestNode
|
||||
End Function
|
||||
Public Function ClearNotMatchedProfiles(Profiles As List(Of ProfileData)) As List(Of ProfileData)
|
||||
Dim oFilteredProfiles As New List(Of ProfileData)
|
||||
For Each oProfile In Profiles
|
||||
If oProfile.IsMatched Then
|
||||
oFilteredProfiles.Add(oProfile)
|
||||
End If
|
||||
Next
|
||||
Return oFilteredProfiles
|
||||
End Function
|
||||
Private Function TransformProfiles() As List(Of ProfileData)
|
||||
Dim oList As New List(Of ProfileData)
|
||||
|
||||
For Each oRow As DataRow In _ProfileTable.Rows
|
||||
Dim oProfileId = oRow.Item("GUID")
|
||||
Dim oProcessList As List(Of ProfileData.ProcessData) = TransformProcesses(oProfileId, _ProcessTable)
|
||||
Dim oWindowList As List(Of ProfileData.WindowData) = TransformWindows(oProfileId, _WindowTable)
|
||||
Dim oControlList As List(Of ProfileData.ControlData) = TransformControls(oProfileId, _ControlTable)
|
||||
|
||||
oList.Add(New ProfileData() With {
|
||||
.Guid = oRow.Item("GUID"),
|
||||
.Regex = NotNull(oRow.Item("REGEX_EXPRESSION"), String.Empty),
|
||||
.Name = NotNull(oRow.Item("NAME"), String.Empty),
|
||||
.Comment = NotNull(oRow.Item("COMMENT"), String.Empty),
|
||||
.ProfileType = NotNull(oRow.Item("PROFILE_TYPE"), String.Empty),
|
||||
.Processes = oProcessList,
|
||||
.Windows = oWindowList,
|
||||
.Controls = oControlList
|
||||
})
|
||||
Next
|
||||
|
||||
Return oList
|
||||
End Function
|
||||
|
||||
Private Function TransformControls(ProfileId As Integer, ControlDatatable As DataTable) As List(Of ProfileData.ControlData)
|
||||
Dim oControlList As New List(Of ProfileData.ControlData)
|
||||
|
||||
For Each oRow As DataRow In ControlDatatable.Rows
|
||||
If oRow.Item("PROFILE_ID") = ProfileId Then
|
||||
oControlList.Add(New ProfileData.ControlData() With {
|
||||
.Guid = oRow.Item("GUID"),
|
||||
.Description = NotNull(oRow.Item("DESCRIPTION"), String.Empty),
|
||||
.Regex = NotNull(oRow.Item("REGEX"), String.Empty),
|
||||
.AutomationId = NotNull(oRow.Item("AUTOMATION_ID"), String.Empty),
|
||||
.WindowId = oRow.Item("WINDOW_ID")
|
||||
})
|
||||
End If
|
||||
Next
|
||||
|
||||
Return oControlList
|
||||
End Function
|
||||
Private Function TransformProcesses(ProfileId As Integer, ProcessDatatable As DataTable) As List(Of ProfileData.ProcessData)
|
||||
Dim oProcessList As New List(Of ProfileData.ProcessData)
|
||||
|
||||
For Each oRow As DataRow In ProcessDatatable.Rows
|
||||
oProcessList.Add(New ProfileData.ProcessData() With {
|
||||
.Guid = oRow.Item("GUID"),
|
||||
.ProfileId = oRow.Item("PROFILE_ID"),
|
||||
.ProcessName = NotNull(oRow.Item("PROC_NAME"), String.Empty)
|
||||
})
|
||||
Next
|
||||
|
||||
Return oProcessList
|
||||
End Function
|
||||
Private Function TransformWindows(ProfileId As Integer, WindowDatatable As DataTable) As List(Of ProfileData.WindowData)
|
||||
Dim oWindowList As New List(Of ProfileData.WindowData)
|
||||
|
||||
For Each oRow As DataRow In WindowDatatable.Rows
|
||||
oWindowList.Add(New ProfileData.WindowData() With {
|
||||
.Guid = oRow.Item("GUID"),
|
||||
.WindowProcessID = oRow.Item("PROCESS_ID"),
|
||||
.Title = NotNull(oRow.Item("DESCRIPTION"), String.Empty),
|
||||
.Regex = NotNull(oRow.Item("REGEX"), String.Empty),
|
||||
.Sequence = NotNull(oRow.Item("SEQUENCE"), 0)
|
||||
})
|
||||
Next
|
||||
|
||||
Return oWindowList
|
||||
End Function
|
||||
End Class
|
||||
@@ -74,7 +74,6 @@
|
||||
<Import Include="System.Threading.Tasks" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ClassProfileFilter.vb" />
|
||||
<Compile Include="Environment.vb" />
|
||||
<Compile Include="My Project\AssemblyInfo.vb" />
|
||||
<Compile Include="My Project\Application.Designer.vb">
|
||||
|
||||
Reference in New Issue
Block a user