Office Graph API et Cortana - Chapitre 2 : Comment faire des requêtes par code à l’Office Graph

 English version / Version anglaise  

Preface

 

Bienvenue dans le deuxième chapitre de cet article dédié à l’intégration de l’Office Graph API au sein d’une application .Net Windows Phone 8.1 et Cortana. Dans le chapitre précédent, nous avons vu quelle était la syntaxe des appels web services à réaliser pour obtenir des résultats de son graphe d’entreprise. Dans ce chapitre, nous allons maintenant voir comment se connecter à l’Office Graph et effectuer des requêtes en .Net. 

Vous pouvez TELECHARGER LE CODE SOURCE complet de cette App dans le fichier ZIP tout en bas de chaque chapitre.

Cet article est publié en vue des TechDays 2015, qui auront lieux du 10 au 12 Janvier 2015 au Palais des Congrés de Paris. Cette année j'aurais la chance de coanimer la session dédiée à Office Graph au côté d'Alexandre Cipriani et Stéphane Palluet. Venez nombreux à notre session pour découvrir cet exemple et bien plus encore. 

 

 Chapitre 1 : Introduction à L’Office Graph

Chapitre 2 : Comment faire des requêtes par code à l’Office Graph ? (cet article)

Chapitre 3 : Intégration avec Cortana

 

Prérequis

 

Dans notre exemple, nous allons utiliser trois briques techniques majeures, que l’on peut représenter par le schéma suivant :

 

Détaillons un peu ces briques :

  1. ADAL (Active Directory Authentication Library)  : est une librairie pour prendre en charge l’authentification à l’Azure Active Directory, sur lequel repose Office 365. ADAL va nous apporter la flexibilité pour être capable d’authentifier l’utilisateur de façon sécurisée, sans stocker le login / mot de passe de l’utilisateur. Elle va permettre de se reposer sur les concepts d’authentification oAuth, avec des niveaux spécifiques et transparents d’accès à vos données.
  2. Office Graph API : Comme détaillé dans l’article précédent, c’est le point d’entrée des web services qui vont nous permettre de parcourir le graphe social
  3. Windows Phone 8.1 Store Apps & Cortana SDK : vont nous permettre de développer facilement une application avec une interface écrite en XAML C#, facilement compatible avec Windows 8.1. Cela va également nous permettre de définir et capter des commandes vocales nous permettant d’interroger l’Office Graph à la voix.

Pour développer l’Application, nous aurons besoin sur notre poste de travail de :

  • Visual Studio 2013
  • Visual Studio 2013 Update 4
  • Windows Phone 8.1 SDK

Ces composants sont installés avec la plupart des versions de Visual Studio, ou en version express gratuite à l’adresse suivante :

https://dev.windows.com/en-us/develop/download-phone-sdk

Dernier point important, il nous faut un Windows Phone 8.1 ou un émulateur (contenu dans la SDK) contenant Windows Phone 8.1, en français et avec le rôle Cortana activé .

 

Type de projet Visual Studio

 

Pour créer un projet Windows Phone 8.1 utilisant Cortana et l’Office Graph, vous pouvez choisir le modèle de projet de type Windows Phone Apps dans la section « Store Apps ».  Ce type d’application, apparue avec Windows 8.1 et Windows Phone 8.1 permet de créer des applications qui ne sont pas en Silverlight, bien qu’écrites en XAML :

 

 

Authentification avec ADAL

 

Pour être capable d’effectuer des requêtes sur l’Office Graph, il faut au préalable s’authentifier. Pour cela, il nous faut impérativement obtenir un jeton d’authentification auprès d’Azure Active Directory, que nous pourrons fournir à nos requêtes.

Avec Azure Active Directory, il est possible de créer facilement une application capable de bénéficier des systèmes d’authentification existants de la plateforme. Par exemple, nous n’aurons pas à stocker le login / mot de passe de l’utilisateur : nous laisserons Azure nous certifier et prendre en charge l’authentification.

Le processus d’authentification avec ADAL peut être résumé par le schéma suivant :

 

Installer ADAL dans votre solution Visual Studio

 

Pour simplifier la tâche du développeur, Microsoft a créé ADAL (Active Directory Authentication Library), qui est une libraire de programmation disponible sur de très nombreuses plateformes (.Net, Javascript, Cordova, Xamarin, etc.). Cette librairie va permettre d’ajouter simplement un processus d’authentification dans notre application.

Pour utiliser ADAL, il faut au préalable importer la librairie dans notre solution. Le processus est très simple grâce au système des nuggets de Visual Studio.

Le package d’ADAL est disponible à l’adresse suivante :

https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/

Vous pouvez l’installer dans votre solution Visual Studio grâce à la commande suivante :

PM>Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory -Version 2.13.112191810

 

Déclarer l’Application dans Azure

 

Pour être capable de communiquer avec Azure Active Directory, il faut au préalable enregistrer son application dans notre tenant Azure. Déclarer une application dans Azure revient à enregistrer les informations de notre application et obtenir en retour une clé, dite « ClientId » qui nous servira au sein de notre application.

Ceci peut être fait de plusieurs manières :

La 2ieme technique est relativement simple. Pour l’utiliser, vous devez simplement télécharger les extensions développeurs Office 365 pour Visual Studio 2013, téléchargeables ici :

https://aka.ms/OfficeDevToolsForVS2013

Une fois installés, il vous suffit de faire un clic droit sur votre solution Visual Studio, puis de cliquer sur “Ajouter”, puis “Service Connecté” :

 

On peut alors accéder au « Services Manager » qui permet de gérer les services Office 365. Vous pouvez vous loguer avec votre compte Office 365 pour  enregistrer votre App sur votre tenant d’entreprise :

 

Avec cet assistant, on peut sélectionner les niveaux d’autorisation nécessaire à l’application sur les différentes ressources Office 365. Pour lire l’Office Graph, nous devons au minimum posséder les permissions de lecture sur les « My Files » et les « Sites ». Il suffit de cliquer sur le nom du service, puis de sélectionner le droit de lecture :

 

 

 

Lorsque vous validez la fenêtre, Visual Studio va se charger d’enregistrer votre application dans Azure, comme vous le feriez via l’interface de management Azure. Il va également insérer les clés nécessaires à votre application (comme le ClientId ou l’autorité d’authentification) dans des paramètres. Dans le cas d’une Windows Phone 8.1 Store App, les paramètres sont automatiquement insérés dans le fichier App.xaml, en tant que ressource de l’application, comme par exemple :

 <Application.Resources>
    <x:String x:Key="ida:ClientID">b53a731a-2638-49a9-8122-1bc9ea233995</x:String>
    <x:String x:Key="ida:AuthorizationUri">https://login.windows.net</x:String>
...
</Application.Resources>

 

Gérer la connexion en C# .Net

 

Maintenant que nous avons vu comment enregistrer l’App, nous sommes prêts pour implémenter le système d’authentification ADAL dans nos classes .Net.

Pour utiliser ADAL dans nos classes, nous devons inclure les namespaces suivant :

 using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Windows.Security.Authentication.Web;

Pour ajouter à une page la prise en charge de l’authentification, nous devons préparer cette page à gérer le processus de reprise. Suite à l’authentification avec Azure, nous allons avoir un retour vers notre application qui pourra continuer les traitements.  Pour implémenter simplement ce principe, nous pouvons créer l’interface IWebAuthenticationContinuable, et il suffira de faire hériter notre page de cette interface :

 /// <summary>
/// Implement this interface if your page invokes the web authentication
/// broker
/// </summary>
interface IWebAuthenticationContinuable
{
    /// <summary>
    /// This method is invoked when the web authentication broker returns
    /// with the authentication result
    /// </summary>
    /// <param name="args">Activated event args object that contains returned authentication token</param>
    void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args);
}
public sealed partial class MainPage : Page, IWebAuthenticationContinuable 
{
…

Au sein de notre page, l’authentification va être prise en charge grâce à la classe AuthenticationContext, qui permet de créer un contexte d’authentification vis-à-vis d’une autorité et, de négocier le processus d’acquisition des jetons.

 AuthenticationContext ac = null;

Pour créer le contexte, nous aurons besoin de fournir à ADAL l’adresse de redirection correspondant à notre application. Cette adresse est une adresse spécifique à l’App en http, mais peut être facilement acquise par la méthode GetCurrentApplicationCallbackUri de la classe WebAuthenticationBroker comme cela :

