Showing posts with label user lists. Show all posts
Showing posts with label user lists. Show all posts

Tuesday, April 26, 2011

Google User Org-Chart with Salesforce

Cloudspokes.com had a challenge to incorporate Google's user hierarchy chart in Salesforce on the user object. And even though I didn't win (totally should have lol) I thought I would share my version of the challenge.  
In mine, the Hierarchy chart is based off of the Manager Id of each User, instead of going into Roles and all that mess. But when I set it up for all users, THe chart produced was very long and made the page scroll, to fix that issue, I made it so it grouped by department, and by default show the current User's Department. Then I added a fixed width on the page so it wouldn't scroll, except inside the frame, and if the department drop-down is on the page it would always show on top right regardless of how long the chart is that gets created. Since not all managers where in the same department as their subordinates, I had to re-query for any managers that weren't in the list the of the users returned, other wise it showed as only an ID above the user, which looked bad lol.
I have set the page up to take a few different params(see below) which control options for the page, including hiding/showing a User table, a select list for departments, and the hiding/showing of the sidebar and header. 
Additional options lie in the JavaScript, there you have the ability to allow people to click on a user and see additional info on them(works/looks really good on iPad). Or control the Google chart options like allow HTML and the collapse feature. To see the  'onclick additional info' in action simply click once on a users name and it will pop up with a small modal of that info.

When trying to make this work, i found that apex and Google do not play nice, especially with re-render, so i had to make a lot of custom JavaScript to make it all work correctly, and i could not use Apex:output or have the JS in the re-render, otherwise it would spit out all the code to the page on re-render. 

Here is the JS
/*SALESFORCEGIRL ORGCHAT 2011*/

/*CONTROLS THE LOADING GIF*/
function Loading(b){
     if(b){
          $('.loadingIcon').removeClass('hideIt');
     }else{
          $('.loadingIcon').addClass('hideIt');
     }
}
/*PUTS THE USER LIST IN THE RIGHT FORMAT WITH OPTIONS FOR GOOGLE TO READ*/
function getUserList(userOptions, CurrentUserId, dptLabel,phLabel,rxtLabel,emLabel) {
     var users = [];
     var UserString = '';
     var uId = '';
     var uName = '';
     var uManId = '';
     var uDept = '';
     var uPhone = '';
     var uExt = '';
     var uEmail = '';                 
     $('#hiddenUserData tr.dataRow').each(function(){
          uId = $(this).find('td:eq(0)').text();
          uManId = $(this).find('td:eq(1)').text();
          uName = $(this).find('td:eq(2)').text();
          uDept = $(this).find('td:eq(3)').text();
          uPhone = $(this).find('td:eq(4)').text();
          uExt = $(this).find('td:eq(5)').text();
          uEmail = $(this).find('td:eq(6)').text();
      
          UserString = '<div class="UserInfo" onClick="ShowMoreInfo(this)">'+uName+'<br /><div class="Title" >'+uDept+'</div><br />';
      
          if (userOptions.showInfo.department || userOptions.showInfo.phone || userOptions.showInfo.ext || userOptions.showInfo.email) {
               UserString += '<div title="Click to hide" class="additionalInfo hideMe"><table><tbody><tr><td colspan="2">';
           
               UserString += '<div class="HeaderTitle">'+uName+'</div></td></tr>';
           
               if (userOptions.showInfo.department) {
                    UserString += '<tr><th>'+dptLabel+'</th><td>'+uDept+'</td></tr>';
               }
           
               if (userOptions.showInfo.phone) {
                    UserString += '<tr><th>'+phLabel+'</th><td>'+uPhone+'</td></tr>';
               }
           
               if (userOptions.showInfo.ext) {
                    UserString += '<tr><th>'+rxtLabel+'</th><td>'+uExt+'</td></tr>';
               }
           
               if (userOptions.showInfo.email) {
                    UserString += '<tr><th>'+emLabel+'</th><td>'+uEmail+'</td></tr>';
               }
           
               UserString += '</tbody></table></div>';
          }
          UserString += '</div>';
 
          users.push([{v: uId, f: UserString}, uManId, 'Click for additional info']);
     });                     
     return users;
}
/*USED TO TRIGGER THE ADDITIONAL INFO POP UP */
function Loading(b){
     if(b){
          $('.loadingIcon').removeClass('hideIt');
     }else{
          $('.loadingIcon').addClass('hideIt');
     }
}
/*USED TO SHOW MORE INFO IF USER IS CLICKED*/
function ShowMoreInfo(t){
     var item = $(t).find('.additionalInfo');
     if($(item).hasClass('hideMe')){
          $('.additionalInfo').addClass('hideMe');
          $(item).removeClass('hideMe');
     }else{
          $(item).addClass('hideMe');
     }
}
/*SETTING UP THE GOOGLE CHART*/
function initializeOrgChart(){
     google.load('visualization', '1', {packages:['orgchart']});
}
/*SETTING UP THE GOOGLE CHART*/
function getUserDataTable(userList){
     var dt = new google.visualization.DataTable();
       dt.addColumn('string', 'Name');
       dt.addColumn('string', 'Manager');
       dt.addColumn('string', 'ToolTip');
       dt.addRows(userList);
       return dt;
}
/*SETTING UP THE GOOGLE CHART*/
function getOrgChart(container,options,data){
     var chart = new google.visualization.OrgChart(container);
       chart.draw(data, options);
     return chart;
}
/*SETTING UP THE GOOGLE CHART*/
function drawOrgChart(container,options) {         
     /* DEFAULT OPTIONS STRUCTURE.
     var options = {
          users: null,                    
          chartOptions: {
               allowHtml: true,
               allowCollapse:true
          }
     };*/
       var data  = getUserDataTable(options.users);
       var chart = getOrgChart(container,options.chartOptions,data);
}

