How to: Create a Project Workspace and Link it to a Project

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

If you choose, you can link each project in Microsoft Office Project Server 2007 to one project workspace. A project workspace is a Windows SharePoint Services 3.0 site in which the project collaboration lists Feature is enabled; it is a site created from a project workspace template. (The code in this article is based on a sample by Eric Zenz, Microsoft Corporation.)

You can define the link between a project and the related workspace automatically when a project is published, manually through the Manage Project Workspaces page in Project Web Access, or by using the Project Server Interface (PSI). The code sample in this article uses methods in the WssInterop PSI Web service and the Microsoft.SharePoint namespace.

The code samples show the following procedures:

  1. Procedures for Creating Project Workspaces

  2. Remove the link between the project and workspace

  3. Create a project workspace

  4. Link the project with the new workspace

Caution noteCaution

Use this code sample for demonstration purposes on a test system only. Do not use the sample to delete SharePoint sites on a Project Server production system unless you modify it to ensure that a user cannot inadvertently delete a Web site.

The code in this article does not show a complete application. The Project 2007 SDK download file installs a directory named Code Samples\ProjectWorkspace with a console application that includes all of the code in this article. For a link to the SDK download, see Welcome to the Microsoft Office Project 2007 SDK.

NoteNote

The project workspace sample runs only on the Project Server computer with Windows SharePoint Services 3.0 installed; you cannot run the application on a remote development client. The sample uses the Microsoft.SharePoint assembly which has dependencies to other SharePoint assemblies in the global assembly cache.

The following procedures show sections of the code for the CreateAndLink method of the WorkspaceTest class in the download sample. The procedures use Microsoft Visual C# and Microsoft Visual Studio 2005. For the full sample code listing of the WorkspaceTest class, see the Example section. For more information about SharePoint integration, see Windows SharePoint Services Infrastructure for Project Server.

Procedures for Creating Project Workspaces

The WorkspaceTest class uses references to Microsoft.SharePoint and Microsoft.Office.Project.Server, as well as Web references to the LoginWindows, Project, and WssInterop Web services in the PSI. In the sample code, the Web reference namespaces are, respectively WebSvcLoginWindows, WebSvcProject, and WebSvcWssInterop.

The sample code creates instances of the PSI classes, and then sets the Url, Credentials, and CookieContainer properties of each instance, as in the following example for the WssInterop class. The value of psiBaseUrl is "http://ServerName/ProjectServerName/_vti_bin/psi/".

WebSvcWssInterop.WssInterop wssInterop =
    new WebSvcWssInterop.WssInterop();

wssInterop.Url = psiBaseUrl + "wssinterop.asmx";
wssInterop.Credentials = CredentialCache.DefaultCredentials;
wssInterop.CookieContainer = cookies;

If the Login method succeeds in the sample application, then the application gets the project and workspace information.

NoteNote

You must publish a project before you can create a workspace site for it.

Procedure 1. To get the existing project and workspace information:

  1. Initialize variables for the workspace settings.

    WebSvcWssInterop.WssSettingsDataSet dsCurrentWssInfo =
        wssInterop.ReadWssSettings();
    WebSvcWssInterop.WssSettingsDataSet.WssAdminRow adminRow =
        dsCurrentWssInfo.WssAdmin[0];
    
    Guid projectUid = new Guid();
    string siteCollection = String.Empty;
    string templateName = String.Empty;
    Guid wssWebAppUid = new Guid();
    int templateLCID;
    bool workspaceUnlinked = false;
    string currentWorkspace = string.Empty;
    
  2. Get the project GUID from the project name.

    WebSvcProject.ProjectDataSet projectList = project.ReadProjectList();
    foreach (DataRow projectRow in projectList.Project)
    {
        if ((String)projectRow[projectList.Project.PROJ_NAMEColumn] 
            == projectName)
        {
            projectUid = (Guid)projectRow[projectList.Project.PROJ_UIDColumn];
            break;
        }
    }
    
  3. Get the current settings for workspace provisioning.

    // If the project exists, do steps 1 through 3.
    if (projectUid != Guid.Empty)
    {
        // Get Web application UID and default site collection
        if (!adminRow.IsWADMIN_DEFAULT_SITE_COLLECTIONNull())
        {
            siteCollection = adminRow.WADMIN_DEFAULT_SITE_COLLECTION;
        }
        wssWebAppUid = adminRow.WADMIN_CURRENT_STS_SERVER_UID;
        templateName = adminRow.WADMIN_STS_TEMPLATE_ID;
        templateLCID = adminRow.WADMIN_STS_TEMPLATE_LCID;
        . . .
        // Add code for steps 1 - 3.
    }
    

    In the previous code, the default value of templateName is "PWS#0" and templateLCID on a U.S. English system is 1033. The value of siteCollection is the name of the Project Web Access instance, typically "ProjectServer".

