Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

Posted in Uncategorized | 1 Comment

Blog Moved

My blog has moved to http://blogs.msdn.com/seanday/.
 
Posted in Uncategorized | 8 Comments

Coordinate Project Server and Windows SharePoint Services security

Project Server 2007 did some improvement on WSS integration (If you want to know about integration between project server 2003 and WSS 2.0, please see my post:Customize WSS Role/User permission for Project Server 2003).  You may read Microsoft TechNet article about this: http://technet2.microsoft.com/Office/en-us/library/c9ca0b66-c167-4cd1-8ecf-6b311cf6976a1033.mspx?mfr=true.
 
Project Server 2007 is completed built on the top of WSS 3.0.  It will create four SharePoint groups on every project workspace:
  • Web Administrator (Microsoft Office Project Server): Users who have global permission "Manage Windows SharePoint Services".
  • Project Managers (Microsoft Office Project Server): Users who have category permission "Save Project to Project Server" on the project.
  • Team Members  (Microsoft Office Project Server): Users who are added in project team and assigned to task.
  • Readers  (Microsoft Office Project Server):  Users who have category permission "View Project Workspace" on the project OR who are added in project team but not assigned to task.
Normally user would not be in two groups at same time, except team members and readers.  User could be in team members and readers groups if users assigned to tasks and have view project workspace permission on my organization category.
 
By default, WSS security settings will change according to PWA security changes.  However, you can change the settings on the Project Workspace Provisioning Settings page to check the box "Automatically add Project Web Access users to project team Web site when SharePoint site is created or when the project manager publishes the project information to Project Server"
 
Then, what scenarios will change the WSS security?
  • New user is created with "Manage Windows SharePoint Services", "Save Project to Project Server", and/or "View Project Workspace" permission.
  • A existing user’s permissions have changed.  The above three permissions have been changed.
  • A resource with those three permissions are activated or de-activated.
  • A resource is added to project and project is published.
Since the first three scenarios may affect large number of project workspace, the best practice is to create/change those resources during non-working hours.
 
Microsoft documents that denying Logon permission and View Project Workspace will affect WSS security.  However, as I tested, I did not find denying those permission will affect WSS.
Posted in Project Server 2007 | 4 Comments

Timesheet Event Workflow

I did not find any thing about Timesheet workflow and which event will occur if you do something in PWA.  So I did some testing and come out with this diagram.  If you found something different, please email me.
 
Posted in Project Server 2007 | Leave a comment

Use PSI event to validate timesheet

In project server 2007, timesheet is a very good new feature.  However, when user enters timesheet and save it back to server, the server does not validate the number.  For example, user can enter 30 hours for a day.  You can set the limitation in Timesheet settings, however project server only validate timesheet hours when user submits it.  What if you don’t want team members to enter ridiculous hours, here is a solution.  You can use PSI event handler to track the changes and cancel the change for whatever reason.  Here is how to do it.

Step 1: create a project server event handler to validate user input.

Timesheet Updating event occurs when a user tries to save timesheet but before it is actually saved.  You can use this event to validate user input.  If the timesheet is valid, you will let timesheet being saved.  Otherwise you can cancel the process and abort saving. 

You can use Timesheet web service to find the hours saved before.  And you can find what user changed in the TimesheetPreUpdateArgs class, which is stored in DsDelta dataset.  DsDelta dataset would only store the changed hours not all hours.  You need to merge the two dataset to get “real” updating dataset.

If you find the hours are not valid, you can set the TimesheetPreUpdateEventArgs.Cancel=True and TimesheetPreUpdateEventArgs.CancelReason=”Timesheet cancel reson”.  Then project server will stop processing timesheet updating and return to timesheet page.  Project server will also save an error message to event log.

However, when you return to timesheet page, the project server does not show any message for cancelling timesheet.  You will need the second step to show the actual reason.

Here is some sample code that it can check if actual hours in one day is more than 24 hours.  If so, it will cancel the event.

public override void OnUpdating(PSContextInfo contextInfo, TimesheetPreUpdateEventArgs e)

{

        if (e.DsDelta == null)

        {

            return;

        }

 

    TimesheetSvc.TimeSheet ts = new TimesheetSvc.TimeSheet();

    ts.Url = _EPMSite + _TimesheetSvc;

    ts.Credentials = CredentialCache.DefaultCredentials;

    TimesheetSvc.TimesheetDataSet tsDs = ts.ReadTimesheet(e.TsUID);

 

    TimesheetSvc.TimesheetDataSet.ActualsDataTable tblActuals = MergeActuals(tsDs.Actuals, e.DsDelta.Actuals);

 

    Dictionary<DateTime, Decimal> TblTotal = AccumulateActuals(tblActuals);

 

    string Msg = ValidateActuals(TblTotal);

    if (Msg != "")

    {

        e.CancelEvent(Msg);

    }

}

 

Step 2: create a custom web part to show cancel reason.

 

