mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-25 06:16:37 +01:00
Merge pull request #1697 from kc284/CA-257927
CA-257927: Fixed scalability issue where XenCenter froze momentarily when disconnecting from a host with a big number of alerts
This commit is contained in:
commit
2a4335231e
@ -131,19 +131,37 @@ namespace XenAdmin.Controls.MainWindowControls
|
||||
|
||||
public void XenConnectionCollectionChanged(CollectionChangeEventArgs e)
|
||||
{
|
||||
IXenConnection connection = (IXenConnection)e.Element;
|
||||
if (connection == null)
|
||||
return;
|
||||
IXenConnection connection = e.Element as IXenConnection;
|
||||
|
||||
if (e.Action == CollectionChangeAction.Add)
|
||||
{
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
connection.BeforeMajorChange += Connection_BeforeMajorChange;
|
||||
connection.AfterMajorChange += Connection_AfterMajorChange;
|
||||
}
|
||||
else if (e.Action == CollectionChangeAction.Remove)
|
||||
{
|
||||
connection.BeforeMajorChange -= Connection_BeforeMajorChange;
|
||||
connection.AfterMajorChange -= Connection_AfterMajorChange;
|
||||
var range = new List<IXenConnection>();
|
||||
if (connection != null)
|
||||
{
|
||||
range.Add(connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
var r = e.Element as List<IXenConnection>;
|
||||
if (r != null)
|
||||
range = r;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var con in range)
|
||||
{
|
||||
con.BeforeMajorChange -= Connection_BeforeMajorChange;
|
||||
con.AfterMajorChange -= Connection_AfterMajorChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ namespace XenAdmin.Dialogs.WarningDialogs
|
||||
{
|
||||
Program.BeginInvoke(this, () =>
|
||||
{
|
||||
ActionBase action = (ActionBase)e.Element;
|
||||
ActionBase action = e.Element as ActionBase;
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
@ -211,7 +211,16 @@ namespace XenAdmin.Dialogs.WarningDialogs
|
||||
}
|
||||
break;
|
||||
case CollectionChangeAction.Remove:
|
||||
RemoveActionRow(action);
|
||||
if (action != null)
|
||||
{
|
||||
RemoveActionRow(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<ActionBase>;
|
||||
if (range != null)
|
||||
BuildList();
|
||||
}
|
||||
break;
|
||||
case CollectionChangeAction.Refresh:
|
||||
BuildList();
|
||||
|
@ -393,14 +393,15 @@ namespace XenAdmin
|
||||
|
||||
Program.BeginInvoke(Program.MainWindow, () =>
|
||||
{
|
||||
ActionBase action = (ActionBase)e.Element;
|
||||
if (action == null)
|
||||
return;
|
||||
|
||||
ActionBase action = e.Element as ActionBase;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
{
|
||||
if (action == null)
|
||||
return;
|
||||
|
||||
var meddlingAction = action as MeddlingAction;
|
||||
if (meddlingAction == null)
|
||||
{
|
||||
@ -419,9 +420,28 @@ namespace XenAdmin
|
||||
}
|
||||
case CollectionChangeAction.Remove:
|
||||
{
|
||||
action.Changed -= actionChanged;
|
||||
action.Completed -= actionCompleted;
|
||||
|
||||
if (action != null)
|
||||
{
|
||||
action.Changed -= actionChanged;
|
||||
action.Completed -= actionCompleted;
|
||||
}
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<ActionBase>;
|
||||
if (range != null)
|
||||
{
|
||||
foreach (var a in range)
|
||||
{
|
||||
a.Changed -= actionChanged;
|
||||
a.Completed -= actionCompleted;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int errors = ConnectionsManager.History.Count(a => a.IsCompleted && !a.Succeeded);
|
||||
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Events, errors);
|
||||
|
||||
@ -752,14 +772,15 @@ namespace XenAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
IXenConnection connection = (IXenConnection)e.Element;
|
||||
if (connection == null)
|
||||
return;
|
||||
IXenConnection connection = e.Element as IXenConnection;
|
||||
|
||||
navigationPane.XenConnectionCollectionChanged(e);
|
||||
|
||||
if (e.Action == CollectionChangeAction.Add)
|
||||
{
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
connection.ClearingCache += connection_ClearingCache;
|
||||
connection.ConnectionResult += Connection_ConnectionResult;
|
||||
connection.ConnectionLost += Connection_ConnectionLost;
|
||||
@ -779,38 +800,54 @@ namespace XenAdmin
|
||||
}
|
||||
else if (e.Action == CollectionChangeAction.Remove)
|
||||
{
|
||||
connection.ClearingCache -= connection_ClearingCache;
|
||||
connection.ConnectionResult -= Connection_ConnectionResult;
|
||||
connection.ConnectionLost -= Connection_ConnectionLost;
|
||||
connection.ConnectionClosed -= Connection_ConnectionClosed;
|
||||
connection.ConnectionReconnecting -= connection_ConnectionReconnecting;
|
||||
connection.XenObjectsUpdated -= Connection_XenObjectsUpdated;
|
||||
connection.Cache.DeregisterCollectionChanged<XenAPI.Message>(MessageCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<Folder>(FolderCollectionChangedWithInvoke);
|
||||
|
||||
connection.Cache.DeregisterCollectionChanged<Task>(TaskCollectionChangedWithInvoke);
|
||||
|
||||
connection.CachePopulated -= connection_CachePopulated;
|
||||
|
||||
foreach (VM vm in connection.Cache.VMs)
|
||||
var range = new List<IXenConnection>();
|
||||
if (connection != null)
|
||||
{
|
||||
ConsolePanel.closeVNCForSource(vm);
|
||||
range.Add(connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
var r = e.Element as List<IXenConnection>;
|
||||
if (r != null)
|
||||
range = r;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Host host in connection.Cache.Hosts)
|
||||
foreach (var con in range)
|
||||
{
|
||||
ConsolePanel.closeVNCForSource(host.ControlDomainZero);
|
||||
con.ClearingCache -= connection_ClearingCache;
|
||||
con.ConnectionResult -= Connection_ConnectionResult;
|
||||
con.ConnectionLost -= Connection_ConnectionLost;
|
||||
con.ConnectionClosed -= Connection_ConnectionClosed;
|
||||
con.ConnectionReconnecting -= connection_ConnectionReconnecting;
|
||||
con.XenObjectsUpdated -= Connection_XenObjectsUpdated;
|
||||
con.Cache.DeregisterCollectionChanged<XenAPI.Message>(MessageCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<Folder>(FolderCollectionChangedWithInvoke);
|
||||
|
||||
foreach (VM vm in host.OtherControlDomains)
|
||||
CvmConsolePanel.closeVNCForSource(vm);
|
||||
con.Cache.DeregisterCollectionChanged<Task>(TaskCollectionChangedWithInvoke);
|
||||
|
||||
con.CachePopulated -= connection_CachePopulated;
|
||||
|
||||
foreach (VM vm in con.Cache.VMs)
|
||||
{
|
||||
ConsolePanel.closeVNCForSource(vm);
|
||||
}
|
||||
|
||||
foreach (Host host in con.Cache.Hosts)
|
||||
{
|
||||
ConsolePanel.closeVNCForSource(host.ControlDomainZero);
|
||||
|
||||
foreach (VM vm in host.OtherControlDomains)
|
||||
CvmConsolePanel.closeVNCForSource(vm);
|
||||
}
|
||||
|
||||
con.EndConnect();
|
||||
}
|
||||
|
||||
connection.EndConnect();
|
||||
|
||||
RequestRefreshTreeView();
|
||||
//CA-41228 refresh submenu items when there are no connections
|
||||
SelectionManager.RefreshSelection();
|
||||
|
@ -40,8 +40,6 @@ using System.Windows.Forms;
|
||||
using XenAdmin.Controls;
|
||||
using XenAdmin.Core;
|
||||
using XenAdmin.Dialogs;
|
||||
using XenAdmin.Network;
|
||||
using XenAPI;
|
||||
using XenAdmin.Alerts;
|
||||
using XenAdmin.Help;
|
||||
using System.Threading;
|
||||
@ -411,7 +409,6 @@ namespace XenAdmin.TabPages
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private DataGridViewRow FindAlertRow(ToolStripMenuItem toolStripMenuItem)
|
||||
{
|
||||
@ -425,6 +422,8 @@ namespace XenAdmin.TabPages
|
||||
select row).FirstOrDefault();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ToolStripMenuItemHelp_Click(object sender, EventArgs e)
|
||||
{
|
||||
// We should only be here if one item is selected, we dont do multi-help
|
||||
@ -585,24 +584,27 @@ namespace XenAdmin.TabPages
|
||||
private void AlertsCollectionChanged(object sender, CollectionChangeEventArgs e)
|
||||
{
|
||||
Program.AssertOnEventThread();
|
||||
if (e.Element == null)
|
||||
{
|
||||
// We take the null element to mean there has been a batch remove
|
||||
Rebuild();
|
||||
return;
|
||||
}
|
||||
Alert a = e.Element as Alert;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
Rebuild(); // rebuild entire alert list to ensure filtering and sorting
|
||||
break;
|
||||
case CollectionChangeAction.Remove:
|
||||
RemoveAlertRow(a);
|
||||
|
||||
var a = e.Element as Alert;
|
||||
if (a != null)
|
||||
RemoveAlertRow(a);
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<Alert>;
|
||||
if (range != null)
|
||||
Rebuild();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void UpdateActionEnablement()
|
||||
{
|
||||
toolStripButtonExportAll.Enabled = Alert.NonDismissingAlertCount > 0;
|
||||
|
2
XenAdmin/TabPages/HistoryPage.Designer.cs
generated
2
XenAdmin/TabPages/HistoryPage.Designer.cs
generated
@ -13,7 +13,7 @@ namespace XenAdmin.TabPages
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
ConnectionsManager.XenConnections.CollectionChanged -= History_CollectionChanged;
|
||||
ConnectionsManager.History.CollectionChanged -= History_CollectionChanged;
|
||||
XenAdmin.Actions.ActionBase.NewAction -= Action_NewAction;
|
||||
|
||||
if (disposing && (components != null))
|
||||
|
@ -93,7 +93,7 @@ namespace XenAdmin.TabPages
|
||||
{
|
||||
Program.BeginInvoke(this, () =>
|
||||
{
|
||||
ActionBase action = (ActionBase)e.Element;
|
||||
ActionBase action = e.Element as ActionBase;
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
@ -104,7 +104,16 @@ namespace XenAdmin.TabPages
|
||||
InsertActionRow(index, action);
|
||||
break;
|
||||
case CollectionChangeAction.Remove:
|
||||
RemoveActionRow(action);
|
||||
if (action != null)
|
||||
{
|
||||
RemoveActionRow(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<ActionBase>;
|
||||
if (range != null)
|
||||
BuildRowList();
|
||||
}
|
||||
break;
|
||||
case CollectionChangeAction.Refresh:
|
||||
BuildRowList();
|
||||
|
@ -91,20 +91,24 @@ namespace XenAdmin.TabPages
|
||||
private void UpdatesCollectionChanged(object sender, CollectionChangeEventArgs e)
|
||||
{
|
||||
Program.AssertOnEventThread();
|
||||
if (e.Element == null)
|
||||
{
|
||||
// We take the null element to mean there has been a batch remove
|
||||
Rebuild();
|
||||
return;
|
||||
}
|
||||
Alert a = e.Element as Alert;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
Rebuild(); // rebuild entire alert list to ensure filtering and sorting
|
||||
break;
|
||||
case CollectionChangeAction.Remove:
|
||||
RemoveUpdateRow(a);
|
||||
Alert a = e.Element as Alert;
|
||||
if (a != null)
|
||||
{
|
||||
RemoveUpdateRow(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<Alert>;
|
||||
if (range != null)
|
||||
Rebuild();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -230,19 +230,40 @@ namespace XenAdmin.TabPages
|
||||
void History_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
||||
{
|
||||
Program.BeginInvoke(Program.MainWindow, () =>
|
||||
{
|
||||
SrRefreshAction a = e.Element as SrRefreshAction;
|
||||
if (a == null)
|
||||
return;
|
||||
{
|
||||
SrRefreshAction a = e.Element as SrRefreshAction;
|
||||
|
||||
if (e.Action == CollectionChangeAction.Add)
|
||||
a.Completed += a_Completed;
|
||||
if (e.Action == CollectionChangeAction.Add)
|
||||
{
|
||||
if (a != null)
|
||||
a.Completed += a_Completed;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Action == CollectionChangeAction.Remove)
|
||||
a.Completed -= a_Completed;
|
||||
if (e.Action == CollectionChangeAction.Remove)
|
||||
{
|
||||
if (a != null)
|
||||
{
|
||||
a.Completed -= a_Completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<SrRefreshAction>;
|
||||
if (range != null)
|
||||
{
|
||||
foreach (var sra in range)
|
||||
sra.Completed -= a_Completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RefreshButtons();
|
||||
});
|
||||
RefreshButtons();
|
||||
});
|
||||
}
|
||||
|
||||
void a_Completed(ActionBase sender)
|
||||
|
@ -84,7 +84,10 @@ namespace XenAdmin.Core
|
||||
{
|
||||
var toRemove = FindAll(match);
|
||||
base.RemoveAll(match);
|
||||
toRemove.ForEach(item => OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, item)));
|
||||
if (toRemove.Count > 1)
|
||||
OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, toRemove));
|
||||
else if (toRemove.Count == 1)
|
||||
OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, toRemove[0]));
|
||||
return toRemove.Count;
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,6 @@ namespace XenAdmin.Model
|
||||
{
|
||||
public class DockerContainers
|
||||
{
|
||||
static DockerContainers()
|
||||
{
|
||||
}
|
||||
|
||||
public static void InitDockerContainers()
|
||||
{
|
||||
Trace.Assert(InvokeHelper.Synchronizer != null);
|
||||
@ -64,22 +60,31 @@ namespace XenAdmin.Model
|
||||
private static void XenConnections_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
||||
{
|
||||
InvokeHelper.BeginInvoke(() =>
|
||||
{
|
||||
IXenConnection connection = e.Element as IXenConnection;
|
||||
if (connection == null)
|
||||
return;
|
||||
{
|
||||
IXenConnection connection = e.Element as IXenConnection;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
AddConnection(connection);
|
||||
break;
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
if (connection != null)
|
||||
AddConnection(connection);
|
||||
break;
|
||||
|
||||
case CollectionChangeAction.Remove:
|
||||
RemoveConnection(connection);
|
||||
break;
|
||||
}
|
||||
});
|
||||
case CollectionChangeAction.Remove:
|
||||
if (connection != null)
|
||||
{
|
||||
RemoveConnection(connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<IXenConnection>;
|
||||
if (range != null)
|
||||
foreach (var con in range)
|
||||
RemoveConnection(con);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static CollectionChangeEventHandler CollectionChangedWithInvoke;
|
||||
@ -106,9 +111,8 @@ namespace XenAdmin.Model
|
||||
{
|
||||
InvokeHelper.AssertOnEventThread();
|
||||
|
||||
Trace.Assert(e.Element is VM);
|
||||
|
||||
var vm = e.Element as VM;
|
||||
Trace.Assert(vm != null, "The item changed is not a VM");
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
|
@ -74,24 +74,32 @@ namespace XenAdmin.Model
|
||||
|
||||
private static void XenConnections_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
||||
{
|
||||
//Program.AssertOnEventThread();
|
||||
InvokeHelper.BeginInvoke(() =>
|
||||
{
|
||||
IXenConnection connection = e.Element as IXenConnection;
|
||||
if (connection == null)
|
||||
return;
|
||||
{
|
||||
IXenConnection connection = e.Element as IXenConnection;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
AddConnection(connection);
|
||||
break;
|
||||
switch (e.Action)
|
||||
{
|
||||
case CollectionChangeAction.Add:
|
||||
if (connection != null)
|
||||
AddConnection(connection);
|
||||
break;
|
||||
|
||||
case CollectionChangeAction.Remove:
|
||||
RemoveConnection(connection);
|
||||
break;
|
||||
}
|
||||
});
|
||||
case CollectionChangeAction.Remove:
|
||||
if (connection != null)
|
||||
{
|
||||
RemoveConnection(connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
var range = e.Element as List<IXenConnection>;
|
||||
if (range != null)
|
||||
foreach (var con in range)
|
||||
RemoveConnection(con);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static CollectionChangeEventHandler CollectionChangedWithInvoke;
|
||||
|
@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using XenAdmin.Network;
|
||||
using XenAPI;
|
||||
@ -93,11 +94,12 @@ namespace XenAdmin
|
||||
return;
|
||||
|
||||
IXenConnection connection = e.Element as IXenConnection;
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
if (e.Action == CollectionChangeAction.Add)
|
||||
{
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
connection.Cache.RegisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
|
||||
connection.Cache.RegisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
|
||||
connection.Cache.RegisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
|
||||
@ -112,15 +114,32 @@ namespace XenAdmin
|
||||
}
|
||||
else if (e.Action == CollectionChangeAction.Remove)
|
||||
{
|
||||
connection.Cache.DeregisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<VDI>(VDICollectionChangedWithInvoke);
|
||||
connection.Cache.DeregisterCollectionChanged<XenAPI.Network>(NetworkCollectionChangedWithInvoke);
|
||||
var range = new List<IXenConnection>();
|
||||
if (connection != null)
|
||||
{
|
||||
range.Add(connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
var r = e.Element as List<IXenConnection>;
|
||||
if (r != null)
|
||||
range = r;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
connection.XenObjectsUpdated -= connection_XenObjectsUpdated;
|
||||
connection.ConnectionStateChanged -= connection_ConnectionStateChanged;
|
||||
foreach (var con in range)
|
||||
{
|
||||
con.Cache.DeregisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<VDI>(VDICollectionChangedWithInvoke);
|
||||
con.Cache.DeregisterCollectionChanged<XenAPI.Network>(NetworkCollectionChangedWithInvoke);
|
||||
|
||||
con.XenObjectsUpdated -= connection_XenObjectsUpdated;
|
||||
con.ConnectionStateChanged -= connection_ConnectionStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
MarkEventsReadyToFire(true);
|
||||
|
Loading…
Reference in New Issue
Block a user