After getting the project and workspace settings, the sample code is divided into three additional steps. Procedures 2 through 4 show these steps:

  • Remove the link between the project and workspace.

  • Create a project workspace.

  • Link the project to the new workspace.

  1. Create variables to track the actions, and use ReadWssData to get a ProjectWSSInfoDataSet. If the ProjWssInfo table has one row, the linked workspace exists.

    // STEP 1: Remove the link between the project and any existing workspace.
            string saveFullUrl = "";
            string siteDeleted = "";
            bool deletedSite = false;
            bool doNextStep = false;
    
            WebSvcWssInterop.ProjectWSSInfoDataSet dsWssInfo =
                wssInterop.ReadWssData(projectUid);
            int numRows = dsWssInfo.ProjWssInfo.Count;
            bool workspaceExists = numRows > 0;
    
  2. If the workspace exists, allow the user to choose whether to remove the link. The sample code uses a message box. To remove a workspace link, use UpdateProjectWorkspaceAddress with the projectUid returned in Procedure 1 along with empty values for the newWebName and newWSSServerUID parameters.

            if (workspaceExists)
            {
                WebSvcWssInterop.ProjectWSSInfoDataSet.ProjWssInfoRow wssInfoRow =
                   dsWssInfo.ProjWssInfo[0];
    
                currentWorkspace = wssInfoRow.PROJECT_WORKSPACE_URL;
                DialogResult result =
                    MessageBox.Show("Remove the link to the workspace site " 
                        + currentWorkspace + " ?",
                        "Unlink workspace for project: " + projectName,
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (result == DialogResult.Yes)
                {
                    project.UpdateProjectWorkspaceAddress(projectUid, 
                        string.Empty, Guid.Empty);
                    workspaceUnlinked = true;
                    doNextStep = true;
                }
            }
            else
            {
                doNextStep = true;
            }
    

In the sample application, if there is an existing workspace and the user chooses leave it linked, you do not need to continue with the next steps. Procedure 3 shows how to create a new project workspace with the requested name.

Procedure 3. To create a project workspace:

  1. Set the URL of the requested workspace, and determine whether the site already exists.

    // STEP 2: Create a project workspace, if it doesn't exist already.
            if (doNextStep)
            {
                string fullUrl = URLPREFIX + serverName + "/" + siteCollection + "/";
                if (workspaceName == string.Empty)
                    fullUrl += projectName;
                else
                    fullUrl += workspaceName;
    
                // Determine whether the workspace site exists.
                WebSvcWssInterop.WssServersDataSet dsWssServers =
                    wssInterop.ReadWssServerInfo();
                string baseCollection = "";
    
                foreach (DataRow wssServerRow in dsWssServers.WssServers)
                {
                    if ((Guid)wssServerRow[dsWssServers.WssServers.WSTS_SERVER_UIDColumn] 
                        == wssWebAppUid)
                    {
                        baseCollection = (string) 
                            wssServerRow[dsWssServers.WssServers.WSS_SERVER_URLColumn];
                        break;
                    }
                }
    

    For example, if the requested workspaceName is "My Workspace" and the baseCollection is "http://ServerName", then the fullUrl value is "http://ServerName/ProjectServerName/My Workspace".

  2. Use the SPSite and SPWebCollection classes in the Microsoft.SharePoint namespace to find the base site collection. If the requested site already exists, you must delete it so that you can create a site using the project workspace template.

                // Use SharePoint classes to find the base site collection.
                SPSite wssSiteCollection = 
                    new SPSite(baseCollection + "/" + siteCollection);
                SPWebCollection sites = wssSiteCollection.AllWebs;
    
                for (int i = 0; i < sites.Count; i++)
                {
                    if (sites[i].Url == fullUrl)
                    {
                        // Delete the site if it exists, 
                        // to create a project workspace.
                        sites[i].Delete();
                        siteDeleted = fullUrl;
                        deletedSite = true;
                        break;
                    }
                }
    
  3. After you delete any existing site with the same URL, you can create the requested project workspace. The sample application determines whether the workspace name is an empty string; if so, the CreateWssSite method uses an empty string to create a workspace site that has the project name.

                // If the workspaceName is null or empty, the workspace gets the project name.
                saveFullUrl = fullUrl;
                if (workspaceName == string.Empty) fullUrl = string.Empty;
    
                // Create a new project workspace.
                wssInterop.CreateWssSite(projectUid, wssWebAppUid, fullUrl,
                    templateLCID, templateName);
            }
    

  • After you create a workspace, you can link the project to it using the UpdateProjectWorkspaceAddress method again. The workspaceName variable is reset to display the actions taken, as shown in the complete WorkspaceTest class.

    // STEP 3: Link the project to the new workspace and show the results.
            if (doNextStep)
            {
                if (workspaceName == string.Empty)
                    workspaceName = projectName;
    
                string newWebName = siteCollection + "/" + workspaceName;
                project.UpdateProjectWorkspaceAddress(projectUid, newWebName, wssWebAppUid);
            }
    

Example

The following code shows the WorkspaceTest class. The complete console application is in the Project 2007 SDK samples download file (pj12ProjectSDKSamples.exe); for a link to the download files, see Welcome to the Microsoft Office Project 2007 SDK.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Data;
// Add for SoapException error handler:
using System.Web.Services.Protocols;
using PSLibrary = Microsoft.Office.Project.Server.Library;
using Microsoft.SharePoint;

namespace Microsoft.Office.Project.Samples.Workspaces
{
    class WorkspaceTest
    {
        
        private const string URLPREFIX = "http://";
        private const string PSIPATH = "/_vti_bin/psi/";

        private string serverName;    // Name of server where Project Web Access is installed.
        private string pwaName;       // Name of Project Web Access instance.
        private string projectName;
        private string workspaceName;
        private string psiBaseUrl;

        public WorkspaceTest(string server, string pwa, string project, string workspace)
        {
            serverName = server;
            pwaName = pwa;
            psiBaseUrl = URLPREFIX + serverName + "/" + pwa + PSIPATH;
            projectName = project;
            workspaceName = workspace;
        }

        public void CreateAndLink()
        {
            WebSvcLoginWindows.LoginWindows loginWindows =
                new WebSvcLoginWindows.LoginWindows();
            WebSvcWssInterop.WssInterop wssInterop =
                new WebSvcWssInterop.WssInterop();
            WebSvcProject.Project project =
                new WebSvcProject.Project();

            CookieContainer cookies = new CookieContainer();

            // Set properties of Web service objects.
            loginWindows.Url = psiBaseUrl + "loginwindows.asmx";
            loginWindows.Credentials = CredentialCache.DefaultCredentials;
            loginWindows.CookieContainer = cookies;

            wssInterop.Url = psiBaseUrl + "wssinterop.asmx";
            wssInterop.Credentials = CredentialCache.DefaultCredentials;
            wssInterop.CookieContainer = cookies;

            project.Url = psiBaseUrl + "project.asmx";
            project.Credentials = CredentialCache.DefaultCredentials;
            project.CookieContainer = cookies;

            try
            {
                // Log on Project Web Access. 
                if (loginWindows.Login())
                {
                    MessageBox.Show("Logon succeeded", serverName,
                        MessageBoxButtons.OK, MessageBoxIcon.Information);

                    // Get the current workspace provisioning settings.
                    WebSvcWssInterop.WssSettingsDataSet dsCurrentWssInfo =
                        wssInterop.ReadWssSettings();
                    WebSvcWssInterop.WssSettingsDataSet.WssAdminRow adminRow =
                        dsCurrentWssInfo.WssAdmin[0];

                    Guid projectUid = new Guid();
                    string siteCollection = String.Empty;
                    string templateName = String.Empty;
                    Guid wssWebAppUid = new Guid();
                    int templateLCID;
                    bool workspaceUnlinked = false;
                    string currentWorkspace = string.Empty;

                    // Get a project UID for the specified project.
                    WebSvcProject.ProjectDataSet projectList = project.ReadProjectList();
                    foreach (DataRow projectRow in projectList.Project)
                    {
                        if ((String)projectRow[projectList.Project.PROJ_NAMEColumn] == projectName)
                        {
                            projectUid = (Guid)projectRow[projectList.Project.PROJ_UIDColumn];
                            break;
                        }
                    }

                    // If the project exists, do steps 1 through 3.  
                    if (projectUid != Guid.Empty)
                    {
                        // Get Web application UID and default site collection
                        if (!adminRow.IsWADMIN_DEFAULT_SITE_COLLECTIONNull())
                        {
                            siteCollection = adminRow.WADMIN_DEFAULT_SITE_COLLECTION;
                        }
                        wssWebAppUid = adminRow.WADMIN_CURRENT_STS_SERVER_UID;
                        templateName = adminRow.WADMIN_STS_TEMPLATE_ID;
                        templateLCID = adminRow.WADMIN_STS_TEMPLATE_LCID;


                // STEP 1: Remove the link between the project and any existing workspace
                        string saveFullUrl = "";
                        string siteDeleted = "";
                        bool deletedSite = false;
                        bool doNextStep = false;

                        WebSvcWssInterop.ProjectWSSInfoDataSet dsWssInfo =
                            wssInterop.ReadWssData(projectUid);
                        int numRows = dsWssInfo.ProjWssInfo.Count;
                        bool workspaceExists = numRows > 0;

                        if (workspaceExists)
                        {
                            WebSvcWssInterop.ProjectWSSInfoDataSet.ProjWssInfoRow wssInfoRow =
                               dsWssInfo.ProjWssInfo[0];

                            currentWorkspace = wssInfoRow.PROJECT_WORKSPACE_URL;
                            DialogResult result =
                                MessageBox.Show("Remove the link to the workspace site " 
                                    + currentWorkspace + " ?",
                                    "Unlink workspace for project: " + projectName,
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                            if (result == DialogResult.Yes)
                            {
                                project.UpdateProjectWorkspaceAddress(projectUid, 
                                    string.Empty, Guid.Empty);
                                workspaceUnlinked = true;
                                doNextStep = true;
                            }
                        }
                        else
                        {
                            doNextStep = true;
                        }

                // STEP 2: Create a project workspace, if it doesn't exist already.
                        if (doNextStep)
                        {
                            string fullUrl = URLPREFIX + serverName + "/" + siteCollection + "/";
                            if (workspaceName == string.Empty)
                                fullUrl += projectName;
                            else
                                fullUrl += workspaceName;

                            // Determine whether the workspace site exists.
                            WebSvcWssInterop.WssServersDataSet dsWssServers =
                                wssInterop.ReadWssServerInfo();
                            string baseCollection = "";

                            foreach (DataRow wssServerRow in dsWssServers.WssServers)
                            {
                                if ((Guid)wssServerRow[dsWssServers.WssServers.WSTS_SERVER_UIDColumn] 
                                    == wssWebAppUid)
                                {
                                    baseCollection = (
                                        string)wssServerRow[dsWssServers.WssServers.WSS_SERVER_URLColumn];
                                    break;
                                }
                            }

                            // Use SharePoint classes to find the base site collection.
                            SPSite wssSiteCollection = new SPSite(baseCollection + "/" + siteCollection);
                            SPWebCollection sites = wssSiteCollection.AllWebs;

                            for (int i = 0; i < sites.Count; i++)
                            {
                                if (sites[i].Url == fullUrl)
                                {
                                    // Delete the site if it exists,
                                    // to create a project workspace.
                                    sites[i].Delete();
                                    siteDeleted = fullUrl;
                                    deletedSite = true;
                                    break;
                                }
                            }

                            // If the workspaceName is null or empty, the workspace gets the project name.
                            saveFullUrl = fullUrl;
                            if (workspaceName == string.Empty) fullUrl = string.Empty;

                            // Create a project workspace.
                            wssInterop.CreateWssSite(projectUid, wssWebAppUid, fullUrl,
                                templateLCID, templateName);
                        }
                       
                // STEP 3: Link the project to the new workspace and show the results.
                        if (doNextStep)
                        {
                            if (workspaceName == string.Empty)
                                workspaceName = projectName;

                            string newWebName = siteCollection + "/" + workspaceName;
                            project.UpdateProjectWorkspaceAddress(projectUid, newWebName, wssWebAppUid);
                        }

                        string actionInfo = "Workspace actions for project:\t" + projectName;
                        actionInfo += "\n\tProject UID:\t\t" + projectUid;
                        actionInfo += "\n\tSharePoint server UID:\t" + wssWebAppUid;
                        actionInfo += "\n\tSite collection:\t\t" + siteCollection;
                        actionInfo += "\n\tSite template:\t\t" + templateName;
                        actionInfo += "\n\tTemplate LCID:\t\t" + Convert.ToString(templateLCID);
                        actionInfo += "\n\nWorkspace changes:";
                        actionInfo += "\n\tWorkspace name:\t\t" + workspaceName ;
                        if (workspaceUnlinked)
                            actionInfo += "\n\tWorkspace unlinked:\t" + currentWorkspace;
                        if (deletedSite)
                            actionInfo += "\n\tSite deleted:\t\t" + siteDeleted;
                        actionInfo += "\n\tNew workspace:\t\t" + saveFullUrl;


                        MessageBox.Show(actionInfo, "Workspace Actions for Project: " + projectName);
                    }
                    else
                    {
                        MessageBox.Show(projectName + " not found.");
                    }
                    loginWindows.Logoff();
                }
                else
                {
                    MessageBox.Show("Logon failed", serverName,
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch (SoapException ex)
            {
                string errMess = "";
                PSLibrary.PSClientError error = new PSLibrary.PSClientError(ex);
                PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();

                for (int j = 0; j < errors.Length; j++)
                {
                    errMess = errMess + errors[j].ErrId.ToString() + "\n";
                }
                errMess = errMess + "\n" + ex.Message.ToString();

                MessageBox.Show(errMess, "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }
    }
}

For a production application that is based on the sample code, include a check to determine whether the user really wants to delete an existing Web site.

In the SDK sample download, the solution file ProjectWorkspace.sln for Visual Studio 2005 opens the Workspaces project. The Debug tab of the Workspaceswindow (click Workspaces Properties on the Project menu) includes the following command line arguments.

-server ServerName -pwa ProjectServer -project "Project Name"

To debug the sample for your test installation of Project Server, change the values of the arguments.

See Also

Concepts

Windows SharePoint Services Infrastructure for Project Server

Extending the Project Workspace Template

Other Resources

SPSite Members

SPWebCollection Members