Partager via


Créer un complément Project qui utilise REST avec un service OData Project Server local

Cet article explique comment créer un complément de volet Office pour Project Professionnel qui compare les données de coût et de travail dans le projet actif avec les moyennes de tous les projets de la Project Web App instance actuelle. Le complément utilise REST avec la bibliothèque jQuery pour accéder au service de création de rapports OData ProjectData dans Project Server.

Le code de cet article est basé sur un exemple développé par Saurabh Sanghvi et Arvind Iyer, Microsoft Corporation.

Configuration requise

Voici les conditions préalables à la création d’un complément du volet Office Project qui lit le service ProjectData d’un Project Web App instance dans une installation locale de Project Server.

  • Project Professionnel est nécessaire pour se connecter à Project Web App. L’ordinateur de développement doit avoir Project Professionnel installé pour activer le débogage F5 avec Visual Studio.

    Remarque

    Project Standard pouvez également héberger des compléments du volet Office, mais vous ne pouvez pas vous connecter à Project Web App.

  • Visual Studio 2015 avec outils de développement Office pour Visual Studio inclut des modèles pour la création de compléments Office et SharePoint. Vérifiez que vous avez installé la version la plus récente des Outils de développement Office ; voir la section Outils des compléments Office et téléchargements SharePoint.

  • Les procédures et exemples de code de cet article accèdent au service ProjectData de Project Server dans un domaine local. Les méthodes jQuery décrites dans cet article ne fonctionnent pas avec Project sur le web.

    Vérifiez que le service ProjectData est accessible à partir de votre ordinateur de développement.

Procédure 1. Vérifier que le service ProjectData est accessible

  1. Pour permettre à votre navigateur d’afficher directement les données XML à partir d’une requête REST, désactivez le mode Lecture du flux. Pour plus d’informations sur la procédure à suivre dans Internet Explorer, consultez Procédure 1, étape 4 dans Interroger des flux OData pour les données de création de rapports Project.

  2. Interrogez le service ProjectData à l’aide de votre navigateur avec l’URL suivante : http://ServerName /ProjectServerName /_api/ProjectData. Par exemple, si le Project Web App instance est http://MyServer/pwa, le navigateur affiche les résultats suivants.

    <?xml version="1.0" encoding="utf-8"?>
        <service xml:base="http://myserver/pwa/_api/ProjectData/"
        xmlns="https://www.w3.org/2007/app"
        xmlns:atom="https://www.w3.org/2005/Atom">
        <workspace>
            <atom:title>Default</atom:title>
            <collection href="Projects">
                <atom:title>Projects</atom:title>
            </collection>
            <collection href="ProjectBaselines">
                <atom:title>ProjectBaselines</atom:title>
            </collection>
            <!-- ... and 33 more collection elements -->
        </workspace>
        </service>
    
  3. You may have to provide your network credentials to see the results. If the browser shows "Error 403, Access Denied," either you do not have logon permission for that Project Web App instance, or there is a network problem that requires administrative help.

Utiliser Visual Studio pour créer un complément de volet Office pour Project

Les Outils de développement Office pour Visual Studio incluent un modèle pour les compléments du volet Office pour Project. Si vous créez une solution nommée HelloProjectOData, la solution contient les deux projets Visual Studio suivants :

  • Le projet de complément prend le nom de la solution. Il inclut uniquement le fichier manifeste du complément et cible le .NET Framework 4.5. La procédure 3 montre les étapes de modification du manifeste du complément HelloProjectOData .

  • Le projet web est nommé HelloProjectODataWeb. Il comprend les pages web, les fichiers JavaScript, les fichiers CSS, les images, les références et les fichiers de configuration du contenu web dans le volet Office. Le projet cible .NET Framework 4. Les procédures 4 et 5 montrent comment modifier les fichiers du projet web pour créer les fonctionnalités du complément HelloProjectOData.

Procédure 2. Créer le complément HelloProjectOData pour Project

  1. Exécutez Visual Studio 2015 en tant qu’administrateur, puis sélectionnez Nouveau projet dans la page Démarrer.

  2. Dans la boîte de dialogue Nouveau projet , développez les nœuds Modèles, Visual C# et Office/SharePoint , puis sélectionnez Compléments Office. Sélectionnez .NET Framework 4.5.2 dans la liste déroulante du framework cible en haut du volet central, puis sélectionnez Complément Office (voir la capture d’écran suivante).

  3. Pour placer les deux projets Visual Studio dans le même répertoire, sélectionnez Créer le répertoire pour la solution, puis accédez à l’emplacement de votre choix.

  4. Dans le champ Nom , tapezHelloProjectOData, puis choisissez OK.

    Figure 1. Créer un complément Office

    Créez un complément Office.

  5. Dans la boîte de dialogue Choisir le type de complément, sélectionnez Volet Office et choisissez Suivant (voir la capture d’écran suivante).

    Figure 2. Choix du type de complément à créer

    Choisissez le type de complément à créer.

  6. Dans la boîte de dialogue Choisir les applications hôtes, désélectionnez toutes les cases, sauf la case Project (voir la capture d’écran suivante) et cliquez sur Terminer.

    Figure 3. Choix de l’application hôte

    Choisissez Projet comme seule application hôte.

    Visual Studio crée le projet HelloProjectOdata et le projet HelloProjectODataWeb .

Le dossier AddIn (voir la capture d’écran suivante) contient le fichier App.css pour les styles CSS personnalisés. Dans le sous-dossier Home, le fichier Home.html contient des références aux fichiers CSS et aux fichiers JavaScript utilisés par le complément, et le contenu HTML5 pour le complément. Par ailleurs, le fichier Home.js est pour votre code JavaScript personnalisé. Le dossier Scripts inclut les fichiers de bibliothèque jQuery. Le sous-dossier Office comprend les bibliothèques JavaScript telles que office.js et project-15.js, ainsi que les bibliothèques de langage pour les chaînes standard dans les compléments Office. Dans le dossier Content, le fichier Office.css contient les styles par défaut pour tous les compléments Office.

Figure 4. Afficher les fichiers de projet web par défaut dans Explorateur de solutions

Affichez les fichiers de projet web dans le Explorateur de solutions.

Le manifeste du projet HelloProjectOData est le fichier HelloProjectOData.xml. Vous pouvez éventuellement modifier le manifeste pour ajouter une description du complément, une référence à une icône, des informations pour d’autres langues et d’autres paramètres. La procédure 3 modifie simplement le nom complet et la description du complément, puis ajoute une icône.

Pour plus d’informations sur le manifeste, voir Manifeste des compléments Office et Référence de schéma pour les manifestes des compléments Office.

Procédure 3. Modifier le manifeste du complément

  1. Dans Visual Studio, ouvrez le fichier HelloProjectOData.xml.

  2. Le nom d’affichage par défaut est le nom du projet Visual Studio (« HelloProjectOData »). Par exemple, remplacez la valeur par défaut de l’élément <DisplayName> par « Hello ProjectData ».

  3. La description par défaut est également « HelloProjectOData ». Par exemple, remplacez la valeur par défaut de l’élément Description par "Test REST queries of the ProjectData service".

  4. Ajoutez une icône à afficher dans la liste déroulante Compléments Office sous l’onglet PROJET du ruban. Vous pouvez ajouter un fichier d’icône dans la solution Visual Studio ou utiliser une URL pour une icône.