 Uri RedirectUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri();

Nous devrons également fournir l’autorité d’authentification. Avec Office 365 et Azure Active Directory, l’adresse est du type « https://login.windows.net/<votre nom de tenant> » :

 string Authority = "https://login.windows.net/yourtenantdomain.com";

Si vous avez utilisez l’Office 365 SDK pour référencer votre application, vous pouvez récupérer la clé de ressource avec la syntaxe suivante :

 string Authority = App.Current.Resources["ida:AuthorizationUri"].ToString() + "/ yourtenantdomain.com ";

Le dernier paramètre à fournir pour l’authentification est la clé cliente de votre application que nous avons générée dans la section précédente. Cette clé est sous la forme d’un GUID :

 string ClientID = "[your-app-id as for example ‘b6ea739a-2608-4919-8123-12asd233b65’";

Si vous avez utilisez l’Office 365 SDK pour référencer votre application, vous pouvez récupérer l’identifiant client depuis la clé de ressources avec la syntaxe suivante :

  string ClientID = App.Current.Resources["ida:ClientID"].ToString();

Pour initialiser le contexte, il faut utiliser la méthode CreateAsync sur l’évènement OnNavigatedTo de la page :  

 /// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached.
/// This parameter is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
      ac = AuthenticationContext.CreateAsync(Authority).GetResults();
}

Il nous faut également implémenter la méthode permettant la reprise de l’activité suite à la redirection. Pour cela, il faut ajouter le code suivant :

 public async void ContinueWebAuthentication(Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs args)
{
      await ac.ContinueAcquireTokenAsync(args); 
}

Créons maintenant la méthode Login, qui sera appelée lorsque l’utilisateur cliquera sur le bouton « Connexion » de notre interface. La méthode Login va tout d’abord essayer d’acquérir le token d’authentification de façon « silencieuse ». Cela veut dire que nous essayons de nous authentifier sans appeler l’interface de connexion. Si l’utilisateur s’est déjà connecté, nous aurons son jeton en cache et pourrons lui donner accès directement. Ceci est possible grâce à l’appel à la méthode AcquireTokenSilentAsync de l’objet AuthenticationContext.

 private async void Login()
{
      AuthenticationResult result = await ac.AcquireTokenSilentAsync(Resource, ClientID);
      if (result != null && result.Status == AuthenticationStatus.Success)
          LoadApp(result);
      else
          ac.AcquireTokenAndContinue(Resource, ClientID, RedirectUri, LoadApp);
}

private void LoadApp(AuthenticationResult ar)
{
     var AuthToken = ar.AccessToken;
     this.Frame.Navigate(typeof(CortanaPage), AuthToken);
}

 

Si le résultat de l’authentification n’est pas concluant, nous appellerons la méthode AcquireTokenAndContinue qui va rediriger l’utilisateur vers l’interface graphique d’authentification de son tenant. L’expérience de connexion va se dérouler en 2 à 3 temps : saisie du login, saisie du mot de passe (séparé du login si l’utilisateur utilise une redirection SSO), acceptation des conditions d’utilisation, puis enfin redirection vers notre application :

 

Lorsque l’identification est terminée, le résultat de l’authentification (via la classe AuthenticationResult) contient le jeton d’authentification via la propriété AccessToken. Nous allons pouvoir utiliser de jeton dans nos requêtes REST de l’office Graph. 

 

Faire une requête sur l’Office Graph en .Net en XML

 

Maintenant que nous avons récupéré notre jeton d’authentification, nous allons pouvoir lancer des requêtes http sur l’Office Graph. 

Pour cela, ajouter la prise en charge des namespaces suivants :

 using System.Net.Http;
using System.Net.Http.Headers;

 

Dans cet exemple, nous allons utiliser la classe HttpClient pour effectuer des requêtes. Prenons quelques exemples simples.

 

Exemple 1 : retrouver les informations sur le manager de l’utilisateur en XML

 

