Building an Employee Corner Web Part
Highlighting employees is a great way to increase collaboration and user engagement. In the past this was done in employee newsletters or other communication methods, but as those methods go electronic and the focus moves towards the Intranet portal as the central communication hub, it becomes another type of information that should be included.
Note
SharePoint Server's user profiles can provide a rich set of details about users, and can be leveraged to provide a great source of dynamic content around important dates, organization structure, interests, clients, and past projects.
The Employee Corner Web Part will present a list of new employees based on the Hire Date field in the user profiles. Additional examples could include employee of the month (or quarter), birth dates, or employee anniversaries.
Approach
To create the Employee Corner Web Part we will create a custom Web Part in Visual Studio 2010. The Web Part will leverage the Search API's FullTextSqlQuery
class to query the People search scope bringing back values stored within the user profiles that are currently indexed.
It is important to understand the underlying architecture in order to know which development path is really an option. Normal SharePoint list and library data is stored inside of the content database associated with the site collection. The content associated with SharePoint's service applications are however stored in separate databases since those services and the content is not tied to any one site, but available globally to all web applications associated with the service application. This means that the service applications are not accessible via the Client OM or via Sandbox Solutions without implementing some sort of Full Trust proxy that would have to be installed on the server and provide access to the server API. Based on these boundaries, a server solution makes the best choice for the approach in most environments. If the solution needs to be deployed to an environment with server deployment limitations the Full Trust proxy or other alternatives would have to be evaluated.
Creating the Web Part
To create the initial project:
1. Open Visual Studio 2010.
2. Select File, then New Project.
3. Browse the Installed Templates and select Visual C# | SharePoint 2010, and then Empty SharePoint Project as shown in the following screenshot:
4. Enter the project details such as Name, Location, and Solution name.
5. Within the SharePoint Customization Wizard, provide a path to your SharePoint site and then be sure to select the option to Deploy as a farm solution as shown in the following screenshot:
6. Right-click on the project file and select Add New Item.
7. From the template selection screen select the Web Part option.
8. Provide the name
EmployeeCorner
and click the Add button as illustrated in the following screenshot:9. Rename the Feature1 item
SPBlueprints.WebParts
.10. Select the
SPBlueprints.WebParts
feature item and provide a Title and Description. It should resemble the following screenshot:11. Edit the definition of the
EmployeeCorner.webpart
file so that the Web Part definition added to the Gallery is meaningful as displayed in the followingEmployeeCorner.webpart
definition:<?xml version="1.0" encoding="utf-8"?> <webParts> <webPart xmlns="http://schemas.microsoft.com/WebPart/v3"> <metaData> <type name="SPBlueprints.WebParts.EmployeeCorner.EmployeeCorner, $SharePoint.Project.AssemblyFullName$" /> <importErrorMessage>$Resources:core,ImportErrorMessage;</import ErrorMessage> </metaData> <data> <properties> <property name="Title" type="string">Employee Corner</property> <property name="Description" type="string">SPBlueprints - The Employee Corner WebPart displays all new employees that started in the last 30 days.</property> <property name="SearchProxyName" type="string">Search Service Application</property> </properties> </data> </webPart> </webParts>
12. The completed project structure should resemble the following screenshot:
Defining a Web Part property
When creating a Web Part, there is often some configuration data that is needed to be able to reuse the Web Part for different sites or purposes. Creating a Web Part property makes it much easier to maintain the code than embedding configuration values in the code.
For the EmployeeCorner
Web Part, we are going to establish a text field that allows the user to specify the Search service application to use when searching for the user profiles in the next section. The SearchProxyName
property is detailed as follows:
private string searchProxyName; [WebBrowsable(true), WebDisplayName("Search Proxy Name"), WebDescription("Please provide the name of your Search Service Application."), Personalizable(PersonalizationScope.Shared)] public string SearchProxyName { get { return searchProxyName; } set { searchProxyName = value; } }
Connecting to the Search service application
To work with the Search service application we need to start by adding a reference to the following namespaces within the project and EmployeeCorner
Web Part:
Microsoft.SharePoint.Administration Microsoft.Office.Server.Search Microsoft.Office.Server.Search.Query Microsoft.Office.Server.Search.Administration
The connection to the service application is established through the SearchServiceApplicationProxy
object, which is loaded using the SearchProxyName
Web Part property previously identified. The following code should be added to a new method called Display()
that is called from the OnLoad()
method:
SearchQueryAndSiteSettingsServiceProxy settingsProxy = SPFarm.Local.ServiceProxies.GetValue<SearchQueryAndSiteSettingsSer viceProxy>(); SearchServiceApplicationProxy searchProxy = settingsProxy.ApplicationProxies.GetValue<SearchServiceApplication Proxy>(this.searchProxyName); FullTextSqlQuery mQuery = new FullTextSqlQuery(searchProxy);
The FullTextSqlQuery
class provides an interface to execute complex queries against the search index. Queries executed against the index will perform faster than queries against the actual content such as a list or a library. As the amount of content increases, and as the number content sources you search across increases, the performance gains are even more significant, since the index provides a pre-processed source for the information.
For the FullTextSqlQuery
, we will define the fields that we want to see, the scope, and the criteria to match it. For the fields you will want to make sure that the desired fields are set up as Managed Properties. The name of a Managed Property may be different than the name in the actual profile. In this example, the user profile field's internal name is SPS-HireDate
, but the Managed Property name is simply HireDate
. You can check the Managed Property mappings within the Search service application by clicking the Metadata Properties link in the Quick Launch menu.
For any search involving people, it is required that you use the People search scope so that it returns user profile information instead of regular site content. The New Hire Query will pull the specified fields, from the People search scope, for anyone with a HireDate
that is within 30 days of today. An example of the query is shown as follows:
mQuery.QueryText = "SELECT LastName, FirstName, JobTitle, accountname, HireDate, Birthday, PictureThumbnailURL FROM SCOPE() WHERE (\"scope\" = 'People') AND HireDate >= DATEADD (DAY, -30, GETGMTDATE())";
After setting the query, there are a few other properties that need to be set before executing the query, which are shown as follows:
mQuery.ResultTypes = ResultType.RelevantResults; mQuery.TrimDuplicates = true; mQuery.RowLimit = 100; ResultTableCollection resultNew = mQuery.Execute();
Formatting the Web Part
Formatting the output of the Web Part begins with identifying any controls that are needed within the CreateChildControls()
method. This method will run as part of the initialization process before the OnLoad()
method, ensuring the controls are available. The output that will be rendered, will be added to the literal control. The CreateChildControls()
method code is shown as follows:
protected override void CreateChildControls() { this.literalMessage = new Literal(); this.literalMessage.ID = "literalMessage"; this.Controls.Add(this.literalMessage); }
Within the Display()
method, after the Execute()
method previously called, we will now process the results. When executing a search query, the resulting ResultsTableCollection
contains a number of different types of results. For the content that will be displayed here, we are interested in the ResultType.RelevantResults
. We will check to validate that there are records returned, then extract just the relevant results.
Content that will be rendered to the screen will be formatted in a StringBuilder
object called messages
. After the main content is structured, we will iterate through the DataTable
object to add each of the individual records returned from the query. The code is shown as follows:
DataTable resultsNewHire = new DataTable(); if (resultNew.Count > 0) { ResultTable relevantResults = resultNew[ResultType.RelevantResults]; resultsNewHire.Load(relevantResults, LoadOption.OverwriteChanges); messages.AppendFormat(@"<table width='360' border='0' cellpadding='0' cellspacing='0'><tr><td align='left' valign='top' width='14' class='ms-wpTdSpace' background='/Style%20Library/Images/shadow- left.png'> </td><td background='/Style%20Library/Images/mid- background.jpg'><table><tr><td colspan='2' class='ms- standardheader ms-WPTitle'><b>{0} New Employees in the last 30 days!</b></td></tr>", resultsNewHire.Rows.Count); foreach (DataRow row in resultsNewHire.Rows) { messages.AppendFormat(@"<tr valign='center'><td width='100'><a href='/my/person.aspx?Accountname={3}'><img src='{5}' alt='{1} {0}' border='0'></a></td><td width='250' align='left' valign='top'><a href='/my/person.aspx?Accountname={3}'>{1} {0}</a><br>{2}<br>{4}</a></td></tr>", row[0].ToString(), row[1].ToString(), row[2].ToString(), row[3].ToString(), String.Format("{0:dddd, MMMM d yyyy}", row[4]), row[6].ToString()); } messages.AppendFormat(@"</table></td><td align='right' valign='top' width='14' class='ms-wpTdSpace' background='http://intranet/Style%20Library/Images/shadow- right.png'> </td></tr></table>"); }
Employee Corner Web Part displayed
The rendered version of the Employee Corner Web Part is shown in the following screenshot: