ZooFlow: Improvements and Performance for frmSearchStart
This commit is contained in:
@@ -1,22 +1,29 @@
|
||||
Option Explicit On
|
||||
|
||||
Imports DigitalData.Modules.Logging
|
||||
Imports DigitalData.Modules.Language
|
||||
Imports DevExpress.XtraTab
|
||||
Imports DevExpress.XtraGrid
|
||||
Imports DevExpress.XtraGrid.Views.Grid
|
||||
Imports DevExpress.XtraEditors
|
||||
Imports DevExpress.XtraSplashScreen
|
||||
Imports DigitalData.GUIs.Common
|
||||
Imports DigitalData.GUIs.ZooFlow.ClassConstants
|
||||
Imports DigitalData.GUIs.ZooFlow.SearchFilter
|
||||
Imports System.Threading.Tasks
|
||||
|
||||
|
||||
Public Class frmSearchStart
|
||||
Private Logger As Logger
|
||||
|
||||
' Constants
|
||||
Private Const DEFAULT_X As Integer = 10
|
||||
Private Const DEFAULT_Y As Integer = 10
|
||||
|
||||
' Runtime Variables
|
||||
Private SelectedTabIndex As Integer
|
||||
Private SelectedTab As XtraTabPage
|
||||
Private HeightBeforeMinimizing As Integer = 600
|
||||
|
||||
Private SEARCH_ID As Integer = 0
|
||||
Private SEARCH_SQL As String
|
||||
@@ -31,11 +38,15 @@ Public Class frmSearchStart
|
||||
Private LastSearchForm As frmDocumentResultList
|
||||
Private ChangedDateControls As List(Of String)
|
||||
|
||||
Private StopWatch As Watch
|
||||
|
||||
Public Sub New(ByVal pDTSearchProfiles As DataTable, Optional ByVal pRunSearch As Boolean = False)
|
||||
' Dieser Aufruf ist für den Designer erforderlich.
|
||||
InitializeComponent()
|
||||
|
||||
' Fügen Sie Initialisierungen nach dem InitializeComponent()-Aufruf hinzu.
|
||||
StopWatch = New Watch(Name)
|
||||
|
||||
DatatableSearchProfiles = pDTSearchProfiles
|
||||
Logger = My.LogConfig.GetLogger()
|
||||
Dim oSearchTerms As New DataTable
|
||||
@@ -48,6 +59,7 @@ Public Class frmSearchStart
|
||||
oSearchTerms.Columns.Add("SearchTerm", GetType(String))
|
||||
oSearchTerms.Columns.Add("BracketRight", GetType(String))
|
||||
oSearchTerms.Columns.Add("Operator", GetType(String))
|
||||
|
||||
End Sub
|
||||
|
||||
Private Async Sub frmSearchStart_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
@@ -56,30 +68,14 @@ Public Class frmSearchStart
|
||||
Try
|
||||
oHandle = SplashScreenManager.ShowOverlayForm(Me)
|
||||
|
||||
Dim oWatch As New Watch("Setting up Form")
|
||||
|
||||
pnlProfileChoose.Visible = False
|
||||
For Each oTab As XtraTabPage In XtraTabControl1.TabPages
|
||||
oTab.PageVisible = False
|
||||
Next
|
||||
|
||||
RepositoryItemComboBox2.Items.AddRange(New List(Of FilterTimeframe) From {
|
||||
New FilterTimeframe() With {.Name = "Kein", .DisableFilter = True},
|
||||
New FilterTimeframe() With {.Name = "Eigener", .CustomFilter = True},
|
||||
New FilterTimeframe() With {
|
||||
.Name = "letzte 7 Tage",
|
||||
.From = Date.Now.Subtract(TimeSpan.FromDays(7)),
|
||||
.[To] = Date.Now
|
||||
},
|
||||
New FilterTimeframe() With {
|
||||
.Name = "letzte 14 Tage",
|
||||
.From = Date.Now.Subtract(TimeSpan.FromDays(14)),
|
||||
.[To] = Date.Now
|
||||
},
|
||||
New FilterTimeframe() With {
|
||||
.Name = "letzte 30 Tage",
|
||||
.From = Date.Now.Subtract(TimeSpan.FromDays(30)),
|
||||
.[To] = Date.Now
|
||||
}
|
||||
})
|
||||
RepositoryItemComboBox2.Items.AddRange(DefaultFilters)
|
||||
|
||||
BarCheckboxOpenSearchInSameWindow.Checked = My.UIConfig.SearchForm.OpenSearchInSameWindow
|
||||
|
||||
@@ -96,6 +92,8 @@ Public Class frmSearchStart
|
||||
For Each oRow As DataRow In DatatableSearchProfiles.Rows
|
||||
RepositoryItemComboBox1.Items.Add(oRow.Item("TITLE"))
|
||||
Next
|
||||
|
||||
oWatch.Stop()
|
||||
Else
|
||||
pnlProfileChoose.Visible = False
|
||||
RibbonPageGroupProfiles.Visible = False
|
||||
@@ -108,7 +106,10 @@ Public Class frmSearchStart
|
||||
SEARCH_SQL = DatatableSearchProfiles.Rows(0).Item("RESULT_SQL")
|
||||
SEARCH_TITLE = DatatableSearchProfiles.Rows(0).Item("TITLE")
|
||||
|
||||
oWatch.Stop()
|
||||
oWatch = New Watch("Loading Attributes")
|
||||
Await Load_Search_Attributes()
|
||||
oWatch.Stop()
|
||||
BarButtonNewSearch.Visibility = DevExpress.XtraBars.BarItemVisibility.Never
|
||||
End If
|
||||
SEARCH_COUNT = DatatableSearchProfiles.Rows.Count
|
||||
@@ -127,37 +128,50 @@ Public Class frmSearchStart
|
||||
MsgBox(ex.Message, MsgBoxStyle.Critical, "Unexpected error while loading ProfileSearches:")
|
||||
Finally
|
||||
SplashScreenManager.CloseOverlayForm(oHandle)
|
||||
StopWatch.Stop()
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Async Function Load_Search_Attributes() As Task
|
||||
Try
|
||||
DataLoaded = False
|
||||
|
||||
Dim oWatch1 As New Watch("Getting data from Database")
|
||||
Dim oSQL = $"SELECT * FROM VWIDB_SEARCH_PROFILE_ATTRIBUTES WHERE SEARCH_PROFIL_ID = {SEARCH_ID} ORDER BY [SEQUENCE]"
|
||||
Dim oDT As DataTable = Await My.DatabaseIDB.GetDatatableAsync(oSQL)
|
||||
oWatch1.Stop()
|
||||
|
||||
Dim oWatch2 As New Watch("Procesing Dataset")
|
||||
DatatableAttributes = Nothing
|
||||
DatatableAttributes = oDT.Clone()
|
||||
oDT.Select("", "SEQUENCE").CopyToDataTable(DatatableAttributes, LoadOption.PreserveChanges)
|
||||
oWatch2.Stop()
|
||||
|
||||
Dim oControlCount As Integer = 1
|
||||
Dim oControlRow As Integer = 0
|
||||
Dim oControls As New ClassControlCreator(SelectedTab, Me)
|
||||
Dim YMax As Integer = 0
|
||||
|
||||
Dim YActControlHeight As Integer = 0
|
||||
Dim XActControlWidth As Integer = 0
|
||||
Dim iList As New List(Of Integer) From {2, 3, 5, 6, 8, 9}
|
||||
|
||||
For Each oAttributeRow As DataRow In oDT.Rows
|
||||
Dim oXPosition As Integer
|
||||
Dim oYPositionControl As Integer
|
||||
Dim oYPositionLabel As Integer
|
||||
Dim oMyLastGridView As GridView
|
||||
Dim oSingleResult As Boolean = False
|
||||
Dim oAttriTitle As String = oAttributeRow.Item("ATTRIBUTE_TITLE").ToString
|
||||
Dim oAttributeTitle As String = oAttributeRow.Item("ATTRIBUTE_TITLE").ToString
|
||||
Dim oAttriID As Integer = CInt(oAttributeRow.Item("ATTRIBUTE_ID"))
|
||||
Dim oAttributeType As String = oAttributeRow.Item("ATTRIBUTE_TYPE").ToString
|
||||
|
||||
Dim oWatch3 As New Watch($"Loading Attribute: {oAttributeTitle}")
|
||||
|
||||
Dim oWatch4 As New Watch($"Calculating Position")
|
||||
|
||||
If oControlCount = 1 Or oControlCount = 5 Or oControlCount = 9 Then
|
||||
oControlRow += 1
|
||||
End If
|
||||
|
||||
If oControlRow = 1 Then
|
||||
If oControlCount = 1 Then
|
||||
oXPosition = 10
|
||||
@@ -177,30 +191,39 @@ Public Class frmSearchStart
|
||||
End If
|
||||
End If
|
||||
|
||||
Dim oControlHeight As Integer = CInt(oAttributeRow.Item("HEIGHT"))
|
||||
oWatch4.Stop()
|
||||
oWatch4 = New Watch("Creating Label")
|
||||
|
||||
'Dim oControlHeight As Integer = CInt(oAttributeRow.Item("HEIGHT"))
|
||||
Dim oControlHeight As Integer = 150
|
||||
Dim oControlWidth As Integer = CInt(oAttributeRow.Item("WIDTH"))
|
||||
'Dim oControlWidth As Integer = 150
|
||||
|
||||
If CBool(oAttributeRow.Item("MULTISELECT")) = True Then
|
||||
oControlWidth += 50
|
||||
End If
|
||||
|
||||
addLabel(oAttriTitle, oXPosition, oYPositionLabel)
|
||||
addLabel(oAttributeTitle, oXPosition, oYPositionLabel)
|
||||
|
||||
'Nun das Control mit dem entsprechenden Abstand und der Größe
|
||||
Dim oCalcHeight As Integer
|
||||
Dim oCalcWidth As Integer
|
||||
If oAttributeType = "VARCHAR" Or oAttributeType = "BIG INTEGER" Then
|
||||
If oAttributeType = ATTR_TYPE_STRING Or oAttributeType = ATTR_TYPE_INTEGER Then
|
||||
oCalcHeight = oControlHeight + oYPositionControl
|
||||
oCalcWidth = oControlWidth
|
||||
ElseIf (oAttributeType = "DATE" Or oAttributeType = "BIT") Then
|
||||
ElseIf (oAttributeType = ATTR_TYPE_DATE Or oAttributeType = ATTR_TYPE_BOOLEAN) Then
|
||||
oCalcHeight = 20 + oYPositionControl
|
||||
oCalcWidth = 100
|
||||
End If
|
||||
If oCalcHeight > YActControlHeight Then
|
||||
YActControlHeight = oCalcHeight
|
||||
End If
|
||||
|
||||
oWatch4.Stop()
|
||||
oWatch4 = New Watch("Creating Control")
|
||||
|
||||
Dim oMyControl As Control = Nothing
|
||||
If oAttributeType = "VARCHAR" Or oAttributeType = "BIG INTEGER" Then
|
||||
If oAttributeType = ATTR_TYPE_STRING Or oAttributeType = ATTR_TYPE_INTEGER Then
|
||||
oMyControl = oControls.CreateExistingGridControl(oAttributeRow, oXPosition, oYPositionControl)
|
||||
Dim myDGV As GridControl = CType(oMyControl, GridControl)
|
||||
|
||||
@@ -212,7 +235,7 @@ Public Class frmSearchStart
|
||||
End If
|
||||
End If
|
||||
|
||||
Dim oView As DevExpress.XtraGrid.Views.Grid.GridView
|
||||
Dim oView As GridView
|
||||
oView = CType(myDGV.MainView, GridView)
|
||||
oMyLastGridView = oView
|
||||
If CBool(oAttributeRow.Item("MULTISELECT")) = True Then
|
||||
@@ -223,28 +246,30 @@ Public Class frmSearchStart
|
||||
End If
|
||||
oView.FocusInvalidRow()
|
||||
|
||||
ElseIf oAttributeType = "DATE" Then
|
||||
ElseIf oAttributeType = ATTR_TYPE_DATE Then
|
||||
oMyControl = oControls.CreateExistingDatepicker(oAttributeRow, oXPosition, oYPositionControl)
|
||||
Dim myDTP As DateEdit = CType(oMyControl, DateEdit)
|
||||
AddHandler myDTP.DisableCalendarDate, AddressOf DisableCalendarDate
|
||||
AddHandler myDTP.DateTimeChanged, AddressOf CalendarChanged 'CalendarChanged
|
||||
ElseIf oAttributeType = "BIT" Then
|
||||
ElseIf oAttributeType = ATTR_TYPE_BOOLEAN Then
|
||||
oMyControl = oControls.CreateExistingCheckbox(oAttributeRow, oXPosition, oYPositionControl)
|
||||
Dim myCheckBox As CheckBox = CType(oMyControl, CheckBox)
|
||||
AddHandler myCheckBox.CheckedChanged, AddressOf CheckBox_CheckedChanged
|
||||
End If
|
||||
|
||||
oWatch4.Stop()
|
||||
oWatch4 = New Watch("Adding Control to Panel")
|
||||
|
||||
oControlCount += 1
|
||||
|
||||
If oMyControl IsNot Nothing Then
|
||||
SelectedTab.Controls.Add(oMyControl)
|
||||
End If
|
||||
|
||||
If oAttributeType = "VARCHAR" Or oAttributeType = "BIG INTEGER" Then
|
||||
oMyLastGridView.FocusInvalidRow()
|
||||
End If
|
||||
|
||||
oXPosition += oControlWidth + 20
|
||||
|
||||
oWatch4.Stop()
|
||||
oWatch3.Stop()
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Logger.Warn("Unexpected error in Load_Search_Attributes - Error: " & ex.Message)
|
||||
@@ -252,7 +277,6 @@ Public Class frmSearchStart
|
||||
Finally
|
||||
DataLoaded = True
|
||||
End Try
|
||||
|
||||
End Function
|
||||
Sub addLabel(pAttrName As String, pXPos As Integer, ylbl As Integer)
|
||||
Dim lbl As New Label With {
|
||||
@@ -296,8 +320,8 @@ Public Class frmSearchStart
|
||||
Continue For
|
||||
End If
|
||||
|
||||
oAttrID = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttrID
|
||||
oAttrTitle = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttrTitle
|
||||
oAttrID = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttributeID
|
||||
oAttrTitle = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttributeTitle
|
||||
For Each oRowHandle As Integer In oSelectedRows
|
||||
Dim oResult = oMyGridView.GetRowCellValue(oRowHandle, oMyGridView.Columns(0).FieldName)
|
||||
Dim oInsert = $"EXEC PRIDB_NEW_USER_SEARCH_CRITERIA {SEARCH_ID.ToString},{My.Application.User.UserId.ToString},{oAttrID.ToString},'{oResult}','{My.Application.User.UserName}'"
|
||||
@@ -315,8 +339,8 @@ Public Class frmSearchStart
|
||||
For Each oName As String In ChangedDateControls
|
||||
If oDateEdit.Name = oName Then
|
||||
If Not IsNothing(oDateEdit.EditValue) Then
|
||||
oAttrID = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttrID
|
||||
oAttrTitle = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttrTitle
|
||||
oAttrID = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttributeID
|
||||
oAttrTitle = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttributeTitle
|
||||
Dim oldValue As Date
|
||||
Dim validDate As Boolean = False
|
||||
Dim oDateValue As Date = DirectCast(oDateEdit.EditValue, Date)
|
||||
@@ -344,8 +368,8 @@ Public Class frmSearchStart
|
||||
Case "System.Windows.Forms.CheckBox"
|
||||
Dim myCheckBox As CheckBox = CType(oControl, CheckBox)
|
||||
If myCheckBox.CheckState <> CheckState.Indeterminate Then
|
||||
oAttrID = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttrID
|
||||
oAttrTitle = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttrTitle
|
||||
oAttrID = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttributeID
|
||||
oAttrTitle = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata).AttributeTitle
|
||||
Dim oInsert = $"EXEC PRIDB_NEW_USER_SEARCH_CRITERIA {SEARCH_ID.ToString},{My.Application.User.UserId.ToString},{oAttrID.ToString},'{myCheckBox.Checked.ToString}','{My.Application.User.UserName}'"
|
||||
My.DatabaseIDB.ExecuteNonQuery(oInsert)
|
||||
End If
|
||||
@@ -363,8 +387,8 @@ Public Class frmSearchStart
|
||||
|
||||
If IsNothing(oRowView) = False Then
|
||||
Dim oResult As String = CType(oRowView.Item(0), String)
|
||||
Dim oAttrID = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttrID
|
||||
Dim oAttrTitle = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttrTitle
|
||||
Dim oAttrID = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttributeID
|
||||
Dim oAttrTitle = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttributeTitle
|
||||
' RenewSearchAttributes()
|
||||
' AddSearchAttribute(oAttrID, oAttrTitle, oResult)
|
||||
|
||||
@@ -375,8 +399,8 @@ Public Class frmSearchStart
|
||||
If DataLoaded = False Then Exit Sub
|
||||
Dim oCurrentCB As CheckBox = DirectCast(sender, CheckBox)
|
||||
Dim oChecked = oCurrentCB.Checked
|
||||
Dim oAttrID = DirectCast(oCurrentCB.Tag, ClassControlCreator.ControlMetadata).AttrID
|
||||
Dim oAttrTitle = DirectCast(oCurrentCB.Tag, ClassControlCreator.ControlMetadata).AttrTitle
|
||||
Dim oAttrID = DirectCast(oCurrentCB.Tag, ClassControlCreator.ControlMetadata).AttributeID
|
||||
Dim oAttrTitle = DirectCast(oCurrentCB.Tag, ClassControlCreator.ControlMetadata).AttributeTitle
|
||||
'RenewSearchAttributes()
|
||||
' AddSearchAttribute(oAttrID, oAttrTitle, oChecked.ToString)
|
||||
End Sub
|
||||
@@ -497,8 +521,8 @@ Public Class frmSearchStart
|
||||
|
||||
If IsNothing(oRowView) = False Then
|
||||
Dim oResult As String = CType(oRowView.Item(0), String)
|
||||
Dim oAttrID = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttrID
|
||||
Dim oAttrTitle = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttrTitle
|
||||
Dim oAttrID = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttributeID
|
||||
Dim oAttrTitle = DirectCast(oCurrentControl.Tag, ClassControlCreator.ControlMetadata).AttributeTitle
|
||||
|
||||
End If
|
||||
End Sub
|
||||
@@ -544,10 +568,19 @@ Public Class frmSearchStart
|
||||
|
||||
Private Sub BarButtonStartSearch_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles BarButtonStartSearch.ItemClick
|
||||
RenewSearchAttributes()
|
||||
Start_Search()
|
||||
|
||||
' Minimize the search form, but only if results were found
|
||||
If Start_Search() Then
|
||||
MinimizeSearchForm()
|
||||
|
||||
' Position Result Window below this window
|
||||
LastSearchForm.Location = GetResultFormLocation()
|
||||
LastSearchForm.Size = GetResultFormSize()
|
||||
End If
|
||||
End Sub
|
||||
Private Sub Start_Search()
|
||||
Private Function Start_Search() As Boolean
|
||||
Dim oHandle As IOverlaySplashScreenHandle = Nothing
|
||||
Dim oItemsFound As Boolean = False
|
||||
|
||||
Try
|
||||
oHandle = SplashScreenManager.ShowOverlayForm(Me)
|
||||
@@ -587,16 +620,14 @@ Public Class frmSearchStart
|
||||
Dim oForm As New frmDocumentResultList(My.LogConfig, oEnvironment, oParams)
|
||||
oForm.Show()
|
||||
|
||||
' Position Result Window below this window
|
||||
oForm.Location = GetResultFormLocation()
|
||||
oForm.Size = GetResultFormSize()
|
||||
|
||||
AddHandler oForm.FormClosed, Sub()
|
||||
LastSearchForm = Nothing
|
||||
End Sub
|
||||
|
||||
LastSearchForm = oForm
|
||||
End If
|
||||
|
||||
oItemsFound = True
|
||||
Else
|
||||
Display_InfoItem("No results for this searchcombination!", Color.OrangeRed, Color.White)
|
||||
End If
|
||||
@@ -605,7 +636,9 @@ Public Class frmSearchStart
|
||||
Finally
|
||||
SplashScreenManager.CloseOverlayForm(oHandle)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Return oItemsFound
|
||||
End Function
|
||||
|
||||
Private Function GetResultFormLocation() As Point
|
||||
Dim oX = Location.X
|
||||
@@ -628,17 +661,24 @@ Public Class frmSearchStart
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Private Class FilterTimeframe
|
||||
Public Property Name As String
|
||||
Public Property From As Date
|
||||
Public Property [To] As Date
|
||||
Public Property DisableFilter As Boolean = False
|
||||
Public Property CustomFilter As Boolean = False
|
||||
Private Sub MinimizeSearchForm()
|
||||
HeightBeforeMinimizing = Height
|
||||
Height = 200
|
||||
|
||||
BarButtonItem2.Visibility = DevExpress.XtraBars.BarItemVisibility.Always
|
||||
End Sub
|
||||
|
||||
Private Sub RestoreSearchForm()
|
||||
Height = HeightBeforeMinimizing
|
||||
|
||||
If LastSearchForm IsNot Nothing Then
|
||||
LastSearchForm.Location = GetResultFormLocation()
|
||||
End If
|
||||
|
||||
BarButtonItem2.Visibility = DevExpress.XtraBars.BarItemVisibility.Never
|
||||
End Sub
|
||||
|
||||
|
||||
Public Overrides Function ToString() As String
|
||||
Return Name.ToString
|
||||
End Function
|
||||
End Class
|
||||
|
||||
Private Sub cmbFilterTimeframe_EditValueChanged(sender As Object, e As EventArgs) Handles cmbFilterTimeframe.EditValueChanged
|
||||
Dim oTimeframe As FilterTimeframe = DirectCast(cmbFilterTimeframe.EditValue, FilterTimeframe)
|
||||
@@ -668,4 +708,16 @@ Public Class frmSearchStart
|
||||
My.UIConfig.SearchForm.Size = Size
|
||||
My.UIConfigManager.Save()
|
||||
End Sub
|
||||
|
||||
Private Sub BarButtonItem1_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles BarButtonItem1.ItemClick
|
||||
RestoreSearchForm()
|
||||
End Sub
|
||||
|
||||
Private Sub BarButtonItem2_ItemClick_1(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles BarButtonItem2.ItemClick
|
||||
RestoreSearchForm()
|
||||
End Sub
|
||||
|
||||
Private Sub RibbonControl1_MinimizedRibbonHiding(sender As Object, e As DevExpress.XtraBars.Ribbon.MinimizedRibbonEventArgs) Handles RibbonControl1.MinimizedRibbonHiding
|
||||
|
||||
End Sub
|
||||
End Class
|
||||
Reference in New Issue
Block a user