Pour rechercher le manager d’un utilisateur, nous pouvons faire la requête suivante en fournissant le jeton d’authentification. Après avoir lu le résultat, on peut facilement obtenir la liste des résultats et récupérer la colonne « DocId » propre aux acteurs de l’Office Graph :

 String query = "https://<your-tenant-url>/_api/search/ query?Querytext=%27*%27&Properties=%27GraphQuery:ACTOR(ME\\,action\\:1013)%27&SourceId=%27b09a7990-05ea-4af9-81ef-edfab16c4e31%27&SelectProperties=%27UserName,DocId%27";
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/atom+xml"));
HttpResponseMessage response = await httpClient.GetAsync(query);
if (response.IsSuccessStatusCode)
{
     Stream resultStream = response.Content.ReadAsStreamAsync().Result;

      // process response..
      XDocument oDataXML = XDocument.Load(resultStream, LoadOptions.None);
      XNamespace atom = "https://www.w3.org/2005/Atom";
      XNamespace d = "https://schemas.microsoft.com/ado/2007/08/dataservices";
      XNamespace m = "https://schemas.microsoft.com/ado/2007/08/dataservices/metadata";

      List<XElement> items = oDataXML.Descendants(d + "query")
                                             .Elements(d + "PrimaryQueryResult")
                                             .Elements(d + "RelevantResults")
                                             .Elements(d + "Table")
                                             .Elements(d + "Rows")
                                             .Elements(d + "element")
                                             .ToList();
      var searchResults = from item in items
         select new
         {
         DocId = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "DocId").Parent.Element(d + "Value").Value
         };

}

 

Dans l’exemple précédent, searchResults contient la liste des acteurs retournés par la requête.

 

Exemple 2 : retrouver tous les documents de l’utilisateur en XML

 

Essayons maintenant de remonter tous les éléments populaires autour de nous, comme c’est le cas sur la page d’accueil de Delve. La méthode est identique à l’exemple précédent, mais cette fois-ci nous allons exploiter les propriétés liés aux documents, comme le titre, le chemin, l’extension, le site parent, etc.

 string query = "https://<your-tenant-url>/_api/search/query?QueryTemplate=%27((NOT%20HideFromDelve%3ATrue)%20AND%20(FileExtension%3Adoc%20OR%20FileExtension%3Adocx%20OR%20FileExtension%3Appt%20OR%20FileExtension%3Apptx%20OR%20FileExtension%3Axls%20OR%20FileExtension%3Axlsx%20OR%20FileExtension%3Apdf%20OR%20ContentTypeId%3A0x010100F3754F12A9B6490D9622A01FE9D8F012*%20OR%20contentclass%3AExternalLink))%27&Properties=%27TitleBasedSummaries%3Atrue,IncludeExternalContent%3Atrue,GraphQuery%3Aand(actor(me%5C%2Caction%5C%3A1021)%5C%2Cactor(me%5C%2Cor(action%5C%3A1021%5C%2Caction%5C%3A1036%5C%2Caction%5C%3A1037%5C%2Caction%5C%3A1039%5C%2Caction%5C%3A1052%5C%2Caction%5C%3A1048))),GraphRankingModel%3Aaction%5C%3A1021%5C%2Cweight%5C%3A1%5C%2CedgeFunc%5C%3Aweight%5C%2CmergeFunc%5C%3Amax%27&SelectProperties=%27Author,AuthorOwsUser,ContentClass,ContentTypeId,DefaultEncodingURL,DocId,DocumentPreviewMetadata,Edges,EditorOwsUser,FileExtension,FileType,HitHighlightedProperties,HitHighlightedSummary,LastModifiedTime,LikeCountLifetime,ListID,ListItemID,MediaDuration,OriginalPath,Path,PictureThumbnailURL,PrivacyIndicator,Rank,SPWebUrl,SecondaryFileExtension,ServerRedirectedPreviewURL,ServerRedirectedURL,SitePath,SiteTitle,Title,ViewCountLifetime,siteID,uniqueID,webID%27&RankingModelId=%270c77ded8-c3ef-466d-929d-905670ea1d72%27&RowLimit=36&ClientType=%27PulseWeb%27&BypassResultTypes=true&EnableQueryRules=false&ProcessBestBets=false&ProcessPersonalFavorites=false";
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/atom+xml"));
HttpResponseMessage response = await httpClient.GetAsync(query);
if (response.IsSuccessStatusCode)
{
     Stream resultStream = response.Content.ReadAsStreamAsync().Result;

      // process response..
      XDocument oDataXML = XDocument.Load(resultStream, LoadOptions.None);
      XNamespace atom = "https://www.w3.org/2005/Atom";
      XNamespace d = "https://schemas.microsoft.com/ado/2007/08/dataservices";
      XNamespace m = "https://schemas.microsoft.com/ado/2007/08/dataservices/metadata";

      List<XElement> items = oDataXML.Descendants(d + "query")
                                             .Elements(d + "PrimaryQueryResult")
                                             .Elements(d + "RelevantResults")
                                             .Elements(d + "Table")
                                             .Elements(d + "Rows")
                                             .Elements(d + "element")
                                             .ToList();

            var graphResults = from item in items
    select new 
    {
        Title = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "Title").Parent.Element(d + "Value").Value,
        Author = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "Author").Parent.Element(d + "Value").Value,
        HitHighlightedSummary = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "HitHighlightedSummary").Parent.Element(d + "Value").Value,
        Path = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "Path").Parent.Element(d + "Value").Value,
        Ext = "Assets/Ext/" + item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "SecondaryFileExtension").Parent.Element(d + "Value").Value + ".png",
        ViewCount = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "ViewCountLifetime").Parent.Element(d + "Value").Value,
        ServerRedirectedURL = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "ServerRedirectedURL").Parent.Element(d + "Value").Value,
        ServerRedirectedPreviewURL = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "ServerRedirectedPreviewURL").Parent.Element(d + "Value").Value,
        SiteTitle = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "SiteTitle").Parent.Element(d + "Value").Value,
        UniqueID = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "uniqueID").Parent.Element(d + "Value").Value,
        SiteID = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "siteID").Parent.Element(d + "Value").Value,
        SiteUrl = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "SPWebUrl").Parent.Element(d + "Value").Value,
        WebID = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "webID").Parent.Element(d + "Value").Value
    };
}

 

