Tuesday, February 23, 2010

FluentPS v1.0 released

We are happy to announce the new FluentPS v1.0 release. The version is adopted and tested on ProjectServer post-Beta2 build (14.0.4730.1010 version).There are number of changes and features in the build:
  • FluentPS is moved from ASMX to WCF interface of the Project Server Interface (PSI)
  • Impersonation changes to work in compliance with WCF interface of PSI
  • Mapping of Nullable properties to PS Custom Fields
  • Solution is moved to VisualStudio 2010 Beta2
Source code and binary is available here: http://fluentps.codeplex.com.

Have fun! Read more...

Monday, February 22, 2010

SharePoint Server and Project Server 2010 Configuration

In the previous post we described SharePoint Server 2010 and Project Server 2010 installation process step-by-step and pointed out some potential issues you might encounter. Once you’ve got these two products installed in the server farm complete configuration you might have noticed that it’s still nothing been made related to SQL Server databases and nothing’s yet been configured.

In this post we will show how to configure SharePoint Server 2010 application, how to activate necessary services and create a PWA site.


SharePoint Server 2010 configuration.

Once Project Server 2010 installation is complete you’re prompted to run the SharePoint Products Configuration Wizard by leaving the corresponding check box in a checked state and pressing “Close” button. If for some reason you left the item unchecked or put off the product configuration you can easily launch it again at “Start -> Microsoft SharePoint 2010 Products –> SharePoint 2010 Products Configuration Wizard”.

By pressing the “Next” button some services are going to be will be restarted.


Next step choose “Create a new server farm” and press “Next”:


Next step you’ll be prompted to set an SQL Server instance name, SharePoint configuration database name and SQL Server account to proceed under:


Type a passphrase:


You could specify a port for a SharePoint Central Administration Web Application and change an authentication provider to Negotiate (Kerberos), but we would leave default settings:


The next step is a review of the settings to be applied:


Press the “Next” button to start configuration off:


Press the “Finish” button. IE will open SharePoint Central Administration page:


Creating a PWA site.

You'll be prompted to start a wizard to configure your SharePoint farm. You will need to choose which services will run in your farm. One of these is going to be Project Server Service Application.


You’re also prompted to choose existing managed account or create a new one. The account that is going to be specified with be used for “SharePoint - 80” and “SharePoint Central Administration v4” application pools.


Press “Next” button to activate chosen services.
Once the services are activated go to Central Administration -> Manage Service Applications -> Project Server Service Application.





In order to create a PWA site click “Create Project Web App Site” link.


Next step prompts you to choose PWA settings: hosting SharePoint Web Application, Project Web App path, Administrator account, DB names etc. Click “OK” and wait for PWA site to get created.


We noticed from time to time PWA creating fails, in most cases the “Retry” link helps.


When the PWA is created it becomes accessible at “http://servername/pwa”. If you would like it to be accessed at “http://localhost/pwa“ as well you should define an alternative URL at “Central Administration -> Application Management -> Configure alternate access mappings” section.

The last thing that should be done is changing the farm admin account. After installation the farm account is the same as the account you’re accessing the PWA site, you could notice that your name is “System Account” on the PWA site under. Changing the farm account “fixes” this and eventually PWA is going to recognize you correctly.

Proceed to “Central administration -> Security -> Configure service accounts”. Expand the dropdown “Select an account for this component”: if the account you need is not listed you should register it by clicking “Register new managed account”. Select “Farm account” from the dropdown at the top right corner; select the appropriate account below and then press OK button.

Read more...

Thursday, February 18, 2010

PS Project Workflow - Programmatic deployment of Workflow Association

One of the key features of MS Project Server (PS) is the ability to track its Project entity life-cycle with the workflow. The basic idea of this feature is to have several stages the project can go through. These stages can manage the accessibility of any particular Project Detail Page (PDP) for the project under the current stage; make its fields required or read-only; track Strategic Impact behavior for project business drivers.