Then on the page:
<apex:page controller="sfgOrgChart" title="Org Chart" showHeader="{!bShowHeader}" sidebar="{!bShowSidebar}" standardstylesheets="false" action="{!SwitchList}">
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />
        <apex:stylesheet value="{!URLFOR($Resource.OrgChart, '/style.css')}"/>
       
        <script type="text/javascript" language="javascript" src="{!URLFOR($Resource.OrgChart, '/js/jquery-1.4.2.min.js')}" ></script>
        <script type="text/javascript" language="javascript" src="{!URLFOR($Resource.OrgChart, '/js/jquery.tablesorter.min.js')}" ></script>
        <script type="text/javascript" language="javascript" src="{!URLFOR($Resource.OrgChart, '/js/sfgOrgChart.js')}" ></script>
        <script type='text/javascript' src='https://www.google.com/jsapi'></script>
    </head>
    <body>
        <apex:form id="form">
            <apex:outputpanel id="pageWrap">
                <apex:outputpanel id="ChooseDepartmentWrap" rendered="{!bShowDepartmentselectList}">
                    <div class="clear"></div>
                    <br />
                    <div class="floatL">
                        <apex:outputpanel rendered="{!NOT(ISBLANK(sDepartment))}">
                            <div class="HeaderTitle">Viewing Department:&nbsp;<apex:outputtext value="{!IF(bIsDept,sDepartment,'All Departments')}" /></div>
                        </apex:outputpanel>
                    </div>
                    <div class="floatR">
                        <apex:outputpanel rendered="{!bShowDepartmentSelectList}">
                            <strong>{!$ObjectType.User.Fields.Department.Label}:</strong>
                            <apex:selectList id="department" size="1" value="{!sDepartment}" styleClass="deptSelect"> 
                                <apex:selectOptions value="{!lstDepartmentOptions}" />
                                <apex:actionSupport event="onchange" onsubmit="Loading(true);$('#chart_div').html('');" action="{!SwitchList}" rerender="pageWrap" id="actionSupportForDeptartment" oncomplete="drawOrgChart($('#chart_div')[0],Go('{!$User.Id}'));ShowTableChart();Loading(false);" />
                            </apex:selectList>
                        </apex:outputpanel>
                    </div>
                    <!-- the loading icon -->
                    <div class="loadingIcon hideIt"><apex:image id="loadingImage" value="{!URLFOR($Resource.OrgChart, 'images/loader_24x26.gif')}" width="24" height="26"/></div>
                    <div class="clear"></div>
                </apex:outputpanel>
                <div id="chart_div"></div>
                <br />
                <div>
                    <table id="hiddenUserData" class="list tablesorter hideMe">
                        <thead class="rich-table-thead">
                        <tr class="headerRow">
                            <th colspan="1" scope="col">Id</th>
                            <th colspan="1" scope="col">Manager Id</th>
                            <th colspan="1" scope="col">Name</th>
                            <th colspan="1" scope="col">Department</th>
                            <th colspan="1" scope="col">Phone</th>
                            <th colspan="1" scope="col">Ext</th>
                            <th colspan="1" scope="col">Email</th>
                        </tr>
                        </thead>
                        <tbody>
                        <apex:repeat value="{!lstOfUsers}" var="u"><!-- Table to get the data from and to show if they wish it with sorting -->
                            <tr class="dataRow" id="{!u.id}">
                                <td>{!JSENCODE(u.id)}</td>
                                <td>{!JSENCODE(u.ManagerId)}</td>
                                <td>{!JSENCODE(u.name)}</td>
                                <td>{!JSENCODE(u.department)}</td>
                                <td>{!JSENCODE(u.phone)}</td>
                                <td>{!JSENCODE(u.extension)}</td>
                                <td>{!JSENCODE(u.email)}</td>
                            </tr>
                        </apex:repeat>
                    </tbody></table>
                </div>
        </apex:outputpanel>
        </apex:form>
        <script type="text/javascript" language="javascript">
          
            initializeOrgChart();  // LOAD API IMPORTANT TO DO THIS.          
            var ShowTable = {!bShowUserTable};
            var dptLabel = '{!$ObjectType.User.Fields.Department.Label}';
            var phLabel = '{!$ObjectType.User.Fields.Phone.Label}';
            var rxtLabel = '{!$ObjectType.User.Fields.Extension.Label}';
            var emLabel = '{!$ObjectType.User.Fields.Email.Label}';
          
            var winWidth = $(window).width() - 75 +'px !important;overflow-y:hidden;';
            var isiPad = navigator.userAgent.match(/iPad/i) != null;
            if(isiPad){
                winWidth = '100% !important;'
            }
              
            function Go(UserId){
              
                // THE ADDITIONAL INFO OPTIONS
               // IF ALL ARE FALSE WILL NOT SHOW ADDITIONAL INFO
                var userOptions = {
                      showInfo: {
                            department: true,      // show dept in additional info box
                            phone: true,           // show phone in additional info box
                            ext: true,             // show ext in additional info box
                            email: true            // show email in additional info box
            }
                };       
           //GOOGLE CHART OPTIONS
           var options = {
                 users: getUserList(userOptions,UserId, dptLabel,phLabel,rxtLabel,emLabel),
                 chartOptions: {               // default google chart options
                       allowHtml: true,        // allow html
                       allowCollapse:true      // allow collapsing 
                 }
           };                

            function ShowTableChart(){
                if(ShowTable){
                    $("table").tablesorter({
                        headers: {
                            0: {sorter: 'text'},
                            1: {sorter: 'text'},
                            2: {sorter: 'text'},
                            3: {sorter: 'text'},
                            4: {sorter: 'digit'},
                            5: {sorter: 'digit'},
                            6: {sorter: 'text'}
                        }
                    });
                    $('#hiddenUserData').removeClass('hideMe');
                }         
            }
            $(document).ready(function(){                 
                //DRAW CHART WITH DATA AND MAKE SURE IT WONT OVERFLOW
                $('#chart_div').attr('style','width:'+winWidth+' margin:5px auto; padding:10px 5px;');
                drawOrgChart($('#chart_div')[0],Go('{!$User.Id}'));
                $('.deptSelect').val('{!sDepartment}');
                //IF SHOW-TABLE START TABLE SORTER AND SHOW TABLE
                ShowTableChart();
            });
        </script>
    </body>
    </html>

</apex:page>
Example: If the dept param(URL_KEY_SHOWDEPT) is passed with URL_DEFAULT_VALUE_SHOWALL it will show all Users under all Departments, and if SHOW_SELECT_DEPT is set to true, then it will also show a Department select list so it is easy to change between departments. If you click on a User in the Chart it will display additional information on that user including: Name, Phone, Title, Email, Department, Extension. This can be turned off/on and/or you can set which info shows by customizing the JavaScript options. If all options are false, then the additional info will not show. 

NOTE: Department select list will only show if a param is passed and the SHOW_SELECT_DEPT is true...this is not the same for the other params.. see below

In the Common class there are a few variables that you can change to change the defaults of the page:
     
     // keys to look for in the url
     public static final string URL_KEY_SHOWDEPT = 'dept'; 
     public static final string URL_KEY_SHOWSIDEBAR = 'side';
     public static final string URL_KEY_SHOWHEADER = 'top';
     public static final string URL_KEY_SHOWUSER = 'user';

     // the show all param value
     public static final string URL_DEFAULT_VALUE_SHOWALL = 'all';

     // if true and param is passed will show select list 
     public static final boolean SHOW_SELECT_DEPT = true;

     // limit on the user Query
     public static final integer USER_SEARCH_LIMIT = 400;

     // show the header
     public static final boolean SHOW_HEAD = true;

     // show the side bar
     public static final boolean SHOW_SIDE = false;

     // show the table of users currently in the google charts
     public static final boolean SHOW_USER_LIST = true;

EXAMPLE URLs:

-shows Users by current users dept (nothing was passed so it defaults to Current Users Dept)
-shows header (nothing was passed so it defaults to SHOW_HEAD )
-hides sidebar (nothing was passed so it defaults to SHOW_SIDE )
-hides User Table (nothing was passed so it defaults to SHOW_USER_LIST )

URL_KEY_SHOWDEPT
-shows all Dept's and all Users(active) and also shows dept select list (if SHOW_SELECT_DEPT is true)

-shows Users in support dept and also shows dept select list (if SHOW_SELECT_DEPT is true)

-shows Users by current users dept and also shows dept select list (if SHOW_SELECT_DEPT is true)

URL_KEY_SHOWSIDEBAR 
-shows side bar

-hides side bar

URL_KEY_SHOWHEADER 
-shows header

-hides header

URL_KEY_SHOWSIDEBAR 
-shows User Table

-hides User Table



Questions? 

Twitter: @SalesForceGirl  or Facebook


Thursday, March 17, 2011

jQuery table sorter meets salesforce

jQuery is a powerful tool, and so I always use it in my APEX projects to help accomplish what I wouldn't be able to with just apex/html/css. One of the things I almost always include is the Tablesorter, but there are a few issues when trying to make it work with Salesfroce standard page markup tags. 
All the documentation for the tablesorter is on their page: Tablesorter, but some points to remember are that if you are using any apex:output(filed/text) that it wraps a span around the text and the tablesorter needs to know that, also if you use an 'a' tag it behaves differently as well. Also there seems to be an issue with using the default sorter params, ie letting the tablesorter pick them for you.
To show you a good example of the jQuery table sorter in action with salesforce, I will be using the 'User Info List' that I recently just updated with it.

(NOTE: that table sorter requires the class tablesorter  on the table tag in order to work)
Example Table markeup:
<td><apex:outputfield value="{!c.Email}" /></td>
<td>{!c.Name}</td>
<td><span><apex:outputfield value="{!c.Phone}" /></span></td>
<td><a target="_blank" href="/{!c.Id}" title="go to Contact">Contact</a></td>

JS markup:
//this checks the table for data on three levels
      //<td>the data</td> $(node).text();
      //<td><node>the data</node></td> $(node).next().text();
      //<td><node><node>the data</node></node></td> $(node).next().next().text();
//this ensures that the data is found no matter how many nodes it needs to transverse.
var myTextExtraction = function(node) 
{  
      var nodeText = $(node).text();
      if(nodeText == null || nodeText == ''){
            nodeText = $(node).next().text();
      }
      if(nodeText == null || nodeText == ''){
            nodeText = $(node).next().next().text();
      }
      return nodeText;
}
//example markup to use the node traversing and establish the sorter params
$("table").tablesorter({
       textExtraction: myTextExtraction,
       headers: {
           0: {sorter: 'text'},
           1: {sorter: 'text'},
           2: {sorter: 'text'},
           3: {sorter: 'text'},
           4: {sorter: 'shortDate'
       }
   });

Now lets look at the User-Info-List page, you'll notice that instead of using the standard apex:pageblocktable I am using a standard table, BUT I have included the style(classes/structure) of what the apex:pageblocktable would have... meaning that if you right click and view source on an apex:pageblocktable you will see the same as below (style/class wise minus a few things for my own customizations) and so when this page renders, it looks like an apex:pageblocktable. I do it this way due to the lack of flexibility in the standard apex:pageblocktable, and the blocktable seems to clash with the jQuery tablesorter when used naively. 


The Page:
<apex:page controller="UserInfoList" standardStylesheets="false" title="User Info" action="{!SwitchList}">
<apex:stylesheet value="{!URLFOR($Resource.UserList, '/NewTQC.css')}"/>
<apex:stylesheet value="{!URLFOR($Resource.UserList, '/style.css')}"/>
<script type="text/javascript" language="javascript" src="{!URLFOR($Resource.UserList, '/js/jquery1_4.js')}" ></script>
<!-- always include the tablesorter script after the jQuery -->
<script type="text/javascript" language="javascript" src="{!URLFOR($Resource.UserList, '/js/jquery.tablesorter.min.js')}" ></script>
<apex:form >
<div id="pageWrap">
<!--wrap the whole page in an outputpanel so that ajax rerenders, it triggers the jQuery.ready() function-->
<apex:outputPanel id="TicketListWrap" styleClass="Wrap">
      <script language="javascript" type="text/javascript">
            //controls the loading gif
            function Loading(b){
                  if(b){
                        $('.loadingIcon').removeClass('hideIt');
                  }else{
                        $('.loadingIcon').addClass('hideIt');
                  }
            }
            $(document).ready(function(){
                  //important to specify the sorting param otherwise it wont always choose the 'correct' one
                  $("table").tablesorter({
                    headers: {
                        0: {sorter: 'text'},
                        1: {sorter: 'text'},
                        2: {sorter: 'digit'},
                        3: {sorter: 'digit'},
                        4: {sorter: 'text'},
                        5: {sorter: 'text'},
                        6: {sorter: 'text'
                    }
                });
            });   
      </script>
      <div class="clear"></div>
      <br />
      <div id="newTicketHeader" class="floatL">Users in {!sDepartment}</div>
      <div class="floatR">
            <strong>Department:</strong>
            <apex:selectList id="department" size="1" value="{!sDepartment}" >  
                  <apex:selectOptions value="{!lstDepartmentOptions}" />
                  <!-- every time the select list is changed it refreshes the list of users -->
                  <apex:actionSupport event="onchange" onsubmit="Loading(true);" action="{!SwitchList}" rerender="TicketListWrap"id="actionsupportforKnownIssues" oncomplete="Loading(false);" />
            </apex:selectList>
      </div>
      <!-- the loading icon -->
      <div class="loadingIcon hideIt"><apex:image id="loadingImage" value="{!URLFOR($Resource.UserList, 'images/loader_24x26.gif')}"width="24" height="26"/></div>
      <div class="clear"></div>
      <div style="margin-left:5px; font-size:10px;">User count: {!iUserCount}</div>
      <table id="Usertable" class="list tablesorter" cellspacing="1">
      <thead class="rich-table-thead">
            <tr class="headerRow">
                  <th colspan="1" scope="col">Name</th>
                  <th colspan="1" scope="col">Phone</th>
                  <th colspan="1" scope="col" style="width:50px;">Ext</th>
                  <th colspan="1" scope="col">ICQ</th>
                  <th colspan="1" scope="col">Title</th>
                  <th colspan="1" scope="col">Location</th>
                  <th colspan="1" scope="col">Action</th>
            </tr>
      </thead>
      <tbody>
            <apex:repeat value="{!CurrentListofUsers}" var="u" id="UserListRepeater">
            <tr class="dataRow">
                  <td>{!u.name}</td>
                  <td>{!u.phone}</td>
                  <td>{!u.Extension}</td>
                  <td>{!u.ICQ__c}</td>
                  <td>{!u.Title}</td>
                  <td>{!u.Location__c}</td>
                  <td>
                        <div>
                              <!-- only current user can edit their own info -->
                              <apex:outputlink value="/{!u.id}/e?retURL=apex/UserInfoList" rendered="{!IF(u.id == $User.Id, true, false)}" >Edit Info</apex:outputlink>
                        </div>
                              <!-- only manager's/Team lead's/VP's can edit the users Queue info -->
                        <apex:outputpanel rendered="{!IF(CONTAINS($UserRole.Name, 'Manager') || CONTAINS($UserRole.Name, 'VP') || CONTAINS($UserRole.Name, 'Team Lead'), true, false)}">
                              <apex:outputlink value="/apex/QueueMemberEdit?UserId={!u.id}" >Edit Queues</apex:outputlink>
                        </apex:outputpanel>
                        
                  </td>
            </tr>
            </apex:repeat>
      </tbody></table>
      <hr />
</apex:outputPanel>
</div>
</apex:form>
</apex:page>



The classes that power it:
public with sharing class UserInfoList {

      public string sDepartment{get;set;}
      public string sCurrentUserDepartment
      {
            get{return UserInfoListHelper.FindDepartment(UserInfo.getUserId());}
            set;
      }
      public List<User> CurrentListofUsers
      {
            get{return UserInfoListHelper.UserList( UserInfoListHelper.FindDepartment(sCurrentUserDepartment, sDepartment));}
            set;
      }
      public integer iUserCount
      {
            get{return CurrentListofUsers.size();}
            set;
      }
      public List<selectOption> lstDepartmentOptions
      {
            get{return UserInfoListHelper.DepartmentKeys();}
            set{lstDepartmentOptions = value;}
      }
            
      public pageReference SwitchList()
      {
            if(!GlobalHelper.CheckForNotNull(sDepartment))
            {
                  sDepartment = sCurrentUserDepartment;
            }
            return null;
      }
}

public with sharing class UserInfoListHelper {
      /// <summary>
      /// OVERLOADED
    /// FINDS THE USERS DEPARTMENT BASED ON ID PASSED
    /// </summary>
    /// <param name="lstInValueToCheck">USER ID</param>
    /// <returns>USER DEPARTMENT</returns>
      public static string FindDepartment(string sInUserId)
      {
            if(GlobalHelper.CheckForNotNull(sInUserId))
            {
                  try
                  {
                        User u = [select id, Department from User where id=:sInUserId limit 1];
                        return u.Department;
                  }
                  catch(exception e)
                  {
                        system.debug('UserInfoListHelper - FindDepartment - error: '+e);
                  }     
            }
            return null;
      }
      /// <summary>
      /// OVERLOADED
    /// RETURNS USER DEPARTMENT IF SELECTED DEPARMENT IS NULL
    /// </summary>
    /// <param name="sInDepartmentSelection">USER DEPARMENT</param>
    /// <param name="sInUserDepartment">SELECTED DEPARTMENT</param>
    /// <returns>DEPARTMENT</returns>
      public static string FindDepartment(string sInUserDepartment, string sInDepartmentSelection)
      {
            if(GlobalHelper.CheckForNotNull(sInDepartmentSelection))
            {
                  return sInDepartmentSelection;
            }
            return sInUserDepartment;
      }
      /// <summary>
    /// ADDS DEPARTMENT KEY NAMES TO LIST OF SELECT OPTIONS
    /// </summary>
    /// <param name="lstInUser">USER ID</param>
    /// <returns>DEPARTMENT SELECT OPTION LIST</returns>
      public static List<selectOption> AddItemToList(List<User> lstInUser)
      {
            List<SelectOption> lstOptions = new List<SelectOption>();
            if(GlobalHelper.CheckForNotNull(lstInUser))
            {
                  for(User u : lstInUser)
                  {
                        if(!GlobalHelper.ContainsItem(u.Department, lstOptions))
                        {
                              lstOptions.add(new SelectOption(u.Department,u.Department, false));
                        }
                  }
                  return lstOptions;
            }
            return null;
      }
      /// <summary>
    /// FINDS ALL DEPARTMENTS AND ADDS THEM TO LIST OF SELECT OPTIONS
    /// </summary>
    /// <returns>DEPARTMENT SELECT OPTION LIST</returns>
      public static List<selectOption> DepartmentKeys()
      {
            User[] u = [Select Department From User where Department != null and IsActive = True limit 200];
            if(GlobalHelper.CheckForNotNull(u))
            {
                  return UserInfoListHelper.AddItemToList(u);
            }
            return null;
      }
      /// <summary>
    /// GETS LIST OF USERS BASED ON THE DEPARTMENT PASSED
    /// </summary>
    /// <param name="sInDepartment">DEPARTMENT</param>
    /// <returns>USER LIST</returns>
      public static List<User> UserList(string sInDepartment)
      {
            if(GlobalHelper.CheckForNotNull(sInDepartment))
            {
                  try
                  {
                        User[] u = [select Name, Phone, Extension, Title, ICQ__c, Location__c,Department from User where IsActive = True AND Department =:sInDepartment Order By Name ASC limit 200];
                        return u;
                  }
                  catch(exception e)
                  {
                        system.debug('UserInfoListHelper - UserList - error: '+e);
                  }
            }
            return null;
      }
}
Questions? 
Twitter: @SalesForceGirl  or Facebook