Gestion des aperçus des documents

 

Dans l’interface de Delve, la plupart des documents affichés disposent de vignettes miniatures présentant un aperçu visuel de l’élément. Développer une telle fonctionnalité « from scratch » pourrait être  fastidieux, mais une fois encore l’API Office Graph vient nous aider.

Pour générer une miniature d’un document au format PNG, on peut appeler un service web qui va nous générer une image lorsque cela est possible. Il nous suffit d’appeler l’adresse suivante avec les paramètres désignant le fichier (UniqueID, WebID et SiteID) : 

https://<your-tenant-url>/_layouts/15/getpreview.ashx?guidFile=<UniqueID>&guidSite=<SiteID>&guidWeb=<WebID>&metadatatoken=230x241x2&ClientType=DelveWeb&size=small"

Depuis une application Windows Phone, on peut facilement faire une requête http pour télécharger l’image, en fournissant là encore le jeton d’authentification :  

 var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken);
var contentBytes = await httpClient.GetByteArrayAsync(DocumentPreviewImage);

Nous pourrions ensuite par exemple afficher cette image dans un contrôle de type Image en écrivant le contenu de l’image dans un objet de type BitmapImage :

 var ims = new InMemoryRandomAccessStream();
var dataWriter = new DataWriter(ims);
dataWriter.WriteBytes(contentBytes);
await dataWriter.StoreAsync();
ims.Seek(0);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(ims);

 

Restitution des résultats dans une ListBox

 

Maintenant que nous avons vu comment récupérer les résultats de l’Office Graph, ainsi que les miniatures des documents, nous pouvons les afficher sur une page XAML.

Pour cela, nous avons utilisé dans cet exemple un contrôle ListBox, permettant d’afficher les résultats sous forme de liste, les uns en dessous des autres.

Pour afficher les champs des items retournés par l’Office Graph, on peut utiliser par exemple la commande {{Binding Title}} pour automatiquement afficher la propriété correspondante de l’objet.

Nous pouvons également utiliser le contrôle MenuFlyout pour afficher un menu contextuel sur chaque élément sélectionner longtemps.

