From 423ee7bd816c8785fd2cd96a2f0d07be6db4e39c Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Thu, 5 Jan 2023 21:50:24 +0000 Subject: [PATCH 1/5] Added RBAC checks to the actions adding/removing custom fields. Also: - Converted them to proper AsyncActions to facilitate further refactoring. - Moved SaveCustomFieldsAction to the same folder as the above. Signed-off-by: Konstantina Chremmou --- XenAdmin/Dialogs/CustomFieldsDialog.cs | 34 +++------- XenAdmin/XenAdmin.csproj | 1 - .../CustomFields/AddCustomFieldAction.cs | 57 +++++++++++++++++ .../CustomFields/RemoveCustomFieldAction.cs | 64 +++++++++++++++++++ .../CustomFields}/SaveCustomFieldsAction.cs | 3 + XenModel/XenModel.csproj | 3 + 6 files changed, 135 insertions(+), 27 deletions(-) create mode 100644 XenModel/Actions/CustomFields/AddCustomFieldAction.cs create mode 100644 XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs rename {XenAdmin/Actions/GUIActions => XenModel/Actions/CustomFields}/SaveCustomFieldsAction.cs (94%) diff --git a/XenAdmin/Dialogs/CustomFieldsDialog.cs b/XenAdmin/Dialogs/CustomFieldsDialog.cs index 6c502568f..0c3b146b3 100644 --- a/XenAdmin/Dialogs/CustomFieldsDialog.cs +++ b/XenAdmin/Dialogs/CustomFieldsDialog.cs @@ -30,10 +30,9 @@ using System; using System.Windows.Forms; +using XenAdmin.Actions; using XenAdmin.CustomFields; using XenAdmin.Network; -using XenAPI; -using XenAdmin.Actions; namespace XenAdmin.Dialogs @@ -74,21 +73,11 @@ namespace XenAdmin.Dialogs private void btnAdd_Click(object sender, EventArgs e) { - NewCustomFieldDialog dialog = new NewCustomFieldDialog(connection); - if (dialog.ShowDialog() != DialogResult.OK) - return; - - CustomFieldDefinition definition = dialog.Definition; - - DelegatedAsyncAction action = new DelegatedAsyncAction(connection, - String.Format(Messages.ADD_CUSTOM_FIELD, definition.Name), - String.Format(Messages.ADDING_CUSTOM_FIELD, definition.Name), - String.Format(Messages.ADDED_CUSTOM_FIELD, definition.Name), - delegate(Session session) - { - CustomFieldsManager.AddCustomField(session, connection, definition); - }); - action.RunAsync(); + using (var dialog = new NewCustomFieldDialog(connection)) + { + if (dialog.ShowDialog() == DialogResult.OK) + new AddCustomFieldAction(connection, dialog.Definition).RunAsync(); + } } private void btnDelete_Click(object sender, EventArgs e) @@ -117,15 +106,8 @@ namespace XenAdmin.Dialogs int selIdx = lbCustomFields.SelectedIndex; lbCustomFields.Items.RemoveAt(selIdx); - DelegatedAsyncAction action = new DelegatedAsyncAction(connection, - String.Format(Messages.DELETE_CUSTOM_FIELD, name), - String.Format(Messages.DELETING_CUSTOM_FIELD, name), - String.Format(Messages.DELETED_CUSTOM_FIELD, name), - delegate(Session session) - { - CustomFieldsManager.RemoveCustomField(session, connection, customFieldDefinition); - }); - action.RunAsync(); + + new RemoveCustomFieldAction(connection, customFieldDefinition).RunAsync(); } private void lbCustomFields_SelectedIndexChanged(object sender, EventArgs e) diff --git a/XenAdmin/XenAdmin.csproj b/XenAdmin/XenAdmin.csproj index c563419bb..3fee7370c 100755 --- a/XenAdmin/XenAdmin.csproj +++ b/XenAdmin/XenAdmin.csproj @@ -2868,7 +2868,6 @@ - Component diff --git a/XenModel/Actions/CustomFields/AddCustomFieldAction.cs b/XenModel/Actions/CustomFields/AddCustomFieldAction.cs new file mode 100644 index 000000000..3e6553198 --- /dev/null +++ b/XenModel/Actions/CustomFields/AddCustomFieldAction.cs @@ -0,0 +1,57 @@ +/* Copyright (c) Cloud Software Group, Inc. + * + * 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 XenAdmin.CustomFields; +using XenAdmin.Network; + +namespace XenAdmin.Actions +{ + /// + /// Adds a new custom field definition in the pool. + /// + public class AddCustomFieldAction : AsyncAction + { + private readonly CustomFieldDefinition _definition; + + public AddCustomFieldAction(IXenConnection connection, CustomFieldDefinition definition) + : base(connection, string.Format(Messages.ADD_CUSTOM_FIELD, definition.Name), + string.Format(Messages.ADDING_CUSTOM_FIELD, definition.Name), false) + { + _definition = definition; + ApiMethodsToRoleCheck.AddRange("pool.add_to_gui_config", "pool.remove_from_gui_config"); + } + + protected override void Run() + { + CustomFieldsManager.AddCustomField(Session, Connection, _definition); + Description = string.Format(Messages.ADDED_CUSTOM_FIELD, _definition.Name); + } + } +} diff --git a/XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs b/XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs new file mode 100644 index 000000000..4f106abb7 --- /dev/null +++ b/XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs @@ -0,0 +1,64 @@ +/* Copyright (c) Cloud Software Group, Inc. + * + * 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 XenAdmin.CustomFields; +using XenAdmin.Network; + +namespace XenAdmin.Actions +{ + /// + /// Removes a custom field definition from the pool. + /// + public class RemoveCustomFieldAction : AsyncAction + { + private readonly CustomFieldDefinition _definition; + + public RemoveCustomFieldAction(IXenConnection connection, CustomFieldDefinition definition) + : base(connection, string.Format(Messages.DELETE_CUSTOM_FIELD, definition.Name), + string.Format(Messages.DELETING_CUSTOM_FIELD, definition.Name), false) + { + _definition = definition; + + ApiMethodsToRoleCheck.AddRange("pool.add_to_gui_config", "pool.remove_from_gui_config", + "pool.remove_from_other_config", + "host.remove_from_other_config", + "VM.remove_from_other_config", + "SR.remove_from_other_config", + "VDI.remove_from_other_config", + "Network.remove_from_other_config"); + } + + protected override void Run() + { + CustomFieldsManager.RemoveCustomField(Session, Connection, _definition); + Description = string.Format(Messages.DELETED_CUSTOM_FIELD, _definition.Name); + } + } +} diff --git a/XenAdmin/Actions/GUIActions/SaveCustomFieldsAction.cs b/XenModel/Actions/CustomFields/SaveCustomFieldsAction.cs similarity index 94% rename from XenAdmin/Actions/GUIActions/SaveCustomFieldsAction.cs rename to XenModel/Actions/CustomFields/SaveCustomFieldsAction.cs index b3df7c13f..fc5aa86df 100644 --- a/XenAdmin/Actions/GUIActions/SaveCustomFieldsAction.cs +++ b/XenModel/Actions/CustomFields/SaveCustomFieldsAction.cs @@ -36,6 +36,9 @@ using XenAdmin.Core; namespace XenAdmin.Actions { + /// + /// Sets custom field values on a given API object. + /// public class SaveCustomFieldsAction : AsyncAction { private readonly IXenObject xenObject; diff --git a/XenModel/XenModel.csproj b/XenModel/XenModel.csproj index c9ba6f3ba..4690b9cfc 100755 --- a/XenModel/XenModel.csproj +++ b/XenModel/XenModel.csproj @@ -63,6 +63,9 @@ Properties\CommonAssemblyInfo.cs + + + From bc40d47ba690b1505dba61081be7b6c03d97b015 Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Thu, 5 Jan 2023 22:23:21 +0000 Subject: [PATCH 2/5] Corrections to removal of custom fields: - Custom fields were not removed from all types of objects the UI allows setting them for, which meant they were left behind in the other_config. - Only remove a custom field if it is set on an object, otherwise we hammer the server with unnecessary requests. Signed-off-by: Konstantina Chremmou --- XenModel/CustomFields/CustomFieldsManager.cs | 68 ++++++++++++++------ XenModel/Utils/Helpers.cs | 15 ----- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/XenModel/CustomFields/CustomFieldsManager.cs b/XenModel/CustomFields/CustomFieldsManager.cs index 53689b831..0a114720f 100644 --- a/XenModel/CustomFields/CustomFieldsManager.cs +++ b/XenModel/CustomFields/CustomFieldsManager.cs @@ -46,14 +46,14 @@ namespace XenAdmin.CustomFields /// "XenCenter.CustomFields.foo1" value /// "XenCenter.CustomFields.foo2" value /// - public class CustomFieldsManager + public static class CustomFieldsManager { #region These functions deal with caching the list of custom fields private static readonly CustomFieldsCache customFieldsCache = new CustomFieldsCache(); private const String CUSTOM_FIELD_DELIM = "."; public const String CUSTOM_FIELD_BASE_KEY = "XenCenter.CustomFields"; - + public const String CUSTOM_FIELD = "CustomField:"; public static event Action CustomFieldsChanged; @@ -109,15 +109,52 @@ namespace XenAdmin.CustomFields public static void RemoveCustomField(Session session, IXenConnection connection, CustomFieldDefinition definition) { List customFields = customFieldsCache.GetCustomFields(connection); + if (customFields.Remove(definition)) { SaveCustomFields(session, connection, customFields); - // Remove from all Objects - RemoveCustomFieldsFrom(session, connection.Cache.VMs, definition); - RemoveCustomFieldsFrom(session, connection.Cache.Hosts, definition); - RemoveCustomFieldsFrom(session, connection.Cache.Pools, definition); - RemoveCustomFieldsFrom(session, connection.Cache.SRs, definition); + // theoretically any object type with other_config may have a custom field set, + // but the object types more likely to have one are those shown on the treeview, + // i.e. pools, hosts, VMs (including snapshots and templates), SRs, VDIs, and networks + + string customFieldKey = GetCustomFieldKey(definition); + + foreach (var pool in connection.Cache.Pools) + { + if (pool.other_config.ContainsKey(customFieldKey)) + Pool.remove_from_other_config(session, pool.opaque_ref, customFieldKey); + } + + foreach (var host in connection.Cache.Hosts) + { + if (host.other_config.ContainsKey(customFieldKey)) + Host.remove_from_other_config(session, host.opaque_ref, customFieldKey); + } + + foreach (var vm in connection.Cache.VMs) + { + if (vm.other_config.ContainsKey(customFieldKey)) + VM.remove_from_other_config(session, vm.opaque_ref, customFieldKey); + } + + foreach (var sr in connection.Cache.SRs) + { + if (sr.other_config.ContainsKey(customFieldKey)) + SR.remove_from_other_config(session, sr.opaque_ref, customFieldKey); + } + + foreach (var vdi in connection.Cache.VDIs) + { + if (vdi.other_config.ContainsKey(customFieldKey)) + VDI.remove_from_other_config(session, vdi.opaque_ref, customFieldKey); + } + + foreach (var network in connection.Cache.Networks) + { + if (network.other_config.ContainsKey(customFieldKey)) + XenAPI.Network.remove_from_other_config(session, network.opaque_ref, customFieldKey); + } } } @@ -155,25 +192,14 @@ namespace XenAdmin.CustomFields return CUSTOM_FIELD_BASE_KEY + CUSTOM_FIELD_DELIM + customFieldDefinition.Name; } - private static void RemoveCustomFieldsFrom(Session session, IEnumerable os, CustomFieldDefinition customFieldDefinition) - { - InvokeHelper.AssertOffEventThread(); - - string customFieldKey = GetCustomFieldKey(customFieldDefinition); - - foreach (IXenObject o in os) - { - Helpers.RemoveFromOtherConfig(session, o, customFieldKey); - } - } - private static void SaveCustomFields(Session session, IXenConnection connection, List customFields) { Pool pool = Helpers.GetPoolOfOne(connection); if (pool != null) { - String customFieldXML = GetCustomFieldDefinitionXML(customFields); - Helpers.SetGuiConfig(session, pool, CUSTOM_FIELD_BASE_KEY, customFieldXML); + string customFieldXML = GetCustomFieldDefinitionXML(customFields); + Pool.remove_from_gui_config(session, pool.opaque_ref, CUSTOM_FIELD_BASE_KEY); + Pool.add_to_gui_config(session, pool.opaque_ref, CUSTOM_FIELD_BASE_KEY, customFieldXML); } } diff --git a/XenModel/Utils/Helpers.cs b/XenModel/Utils/Helpers.cs index db0039127..ef23ab03a 100755 --- a/XenModel/Utils/Helpers.cs +++ b/XenModel/Utils/Helpers.cs @@ -609,21 +609,6 @@ namespace XenAdmin.Core return o.Get("gui_config") as Dictionary; } - public static void SetGuiConfig(Session session, IXenObject o, String key, String value) - { - //Program.AssertOffEventThread(); - - o.Do("remove_from_gui_config", session, o.opaque_ref, key); - o.Do("add_to_gui_config", session, o.opaque_ref, key, value); - } - - public static void RemoveFromGuiConfig(Session session, IXenObject o, string key) - { - //Program.AssertOffEventThread(); - - o.Do("remove_from_gui_config", session, o.opaque_ref, key); - } - #endregion public enum DataSourceCategory From 0d1e4993ec5fb9a3594631475bfd8e6e22d96565 Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Fri, 6 Jan 2023 11:04:05 +0000 Subject: [PATCH 3/5] Added blurb to the NewCustomFieldDialog; improved error message and control state checks. Signed-off-by: Konstantina Chremmou --- .../Dialogs/NewCustomFieldDialog.Designer.cs | 102 +++--- XenAdmin/Dialogs/NewCustomFieldDialog.cs | 39 +- XenAdmin/Dialogs/NewCustomFieldDialog.resx | 336 +++++++++++------- 3 files changed, 284 insertions(+), 193 deletions(-) diff --git a/XenAdmin/Dialogs/NewCustomFieldDialog.Designer.cs b/XenAdmin/Dialogs/NewCustomFieldDialog.Designer.cs index 4f7f42d4c..8a17f25ed 100644 --- a/XenAdmin/Dialogs/NewCustomFieldDialog.Designer.cs +++ b/XenAdmin/Dialogs/NewCustomFieldDialog.Designer.cs @@ -31,11 +31,14 @@ namespace XenAdmin.Dialogs System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(NewCustomFieldDialog)); this.btnCancel = new System.Windows.Forms.Button(); this.okButton = new System.Windows.Forms.Button(); - this.NameTextBox = new System.Windows.Forms.TextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.TypeComboBox = new System.Windows.Forms.ComboBox(); - this.DuplicateWarning = new System.Windows.Forms.Label(); + this.textBoxName = new System.Windows.Forms.TextBox(); + this.labelName = new System.Windows.Forms.Label(); + this.labelType = new System.Windows.Forms.Label(); + this.comboBoxType = new System.Windows.Forms.ComboBox(); + this.labelDuplicate = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.labelBlurb = new System.Windows.Forms.Label(); + this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // btnCancel @@ -52,37 +55,59 @@ namespace XenAdmin.Dialogs this.okButton.Name = "okButton"; this.okButton.UseVisualStyleBackColor = true; // - // NameTextBox + // textBoxName // - resources.ApplyResources(this.NameTextBox, "NameTextBox"); - this.NameTextBox.Name = "NameTextBox"; - this.NameTextBox.TextChanged += new System.EventHandler(this.NameTextBox_TextChanged); + resources.ApplyResources(this.textBoxName, "textBoxName"); + this.tableLayoutPanel1.SetColumnSpan(this.textBoxName, 2); + this.textBoxName.Name = "textBoxName"; + this.textBoxName.TextChanged += new System.EventHandler(this.NameTextBox_TextChanged); // - // label1 + // labelName // - resources.ApplyResources(this.label1, "label1"); - this.label1.Name = "label1"; + resources.ApplyResources(this.labelName, "labelName"); + this.labelName.Name = "labelName"; // - // label2 + // labelType // - resources.ApplyResources(this.label2, "label2"); - this.label2.Name = "label2"; + resources.ApplyResources(this.labelType, "labelType"); + this.labelType.Name = "labelType"; // - // TypeComboBox + // comboBoxType // - resources.ApplyResources(this.TypeComboBox, "TypeComboBox"); - this.TypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.TypeComboBox.FormattingEnabled = true; - this.TypeComboBox.Items.AddRange(new object[] { - resources.GetString("TypeComboBox.Items"), - resources.GetString("TypeComboBox.Items1")}); - this.TypeComboBox.Name = "TypeComboBox"; + resources.ApplyResources(this.comboBoxType, "comboBoxType"); + this.tableLayoutPanel1.SetColumnSpan(this.comboBoxType, 2); + this.comboBoxType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxType.FormattingEnabled = true; + this.comboBoxType.Items.AddRange(new object[] { + resources.GetString("comboBoxType.Items"), + resources.GetString("comboBoxType.Items1")}); + this.comboBoxType.Name = "comboBoxType"; // - // DuplicateWarning + // labelDuplicate // - resources.ApplyResources(this.DuplicateWarning, "DuplicateWarning"); - this.DuplicateWarning.ForeColor = System.Drawing.Color.Red; - this.DuplicateWarning.Name = "DuplicateWarning"; + resources.ApplyResources(this.labelDuplicate, "labelDuplicate"); + this.tableLayoutPanel1.SetColumnSpan(this.labelDuplicate, 2); + this.labelDuplicate.ForeColor = System.Drawing.Color.Red; + this.labelDuplicate.Name = "labelDuplicate"; + // + // tableLayoutPanel1 + // + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); + this.tableLayoutPanel1.Controls.Add(this.labelBlurb, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.textBoxName, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.comboBoxType, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.labelDuplicate, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.btnCancel, 2, 4); + this.tableLayoutPanel1.Controls.Add(this.labelName, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.labelType, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.okButton, 1, 4); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // labelBlurb + // + resources.ApplyResources(this.labelBlurb, "labelBlurb"); + this.tableLayoutPanel1.SetColumnSpan(this.labelBlurb, 3); + this.labelBlurb.Name = "labelBlurb"; // // NewCustomFieldDialog // @@ -90,27 +115,24 @@ namespace XenAdmin.Dialogs resources.ApplyResources(this, "$this"); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.CancelButton = this.btnCancel; - this.Controls.Add(this.DuplicateWarning); - this.Controls.Add(this.TypeComboBox); - this.Controls.Add(this.label2); - this.Controls.Add(this.label1); - this.Controls.Add(this.btnCancel); - this.Controls.Add(this.NameTextBox); - this.Controls.Add(this.okButton); + this.Controls.Add(this.tableLayoutPanel1); this.Name = "NewCustomFieldDialog"; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); this.ResumeLayout(false); - this.PerformLayout(); } #endregion private System.Windows.Forms.Button btnCancel; - private System.Windows.Forms.TextBox NameTextBox; + private System.Windows.Forms.TextBox textBoxName; private System.Windows.Forms.Button okButton; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.ComboBox TypeComboBox; - private System.Windows.Forms.Label DuplicateWarning; + private System.Windows.Forms.Label labelName; + private System.Windows.Forms.Label labelType; + private System.Windows.Forms.ComboBox comboBoxType; + private System.Windows.Forms.Label labelDuplicate; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label labelBlurb; } } \ No newline at end of file diff --git a/XenAdmin/Dialogs/NewCustomFieldDialog.cs b/XenAdmin/Dialogs/NewCustomFieldDialog.cs index 04a7b9081..deeb5811f 100644 --- a/XenAdmin/Dialogs/NewCustomFieldDialog.cs +++ b/XenAdmin/Dialogs/NewCustomFieldDialog.cs @@ -29,6 +29,7 @@ */ using System; +using System.Linq; using XenAdmin.CustomFields; using XenAdmin.Network; @@ -41,36 +42,32 @@ namespace XenAdmin.Dialogs { InitializeComponent(); - okButton.Enabled = !string.IsNullOrEmpty(NameTextBox.Text); - TypeComboBox.SelectedIndex = 0; + comboBoxType.SelectedIndex = 0; + UpdateControls(); } - public CustomFieldDefinition Definition + public CustomFieldDefinition Definition => + new CustomFieldDefinition(textBoxName.Text.Trim(), (CustomFieldDefinition.Types)comboBoxType.SelectedIndex); + + private void UpdateControls() { - get + if (string.IsNullOrWhiteSpace(textBoxName.Text)) { - return new CustomFieldDefinition(NameTextBox.Text.Trim(), - (CustomFieldDefinition.Types)TypeComboBox.SelectedIndex); + okButton.Enabled = false; + labelDuplicate.Visible = false; + return; } + + var existingCustomFields = CustomFieldsManager.GetCustomFields(connection); + var isDuplicate = existingCustomFields.Any(f => f.Name.Trim() == textBoxName.Text.Trim()); + + okButton.Enabled = !isDuplicate; + labelDuplicate.Visible = isDuplicate; } private void NameTextBox_TextChanged(object sender, EventArgs e) { - okButton.Enabled = EnableOKButton(); - DuplicateWarning.Visible = IsDuplicate(); - } - - private bool IsDuplicate() - { - foreach (CustomFieldDefinition customFieldDefinition in CustomFieldsManager.GetCustomFields(connection)) - if (customFieldDefinition.Name.Trim() == Definition.Name.Trim()) - return true; - return false; - } - - private bool EnableOKButton() - { - return !string.IsNullOrEmpty(NameTextBox.Text.Trim()) && !IsDuplicate(); + UpdateControls(); } } } \ No newline at end of file diff --git a/XenAdmin/Dialogs/NewCustomFieldDialog.resx b/XenAdmin/Dialogs/NewCustomFieldDialog.resx index bf8cad3c0..7318ddf68 100644 --- a/XenAdmin/Dialogs/NewCustomFieldDialog.resx +++ b/XenAdmin/Dialogs/NewCustomFieldDialog.resx @@ -112,16 +112,16 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + Bottom, Right - + Segoe UI, 9pt @@ -129,17 +129,14 @@ NoControl - 222, 98 - - - 4, 3, 0, 3 + 246, 135 75, 23 - + - 5 + 7 Cancel @@ -148,10 +145,10 @@ btnCancel - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - $this + tableLayoutPanel1 4 @@ -166,16 +163,13 @@ NoControl - 140, 98 - - - 3, 8, 3, 3 + 165, 135 75, 23 - 4 + 6 OK @@ -184,175 +178,253 @@ okButton - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - $this + tableLayoutPanel1 - 6 + 7 - - Top, Left, Right + + Left, Right - - Segoe UI, 9pt - - - 74, 13 - - - 223, 23 - - - 1 - - - NameTextBox - - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 5 - - - True - - - Segoe UI, 9pt - - - 13, 16 - - - 42, 15 - - - 0 - - - &Name: - - - label1 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - + 3 - + True - + + Fill + + Segoe UI, 9pt - - 13, 62 + + 3, 0 - - 36, 15 + + 3, 0, 3, 15 - - 2 + + 318, 30 - - &Type: + + 0 - - label2 + + Enter a name for the new custom field and select its type. The new field will be available to all resources in the pool. - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + labelBlurb - - $this + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 2 + + tableLayoutPanel1 - - Top, Left, Right + + 0 - + + Left, Right + + Segoe UI, 9pt - + Text - + Date & Time - - 74, 59 + + 51, 94 - - 223, 23 + + 270, 23 - - 3 + + 5 - - TypeComboBox + + comboBoxType - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - $this + + tableLayoutPanel1 - - 1 + + 2 - + True - + Segoe UI, 9pt - - 71, 39 + + 51, 74 - - 195, 15 + + 241, 15 - - 6 + + 3 - - Cannot add duplicate custom fields + + A custom filed with this name already exists. - + False - - DuplicateWarning + + labelDuplicate - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - $this + + tableLayoutPanel1 - + + 3 + + + Left + + + True + + + Segoe UI, 9pt + + + 3, 52 + + + 42, 15 + + + 1 + + + &Name: + + + labelName + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Left + + + True + + + Segoe UI, 9pt + + + 3, 98 + + + 34, 15 + + + 4 + + + &Type: + + + labelType + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + Fill + + + Segoe UI, 9pt + + + 10, 10 + + + 5 + + + 324, 161 + + 0 - + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="labelBlurb" Row="0" RowSpan="1" Column="0" ColumnSpan="3" /><Control Name="textBoxName" Row="1" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="comboBoxType" Row="3" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="labelDuplicate" Row="2" RowSpan="1" Column="1" ColumnSpan="2" /><Control Name="btnCancel" Row="4" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="labelName" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="labelType" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="okButton" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,Percent,100,AutoSize,0" /><Rows Styles="AutoSize,0,AutoSize,0,Absolute,17,AutoSize,0,Percent,100" /></TableLayoutSettings> + + + Segoe UI, 9pt + + + 51, 48 + + + 270, 23 + + + 2 + + + textBoxName + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + True 96, 96 - 310, 127 + 344, 181 Tahoma, 8pt @@ -367,6 +439,6 @@ NewCustomFieldDialog - XenAdmin.Dialogs.XenDialogBase, XenCenterMain, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Dialogs.XenDialogBase, [XenCenter_No_Space], Version=0.0.0.0, Culture=neutral, PublicKeyToken=null \ No newline at end of file From 5c70cbfd3de726e2ac7c1117a8ee8d870afc50b7 Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Fri, 6 Jan 2023 14:09:35 +0000 Subject: [PATCH 4/5] CA-84612: Rewrote the CustomFields Property page to fix some issues: - The CustomFieldsDialog was an unnecessary intermediate step deteriorating the user experience. - Fixed layout issues when we have a long list of custom fields. Signed-off-by: Konstantina Chremmou --- .../Dialogs/CustomFieldsDialog.Designer.cs | 104 ---- XenAdmin/Dialogs/CustomFieldsDialog.cs | 118 ----- XenAdmin/Dialogs/CustomFieldsDialog.ja.resx | 306 ------------ XenAdmin/Dialogs/CustomFieldsDialog.resx | 306 ------------ .../Dialogs/CustomFieldsDialog.zh-CN.resx | 306 ------------ .../SettingsPanels/CustomFieldsDisplayPage.cs | 446 ++++++++++-------- .../CustomFieldsDisplayPage.designer.cs | 20 +- .../CustomFieldsDisplayPage.resx | 103 ++-- XenAdmin/XenAdmin.csproj | 16 - XenModel/Messages.Designer.cs | 2 +- XenModel/Messages.resx | 2 +- 11 files changed, 332 insertions(+), 1397 deletions(-) delete mode 100644 XenAdmin/Dialogs/CustomFieldsDialog.Designer.cs delete mode 100644 XenAdmin/Dialogs/CustomFieldsDialog.cs delete mode 100644 XenAdmin/Dialogs/CustomFieldsDialog.ja.resx delete mode 100644 XenAdmin/Dialogs/CustomFieldsDialog.resx delete mode 100644 XenAdmin/Dialogs/CustomFieldsDialog.zh-CN.resx diff --git a/XenAdmin/Dialogs/CustomFieldsDialog.Designer.cs b/XenAdmin/Dialogs/CustomFieldsDialog.Designer.cs deleted file mode 100644 index 80a9ff567..000000000 --- a/XenAdmin/Dialogs/CustomFieldsDialog.Designer.cs +++ /dev/null @@ -1,104 +0,0 @@ -using XenAdmin.CustomFields; -using XenAdmin.XenSearch; -using System; -using XenAPI; -using XenAdmin.Core; -namespace XenAdmin.Dialogs -{ - partial class CustomFieldsDialog - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - CustomFieldsManager.CustomFieldsChanged -= CustomFields_CustomFieldsChanged; - - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CustomFieldsDialog)); - this.btnClose = new System.Windows.Forms.Button(); - this.btnDelete = new System.Windows.Forms.Button(); - this.btnAdd = new System.Windows.Forms.Button(); - this.CustomFieldsLabel = new System.Windows.Forms.Label(); - this.lbCustomFields = new System.Windows.Forms.ListBox(); - this.SuspendLayout(); - // - // btnClose - // - resources.ApplyResources(this.btnClose, "btnClose"); - this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Yes; - this.btnClose.Name = "btnClose"; - this.btnClose.UseVisualStyleBackColor = true; - // - // btnDelete - // - resources.ApplyResources(this.btnDelete, "btnDelete"); - this.btnDelete.Name = "btnDelete"; - this.btnDelete.UseVisualStyleBackColor = true; - this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); - // - // btnAdd - // - resources.ApplyResources(this.btnAdd, "btnAdd"); - this.btnAdd.Name = "btnAdd"; - this.btnAdd.UseVisualStyleBackColor = true; - this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click); - // - // CustomFieldsLabel - // - resources.ApplyResources(this.CustomFieldsLabel, "CustomFieldsLabel"); - this.CustomFieldsLabel.BackColor = System.Drawing.Color.Transparent; - this.CustomFieldsLabel.Name = "CustomFieldsLabel"; - // - // lbCustomFields - // - resources.ApplyResources(this.lbCustomFields, "lbCustomFields"); - this.lbCustomFields.Name = "lbCustomFields"; - this.lbCustomFields.SelectedIndexChanged += new System.EventHandler(this.lbCustomFields_SelectedIndexChanged); - // - // CustomFieldsDialog - // - this.AcceptButton = this.btnClose; - resources.ApplyResources(this, "$this"); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.CancelButton = this.btnClose; - this.Controls.Add(this.btnClose); - this.Controls.Add(this.btnDelete); - this.Controls.Add(this.btnAdd); - this.Controls.Add(this.CustomFieldsLabel); - this.Controls.Add(this.lbCustomFields); - this.Name = "CustomFieldsDialog"; - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Button btnClose; - private System.Windows.Forms.Button btnDelete; - private System.Windows.Forms.Button btnAdd; - private System.Windows.Forms.Label CustomFieldsLabel; - private System.Windows.Forms.ListBox lbCustomFields; - } -} \ No newline at end of file diff --git a/XenAdmin/Dialogs/CustomFieldsDialog.cs b/XenAdmin/Dialogs/CustomFieldsDialog.cs deleted file mode 100644 index 0c3b146b3..000000000 --- a/XenAdmin/Dialogs/CustomFieldsDialog.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright (c) Cloud Software Group, Inc. - * - * 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.Windows.Forms; -using XenAdmin.Actions; -using XenAdmin.CustomFields; -using XenAdmin.Network; - - -namespace XenAdmin.Dialogs -{ - public partial class CustomFieldsDialog : XenDialogBase - { - public CustomFieldsDialog(IXenConnection connection) : base(connection) - { - InitializeComponent(); - - Build(); - - CustomFieldsManager.CustomFieldsChanged += CustomFields_CustomFieldsChanged; - } - - void CustomFields_CustomFieldsChanged() - { - Build(); - } - - private void Build() - { - lbCustomFields.BeginUpdate(); - - try - { - lbCustomFields.Items.Clear(); - - lbCustomFields.Items.AddRange(CustomFieldsManager.GetCustomFields(connection).ToArray()); - - btnDelete.Enabled = false; - } - finally - { - lbCustomFields.EndUpdate(); - } - } - - private void btnAdd_Click(object sender, EventArgs e) - { - using (var dialog = new NewCustomFieldDialog(connection)) - { - if (dialog.ShowDialog() == DialogResult.OK) - new AddCustomFieldAction(connection, dialog.Definition).RunAsync(); - } - } - - private void btnDelete_Click(object sender, EventArgs e) - { - CustomFieldDefinition customFieldDefinition = lbCustomFields.SelectedItem as CustomFieldDefinition; - if (customFieldDefinition == null) - return; - - string name = customFieldDefinition.Name.Ellipsise(50); - - if (!Program.RunInAutomatedTestMode) - using (var dialog = new WarningDialog(string.Format(Messages.MESSAGEBOX_DELETE_CUSTOM_FIELD, name), - ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo) - {WindowTitle = Messages.MESSAGEBOX_CONFIRM}) - { - if (dialog.ShowDialog(Program.MainWindow) != DialogResult.Yes) - return; - } - - if (connection != null && !connection.IsConnected) - { - MainWindow.ShowDisconnectedMessage(Program.MainWindow); - return; - } - - int selIdx = lbCustomFields.SelectedIndex; - - lbCustomFields.Items.RemoveAt(selIdx); - - new RemoveCustomFieldAction(connection, customFieldDefinition).RunAsync(); - } - - private void lbCustomFields_SelectedIndexChanged(object sender, EventArgs e) - { - btnDelete.Enabled = lbCustomFields.SelectedIndex >= 0; - } - } -} \ No newline at end of file diff --git a/XenAdmin/Dialogs/CustomFieldsDialog.ja.resx b/XenAdmin/Dialogs/CustomFieldsDialog.ja.resx deleted file mode 100644 index add0bc868..000000000 --- a/XenAdmin/Dialogs/CustomFieldsDialog.ja.resx +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - Bottom, Right - - - - Segoe UI, 9pt - - - NoControl - - - 179, 203 - - - 3, 3, 5, 3 - - - 75, 23 - - - - 4 - - - 閉じる - - - btnClose - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 0 - - - Top, Right - - - Segoe UI, 9pt - - - 179, 54 - - - 75, 23 - - - 3 - - - 削除(&D) - - - btnDelete - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 - - - Top, Right - - - Segoe UI, 9pt - - - 179, 25 - - - 75, 23 - - - 2 - - - 追加(&A)... - - - btnAdd - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 2 - - - True - - - Segoe UI, 9pt - - - 9, 9 - - - 174, 21 - - - 0 - - - 現在のカスタム フィールド(&C): - - - CustomFieldsLabel - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 3 - - - Top, Bottom, Left, Right - - - Segoe UI, 9pt - - - True - - - False - - - 15 - - - 12, 25 - - - 161, 201 - - - 1 - - - lbCustomFields - - - System.Windows.Forms.ListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 4 - - - True - - - 96, 96 - - - 268, 238 - - - Tahoma, 8pt - - - カスタム フィールド - - - CustomFieldsDialog - - - XenAdmin.Dialogs.XenDialogBase, XenCenterMain, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - - \ No newline at end of file diff --git a/XenAdmin/Dialogs/CustomFieldsDialog.resx b/XenAdmin/Dialogs/CustomFieldsDialog.resx deleted file mode 100644 index fccfb5322..000000000 --- a/XenAdmin/Dialogs/CustomFieldsDialog.resx +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - Bottom, Right - - - - Segoe UI, 9pt - - - NoControl - - - 179, 203 - - - 3, 3, 5, 3 - - - 75, 23 - - - - 4 - - - Close - - - btnClose - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 0 - - - Top, Right - - - Segoe UI, 9pt - - - 179, 54 - - - 75, 23 - - - 3 - - - &Delete - - - btnDelete - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 - - - Top, Right - - - Segoe UI, 9pt - - - 179, 25 - - - 75, 23 - - - 2 - - - &Add... - - - btnAdd - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 2 - - - True - - - Segoe UI, 9pt - - - 9, 9 - - - 124, 15 - - - 0 - - - &Current custom fields: - - - CustomFieldsLabel - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 3 - - - Top, Bottom, Left, Right - - - Segoe UI, 9pt - - - True - - - False - - - 15 - - - 12, 25 - - - 161, 201 - - - 1 - - - lbCustomFields - - - System.Windows.Forms.ListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 4 - - - True - - - 96, 96 - - - 268, 238 - - - Tahoma, 8pt - - - Custom Fields - - - CustomFieldsDialog - - - XenAdmin.Dialogs.XenDialogBase, XenCenterMain, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - - \ No newline at end of file diff --git a/XenAdmin/Dialogs/CustomFieldsDialog.zh-CN.resx b/XenAdmin/Dialogs/CustomFieldsDialog.zh-CN.resx deleted file mode 100644 index b66031c9b..000000000 --- a/XenAdmin/Dialogs/CustomFieldsDialog.zh-CN.resx +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - Bottom, Right - - - - Segoe UI, 9pt - - - NoControl - - - 179, 203 - - - 3, 3, 5, 3 - - - 75, 23 - - - - 4 - - - 关闭 - - - btnClose - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 0 - - - Top, Right - - - Segoe UI, 9pt - - - 179, 54 - - - 75, 23 - - - 3 - - - 删除(&D) - - - btnDelete - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 - - - Top, Right - - - Segoe UI, 9pt - - - 179, 25 - - - 75, 23 - - - 2 - - - 添加(&A)... - - - btnAdd - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 2 - - - True - - - Segoe UI, 9pt - - - 9, 8 - - - 124, 15 - - - 0 - - - 当前自定义字段(&C): - - - CustomFieldsLabel - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 3 - - - Top, Bottom, Left, Right - - - Segoe UI, 9pt - - - True - - - False - - - 15 - - - 12, 25 - - - 161, 201 - - - 1 - - - lbCustomFields - - - System.Windows.Forms.ListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 4 - - - True - - - 96, 96 - - - 268, 238 - - - Tahoma, 8pt - - - 自定义字段 - - - CustomFieldsDialog - - - XenAdmin.Dialogs.XenDialogBase, XenCenterMain, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - - \ No newline at end of file diff --git a/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.cs b/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.cs index 75fa539d7..7b4f833a2 100644 --- a/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.cs +++ b/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; +using System.Linq; using System.Windows.Forms; using XenAdmin.Actions; @@ -44,12 +45,21 @@ namespace XenAdmin.SettingsPanels { public partial class CustomFieldsDisplayPage : UserControl, IEditPage { - private readonly Dictionary> controls = new Dictionary>(); + /// + /// This is not ideal, but we need something in the last row of the tableLayoutPanel + /// that fills all the way to the bottom of the page; if we leave the controls in the + /// last row, they align vertically in its middle because they don't have vertical + /// anchoring (because they have different heights and it's a pain to get the vertical + /// alignment right by fiddling with the margins) + /// + private readonly Panel _panelDummy; + private IXenObject _xenObject; + private readonly List _fieldRows = new List(); public CustomFieldsDisplayPage() { InitializeComponent(); - + _panelDummy = new Panel { Size = new Size(0, 0), Margin = new Padding(0) }; Text = Messages.CUSTOM_FIELDS; } @@ -57,39 +67,35 @@ namespace XenAdmin.SettingsPanels public AsyncAction SaveSettings() { - List customFields = new List(); + var customFields = new List(); - foreach (KeyValuePair> kvp in controls) + foreach (var row in _fieldRows) { - object currentValue = CustomFieldsManager.GetCustomFieldValue(xenObject, kvp.Key); - object newValue = GetValue(kvp.Key, kvp.Value.Value); + object currentValue = CustomFieldsManager.GetCustomFieldValue(_xenObject, row.CustomFieldDefinition); + object newValue = row.GetValue(); if (currentValue == null && newValue == null) continue; - customFields.Add(new CustomField(kvp.Key, newValue)); + customFields.Add(new CustomField(row.CustomFieldDefinition, newValue)); } - return new SaveCustomFieldsAction(xenObject, customFields, true); + return new SaveCustomFieldsAction(_xenObject, customFields, true); } - private IXenObject xenObject; public void SetXenObjects(IXenObject orig, IXenObject clone) { CustomFieldsManager.CustomFieldsChanged -= CustomFields_CustomFieldsChanged; - xenObject = clone; + _xenObject = clone; - if (xenObject != null) + if (_xenObject != null) { CustomFieldsManager.CustomFieldsChanged += CustomFields_CustomFieldsChanged; Rebuild(true); } } - public bool ValidToSave - { - get { return true; } - } + public bool ValidToSave => true; public void ShowLocalValidationMessages() { @@ -107,10 +113,10 @@ namespace XenAdmin.SettingsPanels { get { - foreach (KeyValuePair> kvp in controls) + foreach (var row in _fieldRows) { - Object currentValue = CustomFieldsManager.GetCustomFieldValue(xenObject, kvp.Key); - Object newValue = GetValue(kvp.Key, kvp.Value.Value); + object currentValue = CustomFieldsManager.GetCustomFieldValue(_xenObject, row.CustomFieldDefinition); + object newValue = row.GetValue(); if (currentValue == null && newValue == null) continue; @@ -132,26 +138,26 @@ namespace XenAdmin.SettingsPanels #region IVerticalTab Members - public String SubText + public string SubText { get { - List fields = new List(); + List fields = new List(); - foreach (KeyValuePair> kvp in controls) + foreach (var row in _fieldRows) { - Object newValue = GetValue(kvp.Key, kvp.Value.Value); + object newValue = row.GetValue(); - if (newValue == null || newValue.ToString() == String.Empty) + if (newValue == null || newValue.ToString() == string.Empty) continue; - fields.Add(kvp.Key.Name + Messages.GENERAL_PAGE_KVP_SEPARATOR + newValue); + fields.Add(row.CustomFieldDefinition.Name + Messages.GENERAL_PAGE_KVP_SEPARATOR + newValue); } if (fields.Count == 0) return Messages.NONE; - return string.Join(", ", fields.ToArray()); + return string.Join(", ", fields); } } @@ -164,192 +170,262 @@ namespace XenAdmin.SettingsPanels Rebuild(false); } - private Object GetValue(CustomFieldDefinition definition, Control control) + private void Rebuild(bool resetValues) { - switch (definition.Type) - { - case CustomFieldDefinition.Types.Date: - { - DateTimePicker dateControl = (DateTimePicker)control; - if (!dateControl.Checked) - return null; - DateTimePicker timeControl = (DateTimePicker)dateControl.Tag; - DateTime date = dateControl.Value; - DateTime time = timeControl.Value; - - return new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second); - } - - case CustomFieldDefinition.Types.String: - TextBox textBox = control as TextBox; - if (textBox == null) - return null; - - string text = textBox.Text; - return (text == "" ? null : text); - - default: - throw new InvalidEnumArgumentException(); - } - } - - private void SetValue(CustomFieldDefinition definition, Control control, Object value) - { - switch (definition.Type) - { - case CustomFieldDefinition.Types.Date: - { - DateTimePicker dateControl = (DateTimePicker)control; - DateTimePicker timeControl = (DateTimePicker)dateControl.Tag; - - if (value != null) - { - dateControl.Checked = true; - dateControl.Value = (DateTime)value; - timeControl.Value = (DateTime)value; - } - else - dateControl.Checked = false; - } - break; - - case CustomFieldDefinition.Types.String: - TextBox textBox = control as TextBox; - if (textBox == null) - return; - - textBox.Text = (String)value; - break; - } - } - - private void Rebuild(bool revertValues) - { - CustomFieldDefinition[] customFieldDefinitions = CustomFieldsManager.GetCustomFields(xenObject.Connection).ToArray(); + var customFieldDefinitions = CustomFieldsManager.GetCustomFields(_xenObject.Connection).ToArray(); tableLayoutPanel.SuspendLayout(); + tableLayoutPanel.Controls.Remove(_panelDummy); // Add new custom fields - foreach (CustomFieldDefinition customFieldDefinition in customFieldDefinitions) + foreach (var definition in customFieldDefinitions) { - Object value = CustomFieldsManager.GetCustomFieldValue(xenObject, customFieldDefinition); + object value = CustomFieldsManager.GetCustomFieldValue(_xenObject, definition); + var row = _fieldRows.FirstOrDefault(r => r.CustomFieldDefinition.Equals(definition)); - if (!controls.ContainsKey(customFieldDefinition)) + if (row == null) { - // Create the display label - Label lblKey = new Label(); - lblKey.Text = customFieldDefinition.Name.EscapeAmpersands(); - lblKey.Margin = new Padding(3, 7, 3, 3); - lblKey.Font = Program.DefaultFont; - lblKey.Width = (int)tableLayoutPanel.ColumnStyles[0].Width; - lblKey.AutoEllipsis = true; - lblKey.AutoSize = false; + row = new CustomFieldRow(definition, value); + row.DeleteCustomFieldClicked += DeleteCustomFieldClicked_Click; - tableLayoutPanel.Controls.Add(lblKey); + _fieldRows.Add(row); + tableLayoutPanel.Controls.AddRange(row.Controls); - // Create value field - Control control; - - switch (customFieldDefinition.Type) - { - case CustomFieldDefinition.Types.String: - TextBox textBox = new TextBox(); - textBox.Text = (String)value; - - tableLayoutPanel.Controls.Add(textBox); - tableLayoutPanel.SetColumnSpan(textBox, 2); - textBox.Dock = DockStyle.Fill; - control = textBox; - break; - - case CustomFieldDefinition.Types.Date: - DateTimePicker date = new DateTimePicker(); - date.MinDate = DateTime.MinValue; - date.MaxDate = DateTime.MaxValue; - date.Dock = DockStyle.Fill; - date.MinimumSize = new Size(0, 24); - date.ShowCheckBox = true; - date.Format = DateTimePickerFormat.Long; - if (value != null) - { - date.Value = (DateTime)value; - date.Checked = true; - } - else - date.Checked = false; - tableLayoutPanel.Controls.Add(date); - - DateTimePicker time = new DateTimePicker(); - time.MinDate = DateTime.MinValue; - time.MaxDate = DateTime.MaxValue; - time.Dock = DockStyle.Fill; - time.MinimumSize = new Size(0, 24); - time.Format = DateTimePickerFormat.Time; - time.ShowUpDown = true; - if (value != null) - { - time.Value = (DateTime)value; - time.Enabled = true; - } - else - time.Enabled = false; - tableLayoutPanel.Controls.Add(time); - // Tag so we can remove this control later - date.Tag = time; - date.ValueChanged += delegate(Object sender, EventArgs e) - { - time.Enabled = date.Checked; - }; - - control = date; - break; - - default: - throw new InvalidEnumArgumentException(); - } - - controls[customFieldDefinition] = new KeyValuePair(lblKey, control); + if (1 < row.Controls.Length && row.Controls.Length < tableLayoutPanel.ColumnCount) + tableLayoutPanel.SetColumnSpan(row.Controls[1], 2); //this is the textbox } - else if (revertValues) + else if (resetValues) { - KeyValuePair kvp = controls[customFieldDefinition]; - - SetValue(customFieldDefinition, kvp.Value, value); + row.SetValue(value); } } - // Remove old ones - CustomFieldDefinition[] definitions = new CustomFieldDefinition[controls.Keys.Count]; - controls.Keys.CopyTo(definitions, 0); + tableLayoutPanel.Controls.Add(_panelDummy); - foreach (CustomFieldDefinition definition in definitions) + var extraRows = _fieldRows.Where(r => !customFieldDefinitions.Contains(r.CustomFieldDefinition)).ToList(); + + foreach (var row in extraRows) { - if (Array.IndexOf(customFieldDefinitions, definition) > -1) - continue; + row.DeleteCustomFieldClicked -= DeleteCustomFieldClicked_Click; + _fieldRows.Remove(row); - KeyValuePair kvp = controls[definition]; - - tableLayoutPanel.Controls.Remove(kvp.Value); - tableLayoutPanel.Controls.Remove(kvp.Key); - - DateTimePicker timeControl = kvp.Value.Tag as DateTimePicker; - if (timeControl != null) + foreach (var control in row.Controls) { - tableLayoutPanel.Controls.Remove(timeControl); + tableLayoutPanel.Controls.Remove(control); + control.Dispose(); } - - controls.Remove(definition); - - kvp.Key.Dispose(); - kvp.Value.Dispose(); } tableLayoutPanel.ResumeLayout(); } - private void buttonEditCustomFields_Click(object sender, EventArgs e) + private void buttonNewCustomField_Click(object sender, EventArgs e) { - new CustomFieldsDialog(xenObject.Connection).ShowDialog(this); + using (var dialog = new NewCustomFieldDialog(_xenObject.Connection)) + { + if (dialog.ShowDialog() == DialogResult.OK) + new AddCustomFieldAction(_xenObject.Connection, dialog.Definition).RunAsync(); + } + } + + private void DeleteCustomFieldClicked_Click(CustomFieldRow row) + { + string name = row.CustomFieldDefinition.Name.Ellipsise(50); + + if (!Program.RunInAutomatedTestMode) + { + using (var dialog = new WarningDialog(string.Format(Messages.MESSAGEBOX_DELETE_CUSTOM_FIELD, name), + ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)) + { + if (dialog.ShowDialog(Program.MainWindow) != DialogResult.Yes) + return; + } + } + + if (_xenObject.Connection != null && _xenObject.Connection.IsConnected) + { + new RemoveCustomFieldAction(_xenObject.Connection, row.CustomFieldDefinition).RunAsync(); + } + else if (!Program.RunInAutomatedTestMode) + { + using (var dlg = new ErrorDialog(Messages.DISCONNECTED_BEFORE_ACTION_STARTED)) + dlg.ShowDialog(this); + } + } + + + private class CustomFieldRow + { + private readonly TextBox _textBox; + private readonly DateTimePicker _datePicker; + private readonly DateTimePicker _timePicker; + + public event Action DeleteCustomFieldClicked; + + public CustomFieldRow(CustomFieldDefinition definition, object value) + { + CustomFieldDefinition = definition; + + var controls = new List(); + + var label = CreateNewLabel(definition); + controls.Add(label); + + switch (definition.Type) + { + case CustomFieldDefinition.Types.String: + _textBox = CreateNewTextBox(value as string); + controls.Add(_textBox); + break; + + case CustomFieldDefinition.Types.Date: + _datePicker = CreateNewDatePicker(value as DateTime?); + _timePicker = CreateNewTimePicker(value as DateTime?); + controls.Add(_datePicker); + controls.Add(_timePicker); + break; + default: + throw new InvalidEnumArgumentException(nameof(definition)); + } + + var deleteButton = CreateNewDeleteButton(); + controls.Add(deleteButton); + Controls = controls.ToArray(); + + UpdateDateTImePickerState(); + } + + public CustomFieldDefinition CustomFieldDefinition { get; } + + public Control[] Controls { get; } + + public object GetValue() + { + if (_textBox != null && !string.IsNullOrEmpty(_textBox.Text)) + return _textBox.Text; + + if (_datePicker != null && _timePicker != null && _datePicker.Checked) + { + DateTime date = _datePicker.Value; + DateTime time = _timePicker.Value; + return new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second); + } + + return null; + } + + public void SetValue(object value) + { + if (_textBox != null) + { + _textBox.Text = value as string; + return; + } + + if (_datePicker != null && _timePicker != null) + { + if (value != null) + { + _datePicker.Checked = true; + _datePicker.Value = (DateTime)value; + _timePicker.Value = (DateTime)value; + } + else + { + _datePicker.Checked = false; + } + } + } + + private Label CreateNewLabel(CustomFieldDefinition customFieldDefinition) + { + return new Label + { + Anchor = AnchorStyles.Left | AnchorStyles.Right, + Text = customFieldDefinition.Name.EscapeAmpersands().Ellipsise(25), + Font = Program.DefaultFont, + AutoSize = true, + AutoEllipsis = false + }; + } + + private TextBox CreateNewTextBox(string text) + { + return new TextBox + { + Anchor = AnchorStyles.Left | AnchorStyles.Right, + Text = text ?? string.Empty + }; + } + + private DateTimePicker CreateNewDatePicker(DateTime? value) + { + var date = new DateTimePicker + { + Anchor = AnchorStyles.Left | AnchorStyles.Right, + MinDate = DateTime.MinValue, + MaxDate = DateTime.MaxValue, + ShowCheckBox = true, + Format = DateTimePickerFormat.Long, + Checked = value.HasValue + }; + + if (value.HasValue) + date.Value = (DateTime)value; + + date.ValueChanged += Date_ValueChanged; + return date; + } + + private DateTimePicker CreateNewTimePicker(DateTime? value) + { + var time = new DateTimePicker + { + Anchor = AnchorStyles.Left | AnchorStyles.Right, + MinDate = DateTime.MinValue, + MaxDate = DateTime.MaxValue, + Format = DateTimePickerFormat.Time, + ShowUpDown = true, + Enabled = value.HasValue + }; + + if (value.HasValue) + time.Value = (DateTime)value; + + return time; + } + + private Button CreateNewDeleteButton() + { + var buttonDelete = new Button + { + Anchor = AnchorStyles.Left, + BackColor = Color.Transparent, + FlatStyle = FlatStyle.Flat, + Image = Images.StaticImages._000_Abort_h32bit_16, + Size = new Size(22, 22), + Text = string.Empty, + }; + buttonDelete.FlatAppearance.BorderSize = 0; + buttonDelete.Click += buttonDelete_Click; + return buttonDelete; + } + + private void UpdateDateTImePickerState() + { + if (_datePicker != null && _timePicker != null) + _timePicker.Enabled = _datePicker.Checked; + } + + private void Date_ValueChanged(object sender, EventArgs e) + { + UpdateDateTImePickerState(); + } + + private void buttonDelete_Click(object sender, EventArgs e) + { + DeleteCustomFieldClicked?.Invoke(this); + } } } } diff --git a/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.designer.cs b/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.designer.cs index 5e440dba0..f2d404f8f 100644 --- a/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.designer.cs +++ b/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.designer.cs @@ -34,7 +34,7 @@ namespace XenAdmin.SettingsPanels { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CustomFieldsDisplayPage)); this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); - this.buttonEditCustomFields = new System.Windows.Forms.Button(); + this.buttonNewtCustomField = new System.Windows.Forms.Button(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.label1 = new System.Windows.Forms.Label(); this.tableLayoutPanel1.SuspendLayout(); @@ -43,22 +43,21 @@ namespace XenAdmin.SettingsPanels // tableLayoutPanel // resources.ApplyResources(this.tableLayoutPanel, "tableLayoutPanel"); - this.tableLayoutPanel.MaximumSize = new System.Drawing.Size(480, 999999); - this.tableLayoutPanel.MinimumSize = new System.Drawing.Size(480, 190); this.tableLayoutPanel.Name = "tableLayoutPanel"; // - // buttonEditCustomFields + // buttonNewtCustomField // - resources.ApplyResources(this.buttonEditCustomFields, "buttonEditCustomFields"); - this.buttonEditCustomFields.Name = "buttonEditCustomFields"; - this.buttonEditCustomFields.UseVisualStyleBackColor = true; - this.buttonEditCustomFields.Click += new System.EventHandler(this.buttonEditCustomFields_Click); + resources.ApplyResources(this.buttonNewtCustomField, "buttonNewtCustomField"); + this.buttonNewtCustomField.Image = global::XenAdmin.Properties.Resources.more_16; + this.buttonNewtCustomField.Name = "buttonNewtCustomField"; + this.buttonNewtCustomField.UseVisualStyleBackColor = true; + this.buttonNewtCustomField.Click += new System.EventHandler(this.buttonNewCustomField_Click); // // tableLayoutPanel1 // resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); - this.tableLayoutPanel1.Controls.Add(this.buttonEditCustomFields, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.buttonNewtCustomField, 0, 1); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; // // label1 @@ -79,13 +78,14 @@ namespace XenAdmin.SettingsPanels this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); this.ResumeLayout(false); + this.PerformLayout(); } #endregion private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; - private System.Windows.Forms.Button buttonEditCustomFields; + private System.Windows.Forms.Button buttonNewtCustomField; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.Label label1; } diff --git a/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.resx b/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.resx index 6509713bb..7f06b5ea3 100644 --- a/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.resx +++ b/XenAdmin/SettingsPanels/CustomFieldsDisplayPage.resx @@ -112,36 +112,40 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + True - 3 + 4 - + + + Fill + + - 0, 82 + 0, 85 1 - 480, 190 + 480, 187 - 3 + 1 tableLayoutPanel - System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this @@ -149,42 +153,50 @@ 0 - - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="Absolute,140,Percent,65,Percent,35" /><Rows Styles="AutoSize,0" /></TableLayoutSettings> + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls /><Columns Styles="AutoSize,0,Percent,45,Percent,35,Percent,20" /><Rows Styles="AutoSize,0" /></TableLayoutSettings> - + Top, Right - + True - - 365, 52 + + 350, 52 - - 112, 23 + + 3, 3, 3, 10 - - 4 + + 127, 23 - - &Edit Custom Fields... - - - buttonEditCustomFields - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tableLayoutPanel1 - - + 1 - - Top, Left, Right + + &New Custom Field... + + + ImageBeforeText + + + buttonNewtCustomField + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + True + + + GrowAndShrink 1 @@ -196,13 +208,13 @@ Fill - 0, 0 + 3, 0 - 0, 0, 0, 0 + 3, 0, 3, 10 - 480, 49 + 474, 39 0 @@ -214,7 +226,7 @@ label1 - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 tableLayoutPanel1 @@ -222,6 +234,9 @@ 0 + + Top + 0, 0 @@ -229,16 +244,16 @@ 2 - 480, 82 + 480, 85 - 5 + 0 tableLayoutPanel1 - System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 $this @@ -247,9 +262,9 @@ 1 - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="buttonEditCustomFields" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,50" /><Rows Styles="Percent,50,Absolute,33" /></TableLayoutSettings> + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label1" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="buttonNewtCustomField" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100" /><Rows Styles="AutoSize,0,AutoSize,0" /></TableLayoutSettings> - + True @@ -265,6 +280,6 @@ CustomFieldsDisplayPage - System.Windows.Forms.UserControl, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Windows.Forms.UserControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/XenAdmin/XenAdmin.csproj b/XenAdmin/XenAdmin.csproj index 3fee7370c..b3e1ce4ec 100755 --- a/XenAdmin/XenAdmin.csproj +++ b/XenAdmin/XenAdmin.csproj @@ -1146,12 +1146,6 @@ AddServerDialog.cs - - Form - - - CustomFieldsDialog.cs - Form @@ -2559,10 +2553,6 @@ AttachDiskDialog.cs Designer - - CustomFieldsDialog.cs - Designer - CopyVMDialog.cs Designer @@ -5192,12 +5182,6 @@ CopyVMDialog.cs - - CustomFieldsDialog.cs - - - CustomFieldsDialog.cs - DateFilterDialog.cs diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index 04839400e..e457d63c3 100755 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -23984,7 +23984,7 @@ namespace XenAdmin { } /// - /// Looks up a localized string similar to This will delete '{0}' permanently, destroying all data associated with it. Continue?. + /// Looks up a localized string similar to This will delete '{0}' permanently, making it unavailable to all resources in the pool and destroying all data associated with it. Do you want to continue?. /// public static string MESSAGEBOX_DELETE_CUSTOM_FIELD { get { diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index a1e374ad6..79433cc62 100755 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -8334,7 +8334,7 @@ This will permanently delete and reinitialize all local storage on the servers. Deactivate Virtual Disk - This will delete '{0}' permanently, destroying all data associated with it. Continue? + This will delete '{0}' permanently, making it unavailable to all resources in the pool and destroying all data associated with it. Do you want to continue? This will delete {0} permanently, destroying the data on it. Continue? From 488f27e2ae8655391654dfc5d76869ffd6c3b5f0 Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Tue, 28 Feb 2023 21:25:02 +0000 Subject: [PATCH 5/5] Revised Rbac checks for addition/removal of custom fields. Signed-off-by: Konstantina Chremmou --- .../CustomFields/AddCustomFieldAction.cs | 4 +++- .../CustomFields/RemoveCustomFieldAction.cs | 17 ++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/XenModel/Actions/CustomFields/AddCustomFieldAction.cs b/XenModel/Actions/CustomFields/AddCustomFieldAction.cs index 3e6553198..6d67c4042 100644 --- a/XenModel/Actions/CustomFields/AddCustomFieldAction.cs +++ b/XenModel/Actions/CustomFields/AddCustomFieldAction.cs @@ -45,7 +45,9 @@ namespace XenAdmin.Actions string.Format(Messages.ADDING_CUSTOM_FIELD, definition.Name), false) { _definition = definition; - ApiMethodsToRoleCheck.AddRange("pool.add_to_gui_config", "pool.remove_from_gui_config"); + + ApiMethodsToRoleCheck.AddWithKey("pool.add_to_gui_config", CustomFieldsManager.CUSTOM_FIELD_BASE_KEY); + ApiMethodsToRoleCheck.AddWithKey("pool.remove_from_gui_config", CustomFieldsManager.CUSTOM_FIELD_BASE_KEY); } protected override void Run() diff --git a/XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs b/XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs index 4f106abb7..5420066cf 100644 --- a/XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs +++ b/XenModel/Actions/CustomFields/RemoveCustomFieldAction.cs @@ -46,13 +46,16 @@ namespace XenAdmin.Actions { _definition = definition; - ApiMethodsToRoleCheck.AddRange("pool.add_to_gui_config", "pool.remove_from_gui_config", - "pool.remove_from_other_config", - "host.remove_from_other_config", - "VM.remove_from_other_config", - "SR.remove_from_other_config", - "VDI.remove_from_other_config", - "Network.remove_from_other_config"); + string key = CustomFieldsManager.GetCustomFieldKey(definition); + + ApiMethodsToRoleCheck.AddWithKey("pool.add_to_gui_config", CustomFieldsManager.CUSTOM_FIELD_BASE_KEY); + ApiMethodsToRoleCheck.AddWithKey("pool.remove_from_gui_config", CustomFieldsManager.CUSTOM_FIELD_BASE_KEY); + ApiMethodsToRoleCheck.AddWithKey("pool.remove_from_other_config", key); + ApiMethodsToRoleCheck.AddWithKey("host.remove_from_other_config", key); + ApiMethodsToRoleCheck.AddWithKey("VM.remove_from_other_config", key); + ApiMethodsToRoleCheck.AddWithKey("SR.remove_from_other_config", key); + ApiMethodsToRoleCheck.AddWithKey("VDI.remove_from_other_config", key); + ApiMethodsToRoleCheck.AddWithKey("Network.remove_from_other_config", key); } protected override void Run()