Walkthrough: Customizing the PWA Ribbon and Accessing the JS Grid

Applies to: Office 2010 | Project 2010 | Project Server 2010 | SharePoint Server 2010

In this article
Creating a Customization Project in Visual Studio
Developing a Project Center Extension
Creating a Page for a Modal Dialog Box
Modifying the Project Web App Ribbon with an Elements.xml File
Testing and Debugging a Project Center Extension
Deploying a Project Center Extension

Many pages in Project Web App use the JS Grid control and the Server ribbon infrastructure of Microsoft SharePoint Foundation 2010, which work together and enable a customizable user interface (UI). This article shows how to add Server ribbon controls that access data and toggle an event handler in the JS Grid control. You can deploy the JS Grid control and Server ribbon customizations in a Project Web App site by using the Windows PowerShell commands with a SharePoint solution package. (The code in this article is based on a sample developed by Pat Malatack, Microsoft Corporation.)

This article includes the following sections:

  • Creating a Customization Project in Visual Studio

  • Developing a Project Center Extension

  • Creating a Page for a Modal Dialog Box

  • Modifying the Project Web App Ribbon with an Elements.xml File

  • Testing and Debugging a Project Center Extension

  • Deploying a Project Center Extension

For more information about modifying the Server ribbon, see User Interface Customization Resource Center | SharePoint 2010 and Customizing and Extending the SharePoint 2010 Server Ribbon. For more information about the JS Grid control, see JS Grid Control in the Microsoft SharePoint 2010 Software Development Kit (SDK). For a video demonstration of using the JS Grid control and customizing Project Web App, see Developing Custom Solutions in PWA.

Prerequisites

Developing a SharePoint solution for Microsoft Project Server 2010 requires Microsoft Visual Studio 2010. Development must be done on a computer running Project Server, to enable automated installation and debugging of a SharePoint Feature by Visual Studio. The Visual Studio project must target the Microsoft .NET Framework 3.5.

Warning

Development and testing of Project Web App customizations should be done only on a test installation of Project Server 2010. When a solution is complete, deploy the .wsp solution file on a production server by using Windows PowerShell commands.

Creating a Customization Project in Visual Studio

Because Project Web App is a SharePoint application, you can create a SharePoint Feature to customize Project Web App. The CustomizeProjectCenter example in this article uses a standard SharePoint project template in Visual Studio 2010.