Le code de notre affichage XAML pourrait être formé de la façon suivante :

 <!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">
    <Grid.Resources>
        <DataTemplate x:Name="resultListTemplate">
            <StackPanel Holding="StackPanel_Holding" >
                <FlyoutBase.AttachedFlyout>
                    <MenuFlyout>
                        <MenuFlyoutItem Text="Ouvrir" />
                        <MenuFlyoutItem Text="Ouvrir le site" />
                        <MenuFlyoutItem Text="Envoyer par email" />
                        <MenuFlyoutItem Text="Partager sur Yammer" />
                    </MenuFlyout>
                </FlyoutBase.AttachedFlyout>
                <Grid Loaded="Grid_Loaded" Background="#FFFFFEFE" Margin="0,0,0,10">

                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <TextBlock Grid.Row="0" Grid.Column="0" TextWrapping="Wrap"
                                    Text="{Binding Title}" 
                                    Foreground="Black" FontSize="30" Height="Auto" Margin="5"  />

                    <Image  Grid.Row="1" Grid.Column="0"
                                    Stretch="UniformToFill" 
                                    Source="{Binding DocumentPreviewImage}" Loaded="Image_Loaded" Margin="5" />

                    <TextBlock Grid.Row="2" Grid.Column="0" TextWrapping="Wrap"
                                    Text="{Binding Author}" 
                                    Foreground="Gray" FontSize="20" Height="Auto" Margin="5"/>

                    <StackPanel Grid.Row="3" Grid.Column="0" Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
                        <TextBlock
                                    Text="{Binding ViewCount}"
                                    Foreground="Gray" FontSize="20" />
                        <TextBlock Text=" vues" Foreground="Gray" FontSize="20"></TextBlock>

                    </StackPanel>

                    <StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" Margin="5">
                        <Image 
                                    Width="32" Height="32" Stretch="UniformToFill" 
                                    Source="{Binding Ext}"/>
                        <TextBlock TextWrapping="Wrap"
                                    Text=" - " 
                                    Foreground="Gray" FontSize="20" Height="Auto"/>
                        <TextBlock
                                    Text="{Binding SiteTitle}"
                                    Foreground="Blue" FontSize="20" />

                    </StackPanel>

                </Grid>
            </StackPanel>

        </DataTemplate>
    </Grid.Resources>

    <!-- actual list content goes here -->
    <ListBox Name="ResultsList" Grid.Row="0" 
                    ItemTemplate="{StaticResource resultListTemplate}" 
                    ItemsSource="{Binding}" Background="#FF0086D8" FontFamily="Global User Interface">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>

</Grid>

 

Avec cet exemple de code, nous sommes capables d’afficher les éléments remontés sous la forme de vignettes ressemblant à l’interface de Delve. Lorsque l’utilisateur cliquera longtemps sur un élément le menu contextuel permettra d’effectuer certaines actions sur l’élément, comme le montrent les captures suivantes :

 

Si nous cliquons sur les éléments du menu, on peut :

  • Ouvrir le document en mode web (ouverture d’Internet Explorer à la page correspondant à l’adresse du document avec Office Online)
  • Ouvrir avec Internet Explorer le site contenant le document
  • Créer un mail  à envoyer contenant l’adresse du document pour le partager
  • Partager le lien du document au sein d’un groupe Yammer

Pour partager du contenu dans Yammer, l’opération est en réalité aussi simple que les autres. En effet, Yammer possède une adresse dédiée aux applications permettant de recommander du contenu.

Pour afficher une fenêtre de partage Yammer, il nous suffit d’ouvrir une page avec l’adresse formatée comme suit :

https://www.yammer.com/messages/new/?status=<url-encodée-du-lien-a-partager>

Par exemple, le partage de l’adresse bing.com va nous afficher la fenêtre de partage illustrée ci-dessous, nous permettant de partager le lien avec des groupes et des collègues :

https://www.yammer.com/messages/new/?status=http%3A%2F%2Fwww.bing.com%2F

 

 

Nous avons vu dans ce chapitre comment interroger l’Office Graph en C# .Net au sein d’une application Windows Phone 8.1. Nous sommes maintenant capables d’effectuer des requêtes permettant d’afficher des éléments du graph avec les propriétés essentielles des documents ainsi qu’une image miniature. Il ne nous reste donc plus qu’à « connecter » nos appels à l’Office Graph avec des commandes vocales de Cortana !!

Rendez-vous dans le 3ieme et dernier chapitre de cet article pour lire la suite :

Chapitre 3 : Intégration avec Cortana

MyOfficeGraph-20150113.zip