Les étapes suivantes montrent comment ajouter un fichier d’icône à la solution Visual Studio.

  1. Dans Explorateur de solutions, accédez au dossier nommé Images.

  2. Pour pouvoir être affichée dans la liste déroulante Compléments Office, l’icône doit avoir une taille de 32 x 32 pixels. Utilisez votre propre icône 32 x 32 ; ou copiez l’image suivante dans un fichier nommé NewIcon.png, puis ajoutez ce fichier au HelloProjectODataWeb\Images dossier .

    Icône de l’application HelloProjectOData.

  3. Dans le HelloProjectOData.xml, ajoutez un <élément IconUrl> sous l’élément <Description> , où la valeur de l’URL de l’icône est le chemin d’accès relatif au fichier d’icône 32x32. Par exemple, ajoutez la ligne suivante : <IconUrl DefaultValue="~remoteAppUrl/Images/NewIcon.png" />. Le fichier HelloProjectOData.xml contient désormais les éléments suivants (votre <valeur Id> sera différente) :

    <?xml version="1.0" encoding="UTF-8"?>
    <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="TaskPaneApp">
        <!--IMPORTANT! Id must be unique for each add-in. If you copy this manifest ensure that you change this id to your own GUID. -->
        <Id>c512df8d-a1c5-4d74-8a34-d30f6bbcbd82</Id>
        <Version>1.0</Version>
        <ProviderName> [Provider name]</ProviderName>
        <DefaultLocale>en-US</DefaultLocale>
        <DisplayName DefaultValue="Hello ProjectData" />
        <Description DefaultValue="Test REST queries of the ProjectData service"/>
        <IconUrl DefaultValue="~remoteAppUrl/Images/NewIcon.png" />
        <SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" />
        <Hosts>
            <Host Name="Project" />
        </Hosts>
        <DefaultSettings>
            <SourceLocation DefaultValue="~remoteAppUrl/AddIn/Home/Home.html" />
        </DefaultSettings>
        <Permissions>ReadWriteDocument</Permissions>
    </OfficeApp>
    

Créer le contenu HTML du complément HelloProjectOData

Le complément HelloProjectOData est un exemple qui inclut le débogage et la sortie d’erreur . il n’est pas destiné à une utilisation en production. Avant de commencer à coder le contenu HTML, concevez l’interface utilisateur et l’expérience utilisateur du complément, et définissez également les fonctions JavaScript qui interagissent avec le code HTML. Pour plus d’informations, voir Design guidelines for Office Add-ins.

Le volet Office affiche le nom complet du complément en haut, qui est la valeur de l’élément <DisplayName> dans le manifeste. L’élément body du fichier HelloProjectOData.html contient les autres éléments d’interface utilisateur, comme suit :

  • Un sous-titre indique la fonctionnalité générale ou le type de l’opération, par exemple ODATA REST QUERY.

  • Le bouton Obtenir le point de terminaison ProjectData appelle la setOdataUrl fonction pour obtenir le point de terminaison du service ProjectData et l’afficher dans une zone de texte. Si Project n’est pas connecté à Project Web App, le complément appelle un gestionnaire d’erreur afin d’afficher un message d’erreur dans une fenêtre contextuelle.

  • Le bouton Comparer tous les projets est désactivé jusqu’à ce que le complément obtienne un point de terminaison OData valide. Lorsque vous sélectionnez le bouton, il appelle la retrieveOData fonction , qui utilise une requête REST pour obtenir les données de coût et de travail du projet à partir du service ProjectData .

  • Un tableau affiche les valeurs moyennes relatives au coût du projet, au coût réel, au travail et au pourcentage achevé. Le tableau compare également les valeurs actuelles du projet actif à la moyenne. Si la valeur actuelle est supérieure à la moyenne de tous les projets, elle est affichée en rouge. Si la valeur actuelle est inférieure à la moyenne, la valeur est affichée en vert. Si la valeur actuelle n’est pas disponible, le tableau affiche NA en bleu.

    La retrieveOData fonction appelle la parseODataResult fonction , qui calcule et affiche les valeurs de la table.

    Remarque

    Dans cet exemple, les données de coût et de travail du projet actif sont dérivées des valeurs publiées. Si vous modifiez des valeurs dans Project, le service ProjectData n’a pas les modifications tant que le projet n’est pas publié.

Procédure 4. Créer le contenu HTML

  1. Dans l’élément head du fichier Home.html, ajoutez tous les éléments de lien supplémentaires pour les fichiers CSS que votre complément utilise. Le modèle de projet Visual Studio inclut un lien pour le fichier App.css que vous pouvez utiliser pour des styles CSS personnalisés.

  2. Ajoutez tous les éléments de script supplémentaires pour les bibliothèques JavaScript que votre complément utilise. Le modèle de projet inclut des liens pour les fichiers jQuery- [version].js, office.js et MicrosoftAjax.js dans le dossier Scripts .

    Remarque

    Avant de déployer le complément, remplacez les références à office.js et jQuery par la référence au réseau de distribution de contenu. Cette dernière permet d’accéder à la version la plus récente et d’obtenir de meilleures performances.

    Le complément HelloProjectOData utilise également un fichier SurfaceErrors.js , qui affiche les erreurs dans un message contextuel. Copiez le code de la section fichierSurfaceErrors.js de cet article dans le dossier Scripts\Office du projet HelloProjectODataWeb en tant que nouveau fichier nommé SurfaceErrors.js.

    Voici le code HTML mis à jour pour l’élément head , avec la ligne supplémentaire pour le fichier SurfaceErrors.js .

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <title>Test ProjectData Service</title>
    
    <link rel="stylesheet" type="text/css" href="../Content/Office.css" />
    
    <!-- Add your CSS styles to the following file. -->
    <link rel="stylesheet" type="text/css" href="../Content/App.css" />
    
    <!-- Use the CDN reference to the mini-version of jQuery when deploying your add-in. -->
    <!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script> -->
    <script src="../Scripts/jquery-1.7.1.js"></script>
    
    <!-- Use the CDN reference to office.js when deploying your add-in. -->
    <!--<script src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>-->
    
    <!-- Use the local script references for Office.js to enable offline debugging -->
    <script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script>
    <script src="../Scripts/Office/1.0/Office.js"></script>
    
    <!-- Add your JavaScript to the following files. -->
    <script src="../Scripts/HelloProjectOData.js"></script>
    <script src="../Scripts/SurfaceErrors.js"></script>
    </head>
    <body>
    <!-- See the code in Step 3. -->
    </body>
    </html>
    
  3. Dans l’élément body , supprimez le code existant du modèle, puis ajoutez le code pour l’interface utilisateur. Si un élément doit être rempli avec des données ou manipulé par une instruction jQuery, l’élément doit inclure un attribut id unique. Dans le code suivant, les attributs id des éléments button, span et td (définition de cellule de tableau) utilisés par les fonctions jQuery sont affichés en gras.

    Le code HTML suivant ajoute une image graphique (NewLogo.png), qui peut être un logo d’entreprise.

    <body>
        <div id="SectionContent">
        <div id="odataQueries">
            ODATA REST QUERY
        </div>
        <div id="odataInfo">
            <button class="button-wide" onclick="setOdataUrl()">Get ProjectData Endpoint</button>
            <br /><br />
            <span class="rest" id="projectDataEndPoint">Endpoint of the 
                <strong>ProjectData</strong> service</span>
            <br />
        </div>
        <div id="compareProjectData">
            <button class="button-wide" disabled="disabled" id="compareProjects"
            onclick="retrieveOData()">Compare All Projects</button>
            <br />
        </div>
        </div>
        <div id="corpInfo">
            <table class="infoTable" aria-readonly="True" style="width: 100%;">
                <tr>
                    <td class="heading_leftCol"></td>
                    <td class="heading_midCol"><strong>Average</strong></td>
                    <td class="heading_rightCol"><strong>Current</strong></td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project Cost</strong></td>
                    <td class="row_midCol" id="AverageProjectCost">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectCost">&amp;nbsp;</td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project Actual Cost</strong></td>
                    <td class="row_midCol" id="AverageProjectActualCost">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectActualCost">&amp;nbsp;</td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project Work</strong></td>
                    <td class="row_midCol" id="AverageProjectWork">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectWork">&amp;nbsp;</td>
                </tr>
                <tr>
                    <td class="row_leftCol"><strong>Project % Complete</strong></td>
                    <td class="row_midCol" id="AverageProjectPercentComplete">&amp;nbsp;</td>
                    <td class="row_rightCol" id="CurrentProjectPercentComplete">&amp;nbsp;</td>
                </tr>
            </table>
        </div>
        <img alt="Corporation" class="logo" src="../../images/NewLogo.png" />
        <br />
        <textarea id="odataText" rows="12" cols="40"></textarea>
    </body>
    