Since the timesheet page cannot show cancel reason from event handler, you may need to create a custom web part to show the reason.  You can just create a very simple web part which contains a label control.  The web part will read event log and display if it found any recent error log.

Posted in Project Server 2007 | 3 Comments

Add More Time Hierarchy in Project Server 2007 Cubes

Many organizations have their own definitions of time.  Project Server standard time dimension may not fit their needs.  Don’t worry, we can extend the cube by adding more time hierarchies. 
 
In Analysis Service 2005, one dimension could have multiply hierarchies.  Now we don’t need to add time dimension but just add another time hierarchy.  If you want to find information about adding time dimension in project server 2000, you may check another post: http://netsleeper.spaces.live.com/blog/cns!2D6B305EBBD50AA5!127.entry
 
Another good thing in project server 2005 is that we can use event and managed code.  We can write code on OnCubeProcessed event.  This event occurs after project server processed OLAP cube.  Since we are creating time hierarchy, we need to wait project server finishing creating time dimension.
 
Here, I am using Analysis Management Object API for Analysis Service 2005, which is similiar but a little different from old way.  The following sample code is to create another time hierarchy that only have year, week and day.  So that week will not split between months.
 
        public override void OnCubeProcessed(PSContextInfo contextInfo, CubeAdminPostCubeProcessEventArgs e)
        {
           
            try
            {
                WriteMessage("Start Cube Extension");
                // connect analysis service server
                Server OLAPServer = new Server();
                OLAPServer.Connect("Data Source=" + e.ServerName + ";Provider=msolap;");
               
                WriteMessage("Analysis service server connected:" + e.ServerName);
                // open EPM OLAP database
                Database EPMOLAP = OLAPServer.Databases.FindByName(e.DbName);
                if (EPMOLAP == null)
                {
                    EventLog.WriteEntry(LogSource, "Database: " + e.DbName + " does not exist in Project Server 2007 cube.", EventLogEntryType.Error);
                    return;
                }
                WriteMessage("Start building new Time hirarchy");
                // open Time dimension
                Dimension dmnTime = EPMOLAP.Dimensions[TimeDimensionID];
                if (dmnTime == null)
                {
                    EventLog.WriteEntry(LogSource, "Time dimension does not exist.", EventLogEntryType.Error);
                    return;
                }
                // check if the custom hierarchy is already created
                if (dmnTime.Hierarchies.FindByName(NewTimeDimensionName) != null)
                {
                    EventLog.WriteEntry(LogSource, "New Time hierarchy has already been created by other process.", EventLogEntryType.Error);
                    return;
                }
                // create custom hierarchy in time dimension
                Hierarchy NewTime = dmnTime.Hierarchies.Add(NewTimeDimensionName);
                // find year, week, and day attribute in time dimension
                DimensionAttribute atrYear = dmnTime.Attributes.FindByName(YearAttributeName);
                if (atrYear == null)
                {
                    EventLog.WriteEntry(LogSource, "Year attribute does not exist in Time dimension.", EventLogEntryType.Error);
                    return;
                }
                DimensionAttribute atrWeek = dmnTime.Attributes.FindByName(WeekAttributeName);
                if (atrWeek == null)
                {
                    EventLog.WriteEntry(LogSource, "Week attribute does not exist in Time dimension.", EventLogEntryType.Error);
                    return;
                }
                DimensionAttribute atrDay = dmnTime.Attributes.FindByName(DayAttributeName);
                if (atrDay == null)
                {
                    EventLog.WriteEntry(LogSource, "Day attribute does not exist in Time dimension.", EventLogEntryType.Error);
                    return;
                }
                // create level for year, week, and day in custom hierarchy
                Level lvlYear = PPGTime.Levels.Add(YearLevelName);
                lvlYear.SourceAttributeID = atrYear.ID;
                Level lvlWeek = PPGTime.Levels.Add(WeekLevelName);
                lvlWeek.SourceAttributeID = atrWeek.ID;
                Level lvlDay = PPGTime.Levels.Add(DayLevelName);
                lvlDay.SourceAttributeID = atrDay.ID;
                WriteMessage("Complete building hirarchy");
                // save the changes to database.
                dmnTime.Update();
               
                WriteMessage("Complete Cube Extension");
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(LogSource, ex.ToString(), EventLogEntryType.Error);
            }
        }
Posted in Project Server 2007 | 1 Comment

Access Active Directory From SharePoint 2007 Web Part

DirectoryEntry, and DirectorySearch class in System.DirectoryService namespace can be used to access and search Windows Active Directory.  When you create DirectoryEntry without any credential, the system will use your default credential to access Active Directory.  In ASP.NET web application, if you set impersonate=true, the application will use your login to access Active Directory.
 
However, in SharePoint 2007 web part, this does not work.  You have to explicitly specify the username and password.  Therefore, in SharePoint 2007 web part, you have to use service account to access Active Directory. 
 
Posted in .NET Framework | Leave a comment