/* Copyright (c) Citrix Systems Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using XenAdmin.Core; using XenAdmin.Network; using XenAPI; using XenAdmin.Alerts; using XenAdmin.Help; using System.Threading; using XenAdmin.Actions; using System.IO; namespace XenAdmin.Dialogs { public partial class AlertSummaryDialog : XenDialogBase { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly int ALERT_CAP = 1000; public AlertSummaryDialog() { InitializeComponent(); toolStripDropDownButtonDateFilter.FilterChanged += toolStripDropDownButtonDateFilter_FilterChanged; toolStripDropDownButtonServerFilter.FilterChanged += toolStripDropDownButtonServerFilter_FilterChanged; GridViewAlerts.Sort(ColumnDate, ListSortDirection.Descending); LabelCappingEntries.Text = String.Format(Messages.ALERT_CAP_LABEL, ALERT_CAP); GridViewAlerts.ScrollBars = ScrollBars.Vertical; UpdateActionEnablement(); toolStripDropDownButtonServerFilter.InitializeHostList(); toolStripDropDownButtonServerFilter.BuildFilterList(); Alert.XenCenterAlerts.CollectionChanged += CollectionChanged; } void AlertSummaryDialog_Load(object sender, EventArgs e) { Rebuild(); } private void SetFilterLabel() { bool filterIsOn = toolStripDropDownButtonDateFilter.FilterIsOn || toolStripDropDownButtonServerFilter.FilterIsOn; toolStripLabelFiltersOnOff.Text = filterIsOn ? Messages.FILTERS_ON : Messages.FILTERS_OFF; } #region AlertListCode Dictionary expandedState = new Dictionary(); private bool inAlertBuild; private bool retryAlertBuild; private void Rebuild() { log.Debug("Rebuilding alertList"); Thread t = new Thread(_Rebuild); t.Name = "Building alert list"; t.IsBackground = true; t.Start(); } private void _Rebuild() { log.Debug("Rebuilding alertList: Starting background thread"); Program.AssertOffEventThread(); lock (GridViewAlerts) { if (inAlertBuild) { // queue up a rebuild after the current one has completed log.Debug("Rebuilding alertList: In build already, exiting"); retryAlertBuild = true; return; } inAlertBuild = true; log.Debug("Rebuilding alertList: taking inAlertBuild lock"); } try { // 1) Add all the alerts that have not been filtered out to an array // 2) Create rows for each of these // 3) Sort them // 4) Take the top n as set by the filters // 5) Add them to the control using the optimized AddRange() Program.Invoke(this, SetFilterLabel); List alerts = Alert.NonDismissingAlerts; alerts.RemoveAll(filterAlert); log.DebugFormat("Rebuilding alertList: there are {0} alerts in total. After filtering we have {1}", Alert.AlertCount, alerts.Count); if (GridViewAlerts.SortedColumn != null) { if (GridViewAlerts.SortedColumn.Index == ColumnDetails.Index) { alerts.Sort(Alert.CompareOnTitle); } else if (GridViewAlerts.SortedColumn.Index == ColumnDate.Index) { alerts.Sort(Alert.CompareOnDate); } else if (GridViewAlerts.SortedColumn.Index == ColumnAppliesTo.Index) { alerts.Sort(Alert.CompareOnAppliesTo); } else if (GridViewAlerts.SortedColumn.Index == ColumnType.Index) { alerts.Sort(Alert.CompareOnPriority); } if (GridViewAlerts.SortOrder == SortOrder.Descending) { alerts.Reverse(); } } int alertsFound = alerts.Count; if (ALERT_CAP < alerts.Count) { log.DebugFormat("Rebuilding alertList: hit alert cap, hiding {0} alerts", alerts.Count - ALERT_CAP); alerts.RemoveRange(ALERT_CAP, alerts.Count - ALERT_CAP); } Program.Invoke(this, delegate { List gridRows = new List(); log.Debug("Rebuilding alertList: Adding alert rows"); foreach (Alert alert in alerts) gridRows.Add(NewAlertRow(alert)); log.DebugFormat("Rebuilding alertList: Added {0} rows", gridRows.Count); List selection = (GridViewAlerts.SelectedRows.Cast().Select( selectedRow => ((Alert)selectedRow.Tag).uuid)).ToList(); GridViewAlerts.Rows.Clear(); log.Debug("Rebuilding alertList: Cleared rows"); GridViewAlerts.Rows.AddRange(gridRows.ToArray()); log.DebugFormat("Rebuilding alertList: Added {0} rows to the grid", GridViewAlerts.Rows.Count); LabelCappingEntries.Visible = (alertsFound > ALERT_CAP); //restore selection if (selection.Count > 0) { log.Debug("Rebuilding alertList: Restoring alert selection"); foreach (DataGridViewRow alertRow in GridViewAlerts.Rows) { alertRow.Selected = selection.Contains(((Alert)alertRow.Tag).uuid); } if (GridViewAlerts.SelectedRows.Count == 0 && GridViewAlerts.Rows.Count > 0) { GridViewAlerts.Rows[0].Selected = true; } log.DebugFormat("Rebuilding alertList: Selected {0} alerts", selection.Count); } UpdateActionEnablement(); }); } catch (Exception e) { log.ErrorFormat("Encountered exception when building list: {0}", e); } finally { log.Debug("Rebuilding alertList: Waiting for lock to clear inAlertBuild"); lock (GridViewAlerts) { inAlertBuild = false; log.Debug("Rebuilding alertList: cleared inAlertBuild"); if (retryAlertBuild) { // we received a request to build while we were building, rebuild in case we missed something retryAlertBuild = false; log.Debug("Rebuilding alertList: we received a request to build while we were building, rebuild in case we missed something"); _Rebuild(); } } } } private DataGridViewRow NewAlertRow(Alert alert) { DataGridViewImageCell expanderCell = new DataGridViewImageCell(); DataGridViewImageCell imageCell = new DataGridViewImageCell(); DataGridViewTextBoxCell appliesCell = new DataGridViewTextBoxCell(); DataGridViewTextBoxCell detailCell = new DataGridViewTextBoxCell(); DataGridViewTextBoxCell dateCell = new DataGridViewTextBoxCell(); DataGridViewRow newRow = new DataGridViewRow(); newRow.Tag = alert; // Get the relevant image for the row depending on the type of the alert Image typeImage = alert is MessageAlert && ((MessageAlert)alert).Message.ShowOnGraphs ? Images.GetImage16For(((MessageAlert)alert).Message.Type) : Images.GetImage16For(alert.Priority); imageCell.Value = typeImage; // Set the detail cell content and expanding arrow if (expandedState.ContainsKey(alert.uuid)) { // show the expanded arrow and the body detail expanderCell.Value = Properties.Resources.expanded_triangle; detailCell.Value = String.Format("{0}\n\n{1}", alert.Title, alert.Description); } else { // show the expand arrow and just the title expanderCell.Value = Properties.Resources.contracted_triangle; detailCell.Value = alert.Title; } appliesCell.Value = alert.AppliesTo; dateCell.Value = HelpersGUI.DateTimeToString(alert.Timestamp.ToLocalTime(), Messages.DATEFORMAT_DMY_HM, true); newRow.Cells.Add(expanderCell); newRow.Cells.Add(imageCell); newRow.Cells.Add(appliesCell); newRow.Cells.Add(detailCell); newRow.Cells.Add(dateCell); return newRow; } #region Alert Row Filtering code /// /// Runs all the current filters on the alert to determine if it should be shown in the list or not. /// /// private bool filterAlert(Alert alert) { bool hide = false; Program.Invoke(this, () => hide = toolStripDropDownButtonDateFilter.HideByDate(alert.Timestamp.ToLocalTime()) || toolStripDropDownButtonServerFilter.HideByLocation(alert.HostUuid)); return hide || HiddenBySeverityFilter(alert); } private bool HiddenBySeverityFilter(Alert alert) { return !((dataLossImminentToolStripMenuItem.Checked && alert.Priority == AlertPriority.Priority1) || (serviceLossImminentToolStripMenuItem.Checked && alert.Priority == AlertPriority.Priority2) || (serviceDegradedToolStripMenuItem.Checked && alert.Priority == AlertPriority.Priority3) || (serviceRecoveredToolStripMenuItem.Checked && alert.Priority == AlertPriority.Priority4) || (informationalToolStripMenuItem.Checked && alert.Priority == AlertPriority.Priority5) || unknownToolStripMenuItem.Checked && alert.Priority == AlertPriority.Unknown); } #endregion private void RemoveAlertRow(Alert a) { for (int i = 0; i < GridViewAlerts.Rows.Count; i++) { if (((Alert)GridViewAlerts.Rows[i].Tag).uuid == a.uuid) GridViewAlerts.Rows.RemoveAt(i); } if (GridViewAlerts.Rows.Count < ALERT_CAP) LabelCappingEntries.Visible = false; } private void GridViewAlerts_MouseDown(object sender, MouseEventArgs e) { //CA-66824, CA-71820 if (e.Button == MouseButtons.Right) { var hitTest = GridViewAlerts.HitTest(e.X, e.Y); if (hitTest.RowIndex < 0 || hitTest.RowIndex >= GridViewAlerts.RowCount) return; if (hitTest.ColumnIndex < 0 || hitTest.ColumnIndex >= GridViewAlerts.ColumnCount) return; GridViewAlerts.ClearSelection(); GridViewAlerts[hitTest.ColumnIndex, hitTest.RowIndex].Selected = true; } } private void AlertsGridView_CellClick(object sender, DataGridViewCellEventArgs e) { // If you click on the headers you can get -1 as the index. if (e.ColumnIndex < 0 || e.RowIndex < 0 || e.ColumnIndex != ColumnExpand.Index) return; toggleExpandedState(e.RowIndex); } private void AlertsGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { // If you click on the headers you can get -1 as the index. if (e.ColumnIndex < 0 || e.RowIndex < 0) return; toggleExpandedState(e.RowIndex); } /// /// Toggles the row specified between the expanded and contracted state /// /// /// private void toggleExpandedState(int RowIndex) { Alert alert = (Alert)GridViewAlerts.Rows[RowIndex].Tag; if (expandedState.ContainsKey(alert.uuid)) { expandedState.Remove(alert.uuid); GridViewAlerts.Rows[RowIndex].Cells[ColumnDetails.Index].Value = alert.Title; GridViewAlerts.Rows[RowIndex].Cells[ColumnExpand.Index].Value = Properties.Resources.contracted_triangle; } else { expandedState.Add(alert.uuid, true); GridViewAlerts.Rows[RowIndex].Cells[ColumnDetails.Index].Value = String.Format("{0}\n\n{1}", alert.Title, alert.Description); GridViewAlerts.Rows[RowIndex].Cells[ColumnExpand.Index].Value = Properties.Resources.expanded_triangle; } } #region sorting code /// /// Handles the automatic sorting of the AlertsGridView for the non-string columns /// /// /// private void AlertsGridView_SortCompare(object sender, DataGridViewSortCompareEventArgs e) { Alert alert1 = (Alert)GridViewAlerts.Rows[e.RowIndex1].Tag; Alert alert2 = (Alert)GridViewAlerts.Rows[e.RowIndex2].Tag; if (e.Column.Index == ColumnDate.Index) { int SortResult = DateTime.Compare(alert1.Timestamp, alert2.Timestamp); e.SortResult = (GridViewAlerts.SortOrder == SortOrder.Descending) ? SortResult *= -1 : SortResult; e.Handled = true; } else if (e.Column.Index == ColumnType.Index) { e.SortResult = Alert.CompareOnPriority(alert1, alert2); e.Handled = true; } } #endregion #endregion #region Alert Actions Logic private void ButtonHelp_Click(object sender, EventArgs e) { // We should only be here if one item is selected, we dont do multi-help if (GridViewAlerts.SelectedRows.Count != 1) log.ErrorFormat("Can only launch help for 1 alert at a time (Attempted to launch {0}). Launching for the first one in the list)", GridViewAlerts.SelectedRows.Count); Alert alert = (Alert)GridViewAlerts.SelectedRows[0].Tag; if (alert.HelpID == null) { log.ErrorFormat("Attempted to launch help for alert {0} ({1}) but no helpID available. Not launching.", alert.Title, alert.uuid); return; } HelpManager.Launch(alert.HelpID); } private void ButtonFix_Click(object sender, EventArgs e) { // We should only be here if one item is selected, we dont do multi-fix if (GridViewAlerts.SelectedRows.Count == 0) { log.ErrorFormat("Attempted to fix alert with no alert selected."); return; } if (GridViewAlerts.SelectedRows.Count != 1) log.ErrorFormat("Only 1 alert can be fixed at a time (Attempted to fix {0}). Fixing the first one in the list.", GridViewAlerts.SelectedRows.Count); Alert alert = (Alert)GridViewAlerts.SelectedRows[0].Tag; if (alert.FixLinkAction == null) { log.ErrorFormat("Attempted to fix alert {0} ({1}) but no fix link action available. Not fixing.", alert.Title, alert.uuid); return; } alert.FixLinkAction.Invoke(); } private void ButtonDismiss_Click(object sender, EventArgs e) { if (new ThreeButtonDialog( new ThreeButtonDialog.Details(null, Messages.ALERT_DISMISS_CONFIRM, Messages.XENCENTER), ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo).ShowDialog(this) != DialogResult.Yes) return; Alert[] selectedAlerts = new Alert[GridViewAlerts.SelectedRows.Count]; for (int i = 0; i < selectedAlerts.Length; i++) selectedAlerts[i] = (Alert)GridViewAlerts.SelectedRows[i].Tag; DismissAlerts(selectedAlerts); } private void DismissAllButton_Click(object sender, EventArgs e) { DialogResult result; if (GridViewAlerts.Rows.Count == Alert.Alerts.Length) //no filter, only two buttons result = new ThreeButtonDialog( new ThreeButtonDialog.Details(null, Messages.ALERT_DISMISS_ALL_NO_FILTER_CONTINUE), "DismissAllAlertsConfirmationDialog", new ThreeButtonDialog.TBDButton(Messages.DISMISS_ALL_YES_CONFIRM_BUTTON, DialogResult.Yes), ThreeButtonDialog.ButtonCancel).ShowDialog(this); else result = new ThreeButtonDialog( new ThreeButtonDialog.Details(null, Messages.ALERT_DISMISS_ALL_CONTINUE), "DismissAllAlertsConfirmationDialog", new ThreeButtonDialog.TBDButton(Messages.DISMISS_ALL_CONFIRM_BUTTON, DialogResult.Yes), new ThreeButtonDialog.TBDButton(Messages.DISMISS_FILTERED_CONFIRM_BUTTON, DialogResult.No, ThreeButtonDialog.ButtonType.NONE), ThreeButtonDialog.ButtonCancel).ShowDialog(this); if (result == DialogResult.Cancel) return; List dismissingAlerts = new List(); if (result == DialogResult.No) { //Dismiss Filtered for (int i = 0; i < GridViewAlerts.Rows.Count; i++) dismissingAlerts.Add((Alert)GridViewAlerts.Rows[i].Tag); } else { //Dismiss All dismissingAlerts.AddRange(Alert.Alerts); } dismissingAlerts.RemoveAll(delegate(Alert a) { return !AllowedToDismiss(a.Connection); }); DismissAlerts(dismissingAlerts.ToArray()); } private void DismissAlerts(Alert[] alerts) { List local_alerts = new List(); // we group the alerts by connection ready for dismissal Dictionary> alertGroups = new Dictionary>(); foreach (Alert a in alerts) { if (a.Dismissing) continue; IXenConnection c = a.Connection; if (c == null) { local_alerts.Add(a); } else { if (!alertGroups.ContainsKey(c)) alertGroups[c] = new List(); List l = alertGroups[c]; l.Add(a); } a.Dismissing = true; } // When we refresh now the alerts we are dismissing will be removed from the GridViewAlerts control as their dismissing flag // is set. This also updates the numbers in the filter list correctly. toolStripDropDownButtonServerFilter.RefreshLists(); foreach (IXenConnection c in alertGroups.Keys) { _DismissAlerts(c, alertGroups[c]); } if (local_alerts.Count > 0) _DismissAlerts(null, local_alerts); } /// May be null, in which case this is expected to be for client-side alerts. private static void _DismissAlerts(IXenConnection connection, List alerts) { new DeleteAllAlertsAction(connection, alerts).RunAsync(); } #endregion private void CollectionChanged(object sender, CollectionChangeEventArgs e) { Program.BeginInvoke(this, (CollectionChangeEventHandler)CollectionChanged_, sender, e); } private void CollectionChanged_(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); break; } } private void closeButton_Click(object sender, EventArgs e) { this.Close(); } protected override void OnFormClosing(FormClosingEventArgs e) { Alert.XenCenterAlerts.CollectionChanged -= CollectionChanged; base.OnFormClosing(e); } private void AlertsGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) { if (GridViewAlerts.Columns[e.ColumnIndex].SortMode == DataGridViewColumnSortMode.Automatic) { Rebuild(); } } private void GridViewAlerts_SelectionChanged(object sender, EventArgs e) { // stop the buttons getting enabled/disabled during refresh, the rebuild will set them once it's finished if (inAlertBuild) return; UpdateActionEnablement(); } private void UpdateActionEnablement() { if (GridViewAlerts.SelectedRows.Count == 0) { toolStripButtonFix.Visible = toolStripButtonHelp.Visible = false; toolStripButtonDismiss.Visible = true; toolStripButtonDismiss.Enabled = false; } else if (GridViewAlerts.SelectedRows.Count > 1) { toolStripButtonDismiss.Visible = true; // the only multiselect action we support is a dismiss toolStripButtonDismiss.Enabled = AllowedToDismissSelected(); toolStripButtonDismiss.AutoToolTip = !toolStripButtonDismiss.Enabled; toolStripButtonDismiss.ToolTipText = toolStripButtonDismiss.Enabled ? string.Empty : Messages.DELETE_MESSAGE_RBAC_BLOCKED; toolStripButtonFix.Visible = toolStripButtonHelp.Visible = false; } else { toolStripButtonDismiss.Visible = true; toolStripButtonDismiss.Enabled = AllowedToDismissSelected(); toolStripButtonDismiss.AutoToolTip = !toolStripButtonDismiss.Enabled; toolStripButtonDismiss.ToolTipText = toolStripButtonDismiss.Enabled ? string.Empty : Messages.DELETE_MESSAGE_RBAC_BLOCKED; Alert alert = (Alert)GridViewAlerts.SelectedRows[0].Tag; if (!string.IsNullOrEmpty(alert.HelpID)) { toolStripButtonHelp.Visible = true; toolStripButtonHelp.Text = alert.HelpLinkText; } else { toolStripButtonHelp.Visible = false; } if (string.IsNullOrEmpty(alert.FixLinkText) || alert.FixLinkAction == null) { toolStripButtonFix.Visible = false; } else { toolStripButtonFix.Visible = true; toolStripButtonFix.Text = alert.FixLinkText; } } toolStripButtonExportAll.Enabled = GridViewAlerts.Rows.Count > 0; toolStripButtonDismissAll.Enabled = AllowedToDismissAtLeastOne(); // We use the nondismissing alert count here because we dont want to offer people the chance to dismiss alerts which are already being dismissed... they aren't even shown in the datagridview toolStripButtonDismissAll.AutoToolTip = !toolStripButtonDismissAll.Enabled; toolStripButtonDismissAll.ToolTipText = toolStripButtonDismissAll.Enabled ? string.Empty : Alert.NonDismissingAlertCount > 0 ? Messages.DELETE_ANY_MESSAGE_RBAC_BLOCKED : Messages.NO_MESSAGES_TO_DISMISS; } /// /// Check that there exists a non dismissing alert that can be dismissed. /// /// private bool AllowedToDismissAtLeastOne() { Dictionary connectionsChecked = new Dictionary(); foreach (Alert a in Alert.Alerts) { if (a.Connection != null && connectionsChecked.ContainsKey(a.Connection)) continue; if (a.Dismissing) continue; if (AllowedToDismiss(a.Connection)) return true; // we can't dismiss from this connection, so don't bother checking in future connectionsChecked.Add(a.Connection, true); } return false; } /// /// Checks that each alert which is currently selected comes from a connection where the user has sufficient RBAC /// privileges to clear the alert. /// /// private bool AllowedToDismissSelected() { List selectedAlertConnections = new List(); foreach (DataGridViewRow r in GridViewAlerts.SelectedRows) { Alert alert = (Alert)r.Tag; if (alert.Connection != null && !selectedAlertConnections.Contains(alert.Connection)) { selectedAlertConnections.Add(alert.Connection); } } foreach (IXenConnection c in selectedAlertConnections) { // Check they are allowed to dismiss server alerts. if (!AllowedToDismiss(c)) return false; } return true; } private bool AllowedToDismiss(IXenConnection c) { // check if local alert if (c == null) return true; // have we disconnected? Alert will dissapear soon, but for now block dismissal. if (c.Session == null) return false; if (c.Session.IsLocalSuperuser || !Helpers.MidnightRideOrGreater(c)) return true; List rolesAbleToCompleteAction = Role.ValidRoleList("Message.destroy", c); foreach (Role possibleRole in rolesAbleToCompleteAction) { if (c.Session.Roles.Contains(possibleRole)) { return true; } } return false; } private void ToolStripMenuItemFix_Click(object sender, EventArgs e) { ButtonFix_Click(null, null); } private void ToolStripMenuItemHelp_Click(object sender, EventArgs e) { ButtonHelp_Click(null, null); } private void ToolStripMenuItemDismiss_Click(object sender, EventArgs e) { ButtonDismiss_Click(null, null); } private void ContextMenuAlertGridView_Opening(object sender, CancelEventArgs e) { Program.Invoke(this, delegate { UpdateActionEnablement(); ContextMenuAlertGridView.Items.Clear(); if (toolStripButtonFix.Visible) { ContextMenuAlertGridView.Items.Add(ToolStripMenuItemFix); ToolStripMenuItemFix.Text = toolStripButtonFix.Text; } if (ToolStripMenuItemHelp.Visible) { ContextMenuAlertGridView.Items.Add(ToolStripMenuItemHelp); ToolStripMenuItemHelp.Text = toolStripButtonHelp.Text; } if (toolStripButtonDismiss.Visible) { ContextMenuAlertGridView.Items.Add(ToolStripMenuItemDismiss); ToolStripMenuItemDismiss.Enabled = toolStripButtonDismiss.Enabled; ToolStripMenuItemDismiss.ToolTipText = toolStripButtonDismiss.ToolTipText; } if (ContextMenuAlertGridView.Items.Count > 0) ContextMenuAlertGridView.Items.Add(toolStripSeparator2); ContextMenuAlertGridView.Items.Add(copyToolStripMenuItem); }); } private void toolStripDropDownButtonDateFilter_FilterChanged() { Rebuild(); } private void toolStripDropDownButtonServerFilter_FilterChanged() { Rebuild(); } private void toolStripDropDownSeveritiesFilter_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e) { Rebuild(); } private void toolStripButtonExportAll_Click(object sender, EventArgs e) { SaveFileDialog dialog = new SaveFileDialog(); dialog.AddExtension = true; dialog.Filter = string.Format("{0} (*.csv)|*.csv|{1} (*.*)|*.*", Messages.CSV_DESCRIPTION, Messages.ALL_FILES); dialog.FilterIndex = 0; dialog.Title = Messages.EXPORT_ALL; dialog.RestoreDirectory = true; dialog.DefaultExt = "csv"; dialog.CheckPathExists = false; dialog.OverwritePrompt = true; if (dialog.ShowDialog(this) != DialogResult.OK) return; new DelegatedAsyncAction(null, String.Format(Messages.EXPORT_SYSTEM_ALERTS, dialog.FileName), String.Format(Messages.EXPORTING_SYSTEM_ALERTS, dialog.FileName), String.Format(Messages.EXPORTED_SYSTEM_ALERTS, dialog.FileName), delegate(Session _) { StreamWriter stream = new StreamWriter(dialog.FileName, false, UTF8Encoding.UTF8); try { stream.WriteLine("{0},{1},{2},{3},{4}", Messages.TITLE, Messages.SEVERITY, Messages.DESCRIPTION, Messages.APPLIES_TO, Messages.TIMESTAMP); foreach (Alert a in Alert.Alerts) { stream.WriteLine(GetAlertDetailsCSVQuotes(a)); } } finally { stream.Close(); stream.Dispose(); } }).RunAsync(); } private string EscapeQuotes(string s) { if (s == null) return null; return s.Replace("\"", "\"\""); } private void copyToolStripMenuItem_Click(object sender, EventArgs e) { // We should only be here if one item is selected, we dont do multi-fix if (GridViewAlerts.SelectedRows.Count == 0) { log.ErrorFormat("Attempted to copy alert with no alert selected."); return; } Alert alert; StringBuilder sb = new StringBuilder(); foreach (DataGridViewRow r in GridViewAlerts.SelectedRows) { alert = (Alert)r.Tag; sb.AppendLine(GetAlertDetailsCSVQuotes(alert)); } string text = sb.ToString().Trim(); try { Clipboard.SetText(sb.ToString()); } catch (Exception ex) { log.Error("Exception while trying to set clipboard text.", ex); log.Error(ex, ex); } } private string GetAlertDetailsCSVQuotes(Alert a) { string date = String.Empty; string description = String.Empty; Program.Invoke(Program.MainWindow, delegate { date = HelpersGUI.DateTimeToString( a.Timestamp.ToLocalTime(), Messages.DATEFORMAT_DMY_HM, true); description = a.Description; }); return String.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\"", EscapeQuotes(a.Title), EscapeQuotes(a.Priority.GetString()), EscapeQuotes(description), EscapeQuotes(a.AppliesTo), EscapeQuotes(date)); } private void toolStripButtonRefresh_Click(object sender, EventArgs e) { Rebuild(); } private void GridViewAlerts_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Right) // expand all selected rows { foreach (DataGridViewBand row in GridViewAlerts.SelectedRows) { Alert alert = (Alert)GridViewAlerts.Rows[row.Index].Tag; if (!expandedState.ContainsKey(alert.uuid)) { toggleExpandedState(row.Index); } } } else if (e.KeyCode == Keys.Left) // collapse all selected rows { foreach (DataGridViewBand row in GridViewAlerts.SelectedRows) { Alert alert = (Alert)GridViewAlerts.Rows[row.Index].Tag; if (expandedState.ContainsKey(alert.uuid)) { toggleExpandedState(row.Index); } } } else if (e.KeyCode == Keys.Enter) // toggle expanded state for all selected rows { foreach (DataGridViewBand row in GridViewAlerts.SelectedRows) { toggleExpandedState(row.Index); } } } } }