Créer le code JavaScript pour le complément

Le modèle d’un complément du volet Office Project inclut le code d’initialisation par défaut conçu pour illustrer les actions d’obtention et de définition de base des données dans un document pour un complément Office qui utilise les API communes. Étant donné que Project ne prend pas en charge les actions qui écrivent dans le projet actif et que le complément HelloProjectOData n’utilise pas la getSelectedDataAsync méthode , vous pouvez supprimer le script dans la Office.initialize fonction et supprimer la fonction et getData la setData fonction dans le fichier HelloProjectOData.js par défaut.

JavaScript comprend des constantes globales pour la requête REST et des variables globales qui sont utilisées dans plusieurs fonctions. Le bouton Obtenir le point de terminaison ProjectData appelle la setOdataUrl fonction , qui initialise les variables globales et détermine si Project est connecté à Project Web App.

Le reste du fichier HelloProjectOData.js comprend deux fonctions : la retrieveOData fonction est appelée lorsque l’utilisateur sélectionne Comparer tous les projets ; et la parseODataResult fonction calcule les moyennes, puis remplit la table de comparaison avec des valeurs mises en forme pour la couleur et les unités.

Procédure 5. Créer le code JavaScript

  1. Supprimez tout le code dans le fichier HelloProjectOData.js par défaut, puis ajoutez les variables globales et Office.initialize la fonction. Les noms de variables qui sont tous des majuscules impliquent qu’il s’agit de constantes ; ils sont ensuite utilisés avec la _pwa variable pour créer la requête REST dans cet exemple.

    let PROJDATA = "/_api/ProjectData";
    let PROJQUERY = "/Projects?";
    let QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative Work Items'";
    let QUERY_SELECT1 = "&amp;$select=ProjectId, ProjectName";
    let QUERY_SELECT2 = ", ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost";
    let _pwa;           // URL of Project Web App.
    let _projectUid;    // GUID of the active project.
    let _docUrl;        // Path of the project document.
    let _odataUrl = ""; // URL of the OData service: http[s]://ServerName /ProjectServerName /_api/ProjectData
    
    // Ensure the Office.js library is loaded.
    Office.onReady(function() {
        // Office is ready.
        $(document).ready(function () {
            // The document is ready.
        });
    });
    
  2. Ajouter setOdataUrl et les fonctions associées. La setOdataUrl fonction appelle getProjectGuid et getDocumentUrl pour initialiser les variables globales. Dans la méthode getProjectFieldAsync, la fonction anonyme du paramètre de rappel active le bouton Comparer tous les projets à l’aide de la removeAttr méthode dans la bibliothèque jQuery, puis affiche l’URL du service ProjectData . Si Project n’est pas connecté à Project Web App, la fonction génère une erreur, ce qui entraîne l’affichage d’un message d’erreur dans une fenêtre contextuelle. Le fichier SurfaceErrors.js inclut la throwError fonction .

    Remarque

    Si vous exécutez Visual Studio sur l’ordinateur Project Server, pour utiliser le débogage F5 , supprimez les marques de commentaire du code après la ligne qui initialise la _pwa variable globale. Pour activer l’utilisation de la méthode jQuery ajax lors du débogage sur l’ordinateur Project Server, vous devez définir la localhost valeur de l’URL PWA. Si vous exécutez Visual Studio sur un ordinateur distant, l’URL localhost n’est pas requise. Before you deploy the add-in, comment out that code.

    function setOdataUrl() {
        Office.context.document.getProjectFieldAsync(
            Office.ProjectProjectFields.ProjectServerUrl,
            function (asyncResult) {
                if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
                    _pwa = String(asyncResult.value.fieldValue);
    
                    // If you debug with Visual Studio on a local Project Server computer, 
                    // uncomment the following lines to use the localhost URL.
                    //let localhost = location.host.split(":", 1);
                    //let pwaStartPosition = _pwa.lastIndexOf("/");
                    //let pwaLength = _pwa.length - pwaStartPosition;
                    //let pwaName = _pwa.substr(pwaStartPosition, pwaLength);
                    //_pwa = location.protocol + "//" + localhost + pwaName;
    
                    if (_pwa.substring(0, 4) == "http") {
                        _odataUrl = _pwa + PROJDATA;
                        $("#compareProjects").removeAttr("disabled");
                        getProjectGuid();
                    }
                    else {
                        _odataUrl = "No connection!";
                        throwError(_odataUrl, "You are not connected to Project Web App.");
                    }
                    getDocumentUrl();
                    $("#projectDataEndPoint").text(_odataUrl);
                }
                else {
                    throwError(asyncResult.error.name, asyncResult.error.message);
                }
            }
        );
    }
    
    // Get the GUID of the active project.
    function getProjectGuid() {
        Office.context.document.getProjectFieldAsync(
            Office.ProjectProjectFields.GUID,
            function (asyncResult) {
                if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
                    _projectUid = asyncResult.value.fieldValue;
                }
                else {
                    throwError(asyncResult.error.name, asyncResult.error.message);
                }
            }
        );
    }
    
    // Get the path of the project in Project web app, which is in the form <>\ProjectName .
    function getDocumentUrl() {
        _docUrl = "Document path:\r\n" + Office.context.document.url;
    }
    
  3. Ajoutez la retrieveOData fonction , qui concatène les valeurs de la requête REST, puis appelle la ajax fonction dans jQuery pour obtenir les données demandées à partir du service ProjectData . La support.cors variable active le partage de ressources cross-origin (CORS) avec la ajax fonction . Si l’instruction support.cors est manquante ou a la valeur false, la ajax fonction retourne une erreur Aucun transport .

    Remarque

    Le code suivant fonctionne avec une installation locale de Project Server. Pour Project sur le web, vous pouvez utiliser OAuth pour l’authentification basée sur les jetons. Pour plus d’informations, voir Résolutions des limites de stratégie d’origine identique dans les compléments Office.

    Dans l’appel ajax , vous pouvez utiliser le paramètre headers ou le paramètre beforeSend . Le paramètre complet est une fonction anonyme afin qu’elle se trouve dans la même étendue que les variables dans retrieveOData. La fonction du paramètre complet affiche les résultats dans le odataText contrôle et appelle également la parseODataResult méthode pour analyser et afficher la réponse JSON. Le paramètre error spécifie la fonction nommée getProjectDataErrorHandler , qui écrit un message d’erreur dans le odataText contrôle et utilise également la throwError fonction pour afficher un message contextuel.

    // Functions to get and parse the Project Server reporting data./
    
    // Get data about all projects on Project Server,
    // by using a REST query with the ajax method in jQuery.
    function retrieveOData() {
        let restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1 + QUERY_SELECT2;
        let accept = "application/json; odata=verbose";
        accept.toLocaleLowerCase();
    
        // Enable cross-origin scripting (required by jQuery 1.5 and later).
        // This does not work with Project on the web.
        $.support.cors = true;
    
        $.ajax({
            url: restUrl,
            type: "GET",
            contentType: "application/json",
            data: "",      // Empty string for the optional data.
            //headers: { "Accept": accept },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("ACCEPT", accept);
            },
            complete: function (xhr, textStatus) {
                // Create a message to display in the text box.
                let message = "\r\ntextStatus: " + textStatus +
                    "\r\nContentType: " + xhr.getResponseHeader("Content-Type") +
                    "\r\nStatus: " + xhr.status +
                    "\r\nResponseText:\r\n" + xhr.responseText;
    
                // xhr.responseText is the result from an XmlHttpRequest, which
                // contains the JSON response from the OData service.
                parseODataResult(xhr.responseText, _projectUid);
    
                // Write the document name, response header, status, and JSON to the odataText control.
                $("#odataText").text(_docUrl);
                $("#odataText").append("\r\nREST query:\r\n" + restUrl);
                $("#odataText").append(message);
    
                if (xhr.status != 200 &amp;&amp; xhr.status != 1223 &amp;&amp; xhr.status != 201) {
                    $("#odataInfo").append("<div>" + htmlEncode(restUrl) + "</div>");
                }
            },
            error: getProjectDataErrorHandler
        });
    }
    
    function getProjectDataErrorHandler(data, errorCode, errorMessage) {
        $("#odataText").text("Error code: " + errorCode + "\r\nError message: \r\n"
        + errorMessage);
        throwError(errorCode, errorMessage);
    }
    
  4. Ajoutez la parseODataResult fonction , qui désérialise et traite la réponse JSON du service OData. La parseODataResult fonction calcule les valeurs moyennes des données de coût et de travail avec une précision d’une ou deux décimales, met en forme les valeurs avec la couleur correcte et ajoute une unité ( $, heures ou %), puis affiche les valeurs dans les cellules de tableau spécifiées.

    Si le GUID du projet actif correspond à la ProjectId valeur, la myProjectIndex variable est définie sur l’index du projet. Si myProjectIndex indique que le projet actif est publié sur Project Server, la parseODataResult méthode met en forme et affiche les données de coût et de travail pour ce projet. Si le projet actif n’est pas publié, les valeurs pour le projet actif sont sous la forme N/A (en bleu).

    // Calculate the average values of actual cost, cost, work, and percent complete
    // for all projects, and compare with the values for the current project.
    function parseODataResult(oDataResult, currentProjectGuid) {
        // Deserialize the JSON string into a JavaScript object.
        let res = Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult);
        let len = res.d.results.length;
        let projActualCost = 0;
        let projCost = 0;
        let projWork = 0;
        let projPercentCompleted = 0;
        let myProjectIndex = -1;
        for (i = 0; i < len; i++) {
            // If the current project GUID matches the GUID from the OData query,  
            // store the project index.
            if (currentProjectGuid.toLocaleLowerCase() == res.d.results[i].ProjectId) {
                myProjectIndex = i;
            }
            projCost += Number(res.d.results[i].ProjectCost);
            projWork += Number(res.d.results[i].ProjectWork);
            projActualCost += Number(res.d.results[i].ProjectActualCost);
            projPercentCompleted += Number(res.d.results[i].ProjectPercentCompleted);
        }
        let avgProjCost = projCost / len;
        let avgProjWork = projWork / len;
        let avgProjActualCost = projActualCost / len;
        let avgProjPercentCompleted = projPercentCompleted / len;
    
        // Round off cost to two decimal places, and round off other values to one decimal place.
        avgProjCost = avgProjCost.toFixed(2);
        avgProjWork = avgProjWork.toFixed(1);
        avgProjActualCost = avgProjActualCost.toFixed(2);
        avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1);
    
        // Display averages in the table, with the correct units.
        document.getElementById("AverageProjectCost").innerHTML = "$"
            + avgProjCost;
        document.getElementById("AverageProjectActualCost").innerHTML
            = "$" + avgProjActualCost;
        document.getElementById("AverageProjectWork").innerHTML
            = avgProjWork + " hrs";
        document.getElementById("AverageProjectPercentComplete").innerHTML
            = avgProjPercentCompleted + "%";
    
        // Calculate and display values for the current project.
        if (myProjectIndex != -1) {
            let myProjCost = Number(res.d.results[myProjectIndex].ProjectCost);
            let myProjWork = Number(res.d.results[myProjectIndex].ProjectWork);
            let myProjActualCost = Number(res.d.results[myProjectIndex].ProjectActualCost);
            let myProjPercentCompleted =
            Number(res.d.results[myProjectIndex].ProjectPercentCompleted);
    
            myProjCost = myProjCost.toFixed(2);
            myProjWork = myProjWork.toFixed(1);
            myProjActualCost = myProjActualCost.toFixed(2);
            myProjPercentCompleted = myProjPercentCompleted.toFixed(1);
    
            document.getElementById("CurrentProjectCost").innerHTML = "$" + myProjCost;
    
            if (Number(myProjCost) <= Number(avgProjCost)) {
                document.getElementById("CurrentProjectCost").style.color = "green"
            }
            else {
                document.getElementById("CurrentProjectCost").style.color = "red"
            }
    
            document.getElementById("CurrentProjectActualCost").innerHTML = "$" + myProjActualCost;
    
            if (Number(myProjActualCost) <= Number(avgProjActualCost)) {
                document.getElementById("CurrentProjectActualCost").style.color = "green"
            }
            else {
                document.getElementById("CurrentProjectActualCost").style.color = "red"
            }
    
            document.getElementById("CurrentProjectWork").innerHTML = myProjWork + " hrs";
    
            if (Number(myProjWork) <= Number(avgProjWork)) {
                document.getElementById("CurrentProjectWork").style.color = "red"
            }
            else {
                document.getElementById("CurrentProjectWork").style.color = "green"
            }
    
            document.getElementById("CurrentProjectPercentComplete").innerHTML = myProjPercentCompleted + "%";
    
            if (Number(myProjPercentCompleted) <= Number(avgProjPercentCompleted)) {
                document.getElementById("CurrentProjectPercentComplete").style.color = "red"
            }
            else {
                document.getElementById("CurrentProjectPercentComplete").style.color = "green"
            }
        }
        else {
            document.getElementById("CurrentProjectCost").innerHTML = "NA";
            document.getElementById("CurrentProjectCost").style.color = "blue"
    
            document.getElementById("CurrentProjectActualCost").innerHTML = "NA";
            document.getElementById("CurrentProjectActualCost").style.color = "blue"
    
            document.getElementById("CurrentProjectWork").innerHTML = "NA";
            document.getElementById("CurrentProjectWork").style.color = "blue"
    
            document.getElementById("CurrentProjectPercentComplete").innerHTML = "NA";
            document.getElementById("CurrentProjectPercentComplete").style.color = "blue"
        }
    }
    