In order to develop the Workflow for PS project it is necessary go through the following steps:

  1. construct the Sequence Workflow in MS Visual Studio Designer (check the http://msdn.microsoft.com/en-us/library/ee767686(office.14).aspx article);
  2. deploy workflow template, create a Workflow Association entity in PWA and Connect the particular PS Enterprise Project Type (EPT) to the newly created Workflow Association (check the http://msdn.microsoft.com/en-us/library/ee767699(office.14).aspx article).

Nevertheless, there is a way to automate workflow deployment process – deploy Workflow Association and Workflow-controlled EPT programmatically. In this post you can find the description of how to deploy a Workflow Association SharePoint feature Event Receiver.

It is possible to create a Workflow Association with help of custom code in FeatureActivated overridden SharePoint feature method. Here is the code sample:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var site = (SPSite)properties.Feature.Parent;
    using (var web = site.OpenWeb())
    {
        var info = new
                    {
                        Name = "[Your_WF_Association_Name]",
                        TemplateName = "[Your_WF_Template_Name]",
                        TaskListTitle = "[Your_Task_list_Title]",
                        HistoryListTitle = "[Your_istory_list_Title]",
                    };

        var workflowTemplate = web.WorkflowTemplates
            .OfType()
            .First(wfTemplate => wfTemplate.Name == info.TemplateName);

        for (int i = 0; i < web.WorkflowAssociations.Count; i++)
            if (web.WorkflowAssociations[i].Name == info.Name)
                web.WorkflowAssociations.Remove(web.WorkflowAssociations[i]);

        SPList taskList = GetListByNameAndBaseType(
                              web,
                              info.TaskListTitle,
                              SPListTemplateType.Tasks)
                          ?? GetFirstListByNameAndType(
                                 web,
                                 SPListTemplateType.Tasks);

        SPList historyList = GetListByNameAndBaseType(
                              web,
                              info.HistoryListTitle,
                               SPListTemplateType.WorkflowHistory)
                          ?? GetFirstListByNameAndType(
                                 web,
                                 SPListTemplateType.WorkflowHistory);

        SPWorkflowAssociation wfAssociation = SPWorkflowAssociation.CreateWebAssociation(
        workflowTemplate,
        info.Name,
        taskList, 
        historyList);

        web.WorkflowAssociations.Add(wfAssociation);
    }
}

private SPList GetFirstListByNameAndType(
    SPWeb web,
    SPListTemplateType listTemplateType)
{
    return web.Lists
        .OfType()
        .FirstOrDefault(list =>
                        list.BaseTemplate == listTemplateType);
}

private SPList GetListByNameAndBaseType(
    SPWeb web,
    string taskListTitle,
    SPListTemplateType listTemplateType)
{
    return web.Lists
        .Cast()
        .FirstOrDefault(list =>
                        list.BaseTemplate == listTemplateType
                        && list.Title == taskListTitle);
}

In this code there is an object named "info" that contains the following properties:

  1. Name – the name of the Association to be created;
  2. TemplateName – should be taken from the Name attribute of Workflow XML declaration node;
  3. TaskListTitle – the SharePoint list name to be used by this workflow association ("Project Server Workflow Tasks" value is used by default);
  4. HistoryListTitle – the SharePoint list name to be used by this workflow association ("Project Server Workflow History" value is used by default);

IMPORTANT. The Workflow Association should be added to the WorkflowAssociations collection of the Root Web of site collection.

In the next post we will show how to programmatically create Workflow-controlled EPT.

Read more...

Monday, February 8, 2010

SharePoint 2010 Ribbon Customization - Adding FlyoutAnchor control

In the previous posts we’ve described the way to add custom buttons, groups and tabs to the SharePoint 2010 Ribbon. In this post we’ll show the way of adding FlyoutAnchor control to the Ribbon – the control representing pull-down menu items list.

It is possible to deploy all the menu items either in FlyoutAnchor declaration or construct them dynamically with JavaScript. Here are the samples of both models.

FlyoutAnchor with static menu items

Here is the example of adding custom FlyoutAnchor control with static menu items:

<CommandUIDefinition Location="[Exisiting_Group_ID].Controls._children">
  <FlyoutAnchor
    Id="[Your_FlyoutAnchor_ID]"
    Command="[Your_FlyoutAnchor_Command_Name]"
    Sequence="10"
    Image32by32="/_layouts/images/RibbonCustomization/images32x32.png"
    Image32by32Top="0"
    Image32by32Left="-32"
    LabelText="FlyoutAnchor Static"
    TemplateAlias="o1"
    ToolTipTitle="FlyoutAnchor Static"
    ToolTipDescription="FlyoutAncor with static menu items">
    <Menu Id="[Your_FlyoutAnchor_ID].Menu">
      ...
      <MenuSection
        Id="[Section_ID]"
        Sequence="10"
        DisplayMode="Menu16">
        <Controls Id="[Section_ID].Controls">
          ...
          <Button
            Id="[Button_ID]"
            Command="[StaticButtonCommand_Name]"
            Sequence="10"
            Alt="Btn 1"
            LabelText="Static Button 1"/>
          ...
        </Controls>
      </MenuSection>
      ...
    </Menu>
  </FlyoutAnchor>
</CommandUIDefinition>

In order to add custom FlyoutAnchor control to the Ribbon you should declare it with all its menu sub-items. There is a way to split menu items into several groups by putting your menu items (buttons) to different MenuSection xml tags. The value of DisplayMode attribute (Menu16/Menu32) shows the size of menu items images – large or small.

If you specify the Command attribute of xml FlyoutAnchor tag, be sure to register this command, otherwise you won’t be able to force menu items to pull down.

If you need one of the menu items to have sub-items, you have to add a FlyoutAnchor xml declaration as a menu item control to the menu section, like you do with usual buttons.

FlyoutAnchor with dynamic menu items

In some cases you have to populate FlyoutAnchor menu items dynamically. Here is the example of adding FlyoutAnchor with dynamic menu items:

<CommandUIDefinition Location="[Exisiting_Group_ID].Controls._children">
  <FlyoutAnchor
    Id="[Your_FlyoutAnchor_ID]"
    Command="[Your_FlyoutAnchor_Command_Name]"
    Sequence="10"
    Image16by16="/_layouts/images/RibbonCustomization/images16x16.png"
    Image16by16Top="0"
    Image16by16Left="-16"
    Image32by32="/_layouts/images/RibbonCustomization/images32x32.png"
    Image32by32Top="0"
    Image32by32Left="-32"
    LabelText="FlyoutAnchor Dymamic"
    TemplateAlias="o1"
    PopulateDynamically="true"
    PopulateOnlyOnce="false"
    PopulateQueryCommand="[Populate_Dymamic_Menu_Items_Query_Command_Name]"
    ToolTipTitle="FlyoutAnchor Dymamic"
    ToolTipDescription="FlyoutAnchor wit dymamic menu items" />
</CommandUIDefinition>

There are three important attributes in the FlyoutAnchor node:

  1. PopulateDynamically – this attribute has to be set to "true";
  2. PopulateOnlyOnce – this Boolean attribute tells if menu items should be constructed every time the FlyoutAnchor is being pulled down. Use "false" to make them build every time;
  3. and PopulateQueryCommand – the name of command which is being invoked every time when the menu items have to be shown (once if PopulateOnlyOnce attribute value is set to "true").

The PopulateQueryCommand has to set PopulationXML value to command properties. In order to do this you have to register this command on the server side (see Server-side command handling post). Here is the example:

commands.Add(new SPRibbonCommand(
                    "[Populate_Dymamic_Menu_Items_Query_Command_Name]",
                    "properties.PopulationXML = GetDynamicMenuXml()"));
The GetDynamicMenuXml JavaScript function mentioned here should construct exactly the same XML, you would use in case of adding menu items declaratively. Here is the example:
function GetDynamicMenuXml() {
    var dynamicMenuXml =
    '<Menu Id="[Your_FlyoutAnchor_ID].Menu">'
    + '<MenuSection Id="[Section_ID]" DisplayMode="Menu16">'
    + '<Controls Id="[Section_ID].Controls">';
    var itemsNumber = Math.floor(Math.random()*10) + 1;
    for (i = 0; i < itemsNumber; i++) {
        var buttonXML = String.format(
           '<Button Id="DynamicButton{0}" '
           + 'Command="[Dynamic_Button_Command_Name]" '
           + 'MenuItemId="{0}" '
           + 'LabelText="DynamicButton {0}" '
           + 'ToolTipTitle="Dynamic Button" '
           + 'ToolTipDescription="Dynamic Button" />',
           i);
        dynamicMenuXml += buttonXML;
    }
    dynamicMenuXml += '</Controls>' + '</MenuSection>' + '</Menu>';
    return dynamicMenuXml;
}

If you have to handle all the menu items commands with one command, you can bypass kind of "unique argument" to this command. You should use MenuItemId button attribute, and then it will be available on the command handler. Here is the example of CommandUIHandler declaration:

<CommandUIHandler
  Command="[Dynamic_Button_Command_Name]"
  CommandAction="javascript:alert('Dynamic Button ' + arguments[2].MenuItemId + ' clicked.');"
  EnabledScript="true" />

The same approach can be applied for extending the Ribbon with DropDown controls. Those can have static/dynamic items declaration as well.

Read more...

Friday, February 5, 2010

Project Server Queue System support in FluentPS

Most of Project Server actions run asynchronously. Every asynchronous action becomes a separate job in queue. You can check them in the “Server Settings”->”Manage Queue Jobs” section on your PWA site. To manage the queue MS Project Server has a special Queuing system service, which works as a separate process. QueueSystem web service from PSI allows developers to communicate with the queue. There is a nice article on MSDN about QueueSystem you may check for more details.( http://msdn.microsoft.com/en-us/library/ms461006.aspx)

We added Project Server queue system support to FluentPS lib, and you can wait for job completion by using WaitForQueue method of the PSQueueSystemService class. So to wait for some operation you can do something like:

var queueService = new PSQueueSystemService(logService, sessionService);
var jobId = projectsService.CheckIn(project.Uid, Guid.NewGuid());
// wait for queue
queueService.WaitForQueue(jobId);

For complete example source code please check EntityMapping project from FluentPS samples solution.

Read more...

Project Server entity mapping: Lookup Custom Fields

The FluentPS library allows mapping of Lookup Custom Fields for projects, resources and tasks to entity properties with help of custom property attributes. Here you can find the examples of mapping Lookup Custom Fields.

Mapping Non-Multiple Value Lookup Fields

Here is the example of mapping text lookup field:
[PSLookupField("{178dcaec-4f4f-4968-a768-8cacd5cf60a7}", "{4b5aa4d3-80ce-4346-bbcc-dd797a111706}",
        FieldName = "Sample Areas Impacted", LookupTableName = "Sample Areas Impacted")]
    public string SampleAreasImpacted { get; set; }

The constructor of PSLookupFieldAttribute class contains two arguments – the string representing the UID of the custom field, and the string representing the UID of the Lookup Table this field is related to.

The type of property points to the type of the Lookup field values. Therefore you should use int or decimal to map Number fields, DateTime for Date etc.

Mapping Multiple Value Lookup Fields

The mapping of fields which allow multiple values to be selected from the lookup table can be produced in the following manner:

[PSLookupField("{eda98e8a-6e2c-49f4-bdf0-97e8d3941e3e}", "{9d02dfc9-cde1-4087-b278-7745086c0830}",
        FieldName = "Sample Primary Objectives", LookupTableName = "Sample Primary Objective")]
    public IList SamplePrimaryObjectives { get; set; }

The inheritor classes or interfaces of IEnumerable<> type (IList<>, List<> etc.) can be used to map a multiple value lookup field. The instance of List<> will be created during filling propertied of your entity, and all selected lookup values will be added as an elements to this list. For instance, if the lookup table has three items (“value 1”, “value 2” and “value 3”), and two of them are chosen in your object, you’ll have “value 1” and “value 2” items in the list.

To add, remove or change the selection of values for the mapped object, you should manipulate with list items.

Mapping Hierarchical Lookup Fields

The Text lookup tables can have hierarchical structure. In this case you have to manipulate with the “full” values of lookup items. For instance, if the lookup table has “value 1” item and “sub-value 1” as its sub-item, you’ll get the “value1 .sub-value1” value in the mapped property if the first sub-item is selected (the “.” character is the delimiter from the lookup table; it is possible to change it to another one). The mapping for hierarchical lookup field is much the same as for text lookup field.

Read more...

Monday, February 1, 2010

PS Project Workflow - InfoPath form usage

There is a way to store any xml-formed data as a metadata of a Workflow Association based on Project Workflow Template. It can be useful in case of dynamic building of the workflow, or if the business rules can be managed by one workflow template with different initial arguments. For this purpose it is possible to use workflow’s association page – the page which is shown right after fills all required Workflow Association data i.e. Association name, Template, etc. This page can be created from scratch and contain any custom logic it needs (check out the article). But there is a way to do create an InfoPath form for this purpose.


Design an InfoPath Form for Project Workflow Association

There is great article posted in MSDN how to create an InfoPath form to be shown in Association page. It can be found in http://msdn.microsoft.com/en-us/library/ms548723(office.14).aspx. When you are done with the development of your InfoPath form, please pay attention to the digital signing and publishing instructions. There is a note in the article: “Do not specify an alternative path to the form. Doing so causes an error that prevents you from publishing the form to the server”. This is very important note, so follow all the publishing steps mentioned in the article.

Deploy InfoPath form for Project Workflow Association

The instructions regarding deployment of InfoPath form for Project Workflow Association can be found in post. To get the value for Association_FormURN xml node you should check your InfoPath FormTemplate Properties->ID.
IMNPORTANT. If your InfoPath form has a code-behind class, than you should deploy both published .xsn and .dll files to the feature folder.

Read more...