UPDATE: Added capability to scroll up and down through the hierarchy.
UPDATE: I have uploaded a document that contains the full source code
(sorry, wordpress won’t allow me to upload a ZIP file).
Get It Here: Org Chart Code
We are going to create a simple Org Chart using the Profile Manager and Google’s Org Chart API (http://code.google.com/apis/visualization/documentation/gallery/orgchart.html).
I recommend starting with the WSP Builder from CodePlex. This add-in to Visual Studio helps you make features quickly by compiling the wsp for you, without you having to create the ddf or manifest files. Using the WSP Builder Template, choose New –> Project –> WSPBuilder –> WSPBuilder Project.
You will first need to add references in your project to SharePoint and Microsoft.Office.Server, which can typically be found in the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI directory.
Next, right-click on the WebPart Project Name, and select Add –> New Item. Choose WSPBuilder –> Web Part Feature. Name the Web Part feature, and set your scope. For the purposes of this excercise, we’ll use the Site scope, which sets the feature at the Site Collection level. This will create the appropriate files and folders for you under the 12 directory
Let’s start by setting 2 private variables for the class
private int orgChartIndex = 0; private int levelsToReturn = 3;
The orgChartIndex is used to keep track of which item you’re on, so that you can populate Google’s array items properly. The levelsToReturn is how many levels deep of hierarchy you wish to display.
Next, let’s set the Render Method. The Render method simply calls the PopulateOrg method.
UPDATE: Updated this code to include the “up and down” feature
protected override void Render(HtmlTextWriter writer)
{
try
{
if (Page.IsPostBack)
{
// This is used to determine if you're navigating up or down the tree
//writer.Write(this.Page.Request["__EVENTARGUMENT"]);
string loginName = System.Web.HttpUtility.UrlDecode(this.Page.Request["__EVENTARGUMENT"]);
if (!string.IsNullOrEmpty(loginName))
{
if (loginName.StartsWith("UP:"))
{
writer.Write(PopulateOrg(loginName.Substring(3), true));
}
else if (loginName.StartsWith("DN:"))
{
writer.Write(PopulateOrg(loginName.Substring(3), false));
}
}
}
else
{
writer.Write(PopulateOrg(null, false));
}
}
catch (Exception ex)
{
writer.Write(ex.Message + " " + ex.StackTrace);
}
}
This code assumes that you have the Manager field in the user’s Profile populated. If you have a custom field, just replace PropertyConstants.Manager with the name of your custom field.
UPDATE: This was modified to use the “Up and Down” feature.
private string PopulateOrg(string overrideString, bool isUp)
{
try
{
string s = "";
SPSecurity.RunWithElevatedPrivileges(delegate()
{
// Open current site
using (SPSite secureSite = new SPSite(SPContext.Current.Site.ID))
{
ServerContext siteContext = ServerContext.GetContext(secureSite);
UserProfileManager profManager = new UserProfileManager(siteContext);
bool runUser = true;
if (!string.IsNullOrEmpty(overrideString))
{
if (profManager.UserExists(overrideString))
{
runUser = false;
UserProfile clickedUsersProfile = profManager.GetUserProfile(overrideString);
if (isUp)
{
string clickedManagersLogin = GetUserProfileProperty(clickedUsersProfile, PropertyConstants.Manager);
if (!string.IsNullOrEmpty(clickedManagersLogin))
{
if (profManager.UserExists(clickedManagersLogin))
{
UserProfile clickedManagersProfile = profManager.GetUserProfile(clickedManagersLogin);
s = OutputManager(clickedManagersProfile);
}
}
else
{
s = OutputManager(clickedUsersProfile);
}
}
else
{
s = OutputManager(clickedUsersProfile);
}
}
else
{
runUser = true;
}
}
if (runUser)
{
string currentUserLogin = SPContext.Current.Web.CurrentUser.LoginName;
if (profManager.UserExists(currentUserLogin))
{
UserProfile currentUsersProfile = profManager.GetUserProfile(currentUserLogin);
string managersLogin = GetUserProfileProperty(currentUsersProfile, PropertyConstants.Manager);
if (!string.IsNullOrEmpty(managersLogin))
{
if (profManager.UserExists(managersLogin))
{
UserProfile managersProfile = profManager.GetUserProfile(managersLogin);
s = OutputManager(managersProfile);
}
else
{
s = OutputManager(currentUsersProfile);
}
}
else
{
s = OutputManager(currentUsersProfile);
}
}
}
}
});
return s;
}
catch (Exception ex)
{
return ex.ToString();
}
}
// ******************************************************************
// ******************************************************************
//
// Some code in this section will need to be replaced with your
// specific information
//
// ******************************************************************
// ******************************************************************
private string OutputManager(UserProfile profile)
{
StringBuilder sb = new StringBuilder();
sb.Append("\n\n <p><div id='google_org_chart_div_ZoomInfo'>Zoom Level is 100%</div></p>");
sb.Append("\n\n <p><div style='height=300px;width=1000px;overflow:auto'><div style='zoom=1' id='google_org_chart_div'></div></div></p>");
sb.Append("\n<a href='javascript:ZoomIn()'>Zoom In</a>");
sb.Append(" <a href='javascript:ZoomOut()'>Zoom Out</a>");
sb.Append("\n\n<script type='text/javascript' src='http://www.google.com/jsapi'></script>");
// If you are running HTTPS then download the jsapi from google and put it into a library on your site
//sb.Append("\n\n<script type='text/javascript' src='https://YOUR-URL-HERE/Shared%20Documents/jsapi'></script>");
sb.Append("\n<script type='text/javascript'> ");
sb.Append("\n var zoom = 1; ");
sb.Append("\n function ZoomOut() { ");
sb.Append("\n zoom = zoom - .1; ");
sb.Append("\n if (zoom < .10) { zoom = .10; } ");
sb.Append("\n var div = document.getElementById('google_org_chart_div'); ");
sb.Append("\n var infoDiv = document.getElementById('google_org_chart_div_ZoomInfo'); ");
sb.Append("\n infoDiv.innerText = 'Zoom Level is ' + Math.round(zoom*100).toString() + '%'; ");
sb.Append("\n if(div.style.zoom) { div.style.zoom = zoom; } ");
sb.Append("\n } ");
sb.Append("\n function ZoomIn() { ");
sb.Append("\n zoom = zoom + .10; ");
sb.Append("\n if (zoom > 2) { zoom = 2; } ");
sb.Append("\n var div = document.getElementById('google_org_chart_div'); ");
sb.Append("\n var infoDiv = document.getElementById('google_org_chart_div_ZoomInfo'); ");
sb.Append("\n infoDiv.innerText = 'Zoom Level is ' + Math.round(zoom*100).toString() + '%'; ");
sb.Append("\n if(div.style.zoom) { div.style.zoom = zoom; } ");
sb.Append("\n } ");
sb.Append("\n google.load('visualization', '1', {packages:['orgchart']}); ");
sb.Append("\n google.setOnLoadCallback(drawChart); ");
sb.Append("\n function drawChart() { ");
sb.Append("\n var data = new google.visualization.DataTable(); ");
sb.Append("\n data.addColumn('string', 'Name'); ");
sb.Append("\n data.addColumn('string', 'Manager'); ");
sb.Append("\n data.addColumn('string', 'ToolTip'); ");
string managersName = GetUserProfileProperty(profile, PropertyConstants.PreferredName);
managersName = managersName.Replace("'", "");
sb.Append("\n data.addRows(1); ");
sb.Append("\n data.setCell(0, 0, '" + managersName + "', '" + GetBoxHTML(profile) + "'); ");
sb.Append("\n data.setCell(0, 2, '" + managersName + "'); ");
orgChartIndex = 0;
foreach (UserProfile directReport in profile.GetDirectReports())
{
sb.Append(OutputDirectReport(directReport, managersName, 2));
}
sb.Append("\n var orgchart = new google.visualization.OrgChart(document.getElementById('google_org_chart_div')); ");
sb.Append("\n orgchart.draw(data, {allowCollapse:true,allowHtml:true,size:'small'}); ");
sb.Append("\n } ");
sb.Append("\n </script>");
return sb.ToString();
}
private static string GetUserProfileProperty(UserProfile profile, string propertyName)
{
if (profile[propertyName] != null)
{
if (profile[propertyName].Value != null)
{
try
{
return profile[propertyName].Value.ToString();
}
catch { return string.Empty; }
}
}
return string.Empty;
}
private string OutputDirectReport(UserProfile directReport, string managersName, int level)
{
orgChartIndex++;
string s = string.Empty;
string directReportsName = GetUserProfileProperty(directReport, PropertyConstants.PreferredName);
directReportsName = directReportsName.Replace("'", "");
s += "\n data.addRows(1); ";
s += "\n data.setCell(" + orgChartIndex.ToString() + ", 0, '" + directReportsName + "', '" + GetBoxHTML(directReport) + "'); "; // Name and other
s += "\n data.setCell(" + orgChartIndex.ToString() + ", 1, '" + managersName + "'); "; // Manager
s += "\n data.setCell(" + orgChartIndex.ToString() + ", 2, '" + directReportsName + "'); "; // Tooltip
foreach (UserProfile dirReport in directReport.GetDirectReports())
{
if (level < levelsToReturn)
{
s += OutputDirectReport(dirReport, directReportsName, level + 1);
}
}
return s;
}
// ******************************************************************
// ******************************************************************
//
// Some code in this section will need to be replaced with your
// specific information
//
// ******************************************************************
// ******************************************************************
private string GetBoxHTML(UserProfile directReport)
{
string directReportsName = GetUserProfileProperty(directReport, PropertyConstants.PreferredName);
directReportsName = directReportsName.Replace("'", "");
string directReportsLoginName = GetUserProfileProperty(directReport, PropertyConstants.AccountName);
// If this is the logged in user, highlight it in red.
if (directReportsLoginName.ToLower() == SPContext.Current.Web.CurrentUser.LoginName.ToLower())
{
directReportsName = "<font color=\"red\">" + directReportsName + "</font>";
}
string directReportsEmail = GetUserProfileProperty(directReport, PropertyConstants.WorkEmail);
string directReportsPhone = GetUserProfileProperty(directReport, PropertyConstants.WorkPhone);
// Replace here...
string postBackHTMLUp = "<a id=\"" + orgChartIndex.ToString() + "_Up\" href=\"javascript:" + Page.ClientScript.GetPostBackEventReference(this, "UP:" + directReportsLoginName.Replace("'", "").Replace(@"YOUR-DOMAIN-HERE\", @"YOUR-DOMAIN-HERE\\")) + "\">";
postBackHTMLUp += "<img src=\"/_layouts/images/ReportsUp.gif\" border=\"0\"></a>";
postBackHTMLUp = postBackHTMLUp.Replace("'", "\\'");
// ... and here...
string postBackHTMLDn = "<a id=\"" + orgChartIndex.ToString() + "_Dn\" href=\"javascript:" + Page.ClientScript.GetPostBackEventReference(this, "DN:" + directReportsLoginName.Replace("'", "").Replace(@"YOUR-DOMAIN-HERE\", @"YOUR-DOMAIN-HERE\\")) + "\">";
postBackHTMLDn += "<img src=\"/_layouts/images/ReportsDown.gif\" border=\"0\"></a>";
postBackHTMLDn = postBackHTMLDn.Replace("'", "\\'");
// ... and here.
return string.Format("<b>{4}{5} <a href=\"https://mysite.YOUR-MYSITE-URL-HERE/Person.aspx?accountname={0}\">{1}</a></b><br/><a href=\"mailto:{2}\">{2}</a><br/>{3}", System.Web.HttpUtility.UrlEncode(directReportsLoginName.Replace("'", "")), directReportsName, directReportsEmail, directReportsPhone, postBackHTMLUp, postBackHTMLDn);
}