Tester le complément HelloProjectOData

Pour tester et déboguer le complément HelloProjectOData avec Visual Studio, Project Professionnel devez être installé sur l’ordinateur de développement. Pour permettre différents scénarios de test, assurez-vous que vous pouvez choisir si Project ouvre les fichiers sur l’ordinateur local ou s’il se connecte à Project Web App. Voici des exemples d’étapes.

  1. Sous l’onglet Fichier , choisissez l’onglet Informations dans l’affichage Backstage, puis choisissez Gérer les comptes.

  2. Dans la boîte de dialogue Comptes project web app, la liste Comptes disponibles peut avoir plusieurs comptes Project Web App en plus du compte Ordinateur local. Dans la section Lors du démarrage, sélectionnez Choisir un compte.

  3. Fermez Project afin que Visual Studio puisse le démarrer pour le débogage du complément.

Voici les tests de base préconisés :

  • Exécutez le complément à partir de Visual Studio, puis ouvrez un projet publié à partir de Project Web App, qui contient des données de coût et de travail. Vérifiez que le complément affiche le point de terminaison ProjectData et affiche correctement les données de coût et de travail dans la table. Vous pouvez utiliser la sortie du contrôle odataText pour vérifier la requête REST et d’autres informations.

  • Réexécutez le complément pour choisir le profil de l’ordinateur local dans la boîte de dialogue Connexion quand Project démarre. Ouvrez un fichier .mpp local, puis testez le complément. Vérifiez que le complément affiche un message d’erreur quand vous essayez d’obtenir le point de terminaison ProjectData.

  • Réexécutez le complément pour créer un projet qui comporte des tâches avec des données de coût et de travail. Vous pouvez enregistrer le projet dans Project Web App mais ne le publiez pas. Vérifiez que le complément affiche les données de Project Server et NA pour le projet actuel.

