Along with the proliferation of sites in SPS, the proliferation of task items can rapidly become overwhelming. Because tasks can be assigned at any site, end users rapidly lose track of their tasks. In this project, you will build a global task list web part that will be deployed on the master My Site so that it is available for every user in the portal. The web part will search for tasks throughout all sites and collect them in a single list. Figure 9-2 shows a view of the final project.
As in the previous project, this project interacts with several sites and lists that an individual user may not have permission to access. Therefore, you must change the identity of the web part to that of an administrator. To accomplish this, you will use credentials stored in the SSO database.
To configure SSO credentials:
Log in to SPSPortal as a member of the MSSSOAdmins group .
Select Start All Programs SharePoint Portal Server SharePoint Portal Server Single Sign-On Administration.
On the Manage Settings page, select Enterprise Application Definition Settings Manage Settings for Enterprise Application Definitions.
On the Manage Enterprise Application Definitions page, click the New Item link.
On the Create Enterprise Application Definition page, enter SPSAuthority in the Display Name box.
Enter SPSAuthority in the Application Name box.
Enter administrator@sps.local in the Contact E-mail Address box.
Enter UserName in the Field 1: Display Name box.
Enter Domain in the Field 2: Display Name box.
Enter Password in the Field 3: Display Name box.
Select Yes for the Mask option associated with Field 3.
Click OK.
Return to the Manage Settings page and select Enterprise Application Definition Settings Manage Account Information for Enterprise Application Definitions.
In the Account Information section, choose SPSAuthority from the drop-down list.
Type sps\Domain Users in the Group Account Name box.
Click OK.
On the "Provide workflow engine account information" page, type administrator in the UserName box.
Type sps in the Domain box.
Type the administrator password in the Password box.
Click OK.
Open Visual Studio and create a new web part project in VB.NET named SPSTasks . When the project is created, rename the class file and the web part description file as SPSTasks.dwp and SPSTaskss.vb respectively. Then, open SPSTasks.dwp from the Solution Explorer and change the file to appear as shown in Listing 9-23.
<?xml version="1.0" encoding="utf-8"?> <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2" > <Title>Your Global Task List</Title> <Description>A web part to collect all tasks for a user</Description> <Assembly>SPSTasks</Assembly> <TypeName>SPSTasks.Lister</TypeName> </WebPart>
Before you begin to modify the web part code, you must add two references to the project. This web part uses a different identity to get permission to examine lists on all the sites. Therefore, you need access to the SSO system. You also have to set a reference to the SharePoint Services namespace.
To set the references, follow these steps:
Select Project References from the Visual Studio menu.
In the Add References dialog, double-click Microsoft.SharePoint. Portal.SingleSignon.dll and Windows SharePoint Services .
Click OK.
Once the references are added, open the SPSTasks.vb file for editing. You will add several Imports statements to the file and modify the class name. Change your web part to appear as shown in Listing 9-24.
Option Explicit On Option Strict On Option Compare Text Imports System Imports System.ComponentModel Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Xml.Serialization Imports Microsoft.SharePoint Imports Microsoft.SharePoint.Utilities Imports Microsoft.SharePoint.WebPartPages Imports Microsoft.SharePoint.WebControls Imports Microsoft.SharePoint.Administration Imports System.Security.Principal Imports System.Runtime.InteropServices Imports Microsoft.SharePoint.Portal.SingleSignon <DefaultProperty(""), ToolboxData("<{0}:Lister runat=server></{0}:Lister>"), _ XmlRoot(Namespace:="SPSTasks")> _ Public Class Lister Inherits Microsoft.SharePoint.WebPartPages.WebPart
Just like several other web parts you have created, this web part displays a grid. The web part creates the global task list by filling a DataSet , which is subsequently bound to a grid for display. When you build the task list, you provide hyperlinks to both the site that hosts the task list and the task list itself. Add the code from Listing 9-25 to define the child controls for the web part.
Protected WithEvents grdTasks As DataGrid Protected WithEvents lblMessage As Label Protected Overrides Sub CreateChildControls() 'Grid to display results grdTasks = New DataGrid With grdTasks .AutoGenerateColumns = False .Width = Unit.Percentage(100) .HeaderStyle.Font.Name = "arial" .HeaderStyle.Font.Size = New FontUnit(FontSize.AsUnit).Point(10) .HeaderStyle.Font.Bold = True .HeaderStyle.ForeColor = System.Drawing.Color.Wheat .HeaderStyle.BackColor = System.Drawing.Color.DarkBlue .AlternatingItemStyle.BackColor = System.Drawing.Color.LightCyan End With Dim objHyperColumn As HyperLinkColumn 'Host Site Name Column objHyperColumn = New HyperLinkColumn With objHyperColumn .HeaderText = "Host Site" .DataTextField = "SiteName" .DataNavigateUrlField = "SiteURL" grdTasks.Columns.Add(objHyperColumn) End With 'Host Site Name Column objHyperColumn = New HyperLinkColumn With objHyperColumn .HeaderText = "Task" .DataTextField = "TaskTitle" .DataNavigateUrlField = "ListURL" grdTasks.Columns.Add(objHyperColumn) End With Controls.Add(grdTasks) 'Label for error messages lblMessage = New Label With lblMessage .Width = Unit.Percentage(100) .Font.Name = "arial" .Font.Size = New FontUnit(FontSize.AsUnit).Point(10) .Text = "" End With Controls.Add(lblMessage) End Sub
As you have done in several web parts, this web part must change identity in order to access the task lists. This code never changes from part to part, but it is essential for the web part to function. Add the code from Listing 9-26 to create this helper function.
Protected Shared Function CreateIdentity(ByVal User As String, _ ByVal Domain As String, ByVal Password As String) As WindowsIdentity Dim objToken As New IntPtr(0) Dim ID As WindowsIdentity Const LOGON32_PROVIDER_DEFAULT As Integer = 0 Const LOGON32_LOGON_NETWORK As Integer = 3 'Initialize token object objToken = IntPtr.Zero ' Attempt to log on Dim blnReturn As Boolean = LogonUser(User, Domain, Password, _ LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, objToken) 'Check for failure If blnReturn = False Then Dim intCode As Integer = Marshal.GetLastWin32Error() Throw New Exception("Logon failed: " & intCode.ToString) End If 'Return new token ID = New WindowsIdentity(objToken) CloseHandle(objToken) Return ID End Function <DllImport("advapi32.dll", SetLastError:=True)> _ Private Shared Function LogonUser(ByVal lpszUsername As String, _ ByVal lpszDomain As String, _ ByVal lpszPassword As String, ByVal dwLogonType As Integer, _ ByVal dwLogonProvider As Integer, _ ByRef phToken As IntPtr) As Boolean End Function <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _ Private Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean End Function
Once the helper function is complete, you may begin to code the main body of the web part. Most of the work is embodied in a function that enumerates the tasks and fills the DataSet . The web part identifies a task when a list item has an Assigned To field and that field contains either the user's login name or display name. Add the code from Listing 9-27 to create the global task list.
Protected Function GetGlobalTasks(ByVal objUser As SPUser) As DataSet 'Purpose: Walk all sites and collect pointers to the tasks 'Context for the new identity Dim objContext As WindowsImpersonationContext Dim arrCredentials() As String Dim strUID As String Dim strDomain As String Dim strPassword As String Dim objDataSet As DataSet Try 'Try to get credentials Credentials.GetCredentials( _ Convert.ToUInt32("0"), "SPSAuthority", arrCredentials) strUID = arrCredentials(0) strDomain = arrCredentials(1) strPassword = arrCredentials(2) 'Change the context Dim objIdentity As WindowsIdentity objIdentity = CreateIdentity(strUID, strDomain, strPassword) objContext = objIdentity.Impersonate Catch x As SingleSignonException lblMessage.Text += "No credentials available." + vbCrLf Catch x As Exception lblMessage.Text += x.Message + vbCrLf End Try Try 'Create new DataTable for tasks objDataSet = New DataSet("root") Dim objTable As DataTable = objDataSet.Tables.Add("Tasks") 'Design Table With objTable.Columns .Add("SiteName", Type.GetType("System.String")) .Add("SiteURL", Type.GetType("System.String")) .Add("TaskTitle", Type.GetType("System.String")) .Add("ListURL", Type.GetType("System.String")) End With 'Fill DataTable with tasks for the current user Dim objAdmin As New SPGlobalAdmin Dim objServer As SPVirtualServer = objAdmin.VirtualServers(0) Dim objSites As SPSiteCollection = objServer.Sites Dim objSite As SPSite 'Walk every site in the installation For Each objSite In objSites Dim objWeb As SPWeb = objSite.OpenWeb() Dim objLists As SPListCollection = objWeb.Lists Dim objList As SPList 'Walk every list on a site For Each objList In objLists If objList.BaseType = SPBaseType.GenericList _ OrElse objList.BaseType = SPBaseType.Issue Then For i As Integer = 0 To objList.ItemCount - 1 Try Dim objItem As SPListItem = objList.Items(i) 'Check to see if this task is assigned to the user Dim strAssignedTo As String = _ UCase(objItem.Item("Assigned To").ToString) If strAssignedTo.IndexOf(_ UCase(objUser.LoginName)) > -1 _ OrElse strAssignedTo.IndexOf(_ UCase(objUser.Name)) > -1 Then 'If so, add it to the DataSet Dim objRow As DataRow = objTable.NewRow() With objRow .Item("SiteName") = objList.ParentWeb.Title .Item("SiteURL") = objList.ParentWeb.Url .Item("TaskTitle") = objItem("Title") .Item("ListURL") = objList.DefaultViewUrl End With objTable.Rows.Add(objRow) End If Catch End Try Next End If Next objWeb.Close() Next 'Tear down the context objContext.Undo() Catch x As Exception lblMessage.Text += x.Message + vbCrLf End Try Return objDataSet End Function
The RenderWebPart method retrieves the current user and makes a call to the GetGlobalTasks method. Once the tasks are all enumerated and contained in the DataSet , you can bind the DataSet to the grid for display. Add the code from Listing 9-28 to show the task list.
Protected Overrides Sub RenderWebPart(_ ByVal output As System.Web.UI.HtmlTextWriter) 'Get the site collection Dim objSite As SPSite = SPControl.GetContextSite(Context) Dim objWeb As SPWeb = objSite.OpenWeb Dim objUser As SPUser = objWeb.CurrentUser 'Get the DataSet of Tasks Dim objDataSet As DataSet = GetGlobalTasks(objUser) 'Display Tasks With grdTasks .DataSource = objDataSet .DataMember = "Tasks" .DataBind() End With 'Show grid grdTasks.RenderControl(output) output.Write("<br>") lblMessage.RenderControl(output) End Sub
Before you can compile the web part, you must give it a strong name and modify the AssemblyInfo file with the name of the key pair file. Just as you have done with every web part, you must also modify the web.config file for SPS to mark the web part as safe. Additionally, you should ensure that the trust level is set to Full. You have accomplished these tasks several times, so I will not repeat the steps here.
Once the web part is compiled, you will want to place it on the shared view for My Site. If you have administrator permissions and modify the shared view, the web part will be available to all users of the portal.
To add the web part, follow these steps:
Log in to SPS as a member of the Administrator site group.
From the portal home page, click the My Site link.
On the My Site home page, select Modify My Page Shared View.
On the shared view, select Modify Shared Page Add Web Parts Import.
Add the global task list web part to the shared page to make it available to all portal users.
Note | The task list aggregates tasks for an individual based on the logon name and the display name. Errors in either of these values can negatively impact the accuracy of the final task list. See the next project for more details on this issue. |