/* 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.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using XenAPI; using XenAdmin.Core; namespace XenAdmin.Dialogs { public partial class RoleSelectionDialog : XenDialogBase { private Subject[] subjects; private Pool pool; private Dictionary> roleAssignment; // Used to stop 'changes made' checks during a batch select bool batchChange = false; public RoleSelectionDialog() { InitializeComponent(); } public RoleSelectionDialog(Subject[] subjects, Pool pool) : this() { this.subjects = subjects; this.pool = pool; roleAssignment = new Dictionary>(); if (subjects.Length == 1) { Subject subject = subjects[0]; string sName = (subject.DisplayName ?? subject.SubjectName ?? Messages.UNKNOWN_AD_USER).Ellipsise(30); if (subject.IsGroup) { labelBlurb.Text = String.Format(Messages.AD_SELECT_ROLE_GROUP, sName); pictureBoxSubjectType.Image = XenAdmin.Properties.Resources._000_UserAndGroup_h32bit_32; } else { labelBlurb.Text = String.Format(Messages.AD_SELECT_ROLE_USER, sName); pictureBoxSubjectType.Image = XenAdmin.Properties.Resources._000_User_h32bit_16; } } else { labelBlurb.Text = Messages.AD_SELECT_ROLE_MIXED; pictureBoxSubjectType.Image = XenAdmin.Properties.Resources._000_User_h32bit_16; } // Get the list of roles off the server and arrange them into rank List serverRoles = new List(pool.Connection.Cache.Roles); //hide basic permissions, we only want the roles. serverRoles.RemoveAll(delegate(Role r){return r.subroles.Count < 1;}); serverRoles.Sort(); serverRoles.Reverse(); foreach (Role r in serverRoles) { roleAssignment.Add(r, new List()); } foreach (Subject s in subjects) { List subjectRoles = (List)pool.Connection.ResolveAll(s.roles); foreach (Role r in subjectRoles) { roleAssignment[r].Add(s); } } foreach (Role role in serverRoles) { DataGridViewRow r = new DataGridViewRow(); DataGridViewCheckBoxCellEx c1 = new DataGridViewCheckBoxCellEx(); c1.ThreeState = true; if (roleAssignment[role].Count == subjects.Length) { c1.Value = CheckState.Checked; c1.CheckState = CheckState.Checked; } else if (roleAssignment[role].Count == 0) { c1.Value = CheckState.Unchecked; c1.CheckState = CheckState.Unchecked; } else { c1.Value = CheckState.Indeterminate; c1.CheckState = CheckState.Indeterminate; } DataGridViewTextBoxCell c2 = new DataGridViewTextBoxCell(); c2.Value = role.FriendlyName; r.Cells.Add(c1); r.Cells.Add(c2); r.Tag = role; gridRoles.Rows.Add(r); } setRoleDescription(); } private void setRoleDescription() { if (gridRoles.SelectedRows.Count < 1) return; Role r = gridRoles.SelectedRows[0].Tag as Role; if (r == null) return; labelDescription.Text = r.FriendlyDescription; } private void buttonSave_Click(object sender, EventArgs e) { foreach (Subject s in subjects) { List rolesToAdd = new List(); List rolesToRemove = new List(); foreach (DataGridViewRow r in gridRoles.Rows) { bool check = (bool)r.Cells[0].Value; Role role = r.Tag as Role; if (check && !(roleAssignment[role].Contains(s))) rolesToAdd.Add(role); else if (!check && (roleAssignment[role].Contains(s))) rolesToRemove.Add(role); } XenAdmin.Actions.AddRemoveRolesAction a = new XenAdmin.Actions.AddRemoveRolesAction(pool, s, rolesToAdd, rolesToRemove); a.RunAsync(); } Close(); } private bool changesMade() { foreach (DataGridViewRow r in gridRoles.Rows) { Role role = r.Tag as Role; DataGridViewCheckBoxCellEx c = r.Cells[0] as DataGridViewCheckBoxCellEx; if (c.CheckState == CheckState.Checked && roleAssignment[role].Count < subjects.Length) { return true; } else if (c.CheckState == CheckState.Unchecked && roleAssignment[role].Count > 0) { return true; } // Don't care about intermediate values, it's not possible to set them in the dialog } return false; } private void buttonCancel_Click(object sender, EventArgs e) { Close(); } private void gridRoles_SelectionChanged(object sender, EventArgs e) { setRoleDescription(); } // We want to allow the user to select rows without changing the check boxes so they can browse the descriptions // so handle mouseclicks only on the check box column // Only allow them to select a single role, which also means all that tri state rubbish dissapears as soon as they make a selection private void gridRoles_CellContentClick(object sender, DataGridViewCellEventArgs e) { if (e.ColumnIndex != 0 || e.RowIndex < 0 || batchChange) return; selectIndex(e.RowIndex); buttonSave.Enabled = changesMade(); } // We want to allow the user to select rows without changing the check boxes so they can browse the descriptions // but we want it to be keyboard accessible. Capture the select keypress and toggle the checkbox private void gridRoles_KeyPress(object sender, KeyPressEventArgs e) { // makes assumption about multiselect System.Diagnostics.Trace.Assert(!gridRoles.MultiSelect); if (batchChange || e.KeyChar != ' ') return; selectIndex(gridRoles.SelectedRows[0].Index); buttonSave.Enabled = changesMade(); } private void selectIndex(int i) { batchChange = true; foreach (DataGridViewRow r in gridRoles.Rows) { if (i == r.Index) { r.Cells[0].Value = true; ((DataGridViewCheckBoxCellEx)(r.Cells[0])).CheckState = CheckState.Checked; continue; } r.Cells[0].Value = false; ((DataGridViewCheckBoxCellEx)(r.Cells[0])).CheckState = CheckState.Unchecked; } batchChange = false; } // Data grid view check box cells dont commit their value immeadiately, though the UI updates (edit mode). // To prevent messing around we handle the check state value ourselves. class DataGridViewCheckBoxCellEx : DataGridViewCheckBoxCell { public CheckState CheckState = CheckState.Unchecked; } } }