Procédure 6. Test du complément

  1. Exécutez Project Professionnel, connectez-vous avec Project Web App, puis créez un projet de test. Affectez des tâches aux ressources locales ou à des ressources d’entreprise, définissez diverses valeurs de pourcentage achevé pour certaines tâches, puis publiez le projet. Quittez Project, ce qui permet à Visual Studio de démarrer Project pour le débogage du complément.

  2. Dans Visual Studio, appuyez sur F5. Connectez-vous à Project Web App, puis ouvrez le projet que vous avez créé à l’étape précédente. Vous pouvez ouvrir le projet en mode lecture seule ou en mode d’édition.

  3. Sous l’onglet PROJET du ruban, dans la liste déroulante Compléments Office, sélectionnez Hello ProjectData (voir figure 5). Le bouton Comparer tous les projets devrait être désactivé.

    Figure 5. Démarrer le complément HelloProjectOData

    Testez l’application HelloProjectOData.

  4. Dans le volet Office Hello ProjectData, sélectionnez Obtenir le point de terminaison ProjectData. La ligne projectDataEndPoint doit afficher l’URL du service ProjectData et le bouton Comparer tous les projets doit être activé (voir figure 6).

  5. Sélectionnez Comparer tous les projets. Le complément peut s’interrompre pendant qu’il récupère des données du service ProjectData , puis il doit afficher les valeurs moyennes et actuelles mises en forme dans la table.

    Figure 6. Afficher les résultats de la requête REST

    Afficher les résultats de la requête REST.

  6. Examinez la sortie dans la zone de texte. Il doit afficher le chemin du document, la requête REST, les informations status et les résultats JSON des appels à ajax et parseODataResult. La sortie permet de comprendre, de créer et de déboguer du code dans la parseODataResult fonction telle que projCost += Number(res.d.results[i].ProjectCost);.

    Voici un exemple de sortie avec des sauts de ligne et des espaces ajoutés au texte pour plus de clarté, pour trois projets d’un Project Web App instance.

    Document path: <>\WinProj test1
    
    REST query:
    http://sphvm-37189/pwa/_api/ProjectData/Projects?$filter=ProjectName ne 'Timesheet Administrative Work Items'
        &amp;$select=ProjectId, ProjectName, ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost
    
    textStatus: success
    ContentType: application/json;odata=verbose;charset=utf-8
    Status: 200
    
    ResponseText:
    {"d":{"results":[
    {"__metadata":
        {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-00155d157123')",
        "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-00155d157123')",
        "type":"ReportingData.Project"},
        "ProjectId":"ce3d0d65-3904-e211-96cd-00155d157123",
        "ProjectActualCost":"0.000000",
        "ProjectCost":"0.000000",
        "ProjectName":"Task list created in PWA",
        "ProjectPercentCompleted":0,
        "ProjectWork":"16.000000"},
    {"__metadata":
        {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-3c075433b7bd')",
        "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-3c075433b7bd')",
        "type":"ReportingData.Project"},
        "ProjectId":"c31023fc-1404-e211-86b2-3c075433b7bd",
        "ProjectActualCost":"700.000000",
        "ProjectCost":"2400.000000",
        "ProjectName":"WinProj test 2",
        "ProjectPercentCompleted":29,
        "ProjectWork":"48.000000"},
    {"__metadata":
        {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-3c075433b7bd')",
        "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-3c075433b7bd')",
        "type":"ReportingData.Project"},
        "ProjectId":"dc81fbb2-b801-e211-9d2a-3c075433b7bd",
        "ProjectActualCost":"1900.000000",
        "ProjectCost":"5200.000000",
        "ProjectName":"WinProj test1",
        "ProjectPercentCompleted":37,
        "ProjectWork":"104.000000"}
    ]}}
    
  7. Arrêtez le débogage (appuyez sur Maj + F5), puis appuyez à nouveau sur F5 pour exécuter une nouvelle instance de Project. Dans la boîte de dialogue Connexion, choisissez le profil Ordinateur local et non Project Web App. Créez ou ouvrez un fichier Project .mpp local, ouvrez le volet Office Hello ProjectData, puis cliquez sur Obtenir le point de terminaison ProjectData. Le complément doit afficher une erreur Aucune connexion ! (voir figure 7) et le bouton Comparer tous les projets doit rester désactivé.

    Figure 7. Utiliser le complément sans connexion d’application web Project

    Utilisez l’application sans connexion Project Web App.

  8. Arrêtez le débogage, puis appuyez à nouveau sur F5. Connectez-vous à Project Web App, puis créez un projet qui contient des données de coût et de travail. Vous pouvez enregistrer le projet mais pas le publier.

    Dans le volet Office Hello ProjectData, lorsque vous sélectionnez Comparer tous les projets, vous devez voir un NA bleu pour les champs dans la colonne Actuel (voir figure 8).

    Figure 8. Comparer un projet non publié avec d’autres projets

    Comparez un projet non publié avec d’autres projets.