Procedure 1. To create a Project Web App customization project

  1. On a computer with a test installation of Project Server, run Visual Studio 2010 as an administrator.

  2. Create an empty SharePoint project. For example, in the New Project dialog box, expand the SharePoint node in the Installed Templates pane, and then click 2010.

    In the top drop-down list, select .NET Framework 3.5. Name the project CustomizeProjectCenter, choose a location for the project, and then click OK.

  3. In the SharePoint Customization Wizard, select and validate the local Project Web App site to use for debugging, click Deploy as a farm solution, and then click Finish.

    Visual Studio creates the empty SharePoint project, including the key.snk file to sign the assembly.

  4. Because the customization will use Windows Communication Foundation (WCF) with the Project Server Interface (PSI), add the following references to the CustomizeProjectCenter project:

    • System.Data.DataSetExtensions

    • System.Runtime.Serialization

    • System.ServiceModel

    • System.Web

    • System.Web.Extensions

  5. In Solution Explorer, right-click the Features folder, and then click Add Feature. Right-click the Feature1 node, and then click Rename. For example, rename the feature ProjectCenterExtension. Visual Studio similarly renames the feature subnodes.

  6. Double-click the ProjectCenterExtension node, and then add a description in the ProjectCenterExtension.feature tab. For example, type Adds functionality to the Project Center page of Project Web App.

  7. Right-click the CustomizeProjectCenter project, and then add a folder named Elements.

  8. Right-click the Elements folder, and then add a new item. In the Add New Item – CustomizeProjectCenter dialog box, click Empty Element in the SharePoint 2010 template, name the element ProjectCenterExtension, and then click Add.

    Double-click the ProjectCenterExtension feature, and then click the Manifest tab at the bottom of the ProjectCenterExtension.feature pane. Verify that the manifest XML file includes the correct Location attribute of the ElementManifest element, as follows:

    <Feature [xmlns and Id attributes] Title="ProjectCenterExtension" Scope="Web">
      <ElementManifests>
        <ElementManifest Location="ProjectCenterExtension\Elements.xml" />
      </ElementManifests>
    </Feature>
    

    The Elements.xml file will contain the Server ribbon control definitions, as described in the Modifying the Project Web App Ribbon with an Elements.xml File section.

  9. Right-click the CustomizeProjectCenter project, click Add, and then click SharePoint "Layouts" Mapped Folder. Visual Studio creates a Layouts folder that contains an empty CustomizeProjectCenter folder. Items that you add to this CustomizeProjectCenter folder are installed in the following folder when you deploy the solution to a Project Server computer: [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\CustomizeProjectCenter.

Figure 1 shows all of the folders, references, and files in Solution Explorer after you complete Procedure 1. If you click the Elements.xml file and open the Properties pane, the Deployment Location field shows where the feature definition files will be installed.

For example, the Deployment Location value {SharePointRoot}\Template\Features\{FeatureName}\ProjectCenterExtension\ means that the Feature.xml file will be installed in the [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\CustomizeProjectCenter_ProjectCenterExtension directory, and the Elements.xml file will be installed in the ProjectCenterExtension subdirectory.

Figure 1. SharePoint solution files in Solution Explorer, after completing Procedure 1

SharePoint solution files in Solution Explorer

Developing a Project Center Extension

As described in Procedure 4, the Elements.xml file will contain a CommandUIHandler element for each custom control in the Server ribbon. The CommandAction attribute specifies an ECMAScript (JavaScript, JScript) method that performs an action when the control is used. Procedure 2 shows how to create the JavaScript file and the code that implements the functionality.

The solution adds two buttons to the Project Center page. One button runs the DisplaySelectedRecords method that gets the project GUID for each selected row on the JS Grid control and shows a dialog box that contains the data. Another button runs the ToggleMode method that changes the toggleMode global variable. If toggleMode is true, the RowChanged event handler runs the GridDataCallBack method that displays an alert.

Procedure 2. To develop the extension

  1. In Solution Explorer, expand the Layouts folder, right-click the CustomizeProjectCenter folder, and then add a new item.

  2. In the Add New Item – CustomizeProjectCenter dialog box (Figure 2), click Web under the Visual C# node in the Installed Templates pane. In the list of templates, click Jscript File, name the file CustomizeProjectCenter.js, and then click Add.

    Figure 2. Adding a JScript file for the customization script

    Adding a JScript file for the customization script

  3. Open the CustomizeProjectCenter.js file, and then add the following lines:

    /// <reference name="MicrosoftAjax.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/Utility.debug.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/WebMethodManager.debug.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/shell.debug.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/TimesheetSatellite.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/RemoteTextConv.debug.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/ProjectFramework.debug.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/GridSatellite.debug.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/projectserverscripts.debug.js"/>
    /// <reference path="~/_layouts/inc/pwa/library/pagepropertymgr.debug.js"/>
    
    /// <reference path="~/_layouts/SP.core.debug.js"/>
    /// <reference path="~/_layouts/JsGrid.debug.js"/>
    /// <reference path="~/_layouts/JsGrid.Gantt.debug.js"/>
    

    The /// <reference . . . /> comments provide Intellisense completion for classes and methods in JavaScript files that are installed with SharePoint Foundation 2010 and Project Server 2010. Each file has a version that is formatted for efficient use by applications and a version that is formatted to be read by humans. For example, JsGrid.debug.js in the [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS directory is the human-readable file that defines classes and members for the JS Grid control. The corresponding JsGrid.js file contains the same class and member descriptions for use by applications.

    Note

    The previous list of .js files for Intellisense includes typical classes and functions that can be used in Project Web App customizations. The code in this article does not make use of all the files in the list.

  4. Add global variables, register the ProjectCenterMain function for Project Web App pages, and create the ProjectCenterExtension object.

    In the following code, _spBodyOnLoadFunctionNames is a global variable that is defined in INIT.debug.js as an array of functions that are registered when a SharePoint Foundation 2010 page loads. When a page in Project Web App loads, it runs the ProjectCenterMain function, which creates the ProjectCenterExtension object and initializes any other global variables.

    In the ProjectCenterExtension function, the local _grid variable contains a reference to the user interface of the JS Grid control. The local _satellite variable contains a reference to the control wrapper for the JS Grid control. In effect, the satellite object serves as a page controller for the JS Grid control and interacts with other page components, such as the Server ribbon, Project Server methods, and extension applications. The JS Grid control itself acts as the view for the data.

    var pc;             // Contains the Project Center extension object.
    var toggleMode;     // Maintains state of the toggle button.
    
    _spBodyOnLoadFunctionNames.push("ProjectCenterMain");
    
    function ProjectCenterMain() {
        pc = new ProjectCenterExtension();
        toggleMode = false;
    }
    
    function ProjectCenterExtension() {
        var _grid;          // Display surface (a view) of the JS Grid.
        var _satellite;     // Control wrapper for the JS Grid.
    
        // Prevent ECMAScript errors if PJ or the AddGridSatellite function are not defined.
        // If the page is from Project Server, the PJ namespace is defined.
        // If the Project page includes JS Grid, the AddGridSatelliteInitializationNotifier 
        // function is defined.
        if (window.PJ == null || PJ.AddGridSatelliteInitializationNotifier == null) {
            return;
        }
    
        // Use the PJ.AddGridSatelliteInitializationNotifier function to get an instance 
        // of the satellite.
        PJ.AddGridSatelliteInitializationNotifier
        (
            function (satellite) {
                if (PJ.ProjectCenterSatellite != null) {
                    /*** Satellite override: Project code should pass in "satellite" instead of "this". ***/
                    satellite = PJ._NotifySatelliteInitComplete.arguments[0];
                    _satellite = satellite;
                    /***End Satellite override***/
    
                    _grid = satellite.GetJsGridControlInstance();
                    _grid.AttachEvent(SP.JsGrid.EventType.OnRowFocusChanged, RowChanged);
                }
            }
        );
        // Other functions. . .
    }
    

    In the previous code, PJ is an JavaScript namespace that is included only on pages that are controlled by Project Server. PJ is defined in the ProjectFramework.debug.js file. For example, pages such as Project Center and the Project Web App Home page qualify. Pages in a project site, such as the Issues page, do not include the PJ namespace.

    The PJ.AddGridSatelliteInitializationNotifier function is included only on Project Web App pages that include a JS Grid control instance. For example, the Project Center page includes a grid, but the Project Web App Home page does not. Because the ProjectCenterExtension function requires a page that has a grid and is controlled by Project Server, it simply exits if those two conditions are not satisfied.

    Figure 3 shows the Intellisense list of methods and classes for the PJ namespace. The only member supported for third-party use is AddGridSatelliteInitializationNotifier. It adds the function with the satellite parameter; that function initializes the _grid variable and attaches the RowChanged event handler to the OnRowFocusChanged event of the grid.

    Tip

    The Project Center page requires the two override statements to correctly initialize the local _satellite variable. Other Project Web App pages that include a JS Grid control pass the satellite object to the function and do not need the override.

    Figure 3. Intellisense list of members in the PJ namespace

    Intellisense completion for JScript

    For a list of events, see JS Grid Control Events.

  5. Define a DisplaySelectedRecords method in the ProjectCenterExtension object (which is accessed from the pc object). The GetSelectedRecordKeys function is defined in the JsGrid.debug.js file. In the JS Grid control on the Project Center page, the key of each project row is the project GUID.

    Procedure 3 in the Creating a Page for a Modal Dialog Box section shows how to create the SelectedItems.aspx page, which is a modal dialog box. The DisplaySelectedRecords function adds the GUID of each selected project, separated by the pipe character ("|"), to the ProjUIDs URL option of the SelectedItems.aspx page.

    Note

    All of the remaining JavaScript functions are included within the ProjectCenterExtension function.

    this.DisplaySelectedRecords = function () {
        var selection = _grid.GetSelectedRecordKeys(false);
        var url = "./_layouts/CustomizeProjectCenter/SelectedItems.aspx?ProjUIDS=";
    
        for (var i = 0; i < selection.length; i++) {
            url += selection[i] + "|";
        }
        ShowSPDialog(url);
    }
    

    Tip

    The JS Grid control on the Project Details page stores an index for each task, not the GUID of the task. For the Project Details page only, to get a task GUID from the row index, you must use the LookupGuidForIndex method, as in the following code.

    selection = _grid.GetSelectedRecordKeys(false);

    if (selection != null) {

       for(var i = 0; i < selection.length; i++) {

          selection[i] = SP.JsGrid.GuidManager.LookupGuidForIndex(selection[i]);

       }

    }

    Similarly, to write row keys into the JS Grid control, for example for property updates, you must call the SP.JsGrid.GuidManager.LookupIndexForGuid method before passing the keys into the JS Grid control.

  6. Create the ShowSPDialog function, which shows a SharePoint modal dialog box that contains the selected project URLs. The options object definition uses JavaScript Object Notation (JSON) syntax.

    The NotifyCallBack function.

    function ShowSPDialog(pageToLoad) {
        var options = {
            url: pageToLoad,
            title: 'GUIDs of Selected Projects',
            showClose: true,
            width: 700,
            height: 250,
            allowMaximize: false,
            dialogReturnValueCallback: NotifyCallBack
        };
    
        SP.UI.ModalDialog.showModalDialog(options);
    }
    
    // Show a notification when the modal dialog box is closed.
    function NotifyCallBack(dialogResult, returnValue) {
        SP.UI.Notify.addNotification('Successfully showed selected project GUIDs.');
    }
    

    For a list of possible fields in the options object, see lines 20–87 in the SP.UI.Dialog.debug.js file in [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS. See also SP.UI.ModalDialog.showModalDialog(options) Method.

  7. Add the functions for the OnRowFocusChanged event. The RowChanged event handler registers the key for the selected record in the JS Grid control and registers the GridDataCallBack function with the satellite object. The GridDataCallBack function shows an alert dialog box that contains the project name. Because the project name is the same for any cell that is selected within the same row, the RowChanged function gets called only when a cell in a different grid row is selected.

    The ToggleMode method is called by the ToggleButton control that is added in Procedure 4 for the Server ribbon (see Modifying the Project Web App Ribbon with an Elements.xml File).

    // Event handler for the OnRowFocusChanged event in the JS Grid.
    function RowChanged(eventArgs) {
        if (toggleMode) {
            var array = new Array(1);
            array[0] = eventArgs.newRecordKey;
            _satellite._tableCache.GetRecordsByKey(array, GridDataCallBack);
        }
    }
    
    // Callback function for the RowChanged event handler, which shows an alert dialog box 
    // with the project name.
    function GridDataCallBack(arrayofKeys, arrayofRecords, bSucceeded) {
        alert(arrayofRecords[0].properties["PROJ_NAME"].GetLocalized());
    }
    
    this.ToggleMode = function () {
        if (toggleMode) {
            toggleMode = false;
        }
        else {
            toggleMode = true;
        }
    }
    

The CustomizeProjectCenter.js file is complete. SharePoint runs the ProjectCenterMain function when Project Web App starts; in turn, that creates the ProjectCenterExtension object and initializes the toggleMode global variable. When the Project Center page displays, the satellite object on that page gets the JS Grid control instance and attaches the RowChanged event handler to the OnRowFocusChanged event. Buttons on the Server ribbon will call the DisplaySelectedRecords method and the ToggleMode method.

Creating a Page for a Modal Dialog Box

Procedure 3 shows how to create a page for use by the SharePoint showModalDialog method. The page shows the list of GUIDs and project names for the items selected in the JS Grid control on the Project Center page.

Procedure 3. To create the page that displays the selected items

  1. In Solution Explorer, expand the Layouts folder, right-click the CustomizeProjectCenter folder, and then add a new item. In the Add New Item – CustomizeProjectCenter dialog box, click Application Page in the list of SharePoint 2010 templates. Name the page SelectedItems.aspx, and then click Add.

  2. Open the SelectedItems.aspx file, and then add a table within the asp:Content element on the page.

    Tip

    You can drag a standard Table from the HTML tab on the Toolbox, to create a table with three rows and three columns. Delete the unused rows and columns.

    The first row of the table contains an asp:Label object. You can drag it from the Standard tab on the Toolbox. Code in the SelectedItems.aspx.cs file will change the Text property. The second row of the table contains an asp:ListBox object.

    <asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
      <table style="width: 90%;">
        <tr style="font-family: Calibri; font-size: medium; padding: 6pt">
          <td>
              <asp:Label ID="Label" runat="server" Text="Label"></asp:Label>
          </td>
        </tr>
        <tr>
          <td>
              <asp:ListBox ID="ListBox" runat="server" Font-Size="Small" Width="680" Height="120"></asp:ListBox>
          </td>
        </tr>
      </table>
    
    </asp:Content>
    
  3. Because the SelectedItems.aspx page uses the Project service, right-click the CustomizeProjectCenter folder, and then add the wcf.Project.cs file that is created when you make a proxy assembly for WCF services in the PSI. For information about creating a PSI proxy assembly and the related source code files, see Prerequisites for WCF-Based Code Samples.

    Right-click the wcf.Project.cs file, and then click View Code (the Windows Service designer does not work with the proxy source code file). Note the namespace for the Project service proxy. For example, the namespace may be SvcProject.

  4. Open the SelectedItems.aspx.cs file, which is specified in the CodeBehind attribute of the SelectedItems.aspx page. Add using statements needed for WCF and the Project service, as follows:

    using System;
    using System.ServiceModel;
    using System.Security.Principal;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using System.Web;
    using SvcProject;
    
  5. In the SelectedItems class, add a static variable for the ProjectClient class, and then add code in the Page_Load event handler that processes data from the ProjUIDs option of the SelectedItems page URL. The Page_Load event handler changes the Label.Text property and adds the GUID and project name to items in the ListBox on the SelectedItems.aspx page. The ReadProject method returns a ProjectDataSet, from which the project name is extracted.

    The SetClientEndPoints method and the DisposeClient method are defined in step 6. They initialize and enable use of the ProjectClient object.

    public partial class SelectedItems : LayoutsPageBase
    {
        private static ProjectClient projectClient;
    
        protected void Page_Load(object sender, EventArgs e)
        {
            string queryString;
            string[] projUids;
    
            if (HttpContext.Current.Request.QueryString["ProjUIDs"] != null)
            {
                bool isHttps = false;
                Uri pwaUri = Request.UrlReferrer;
    
                if (pwaUri.Scheme.Equals(Uri.UriSchemeHttps)) isHttps = true;
    
                string pwaUrl = pwaUri.AbsoluteUri;
                int lastSlash = pwaUrl.LastIndexOf('/');
                pwaUrl = pwaUrl.Substring(0, lastSlash + 1);
    
                SetClientEndpoints(pwaUrl, isHttps);
    
                ProjectDataSet dsProject = new ProjectDataSet();
    
                queryString = Request.QueryString["ProjUIDs"];
    
                char[] splitChars = { '|' };
                projUids = queryString.Split(splitChars, StringSplitOptions.RemoveEmptyEntries);
    
                this.Label.Text = "You have selected the following projects:<br/>";
    
                foreach (string uid in projUids)
                {
                    Guid projUid = new Guid(uid);
                    dsProject = projectClient.ReadProject(projUid, DataStoreEnum.PublishedStore);
    
                    ProjectDataSet.ProjectRow row =
                        (ProjectDataSet.ProjectRow)dsProject.Tables["Project"].Rows[0];
    
                    this.ListBox.Items.Add(uid + " :\t" + row.PROJ_NAME);
                }
                DisposeClients();
            }
        }
    
        // Add the SetClientEndpoints and DisposeClients methods here.
    }
    
  6. Add the SetClientEndpoints method to create WCF bindings and endpoints, and to initialize the ProjectClient object. The SetClientEndpoints method enables use of the ProjectClient object through WCF. The DisposeClients method disposes the ProjectClient object.

    private void SetClientEndpoints(string pwaUrl, bool isHttps)
    {
        const int MAXSIZE = 500000000;
        const string svcRouter = "_vti_bin/PSI/ProjectServer.svc";
    
        // Create a binding for HTTP.
    
        BasicHttpBinding binding = null;
    
        if (isHttps)
        {
            // Create a binding for HTTPS.
            binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
        }
        else
        {
            // Create a binding for HTTP.
            binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
        }
    
        binding.Name = "basicHttpConf";
        binding.SendTimeout = TimeSpan.MaxValue;
        binding.MaxReceivedMessageSize = MAXSIZE;
        binding.ReaderQuotas.MaxNameTableCharCount = MAXSIZE;
        binding.MessageEncoding = WSMessageEncoding.Text;
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
    
        // The endpoint address is the ProjectServer.svc router for all public PSI calls.
        EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter);
    
        projectClient = new ProjectClient(binding, address);
        projectClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel
            = TokenImpersonationLevel.Impersonation;
        projectClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
    }
    
    public void DisposeClients()
    {
        projectClient.Close();
    }
    

    The CustomizeProjectCenter sample uses programmatic configuration of the bindings and endpoint for the ProjectClient object, rather than changing the web.confile file of Project Web App. For more information about configuring client endpoints, see Walkthrough: Developing PSI Applications Using WCF.

The SelectedItems.aspx page is complete. It is displayed by the ShowSPDialog method in the JavaScript code that is loaded on the Project Center page in Project Web App.

Modifying the Project Web App Ribbon with an Elements.xml File

The Server ribbon for Project Web App is defined in the pwaribbon.xml file in [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\PWARibbon\listtemplates. To modify the Project Web App ribbon, you can use a custom Elements.xml file to add, change, or remove tabs, groups, and controls in the ribbon. For general information about modifying the Server ribbon in SharePoint applications, see Server Ribbon XML. For more examples of modifying the ribbon in Project Web App, see How to: Modify the Ribbon in PWA.

Warning

You should not modify the pwaribbon.xml file itself. Updates and service packs can replace the pwaribbon.xml file and overwrite your changes. Changes that you make in the pwaribbon.xml file are not supported.

Procedure 4 shows how to add buttons on the ribbon for the Project Center page. The Elements.xml file in the Elements\ProjectCenterExtension folder of the CustomizeProjectCenter project includes two CustomAction elements. The first element defines a unique ID, the loading option, and the relative path of the custom script. The second CustomAction element includes two CommandUIDefinition elements that define the location, placement, appearance, and type of Server ribbon control to add. The CommandUIHandler elements specify the JavaScript method to use when a control is activated, and a function that enables or disables a control.

Following is the general outline of the Elements.xml content for adding controls to the Server ribbon:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="https://schemas.microsoft.com/sharepoint/">
    <CustomAction Id="[ID of the custom action for loading the script.]"
        Location="[Option for loading the script.]"
        ScriptSrc="[Relative path of the custom script.]" />
    <CustomAction
        <!-- Specify the ID, location, and title for the action that customizes the ribbon. -->
        <CommandUIExtension>
            <CommandUIDefinitions>
                <CommandUIDefinition Location="[Specify the tab, group, and control collection.]">
                    <!-- Specify the ID, position, type, and appearance of the control. -->
                </CommandUIDefinition>
                <!-- More CommandUIDefinition elements. -->
            </CommandUIDefinitions>
            <CommandUIHandlers>
                <CommandUIHandler
                    Command="[ID of the ribbon control]"
                    CommandAction="[ECMAScript method to run]"
                    EnabledScript="[ECMAScript function that enables the ribbon control.]"/>
                <!-- More CommandUIHandler elements. -->
            </CommandUIHandlers>
        </CommandUIExtension>
    </CustomAction>
</Elements>

Procedure 4 explains the specific attributes and values for the sample in this article.

Procedure 4. To add buttons on the ribbon for the Project Center page

  1. In Solution Explorer, expand the Elements\ProjectCenterExtension node, and then open the Elements.xml file. Add two CustomAction child elements, as follows:

    <?xml version="1.0" encoding="utf-8"?>
    <Elements xmlns="https://schemas.microsoft.com/sharepoint/">
      <CustomAction Id="ProjectCenterPage.AdditionalScript"
          Location="ScriptLink"
          ScriptSrc="CustomizeProjectCenter/CustomizeProjectCenter.js"/>
      <!--Add two buttons to the ribbon in the Project Center view. -->
      <CustomAction>
      </CustomAction>
    </Elements>
    

    The Id attribute must be a unique value within all CustomAction elements that apply to Project Web App. The Location attribute value of ScriptLink means that the custom script is loaded at the Project Web App site level, so that every page in the site includes the custom script. For information about other ways to add JavaScript to a page, see ECMAScript and the Server Ribbon. The ScriptSrc attribute value is the relative location of the script file that is installed when the CustomizeProjectCenter project is deployed. Installation of the .wsp package creates a ]Program Files]\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\CustomizeProjectCenter subdirectory that includes the CustomizeProjectCenter.js file.

  2. In the second CustomAction element, add attributes and child elements as follows:

    <!--Add two buttons to the ribbon in the Project Center view. -->
    <CustomAction Id="Ribbon.ContextualTabs.ProjectCenter.Home.Editing.CustomRibbon"
      Location="CommandUI.Ribbon"
      Title="Add custom buttons to the Project Center">
      <CommandUIExtension>
        <CommandUIDefinitions>
    
        </CommandUIDefinitions>
        <CommandUIHandlers>
    
        </CommandUIHandlers>
      </CommandUIExtension>
    </CustomAction>
    

    The Id attribute specifies that the controls to be added are in a CustomRibbon collection on the Home tab of the Editing group on the Project Center page. The collection name can be an arbitrary unique value. To find the Id values of pages, tabs, and groups in the Project Web App ribbon, see the pwaribbon.xml file previously described. For example, the following element in the pwaribbon.xml file specifies the first group in the ribbon on the Project Center page. The Sequence value of default groups are multiples of 10. To create a custom group between the first and second default groups on that page, create a group with attributes that include Id="Ribbon.ContextualTabs.ProjectCenter.Home.MyGroup" Sequence="11".

    <Group Id="Ribbon.ContextualTabs.ProjectCenter.Home.Editing" Command="ProjectCenterHomeTabEditing" Sequence="10"
       <!-- Other attributes... --> />
    

    The Location attribute specifies that the CustomAction is added to the Ribbon element in the parent CommandUI element that is defined in the global CMDUI.xml file for SharePoint application ribbons. The CMDUI.xml file is located in the ~\TEMPLATE\GLOBAL\XML subdirectory. The Title element is arbitrary text that can explain the purpose of the CustomAction element.

  3. Within the CommandUIDefinitions element, add a child CommandUIDefinition element for each control to be added. The child element of each CommandUIDefinition element is a specific type of control and the attributes that specify how that control appears. In the following code, the first control is a Button element and the second control is a ToggleButton element. For a list of the ribbon controls available, see Server Ribbon Schema. The topic for each ribbon control includes an explanation of the attributes for that control.

    <CommandUIDefinition Location="Ribbon.ContextualTabs.ProjectCenter.Home.Editing.Controls._children">
      <Button Id="Ribbon.ContextualTabs.ProjectCenter.Home.Editing.ProjectCenterShowSelected"
        Sequence="41"
        Command="ProjectCenterShowSelected"
        LabelText="Show Selected Projects"
        Alt="Show Selected Projects"
        ToolTipTitle="Show Selected Projects"
        ToolTipDescription="Show the GUID and project name for the selected projects."
        Image16by16="/_layouts/images/CustomizeProjectCenter/LookupInfo_16x16.png"
        Image32by32="/_layouts/images/CustomizeProjectCenter/LookupInfo_32x32.png"
        TemplateAlias="o1" />
    </CommandUIDefinition>
    <CommandUIDefinition Location="Ribbon.ContextualTabs.ProjectCenter.Home.Editing.Controls._children">
      <ToggleButton Id="Ribbon.ContextualTabs.ProjectCenter.Home.Editing.ProjectCenterToggleRowMonitor"
        Sequence="42"
        Command="ProjectCenterToggleRowMonitor"
        LabelText="Toggle Row Events"
        Alt="Toggle Row Events"
        ToolTipTitle="Toggle Row Events"
        ToolTipDescription="Enable or disable monitoring of row change events."
        Image16by16="/_layouts/images/CustomizeProjectCenter/AddEventHandler_16x16.png"
        Image32by32="/_layouts/images/CustomizeProjectCenter/AddEventHandler_32x32.png"
        TemplateAlias="o1" />
    </CommandUIDefinition>
    

    The Location attribute of each control in the preceding XML is the same, which specifies that the new button is located in the collection of child controls of the Editing group. The Id attribute of each control shows the full element path and must end with a unique name for that control. For example, the Id attribute of ToggleButton shows that the control is located in the Editing group and that the control name is ProjectCenterToggleRowMonitor. The control name is used in the related CommandUIHandler element (described in step 5), which specifies the action for that control.

    The Sequence attribute values ensure that the two custom controls are placed after the default controls in the Editing group (Figure 4 shows the custom controls in the Editing group, which has the label of Project). The Sequence attributes of default controls are multiples of 10. To avoid collisions, the Sequence attributes of custom controls should not be multiples of 10. The Show Selected Projects control and the Toggle Row Events control could also have arbitrary Sequence values of 33 and 34, for example, and display in the same way.

    The TemplateAlias attribute specifies the alias of the template that is used to size and position the controls within a group when the page is resized. The value o1 specifies a template that is defined in the Templates element in the CMDUI.xml file. For example, the layout definitions under the <GroupTemplate Id="Ribbon.Templates.ManageViewsGroup"> line specify how controls change size and overflow into subgroups. For an explanation of other control attributes, see the control topic in Server Ribbon Schema.

  4. If the controls use images that are not already included in the ~\TEMPLATE\IMAGES directory, add the custom images specified in the control elements to the CustomizeProjectCenter project:

    1. In Solution Explorer, right-click the CustomizeProjectCenter project, click Add, and then click SharePoint "Images" Mapped Folder.

    2. Right-click the Images folder, and then add a folder named CustomizeProjectCenter.

    3. Create or copy the 16-pixel x 16-pixel images and the 32-pixel x 32-pixel images that you plan to use, and then add them to the Images\CustomizeProjectCenter folder in Solution Explorer. For example, the Project 2010 SDK download includes the following images in the \Samples\CustomizeProjectCenter\Images\CustomizeProjectCenter subdirectory:

      • AddEventHandler_16x16.png

      • AddEventHandler_32x32.png

      • LookupInfo_16x16.png

      • LookupInfo_32x32.png

  5. Within the CommandUIHandlers element, add a CommandUIHandler child element for each control. In the following XML, the Command attribute specifies the last name from the Id value of each control.

    <CommandUIHandlerCommand="ProjectCenterShowSelected"
      CommandAction="javascript:window.pc.DisplaySelectedRecords();"
      EnabledScript="javascript: function AlwaysTrue(){return true;} AlwaysTrue();"/>
    <CommandUIHandler
      Command="ProjectCenterToggleRowMonitor"
      CommandAction="javascript:window.pc.ToggleMode();"
      EnabledScript="javascript: function AlwaysTrue(){return true;} AlwaysTrue();"/>
    

    The CommandAction attribute specifies the JavaScript method to run when the control is clicked. For example, the Button control runs the DisplaySelectedRecords method and the ToggleButton control runs the ToggleMode method. The methods are accessible in the pc object (defined in the CustomizeProjectCenter.js file) that is loaded on the current window object.

    The EnabledScript attribute is the same for both controls; it specifies the JavaScript function that enables the control. In this case, the AlwaysTrue function is defined inline, not in the CustomizeProjectCenter.js file. The function always returns true, so both buttons are always enabled.

When the CustomizeProjectCenter extension is deployed, the Project Center page includes the custom controls in the Project group on the ribbon (items 3 and 4 in Figure 4).

Figure 4. Ribbon controls in the Project group and the Navigate group

Ribbon groups and buttons on Project Center

Callouts in Figure 4 are for the following items:

  1. Project group on the ribbon, which has the internal Editing group name, has a group Sequence value of 10. It contains three default controls (New, Open, and Update List) and the two custom controls (items 3 and 4).

    The controls in the Project group have control Sequence values, in order, of 10, 20, 30, 41, and 42. If a future service pack adds another control in the Editing group, it will have a Sequence value of 40.

  2. Navigate group, which has the group Sequence value 20.

  3. Show Selected Projects is the custom Button control. The pop-up tooltip shows the ToolTipTitle and ToolTipDescription properties of the control.

  4. Toggle Row Events is the custom ToggleButton control. The width of the ribbon on the Project Center page enables the full-size image specified by the Image32by32 property to be shown. When the window width is decreased, both of the custom buttons switch to use the image specified in the Image16by16 property. The controls follow the "o1" template rules that are specified in the CMDUI.xml file for size changes and overflow into subgroups.

Testing and Debugging a Project Center Extension

When you develop a SharePoint Feature for Project Web App on a computer with a test installation of Project Server, you can easily deploy, retract, and debug the Feature. Visual Studio 2010 automatically attaches to the correct w3wp.exe process for Internet Explorer when you start the debugger. Examining the values of grid properties and event arguments is a good way to help understand how the grid and the extension work.

Procedure 5. To test and debug the CustomizeProjectCenter sample

  1. In Solution Explorer, open the CustomizeProjectCenter.js file, and then set breakpoints where you want to debug the script. For example, put one breakpoint on the second line of the RowChanged function (array[0] = eventArgs.newRecordKey;), and put another breakpoint in the GridDataCallBack function (on the alert(arrayofRecords[0].properties["PROJ_NAME"].GetLocalized()); line).

  2. Press F5 to run the application and start debugging. Visual Studio builds and deploys the application, and then starts Project Web App. Navigate to the Project Center page, and then click Toggle Row Events. Click a cell in the JS Grid control that is on a different row. Visual Studio stops execution at the first breakpoint.

  3. Open the Locals pane in Visual Studio, expand the eventArgs node, and then compare the newRecordKey value and the oldRecordKey value. They are the GUIDs of the current and the previous project selections. Press F5 to continue to the next breakpoint.

    Tip

    When you are running the extension in the debugger, Solution Explorer shows the dynamic ASPX page and all of the scripts that are loaded with the current page in Project Web App. For example, the Windows Internet Explorer node in Solution Explorer includes customizeprojectcenter.js and jsgrid.debug.js. You can open other scripts that are loaded on the page and set breakpoints in them.

  4. At the GridDataCallBack breakpoint, in the Locals pane, expand the arrayofRecords[0] node, and then navigate to the properties\PROJ_NAME\localizedValue subnode. The text value is the string shown in the standard JavaScript alert (Figure 5).

    Figure 5. When the Toggle Row Events button is active, a RowChange event shows an alert

    Alert dialog box for the RowChange event

  5. Click Toggle Row Events again to deactivate the RowChange event handler. Hold down the Option key and select several rows in the grid. Click Show Selected Projects to show the SharePoint modal dialog box (Figure 6), which is the SelectedItems.aspx page.

    Figure 6. Modal dialog box showing GUIDs of selected projects

    Modal dialog showing GUIDs of selected projects

  6. When you are finished debugging, you can either close Project Web App or press the Stop Debugging icon (Shift + F5) in Visual Studio. After it detaches from the w3wp.exe process, Visual Studio automatically retracts the solution so that you can make changes.

    Note

    When you make changes in the CustomizeProjectCenter.js script or the user interface that is defined in Elements.xml, you must clear the Internet Explorer cache before deploying the solution again. On the General tab of the Internet Options dialog box, click Delete. Select Temporary Internet Files, and then click Delete.

In addition to debugging, you can manually deploy and retract the solution on the local computer. In Solution Explorer, right-click CustomizeProjectCenter, and then click Deploy. You can then open Project Web App from another computer to test the extension. To make changes in the solution, click Retract.

Deploying a Project Center Extension

To deploy a Project Web App extension to another installation of Project Server 2010, Visual Studio 2010 can create a .wsp package file. You can then use Windows PowerShell commands to install and activate the extension.

Procedure 6. To deploy the CustomizeProjectCenter sample

  1. In Solution Explorer, expand the Package folder, and then double-click the Package.package node to open the Package Designer and the Packaging Explorer pane. Verify that the Items in the Package pane and the Items in the Solution pane appear, as in Figure 7.

    Figure 7. Using the Package Designer and the Packaging Explorer

    Using the Package Designer

  2. On the Package.package tab, click Manifest. Verify that the manifest contains the following XML:

    <Solution xmlns="https://schemas.microsoft.com/sharepoint/" SolutionId="bb0556cc-3d5b-433e-9bfa-fec96c2d2154" SharePointProductVersion="14.0">
      <Assemblies>
        <Assembly Location="CustomizeProjectCenter.dll" DeploymentTarget="GlobalAssemblyCache" />
      </Assemblies>
      <TemplateFiles>
        <TemplateFile Location="Layouts\CustomizeProjectCenter\CustomizeProjectCenter.js" />
        <TemplateFile Location="Layouts\CustomizeProjectCenter\SelectedItems.aspx" />
        <TemplateFile Location="Images\CustomizeProjectCenter\AddEventHandler_16x16.png" />
        <TemplateFile Location="Images\CustomizeProjectCenter\AddEventHandler_32x32.png" />
        <TemplateFile Location="Images\CustomizeProjectCenter\LookupInfo_16x16.png" />
        <TemplateFile Location="Images\CustomizeProjectCenter\LookupInfo_32x32.png" />
      </TemplateFiles>
      <FeatureManifests>
        <FeatureManifest Location="CustomizeProjectCenter_ProjectCenterExtension\Feature.xml" />
      </FeatureManifests>
    </Solution>
    

    Note the Location attribute of the FeatureManifest element. You will use the CustomizeProjectCenter_ProjectCenterExtension value when you deploy the package on another computer.

  3. In Solution Explorer, right-click the CustomizeProjectCenter project, and then click Package. Visual Studio builds the CustomizeProjectCenter.wsp package file in the CustomizeProjectCenter\bin\Debug directory.

    Tip

    You can easily check the contents of a .wsp file. Change the file name extension to .cab, and then open the file by using WinZip.exe. Before you deploy the package, change the file name extension back to .wsp.

  4. Copy the CustomizeProjectCenter.wsp file to another computer where Project Web App is installed. For example, copy the file to the C:\PWASamples directory.

  5. Run the SharePoint 2010 Management Shell as an administrator (on the Start menu, click Microsoft SharePoint 2010 Products, right-click SharePoint 2010 Management Shell, and then click Run as Administrator).

  6. Run the following commands:

    1. The Add-SPSolution command adds the solution files to the correct SharePoint directories, but does not install the solution or enable the feature. Following is an example command and output:

      PS > add-spsolution -literalpath C:\PWASamples\CustomizeProjectCenter.wsp
      
      Name                           SolutionId                           Deployed
      ----                           ----------                           --------
      customizeprojectcenter.wsp     44f66b39-3087-4580-a0c6-3ddf23cf9d4d False
      
    2. The Install-SPSolution command installs the solution in the Project Web App site:

      PS > install-spsolution -identity CustomizeProjectCenter.wsp -force -gacdeployment
      

      After you run the Install-SPSolution command, open Project Web App, click Site Actions, click Site Settings, and then click Manage site features. Figure 8 shows that the ProjectCenterExtension feature is installed, but is not activated.

      Figure 8. The Install-SPSolution command installs the ProjectCenterExtension feature

      The ProjectCenterExtension feature is installed

    3. To enable the feature, you can click Activate for the ProjectCenterExtension, or you can run the following command:

      PS > enable-spfeature -identity CustomizeProjectCenter_ProjectCenterExtension -url https://ServerName/pwa
      

      Note

      The identity parameter must be the same value as the Location attribute of the FeatureManifest element in the package manifest.

Deployment is now complete for the CustomizeProjectCenter extension. To uninstall the extension, you can use the following Windows PowerShell commands, in order:

PS> disable-spfeature -identity CustomizeProjectCenter_ProjectCenterExtension -url https://ServerName/pwa
PS> uninstall-spsolution -identity CustomizeProjectCenter.wsp 
PS> remove-spsolution -identity CustomizeProjectCenter.wsp

Tip

To suppress the confirmation, add the -Confirm:$false argument to each command.

For a Windows PowerShell script that can be adapted to handle general installation, updates, and removal of SharePoint features, see How to: Deploy a Project Server Workflow.

Next Steps

The ability to create SharePoint Features that modify the Server ribbon in Project Web App, interact with the JS Grid control, and access the PSI through WCF is a powerful way to extend Project Web App functionality. This article does not discuss creating Web Parts, which is another way to extend Project Web App. With a custom Web Part, JavaScript content can be limited to one page.

See Also

Tasks

Walkthrough: Developing PSI Applications Using WCF

How to: Deploy a Project Server Workflow

How to: Modify the Ribbon in PWA

Concepts

Prerequisites for WCF-Based Code Samples

Other Resources

Customizing and Extending the SharePoint 2010 Server Ribbon

Server Ribbon Schema

User Interface Customization Resource Center | SharePoint 2010

ECMAScript and the Server Ribbon

JS Grid Control

JS Grid Control Events

SP.UI.ModalDialog.showModalDialog(options) Method

Microsoft.SharePoint.JSGrid Namespace

Developing Custom Solutions in PWA