Monday, February 11, 2013

Project Server - Backup-restore tool for Server Settings

If you have defined lots of Project Detail Pages or Lookup Tables or Custom Fields or Groups or Users or Templates or Workflows or any other setting in Project Server, you don't have to fear of loosing your settings anymore. 

If you have need to backup all your settings or you need to transfer all (or some) setting to another Project Server environment, you can do that easily with Microsoft's Playbooks.exe utility.

This utility is a part of Microsoft Project Server 2010 Resource Kit (PRK). It enables Project Server 2010 administrators to back up server settings from a selected Project Server 2010 instance to an XML or binary .playbook file. The tool can then restore the server settings to another Project Server 2010 instance. The data from the exported XML (or binary) file is what is imported to the target Project Server 2010 instance. This tool can be especially useful when you move server settings from a test to a production environment, but could also be used to generate a simple “playbook” of custom fields and views (for example, for different industries).

Friday, February 8, 2013

Project Server - Open site in SharePoint Designer

If you want to edit some of the forms of SharePoint lists in your Project Server (like Project Server Worklow Tasks or Project Server Workflow History, etc), logically, you would think of opening your PWA site in SharePoint Designer and edit it there.
But, you'd be wrong.

Opening Project Server web site in SharePoint has been disabled, and if you try to open in in Designer, you'll get following error:

“Web Site Editing is Disabled. This web site has been configured to disallow editing with SharePoint Designer. Contact your web site administrator for more information.”

To resolve this you just need to remove the DisableWebDesignFeatures="wdfopensite" from the ONET.XML File.

You can do that by following procedure:

1. Open Windows Explorer and go to folder (<site_name> is usually PWA):

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\SiteTemplates\<site_name>\XML

2. Make backup copy of  ONET.xml file in that folder before making changes in that file.

3. Find text DisableWebDesignFeatures="wdfopensite" in ONET.xml and remove it from file.

4. Save changes and restart IIS.

Wednesday, February 6, 2013

Project Server workflow - Solution Starters Dynamic Workflow

If you need to use workflows in Project Server, the best candidate for this job is Dynamic Workflow from Microsoft Project 2010 Solution Starters.

The whole package of Solution Starters can be downloaded from Microsoft's pages.

In that package, you can find many great solutions for Project Server (I'll write about some of them in some of my following posts), but for now, all you need is Dynamic Workflow.

Dynamic Workflow is absolutely fabulous. To each workflow you define, you can add customized stages in which you can define who are workflow approvers, who is to be notified, etc. And all that in great graphical interface!

Maybe the best thing of all is that you get source code of entire workflow (and all other solutions in Solution Starters package), so, you can customize almost everything in this workflow (approval, decision making, notifications, etc.) and make it suit your needs.

Friday, January 25, 2013

SharePoint & Project Server Remote debugging

In the following text, „Production“ is server environment on which solution is deployed and which you need to debug for errors.  „Test“ is server environment on which your code is located and Visual Studio is installed.
 
User on „Test“ and user on „Production“ must have same username, they do not have to be in the same domain, but, they must have same username / logon name.

1. First check on „Test“ does msvsmon.exe exist in:

C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\Remote Debugger\x64\

or in:

C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Remote Debugger\x64

If you have msvsmon.exe on „Test“ skip step 2 and go to step 3.

2. If msvsmon.exe does not exist, it can be downloaded from the Microsoft's web pages.

(there is 64 bit and 32 bit version, choose the version that matches your environment) 

3. Copy to msvsmon.exe to „Production“ and Run it as Administrator


4. User on „Production“ must have permissions for debugging


5. Build and deploy your solution/wsp to the server (make sure that the versions of application on „Test“ and applications code on „Production“ are the same


6. Copy the .pdb file from your build directory from „Test“  to the GAC folder on the „Production“ 

NOTE: You can't open GAC folder and copy there directly, follow these steps to copy:
· On „Production“ go to Start à Run

· Write c:\windows\assembly\gac_msil

· Now, you can copy file to the folder where your application  is located in GAC

7. Back to „Test“ and open Visual Studio with code of application


8. Go to Debug --> Attach to process


9. Write the name of „Production“ machine in field Qualifier


10. Attach to appropriate (or all) w3wp.exe


It should sit there for a while loading a whole bunch of symbol files


You should now be able to debug the server


Thursday, January 17, 2013

Project Server 2010 - Get group members emails

In one of my previous posts, I showed you how can you get email addresses of all members of a Project Server group using SQL query.

But, SQL query isn't practical if you want to use it in code behind, nor does Microsoft advise accessing project Servers Published database from code behind through SQL queries. It is always better to use PSI functions.

This is the code for accessing user emails using PSI functions:

  
public static bool GetMembersEmails(Guid projectUid, string pGroupName)
{
   var logService = new LogService();
   var sessionService = new PSSessionService()
   {
       HostName = "serverName", // your PWA host name
       SiteName = "pwa" // your PWA site name
   };

   // we need this user for LoginContext (it can be read from app settings)
   string _user = "Username";
   string _userPwd = "Pass";
   string _userDomain = "Domain";

   using (var _loginCtx = new LoginContext(_userDomain + "\\" + _user, _userPwd, sessionService))
   {

       sessionService.SetLoginContext(_loginCtx);
       FluentPS.Services.Impl.PsiContextService psiContextService = new PsiContextService();
       FluentPS.Services.Impl.PSISvcsFactory psiSvcsFactory = new PSISvcsFactory(sessionService, psiContextService);

       FluentPS.WebSvcProject.Project svcProject = psiSvcsFactory.CreateSvcClient<FluentPS.WebSvcProject.Project>();
       FluentPS.WebSvcResource.Resource svcResource = psiSvcsFactory.CreateSvcClient<FluentPS.WebSvcResource.Resource>();
       FluentPS.WebSvcSecurity.Security svcSecurity = psiSvcsFactory.CreateSvcClient<FluentPS.WebSvcSecurity.Security>();

       try
       {
            List<Guid> _userGuidList = new List<Guid>();
            List<string> _emailsList = new List<string>();

            Guid _groupGuid = Guid.Empty;
            using (var dsSecurityGroups = svcSecurity.ReadGroupList())       
            {
                 SecurityGroupsDataSet.SecurityGroupsDataTable _groups = dsSecurityGroups.SecurityGroups;
                 for (int i = 0; i < _groups.Count; i++)
                 {
                      if (_groups[i].WSEC_GRP_NAME == pGroupName.Trim().Split('@')[0])
                      {
                           _groupGuid = _groups[i].WSEC_GRP_UID;
                      }
                 }
                
if (_groupGuid.Equals(Guid.Empty))  return null;
            }

            using (var dsSecurityGroupsMem = svcSecurity.ReadGroup(_groupGuid))
            {
                 //read group members' uids
                 for (int i = 0; i < dsSecurityGroupsMem.GroupMembers.Count; i++)
                 {
                      Guid _userGuid = dsSecurityGroupsMem.GroupMembers[i].RES_UID;
                      _userGuidList.Add(_userGuid);
                 }
            }

            foreach (Guid _resUid in _userGuidList)
            {
                 using (var dsResource = svcResource.ReadResource(_resUid))
                 {
                      var resourceRow = dsResource.Resources.FindByRES_UID(_resUid);
                      _emailsList.Add(resourceRow.WRES_EMAIL);                                         }
            }

            return _emailsList;
 
       }
       catch (Exception ex)
       {
            return false;
       }
   }
}