Même si votre complément fonctionne correctement dans les tests précédents, il existe d’autres tests à exécuter. Par exemple :

  • À partir de Project Web App, ouvrez un projet qui ne dispose pas de données de coût ou de travail pour les tâches. Vous devriez voir des valeurs égales à zéro dans les champs de la colonne Actif.

  • Testez un projet qui n’a pas de tâches.

  • Si vous modifiez le complément et que vous le publiez, vous devez réexécuter des tests similaires avec le complément publié. Pour d’autres considérations, voir Étapes suivantes.

Remarque

Il existe des limites quant à la quantité de données pouvant être retournées dans une requête du service ProjectData . la quantité de données varie selon l’entité. Par exemple, le Projects jeu d’entités a une limite par défaut de 100 projets par requête, mais le Risks jeu d’entités a une limite par défaut de 200. For a production installation, the code in the HelloProjectOData example should be modified to enable queries of more than 100 projects. For more information, see Next steps and Querying OData feeds for Project reporting data.

Exemple de code pour le complément HelloProjectOData

Fichier HelloProjectOData.html

Le code suivant se trouve dans le fichier Pages\HelloProjectOData.html du projet HelloProjectODataWeb.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <title>Test ProjectData Service</title>

        <link rel="stylesheet" type="text/css" href="../Content/Office.css" />

        <!-- Add your CSS styles to the following file. -->
        <link rel="stylesheet" type="text/css" href="../Content/App.css" />

        <!-- Use the CDN reference to the mini-version of jQuery when deploying your add-in. -->
        <!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script> -->
        <script src="../Scripts/jquery-1.7.1.js"></script>

        <!-- Use the CDN reference to Office.js when deploying your add-in -->
        <!--<script src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>-->

        <!-- Use the local script references for Office.js to enable offline debugging -->
        <script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script>
        <script src="../Scripts/Office/1.0/Office.js"></script>

        <!-- Add your JavaScript to the following files. -->
        <script src="../Scripts/HelloProjectOData.js"></script>
        <script src="../Scripts/SurfaceErrors.js"></script>
    </head>
    <body>
        <div id="SectionContent">
        <div id="odataQueries">
            ODATA REST QUERY
        </div>
        <div id="odataInfo">
            <button class="button-wide" onclick="setOdataUrl()">Get ProjectData Endpoint</button>
            <br />
            <br />
            <span class="rest" id="projectDataEndPoint">Endpoint of the 
            <strong>ProjectData</strong> service</span>
            <br />
        </div>
        <div id="compareProjectData">
            <button class="button-wide" disabled="disabled" id="compareProjects"
            onclick="retrieveOData()">
            Compare All Projects</button>
            <br />
        </div>
        </div>
        <div id="corpInfo">
        <table class="infoTable" aria-readonly="True" style="width: 100%;">
            <tr>
            <td class="heading_leftCol"></td>
            <td class="heading_midCol"><strong>Average</strong></td>
            <td class="heading_rightCol"><strong>Current</strong></td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project Cost</strong></td>
            <td class="row_midCol" id="AverageProjectCost">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectCost">&amp;nbsp;</td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project Actual Cost</strong></td>
            <td class="row_midCol" id="AverageProjectActualCost">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectActualCost">&amp;nbsp;</td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project Work</strong></td>
            <td class="row_midCol" id="AverageProjectWork">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectWork">&amp;nbsp;</td>
            </tr>
            <tr>
            <td class="row_leftCol"><strong>Project % Complete</strong></td>
            <td class="row_midCol" id="AverageProjectPercentComplete">&amp;nbsp;</td>
            <td class="row_rightCol" id="CurrentProjectPercentComplete">&amp;nbsp;</td>
            </tr>
        </table>
        </div>
        <img alt="Corporation" class="logo" src="../../images/NewLogo.png" />
        <br />
        <textarea id="odataText" rows="12" cols="40"></textarea>
    </body>
</html>

Fichier HelloProjectOData.js

Le code suivant se trouve dans le fichier Scripts\Office\HelloProjectOData.js du projet HelloProjectODataWeb.

/* File: HelloProjectOData.js
* JavaScript functions for the HelloProjectOData example task pane app.
* October 2, 2012
*/

let PROJDATA = "/_api/ProjectData";
let PROJQUERY = "/Projects?";
let QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative Work Items'";
let QUERY_SELECT1 = "&amp;$select=ProjectId, ProjectName";
let QUERY_SELECT2 = ", ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost";
let _pwa;           // URL of Project Web App.
let _projectUid;    // GUID of the active project.
let _docUrl;        // Path of the project document.
let _odataUrl = ""; // URL of the OData service: http[s]://ServerName /ProjectServerName /_api/ProjectData

// The initialize function is required for all add-ins.
Office.initialize = function (reason) {
    // Checks for the DOM to load using the jQuery ready method.
    $(document).ready(function () {
        // After the DOM is loaded, app-specific code can run.
    });
}

// Set the global variables, enable the Compare All Projects button,
// and display the URL of the ProjectData service.
// Display an error if Project is not connected with Project Web App.
function setOdataUrl() {
    Office.context.document.getProjectFieldAsync(
        Office.ProjectProjectFields.ProjectServerUrl,
        function (asyncResult) {
            if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
                _pwa = String(asyncResult.value.fieldValue);

                // If you debug with Visual Studio on a local Project Server computer,
                // uncomment the following lines to use the localhost URL.
                //let localhost = location.host.split(":", 1);
                //let pwaStartPosition = _pwa.lastIndexOf("/");
                //let pwaLength = _pwa.length - pwaStartPosition;
                //let pwaName = _pwa.substr(pwaStartPosition, pwaLength);
                //_pwa = location.protocol + "//" + localhost + pwaName;

                if (_pwa.substring(0, 4) == "http") {
                    _odataUrl = _pwa + PROJDATA;
                    $("#compareProjects").removeAttr("disabled");
                    getProjectGuid();
                }
                else {
                    _odataUrl = "No connection!";
                    throwError(_odataUrl, "You are not connected to Project Web App.");
                }
                getDocumentUrl();
                $("#projectDataEndPoint").text(_odataUrl);
            }
            else {
                throwError(asyncResult.error.name, asyncResult.error.message);
            }
        }
    );
}

// Get the GUID of the active project.
function getProjectGuid() {
    Office.context.document.getProjectFieldAsync(
        Office.ProjectProjectFields.GUID,
        function (asyncResult) {
            if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
                _projectUid = asyncResult.value.fieldValue;
            }
            else {
                throwError(asyncResult.error.name, asyncResult.error.message);
            }
        }
    );
}

