Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Douglas committed Jul 20, 2011
0 parents commit 482358f
Show file tree
Hide file tree
Showing 21 changed files with 519 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
.DS_Store
13 changes: 13 additions & 0 deletions README
@@ -0,0 +1,13 @@
Salesforce.com Org Chart

URL: http://www.cloudspokes.com/challenge_detail.html?contestID=152
Blog: http://blog.cloudspokes.com/2011/04/salesforcecom-org-chart-winner.html
Challenge end date: Mon Apr 04 05:00:00 GMT 2011

Description
===========
We'd like to create a Visualforce page that displays an Org Chart for Salesforce.com users. The chart should be displayed using the Google Visualization API.

Requirements
============
Using a Developer org, create a Visualforce page and Apex controller that displays an Org Chart for the current active users. Displaying titles is optional but it would be nice. Ideally, place the business logic for the Org Chart creation in a separate Apex class that is called by the controller.
Binary file added docs/Org Chart Documentation.pdf
Binary file not shown.
Binary file added docs/Screen1.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Screen2.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions docs/license.txt
@@ -0,0 +1,7 @@
Copyright (c) 2011 CloudSpokes

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 changes: 7 additions & 0 deletions source/applications/Org_Chart.app
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomApplication xmlns="http://soap.sforce.com/2006/04/metadata">
<defaultLandingTab>standard-home</defaultLandingTab>
<label>Org Chart</label>
<tab>standard-Chatter</tab>
<tab>Org_Chart</tab>
</CustomApplication>
176 changes: 176 additions & 0 deletions source/classes/OrgChart.cls
@@ -0,0 +1,176 @@
/**
* @author KevinSwiggum
* Copyright 2011 Radial Web Solutions LLC
*/
public with sharing class OrgChart {

static Boolean chatterEnabled = true;

/**
* populates a list of RoleWithUsers object for the given selectedRoleIds
* @author KevinSwiggum
*/
public static List<RoleWithUsers> getRolesWithUsers(Set<ID> roleIds) {
List<RoleWithUsers> rolesWithUsersList = new List<RoleWithUsers>();
List<UserRole> roles = new List<UserRole>();
try {
roles = [SELECT ID
, Name
, ParentRoleId
, (SELECT ID, name, title, email, phone, SmallPhotoUrl FROM Users WHERE IsActive=true)
FROM UserRole
WHERE parentRoleId IN :roleIds
OR ID IN :roleIds
ORDER BY ParentRoleId NULLS FIRST, ID];
} catch (System.Requiredfeaturemissingexception e) {
//catches an error in the event that chatter is not enabled in this org.
roles = [SELECT ID
, Name
, ParentRoleId
, (SELECT ID, name, title, email, phone FROM Users WHERE IsActive=true)
FROM UserRole
WHERE parentRoleId IN :roleIds
OR ID IN :roleIds
ORDER BY ParentRoleId NULLS FIRST, ID];
chatterEnabled = false;
}
//Populate the list
rolesWithUsersList = new List<RoleWithUsers>();
for (UserRole ur : roles) {
rolesWithUsersList.add(new RoleWithUsers(ur, (roleIds.contains(ur.id))));
}

return rolesWithUsersList;
}

public static Set<ID> getTopUserRoleIds() {
Set<Id> roleIds = new Set<Id>();
List<UserRole> topRoles = [SELECT ID FROM UserRole WHERE parentRoleId = null];
for (UserRole ur : topRoles) {
roleIds.add(ur.ID);
}
return roleIds;
}

public static Set<ID> getAllRoleIds() {
Set<Id> roleIds = new Set<ID>();
List<UserRole> topRoles = [SELECT ID FROM UserRole LIMIT 500];
for (UserRole ur : topRoles) {
roleIds.add(ur.ID);
}
return roleIds;
}

/**
* Inner class that holds the UserRole (with the Users related list already populated).
* Provides helper methods for formatting output text
* @author KevinSwiggum
*/
public class RoleWithUsers {
public UserRole role {get; set;}
public Boolean isExpanded {get; set;}

/**
* Contructor. Expanded tells the class if this role has been clicked on and expanded
* @author KevinSwiggum
*/
public RoleWithUsers(UserRole rl, Boolean expanded) {
this.role = rl;
this.isExpanded = expanded;
}

/**
* returns the UserRoleId
* @author KevinSwiggum
*/
public String getId() {
return role.id;
}

/**
* Returns the ID of the parent role
* @author KevinSwiggum
*/
public String getParent() {
return role.parentRoleId;
}

/**
* returns the name of the role
* @author KevinSwiggum
*/
public String getTitle() {
return role.name;
}


/**
* Returns an HTML-formated string representing users within this role. Done here instead of VF because of the requirements of Google Visualization. Components didn't work well
* @author KevinSwiggum
*/
public String getRoleBody() {
if (role != null) {
//String roleBody = '';
String roleBody = '<div class="chartCell"> <div class="cellTitle">' +
'<a href="javascript://nop/" onclick="loadMore(\''+role.id+'\');">';

if (!isExpanded)
roleBody += '<img class="expandIcon" src="/s.gif" style="float:left;"/>';

roleBody += role.name + '</a></div><div style="clear:both;"></div>';

if (role.users.size() > 0) {
String template = '<a target="_blank" href="/{!id}" title="View Profile">';
if (chatterEnabled) {
template += '<img class="chartCellIcon" src="{!SmallPhotoUrl}"/>';
}
template += '<div class="cellTitle">{!name}</div>' +
'<div class="chartCellBody" style="margin:0px;padding:1px;">{!title}</div>' +
'<div class="chartCellBody" style="margin:0px;padding:1px;">{!email}</div>' +
'<div class="chartCellBody" style="margin:0px;padding:1px;">{!phone}</div>' +
'</a><div style="clear:both;height:10px;"></div>';


for (User u : role.users) {
String uBody = template;
uBody = replace(uBody, 'id', u);
if (chatterEnabled) uBody = replace(uBody,'SmallPhotoUrl', u);
uBody = replace(uBody,'name', u);
uBody = replace(uBody,'title', u);
uBody = replace(uBody,'email', u);
uBody = replace(uBody,'phone', u);

roleBody += uBody;

}
} else {
roleBody += '<i>No Users Defined</i>';
}

roleBody += '</div>';
return roleBody;
}
return '';
}

}

/**
* replaces template string with an object's value
* @author KevinSwiggum
*/
static String replace(String inString, String key, sObject o) {

String objectValue = '';
try {
Object obj = o.get(key);
objectValue = obj != null ? String.valueOf(obj) : '';

} catch (Exception e) {}


return inString.replace('{!' + key + '}', objectValue);
}


}
5 changes: 5 additions & 0 deletions source/classes/OrgChart.cls-meta.xml
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>20.0</apiVersion>
<status>Active</status>
</ApexClass>
80 changes: 80 additions & 0 deletions source/classes/OrgChartController.cls
@@ -0,0 +1,80 @@
/**
* @author KevinSwiggum
* Copyright 2011 Radial Web Solutions LLC
*/
public with sharing class OrgChartController {

transient List<OrgChart.RoleWithUsers> rolesWithUsersList;
public String roleId {get; set;}
private Set<ID> selectedRoleIds = new Set<ID>();

/**
* Constructor grabs the top level role(s) and their direct children
* @author KevinSwiggum
*/
public OrgChartController() {
init();
}

/**
* sets the initial set of role ids to just the top role ids
* @author KevinSwiggum
*/
private void init() {
selectedRoleIds = OrgChart.getTopUserRoleIds();
}

/**
* sets the role list back to null so that it will be re-pulled
* @author KevinSwiggum
*/
private void clearRoleList() {
rolesWithUsersList = null;
}

/**
* populates a list of RoleWithUsers object for the given selectedRoleIds
* @author KevinSwiggum
*/
public List<OrgChart.RoleWithUsers> getRolesWithUsersList() {
if (rolesWithUsersList == null) {
rolesWithUsersList = OrgChart.getRolesWithUsers(selectedRoleIds);
}
return rolesWithUsersList;
}

/**
* user clicked on a role to load children for that role
* @author KevinSwiggum
*/
public PageReference loadMore() {
if (roleId != null) {
selectedRoleIds.add(roleId);
clearRoleList();
}
return null;
}

/**
* Pull all roles in the organization
* @author KevinSwiggum
*/
public PageReference expandAll() {
selectedRoleIds = OrgChart.getAllRoleIds();
clearRoleList();

return Page.OrgChart;
}

/**
* Collapse back down to the default view (parent and direct children)
* @author KevinSwiggum
*/
public PageReference collapseAll() {
clearRoleList();
init();
return Page.OrgChart;
}


}
5 changes: 5 additions & 0 deletions source/classes/OrgChartController.cls-meta.xml
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>20.0</apiVersion>
<status>Active</status>
</ApexClass>
37 changes: 37 additions & 0 deletions source/classes/OrgChartTests.cls
@@ -0,0 +1,37 @@
/**
* @author KevinSwiggum
* Copyright 2011 Radial Web Solutions LLC
*/
@isTest
private class OrgChartTests {


static testMethod void testOrgChart() {
Set<ID> roleIds = OrgChart.getTopUserRoleIds();
List<OrgChart.RoleWithUsers> roleList = OrgChart.getRolesWithUsers(roleIds);
for (OrgChart.RoleWithUsers rwu : roleList) {
rwu.getParent();
rwu.getId();
rwu.getTitle();
rwu.getRoleBody();
}

roleIds = OrgChart.getAllRoleIds();
System.assert(roleIds.size() > 0);
}

public static testMethod void testOrgChartController() {
OrgChartController ctl = new OrgChartController();
List<OrgChart.RoleWithUsers> roleList = ctl.getRolesWithUsersList();
OrgChart.RoleWithUsers lastVal = roleList[roleList.size()-1];
ctl.roleId = lastVal.getId();
ctl.loadMore();
roleList = ctl.getRolesWithUsersList();

System.assert(roleList.size() > 0);
ctl.expandAll();
ctl.collapseAll();

}

}
5 changes: 5 additions & 0 deletions source/classes/OrgChartTests.cls-meta.xml
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>20.0</apiVersion>
<status>Active</status>
</ApexClass>
32 changes: 32 additions & 0 deletions source/package.xml
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>*</members>
<name>ApexClass</name>
</types>
<types>
<members>*</members>
<name>ApexComponent</name>
</types>
<types>
<members>*</members>
<name>ApexPage</name>
</types>
<types>
<members>*</members>
<name>ApexTrigger</name>
</types>
<types>
<members>*</members>
<name>CustomApplication</name>
</types>
<types>
<members>*</members>
<name>CustomTab</name>
</types>
<types>
<members>*</members>
<name>StaticResource</name>
</types>
<version>20.0</version>
</Package>

0 comments on commit 482358f

Please sign in to comment.