Pages

Tuesday, January 22, 2013

How to get patronymic from Active Directory

As there is no special field for patronymic in the Active Directory (AD) it is often filled in the Display Name field, while as surname of the user is in the Surname field and his name is in the Given Name field.
So, if you want to import users from the AD with full names and hierarchy, you might try to simply load names, surnames and everything else you need. For patronymics I suggest parse full names: split it by spaces, then remove the name and the surname and join the rest. It should be your patronymic. At least it might be, as administrators might put only reduced forms of a name into display name...
Overral, here is my code, which recursively loads users:

/// 
/// Class for imported users
/// 
public class DomainUser
{
    public string FullName;
    public string Name;
    public string Surname;
    public string Login;
    public string EMail;
    public System.Security.Principal.SecurityIdentifier SID;

    public bool IsOrgUnit;
    public bool IsChecked; //used later for treeview

    public List SubUsers;

    public override string ToString()
    {
        return FullName;
    }
}

public static List GetADUsers(DirectoryEntry root = null)
{
    DirectorySearcher searcher;
    if (root != null)
        searcher = new DirectorySearcher(root);
    else
        searcher = new DirectorySearcher();

    searcher.SearchScope = SearchScope.OneLevel;
    //get only organizational units (for hierarchy) and users-persons
    searcher.Filter = "(|(objectclass=organizationalunit)(&(objectclass=user)(objectcategory=person)))";
            
    List users = new List();

    ResultPropertyValueCollection vc;
    foreach (SearchResult result in searcher.FindAll())
    {
        vc = result.Properties["objectclass"];
        if (vc == null || vc.Count == 0) continue;

        DomainUser currentEntry = new DomainUser();
        currentEntry.IsChecked = true;

        //object class might contain "top" as first entry - so check its actual class with "Contains"
        if (vc.Contains("organizationalUnit"))
        {
            currentEntry.IsOrgUnit = true;

            vc = result.Properties["name"];
            if (vc != null && vc.Count > 0) currentEntry.FullName = vc[0].ToString();

            //as OU can have subusers - look into it:
            currentEntry.SubUsers = GetADUsers(result.GetDirectoryEntry());
        }
        else if (vc.Contains("user"))
        {
            currentEntry.IsOrgUnit = false;

            vc = result.Properties["displayName"];
            if (vc != null && vc.Count > 0) currentEntry.FullName= vc[0].ToString();

            vc = result.Properties["sn"];
            if (vc != null && vc.Count > 0) currentEntry.Surname= vc[0].ToString();

            vc = result.Properties["givenname"];
            if (vc != null && vc.Count > 0) currentEntry.Name = vc[0].ToString();

            vc = result.Properties["objectsid"];
            if (vc != null && vc.Count > 0) currentEntry.SID = new System.Security.Principal.SecurityIdentifier((byte[])vc[0], 0);

            vc = result.Properties["mail"];
            if (vc != null && vc.Count > 0) currentEntry.EMail = vc[0].ToString();

            vc = result.Properties["samAccountName"];
            if (vc != null && vc.Count > 0) currentEntry.Login = vc[0].ToString();
        }

        users.Add(currentEntry);
    }

    return users;
}

private void GetUserFullName(DomainUser user)
{
    //if that user has surname and name - we can try to get its patronymic
    if (!string.IsNullOrEmpty(user.Surname) && !string.IsNullOrEmpty(user.Name))
    {
        string name = user.Name.Trim();
        string surname = user.Surname.Trim();

        List fullNameParts = new List(user.FullName.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
        //if there is name and surname in display name
        if (fullNameParts.Remove(name) && fullNameParts.Remove(surname))
        {
            string patronymic = string.Join(" ", fullNameParts);
        }
    }
}

Wednesday, January 16, 2013

How to disable glue in Visio

Recently I was implementing text labels for shapes inside visio control. This task was not very hard - I had to add new shape for labels into our stencil, add action for specific shapes and drop labels with that action.
Here is the method to add action for shape:

class VisioUtility
{
    public static string GetCellValue(Shape shape, VisSectionIndices section, VisRowIndices row, VisCellIndices column)
    {
        Cell cell = GetCell(shape, section, row, column);
        return cell.Formula;
    }

    public static Cell GetCell(Shape shape,VisSectionIndices section,VisRowIndices row, VisCellIndices column)
 {
  return shape.get_CellsSRC((short)section,(short)row,(short)column);
  }

    public static void AddSectionRow(Shape shape, short sectionIndex, VisRowIndices rowIndex, VisRowTags rowTag)
    {
        shape.AddRow(sectionIndex, (short)rowIndex, (short)rowTag);
    }

    public static void SetCellValue(Shape shape, VisSectionIndices section, VisRowIndices row, VisCellIndices column, string value)
    {
        Cell cell = GetCell(shape, section, row, column);
        if (cell.Formula != value)
        {   //in case of UNDO...
            try
            {
                cell.FormulaU = value;
            }
            catch (System.Runtime.InteropServices.COMException ex) //7580 - workaround
            {
                //handle the exception
            }
        }
    }
}

class VisioForm
{
    public static void AddAction(Shape element, string actionMarker, string title, bool isSubMenu, bool beginGroup)
    {
        //Check if this menu item is already exists
        short newItemNumber = 0;
        while (element.get_CellsSRCExists((short)VisSectionIndices.visSectionAction, (short)(VisRowIndices.visRowAction + newItemNumber), (short)VisCellIndices.visActionAction, 0) != 0)
        {
            string currentTitle = VisioUtility.GetCellValue(element, VisSectionIndices.visSectionAction, VisRowIndices.visRowAction + newItemNumber, VisCellIndices.visActionMenu);

            if (VisioUtility.StripQuotes(currentTitle) == title)
                return;

            newItemNumber++;
        }

        //add new item as last one
        newItemNumber = element.get_RowCount((short)VisSectionIndices.visSectionAction);

        VisioUtility.AddSectionRow(element, (short)VisSectionIndices.visSectionAction, VisRowIndices.visRowAction + newItemNumber, (short)VisRowTags.visTagDefault);
        Cell cell = element.get_CellsSRC((short)VisSectionIndices.visSectionAction, (short)(VisRowIndices.visRowAction + newItemNumber), (short)VisCellIndices.visActionAction);
        //set action marker, so we can handle it after
        cell.FormulaU = string.Format("RUNADDONWARGS(\"QueueMarkerEvent\",\"{0}\")", actionMarker);

        //set title
        VisioUtility.SetCellValue(element, VisSectionIndices.visSectionAction, VisRowIndices.visRowAction + newItemNumber, VisCellIndices.visActionMenu, title);

        if (isSubMenu)
            VisioUtility.SetCellValue(element, VisSectionIndices.visSectionAction, VisRowIndices.visRowAction + newItemNumber, VisCellIndices.visActionFlyoutChild, "1");

        if (beginGroup)
            VisioUtility.SetCellValue(element, VisSectionIndices.visSectionAction, VisRowIndices.visRowAction + newItemNumber, VisCellIndices.visActionBeginGroup, "1");
    }
}

And I had to handle MarkerEvent for visio application
visioApp.MarkerEvent += new EApplication_MarkerEventEventHandler(visioApp_MarkerEvent);

The main problem I faced with this task is how to disable glue for those labels, as for visio document GlueSettings we have visGlueToGeometry. I asked for this question on visguy forum and got few interesting advices, however they were not what I was looking for. The most usefull advice was to create a group and set IsSnapTarget to False. I gave up on this path, since creating the group was too complex.


The solution turned to be very easy. There is cell that disables connect for geometry NoSnap, so simply put True there!