// Get the path of the project in Project web app, which is in the form <>\ProjectName .
function getDocumentUrl() {
    _docUrl = "Document path:\r\n" + Office.context.document.url;
}

//  Functions to get and parse the Project Server reporting data./

// Get data about all projects on Project Server,
// by using a REST query with the ajax method in jQuery.
function retrieveOData() {
    let restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1 + QUERY_SELECT2;
    let accept = "application/json; odata=verbose";
    accept.toLocaleLowerCase();

    // Enable cross-origin scripting (required by jQuery 1.5 and later).
    // This does not work with Project on the web.
    $.support.cors = true;

    $.ajax({
        url: restUrl,
        type: "GET",
        contentType: "application/json",
        data: "",      // Empty string for the optional data.
        //headers: { "Accept": accept },
        beforeSend: function (xhr) {
            xhr.setRequestHeader("ACCEPT", accept);
        },
        complete: function (xhr, textStatus) {
            // Create a message to display in the text box.
            let message = "\r\ntextStatus: " + textStatus +
                "\r\nContentType: " + xhr.getResponseHeader("Content-Type") +
                "\r\nStatus: " + xhr.status +
                "\r\nResponseText:\r\n" + xhr.responseText;

            // xhr.responseText is the result from an XmlHttpRequest, which 
            // contains the JSON response from the OData service.
            parseODataResult(xhr.responseText, _projectUid);

            // Write the document name, response header, status, and JSON to the odataText control.
            $("#odataText").text(_docUrl);
            $("#odataText").append("\r\nREST query:\r\n" + restUrl);
            $("#odataText").append(message);

            if (xhr.status != 200 &amp;&amp; xhr.status != 1223 &amp;&amp; xhr.status != 201) {
                $("#odataInfo").append("<div>" + htmlEncode(restUrl) + "</div>");
            }
        },
        error: getProjectDataErrorHandler
    });
}

function getProjectDataErrorHandler(data, errorCode, errorMessage) {
    $("#odataText").text("Error code: " + errorCode + "\r\nError message: \r\n"
        + errorMessage);
    throwError(errorCode, errorMessage);
}

// Calculate the average values of actual cost, cost, work, and percent complete
// for all projects, and compare with the values for the current project.
function parseODataResult(oDataResult, currentProjectGuid) {
    // Deserialize the JSON string into a JavaScript object.
    let res = Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult);
    let len = res.d.results.length;
    let projActualCost = 0;
    let projCost = 0;
    let projWork = 0;
    let projPercentCompleted = 0;
    let myProjectIndex = -1;

    for (i = 0; i < len; i++) {
        // If the current project GUID matches the GUID from the OData query,  
        // then store the project index.
        if (currentProjectGuid.toLocaleLowerCase() == res.d.results[i].ProjectId) {
            myProjectIndex = i;
        }
        projCost += Number(res.d.results[i].ProjectCost);
        projWork += Number(res.d.results[i].ProjectWork);
        projActualCost += Number(res.d.results[i].ProjectActualCost);
        projPercentCompleted += Number(res.d.results[i].ProjectPercentCompleted);

    }
    let avgProjCost = projCost / len;
    let avgProjWork = projWork / len;
    let avgProjActualCost = projActualCost / len;
    let avgProjPercentCompleted = projPercentCompleted / len;

    // Round off cost to two decimal places, and round off other values to one decimal place.
    avgProjCost = avgProjCost.toFixed(2);
    avgProjWork = avgProjWork.toFixed(1);
    avgProjActualCost = avgProjActualCost.toFixed(2);
    avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1);

    // Display averages in the table, with the correct units. 
    document.getElementById("AverageProjectCost").innerHTML = "$"
        + avgProjCost;
    document.getElementById("AverageProjectActualCost").innerHTML
        = "$" + avgProjActualCost;
    document.getElementById("AverageProjectWork").innerHTML
        = avgProjWork + " hrs";
    document.getElementById("AverageProjectPercentComplete").innerHTML
        = avgProjPercentCompleted + "%";

    // Calculate and display values for the current project.
    if (myProjectIndex != -1) {

        let myProjCost = Number(res.d.results[myProjectIndex].ProjectCost);
        let myProjWork = Number(res.d.results[myProjectIndex].ProjectWork);
        let myProjActualCost = Number(res.d.results[myProjectIndex].ProjectActualCost);
        let myProjPercentCompleted = Number(res.d.results[myProjectIndex].ProjectPercentCompleted);

        myProjCost = myProjCost.toFixed(2);
        myProjWork = myProjWork.toFixed(1);
        myProjActualCost = myProjActualCost.toFixed(2);
        myProjPercentCompleted = myProjPercentCompleted.toFixed(1);

        document.getElementById("CurrentProjectCost").innerHTML = "$" + myProjCost;

        if (Number(myProjCost) <= Number(avgProjCost)) {
            document.getElementById("CurrentProjectCost").style.color = "green"
        }
        else {
            document.getElementById("CurrentProjectCost").style.color = "red"
        }

        document.getElementById("CurrentProjectActualCost").innerHTML = "$" + myProjActualCost;

        if (Number(myProjActualCost) <= Number(avgProjActualCost)) {
            document.getElementById("CurrentProjectActualCost").style.color = "green"
        }
        else {
            document.getElementById("CurrentProjectActualCost").style.color = "red"
        }

        document.getElementById("CurrentProjectWork").innerHTML = myProjWork + " hrs";

        if (Number(myProjWork) <= Number(avgProjWork)) {
            document.getElementById("CurrentProjectWork").style.color = "red"
        }
        else {
            document.getElementById("CurrentProjectWork").style.color = "green"
        }

        document.getElementById("CurrentProjectPercentComplete").innerHTML = myProjPercentCompleted + "%";

        if (Number(myProjPercentCompleted) <= Number(avgProjPercentCompleted)) {
            document.getElementById("CurrentProjectPercentComplete").style.color = "red"
        }
        else {
            document.getElementById("CurrentProjectPercentComplete").style.color = "green"
        }
    }
    else {    // The current project is not published.
        document.getElementById("CurrentProjectCost").innerHTML = "NA";
        document.getElementById("CurrentProjectCost").style.color = "blue"

        document.getElementById("CurrentProjectActualCost").innerHTML = "NA";
        document.getElementById("CurrentProjectActualCost").style.color = "blue"

        document.getElementById("CurrentProjectWork").innerHTML = "NA";
        document.getElementById("CurrentProjectWork").style.color = "blue"

        document.getElementById("CurrentProjectPercentComplete").innerHTML = "NA";
        document.getElementById("CurrentProjectPercentComplete").style.color = "blue"
    }
}

Fichier App.css

Le code suivant se trouve dans le fichier Content\App.css du projet HelloProjectODataWeb.

/*
*  File: App.css for the HelloProjectOData app.
*  Updated: 10/2/2012
*/

body
{
    font-size: 11pt;
}
h1
{
    font-size: 22pt;
}
h2
{
    font-size: 16pt;
}

/******************************************************************
Code label class
******************************************************************/

