Adding a Project to a Category

Brian Smith from PSS has passed along this sample that we thought might be helpful:

The scenario here is that you have a lookup table that shows the categories you want users to select from when creating a project, and then the GUID for the "real" security category is held in the description for the lookup table value.  You make the CF that feeds from the Lookup Table a required Project Level text field.  The Project.Created event fires and the dataset is read - the custom field identified and the GUID of the security category is then used to add the project to the security category.

No error checking or exception handling is shown - you can do this bit. You would also need to set the categories to the rule "only projects...".  I've hardcoded my lookup table and a reference required to the Microsoft.Office.Project.Server.Library and Events. A Web References to LookupTable, Project, Security and LoginWindows is also required.

The code will run as the user running the services - so you will either need that account to have PWA permissions or to change to use impersonation.

using System;
using System.Collections.Generic;
using System.Net;
using System.Diagnostics;
using System.Text;
using Microsoft.Office.Project.Server.Events;
using Microsoft.Office.Project.Server.Library;

namespace TestEventHandler

    public class AutoCategory:ProjectEventReceiver

        public override void OnCreated(PSContextInfo contextInfo, ProjectPostEventArgs e)

            // cfGuid holds CF for Project Category
            Guid cfGuid = new Guid("9bbc698f-5c1d-4f8d-a3d0-163006416bf2");

            // ltGuid holds LT for Categories
            Guid ltGuid = new Guid("625bab60-4427-4f0b-941b-9860d1293338");

            // lt_Struct_Uid gets the id for the selected LT value
            Guid lt_Struct_Uid = new Guid("00000000-0000-0000-0000-000000000000");

            // securityCategoryGuid gets the Security Categorty Guid from the Descriptio field in the lookup table
            Guid securityCategoryGuid = new Guid("00000000-0000-0000-0000-000000000000");

            // 32 is used to just get the CF entities from the readProjectEntities

            Guid SECURITY_CATEGORY_OBJECT_TYPE_PROJECT = new Guid("1771B1C0-6E26-4FB3-A480-C798AB506E82");

            WebSvcLoginWindows.LoginWindows loginWindows = new TestEventHandler.WebSvcLoginWindows.LoginWindows();
            WebSvcProject.Project project = new TestEventHandler.WebSvcProject.Project();
            WebSvcSecurity.Security security = new TestEventHandler.WebSvcSecurity.Security();
            WebSvcLookupTable.LookupTable lookupTable = new TestEventHandler.WebSvcLookupTable.LookupTable();


            //login to Project Server - this assumes the event service has a login with permissions

            // Impersonation would be better
            loginWindows.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/LoginWindows.asmx";
            loginWindows.Credentials = CredentialCache.DefaultCredentials;

            // Get the dataset
            project.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/Project.asmx";
            project.Credentials = CredentialCache.DefaultCredentials;

            lookupTable.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/LookupTable.asmx";
            lookupTable.Credentials = CredentialCache.DefaultCredentials;

            security.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/security.asmx";
            security.Credentials = CredentialCache.DefaultCredentials;

            WebSvcProject.ProjectDataSet dsProjectDataSet = new TestEventHandler.WebSvcProject.ProjectDataSet();

            dsProjectDataSet = project.ReadProjectEntities(e.ProjectGuid, PROJECT_ENTITY_TYPE_PROJECTCUSTOMFIELD, TestEventHandler.WebSvcProject.DataStoreEnum.WorkingStore);

            for (int i = 0; i < dsProjectDataSet.ProjectCustomFields.Count; i++)
                if (dsProjectDataSet.ProjectCustomFields[i].MD_PROP_UID == cfGuid)
                    lt_Struct_Uid = dsProjectDataSet.ProjectCustomFields[i].CODE_VALUE;

            Guid[] arrayLtUid = new Guid[1]{ltGuid};
            WebSvcLookupTable.LookupTableDataSet dsLookupTable = new TestEventHandler.WebSvcLookupTable.LookupTableDataSet();
            dsLookupTable = lookupTable.ReadLookupTablesByUids(arrayLtUid, false, 1033);

            for (int i = 0; i < dsLookupTable.LookupTableTrees.Count; i++)
                if (dsLookupTable.LookupTableTrees[i].LT_STRUCT_UID == lt_Struct_Uid)
                    securityCategoryGuid = new Guid(dsLookupTable.LookupTableTrees[i].LT_VALUE_DESC.ToString());

WebSvcSecurity.SecurityCategoriesDataSet dsSecurityCategories
   = new TestEventHandler.WebSvcSecurity.SecurityCategoriesDataSet();

// Read the existing values for the security category into the dataset
dsSecurityCategories = security.ReadCategory(securityCategoryGuid);

            // Get a new objects row to put the created project into

            WebSvcSecurity.SecurityCategoriesDataSet.SecurityCategoryObjectsRow dsSecurityCategoryObjectsRow
= dsSecurityCategories.SecurityCategoryObjects.NewSecurityCategoryObjectsRow();

            //Set the values
            dsSecurityCategoryObjectsRow.WSEC_CAT_UID = securityCategoryGuid;
            dsSecurityCategoryObjectsRow.WSEC_OBJ_UID = e.ProjectGuid;

            // Add the row to the dataset and then pass to SetCategories to update

            // Create an EventLog instance and assign its source.
            EventLog myLog = new EventLog();
            myLog.Source = "Project Event Handler";

            // Get information from the event arguments, and

            // write an entry to the Application event log.
            string userName = contextInfo.UserName.ToString();
            string projectName = e.ProjectName.ToString();
            string secCatUid = securityCategoryGuid.ToString();
            int eventId = 3652;
            string logEntry;


            logEntry = "User: " + userName +

                    "\nProject: " + projectName +

                    "\nSecurity Category Uid: " + secCatUid;

                myLog.WriteEntry(logEntry, EventLogEntryType.Information, eventId);




Comments (3)

  1. yonzo says:


    Thanx for an excellent article!

    I have a question that I was hoping you could answer. I’m wondering how to retrieve the different webservice-urls that you have hardcoded in you examplecode, from within the eventhandler?

    What I’d like to do is to "re-use" the logic that (I guess) allready is in use within the applicationlogic today.

    English is not my native language, so just to clearify what I mean;

    When for instance a project is saved, the client makes a call to the PSI by referencing the correct url. My question is simply, in what way does the client retrieve the url?



  2. Paul Congdon says:


    In the above sample, you show identifying the security objects of type PROJECT with a particular GUID (as shown in the following line):

      Guid SECURITY_CATEGORY_OBJECT_TYPE_PROJECT = new Guid("1771B1C0-6E26-4FB3-A480-C798AB506E82");

    Q. Is this a hard-coded GUID in the product to identify the types?

    Q. If so, is there way to look this up dynamically?  

    Q. Where is the list of GUIDs for the different possible types of security objects?

    Q. Could this have been an enum that gets mapped internally in the PSI to the correct GUID?


Skip to main content