.rest 
{
    font-family: 'Courier New';
    font-size: 0.9em;
}

/******************************************************************
Button classes
******************************************************************/

.button-wide {
    width: 210px;
    margin-top: 2px;
}
.button-narrow 
{
    width: 80px;
    margin-top: 2px;
}

/******************************************************************
Table styles
******************************************************************/

.infoTable
{
    text-align: center; 
    vertical-align: middle
}
.heading_leftCol
{
    width: 20px;
    height: 20px;
}
.heading_midCol
{
    width: 100px;
    height: 20px;
    font-size: medium; 
    font-weight: bold; 
}
.heading_rightCol
{
    width: 101px;
    height: 20px;
    font-size: medium;
    font-weight: bold;
}
.row_leftCol
{
    width: 20px;
    font-size: small;
    font-weight: bold;
}
.row_midCol
{
    width: 100px;
}
.row_rightCol
{
    width: 101px;
}
.logo
{
    width: 135px;
    height: 53px;
}

Fichier SurfaceErrors.js

Le code suivant inclut une throwError fonction qui crée un Toast objet .

/*
 * Show error messages in a "toast" notification.
 */

// Throws a custom defined error.
function throwError(errTitle, errMessage) {
    try {
        // Define and throw a custom error.
        let customError = { name: errTitle, message: errMessage }
        throw customError;
    }
    catch (err) {
        // Catch the error and display it to the user.
        Toast.showToast(err.name, err.message);
    }
}

// Add a dynamically-created div "toast" for displaying errors to the user.
let Toast = {

    Toast: "divToast",
    Close: "btnClose",
    Notice: "lblNotice",
    Output: "lblOutput",

    // Show the toast with the specified information.
    showToast: function (title, message) {

        if (document.getElementById(this.Toast) == null) {
            this.createToast();
        }

        document.getElementById(this.Notice).innerText = title;
        document.getElementById(this.Output).innerText = message;

        $("#" + this.Toast).hide();
        $("#" + this.Toast).show("slow");
    },

    // Create the display for the toast.
    createToast: function () {
        let divToast;
        let lblClose;
        let btnClose;
        let divOutput;
        let lblOutput;
        let lblNotice;

        // Create the container div.
        divToast = document.createElement("div");
        let toastStyle = "background-color:rgba(220, 220, 128, 0.80);" +
            "position:absolute;" +
            "bottom:0px;" +
            "width:90%;" +
            "text-align:center;" +
            "font-size:11pt;";
        divToast.setAttribute("style", toastStyle);
        divToast.setAttribute("id", this.Toast);

        // Create the close button.
        lblClose = document.createElement("div");
        lblClose.setAttribute("id", this.Close);
        let btnStyle = "text-align:right;" +
            "padding-right:10px;" +
            "font-size:10pt;" +
            "cursor:default";
        lblClose.setAttribute("style", btnStyle);
        lblClose.appendChild(document.createTextNode("CLOSE "));

        btnClose = document.createElement("span");
        btnClose.setAttribute("style", "cursor:pointer;");
        btnClose.setAttribute("onclick", "Toast.close()");
        btnClose.innerText = "X";
        lblClose.appendChild(btnClose);

        // Create the div to contain the toast title and message.
        divOutput = document.createElement("div");
        divOutput.setAttribute("id", "divOutput");
        let outputStyle = "margin-top:0px;";
        divOutput.setAttribute("style", outputStyle);

        lblNotice = document.createElement("span");
        lblNotice.setAttribute("id", this.Notice);
        let labelStyle = "font-weight:bold;margin-top:0px;";
        lblNotice.setAttribute("style", labelStyle);

        lblOutput = document.createElement("span");
        lblOutput.setAttribute("id", this.Output);

        // Add the child nodes to the toast div.
        divOutput.appendChild(lblNotice);
        divOutput.appendChild(document.createElement("br"));
        divOutput.appendChild(lblOutput);
        divToast.appendChild(lblClose);
        divToast.appendChild(divOutput);

        // Add the toast div to the document body.
        document.body.appendChild(divToast);
    },

    // Close the toast.
    close: function () {
        $("#" + this.Toast).hide("slow");
    }
}

Étapes suivantes

Si HelloProjectOData était un complément de production à vendre dans AppSource ou à distribuer dans un catalogue d’applications SharePoint, il serait conçu différemment. Par exemple, il n’y aurait pas de sortie de débogage dans une zone de texte et probablement pas de bouton permettant d’obtenir le point de terminaison de ProjectData. Vous devez également réécrire la retrieveOData fonction pour gérer Project Web App instances qui ont plus de 100 projets.

Le complément devrait contenir des contrôles d’erreurs supplémentaires, ainsi qu’une logique permettant d’identifier et d’expliquer ou d’illustrer les cas extrêmes. Par exemple, si une instance de Project Web App a 1 000 projets d’une durée moyenne de cinq jours et d’un coût moyen de 2 400 €, et que le projet actif est le seul dont la durée est supérieure à 20 jours, la comparaison des coûts et du travail est faussée. Cela pourrait être illustré avec un graphique de fréquences. Vous pouvez ajouter des options pour afficher la durée, comparer les projets de durée similaire ou comparer les projets de services identiques ou distincts. Sinon, vous pouvez également permettre à l’utilisateur d’effectuer des choix parmi une liste de champs affichés.

Pour les autres requêtes du service ProjectData , la longueur de la chaîne de requête est limitée, ce qui affecte le nombre d’étapes qu’une requête peut effectuer à partir d’une collection parente vers un objet d’une collection enfant. Par exemple, une requête en deux étapes telle que Projects vers Tasks, puis vers un élément de tâche fonctionne, mais une requête en trois étapes telle que Projects vers Tasks vers Assignments, puis vers l’élément d’affectation risque de dépasser la longueur maximale par défaut de l’URL. Pour plus d’informations, consultez Interroger des flux OData pour les données de création de rapports Project.

Si vous modifiez le complément HelloProjectOData pour une utilisation en production, procédez comme suit.

  • Dans le fichier HelloProjectOData.html, pour de meilleures performances, remplacez la référence du projet local à office.js par la référence au réseau de distribution de contenu :

    <script src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
    
  • Réécrire la retrieveOData fonction pour activer les requêtes de plus de 100 projets. Par exemple, vous pouvez obtenir le nombre de projets avec une requête ~/ProjectData/Projects()/$count, puis utiliser l’opérateur $skip et l’opérateur $top de la requête REST pour les données de projet. Exécutez plusieurs requêtes dans une boucle, puis établissez la moyenne des données de chaque requête. Chaque requête de données de projet se présente sous la forme suivante :

    ~/ProjectData/Projects()?skip= [numSkipped]&amp;$top=100&amp;$filter=[filter]&amp;$select=[field1,field2, ???????]

    Pour plus d’informations, consultez Options de requête système OData à l’aide du point de terminaison REST. Vous pouvez également utiliser la commande Set-SPProjectOdataConfiguration dans Windows PowerShell pour remplacer la taille de page par défaut pour une requête du jeu d’entités Projects (ou n’importe lequel des 33 jeux d’entités). Voir ProjectData – Référence de service Project OData.

  • Pour déployer le complément, voir Publier votre complément Office.

Voir aussi