Sharepoint 2010- FBA LDAP – ODATA et Windows Phone 7

Posted on

Une autre façon de récupérer des données de notre plateforme Sharepoint 2010 est de passer par les services ODATA. En effet nous allons voir une autre façon d’implémenter une authentification relative au FBA LDAP sans passer par un ajout de référence ou de la modification du fichier web.config. Cela permettra d’avoir un code un peu plus portable et facilitant la création d’une DLL de connexion.

 

Avant tout veiller à ce que les services ODATA soit bien effectif. Si vous n’avez pas fait les mise à jour de la plateforme SP2010, vous devriez rencontrer une erreur du type lorsque vous voulez accéder à l’adresse suivante : http://%5Byourdomain%5D/_vti_bin/ListData.svc

"Could not load type

'System.Data.Services.Providers.IDataServiceUpdateProvider'

from assembly 'System.Data.Services, Version=3.5.0.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089'."

Il vous faudra alors installer la mise à jour ADO.Net Data Services Update for .NET Framework 3.5 SP1 (x64). 

Une fois cela réglé passons au code :

Etape 1 – XAML

Je créer un nouveau projet de type PANORAMA  pour plateforme 7.1. Et je modifie le binding du premier item “premier élément” en mettant les bindings suivants :

<!--Élément un de panorama-->
 <controls:PanoramaItem Header="premier élément">
   <!--Liste double trait avec habillage du texte-->
  <ListBox Margin="0,0,-12,0" ItemsSource="{Binding Items}" Name="MyListBox">
   <ListBox.ItemTemplate>
    <DataTemplate>
     <StackPanel Margin="0,0,0,17" Width="432" Height="78">
     <TextBlock Text="{Binding Title}" TextWrapping="Wrap" 
      Style="{StaticResource PhoneTextExtraLargeStyle}" FontWeight="Bold" />
     <TextBlock Text="{Binding Description}" TextWrapping="Wrap" Margin="12,-6,12,0" 
       Style="{StaticResource PhoneTextSubtleStyle}"/>
     </StackPanel>
   </DataTemplate>
    </ListBox.ItemTemplate>
    </ListBox>
 </controls:PanoramaItem>

Etape 2 – Classe d’authentification

 

#region Using REF
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Xml; 
#endregion

namespace PanoramaApp1
{
    //Delegate d'authentification
    public delegate void Authenticated();
    public class SPAuthentication
    {
        private readonly string username = "demofba";
        private readonly string password = "c#mws2008";

        public CookieContainer CookiJar { get; private set; }

        public SPAuthentication()
        {
            CookiJar = new CookieContainer();
        }
        //Methode d'authentification
        /// <summary>
        /// Besoin du service authentication.asmx
        /// du cookieContainer et des appels asynchrones
        /// </summary>
        public void Authenticate()
        {
          
            var authWebService = "http://crm2011/sites/fba/_vti_bin/authentication.asmx";
            var authWebRequest = (HttpWebRequest)HttpWebRequest.Create(authWebService);
            authWebRequest.Method = "POST";
            authWebRequest.ContentType = "text/xml; charset=utf-8";
            authWebRequest.Headers["SOAPAction"] = "http://schemas.microsoft.com/sharepoint/soap/Login";
            authWebRequest.CookieContainer = CookiJar;

            authWebRequest.BeginGetRequestStream(new AsyncCallback(AuthenticationRequestCallback),
                                                  authWebRequest);
        }

        //Méthode de Parsing de l'enveloppe SOAP
        private void AuthenticationRequestCallback(IAsyncResult asyncResult)
        {
            var request = (HttpWebRequest)asyncResult.AsyncState;
            var soapEnvelope = 
                     string.Format(@"<?xml version=""1.0"" encoding=""utf-8""?>
                        <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" 
                          xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" 
                          xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
                        <soap:Body>
                        <Login xmlns=""http://schemas.microsoft.com/sharepoint/soap/"">
                          <username>{0}</username>
                          <password>{1}</password>
                          </Login>
                       </soap:Body>
                     </soap:Envelope>", username, password);
            var encoder = new UTF8Encoding();

            using (var reqStream = request.EndGetRequestStream(asyncResult))
            {
                var body = encoder.GetBytes(soapEnvelope);
                reqStream.Write(body, 0, body.Length);
                reqStream.Close();
            }

            request.BeginGetResponse(new AsyncCallback(AuthenticationResponseCallback), request);
        }

        //Méthode de réponse à l'appel SOAP et Jeton d'authentification
        private void AuthenticationResponseCallback(IAsyncResult asyncResult)
        {
            var request = (HttpWebRequest)asyncResult.AsyncState;
            var response = (HttpWebResponse)request.EndGetResponse(asyncResult);
            var errorCode = string.Empty;
            using (var responseStream = new StreamReader(response.GetResponseStream()))
            {
                var xResult = XmlReader.Create(responseStream);
                xResult.ReadToDescendant("ErrorCode");
                errorCode = xResult.ReadElementContentAsString();
                xResult.Close();
                responseStream.Close();
            }

            if (!string.IsNullOrEmpty(errorCode) && (errorCode.ToLowerInvariant() == "noerror"))
            {
                OnAuthenticationTokenSet();
            }
        }

        public event Authenticated AuthenticationTokenSet;
        private void OnAuthenticationTokenSet()
        {
            if (AuthenticationTokenSet != null)
                AuthenticationTokenSet();
        }
    }
}

Etape 3 – Classes de liste DEMO

 

namespace PanoramaApp1
{
    //Nom de ma liste dans Sharepoint
    public class Demo
    {
        public string Title { get; set; }
        public string Description { get; set; }

    }
}

Etape 4 – Classe Mère DEMOS

 

#region UsingREF
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml.Linq; 
#endregion

namespace PanoramaApp1
{
    public class Demos: INotifyPropertyChanged
    {
        #region Variables
            private CookieContainer token;
            private HttpWebRequest _httpWebRequest;
            private readonly string url = "http://crm2011/sites/fba/_vti_bin/listdata.svc";
            private readonly string query = "/Demo";
            private IEnumerable<Demo> mDemoList; //Listes d'objets Demo
        #endregion

        public IEnumerable<Demo> DemoList
        {
            get { return mDemoList; }
            private set
            {
                mDemoList = value;
                NotifyPropertyChanged("DemoList");
            }
        }

        //Besoin du jeton sinon erreur
        public Demos(CookieContainer authToken)
        {
            token = authToken;
        }
        //Méthode de récupération de la liste
        public void GetLists()
        {

            _httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(new Uri(url + string.Format(query)));
            _httpWebRequest.CookieContainer = token;
            _httpWebRequest.Accept = "application/atom+xml";
            _httpWebRequest.BeginGetResponse(new AsyncCallback(asyncCallback), _httpWebRequest);
        }

        //Appel Aync pour lire le flux
        private void asyncCallback(IAsyncResult asyncResult)
        {
            var request = (HttpWebRequest)asyncResult.AsyncState;
            var response = (HttpWebResponse)request.EndGetResponse(asyncResult);
            using (var sr = new StreamReader(response.GetResponseStream()))
            {
                var result = sr.ReadToEnd();
                ParseResults(result);
            }
        }
        //Méthode qui Parse le flux lu
        private void ParseResults(string result)
        {

            var atomElement = XElement.Parse(result);
            XNamespace atom = "http://www.w3.org/2005/Atom";
            XNamespace dataServices = "http://schemas.microsoft.com/ado/2007/08/dataservices";
            XNamespace metaData = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
            //Element XML
            var entries = 
                from entry in atomElement.Descendants(atom + "entry")
                let description = entry.Element(atom + "content").
                 Element(metaData + "properties").Element(dataServices + "Description").Value
                 select new Demo()
                    {
                    Title = entry.Element(atom + "title").Value,
                    Description = description
                   };
            DemoList = entries;//affection de chaque entrée dans la liste
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
       
    }
}

Etape 5 – MainPage

 

using System.ComponentModel;
using System.Windows;
using Microsoft.Phone.Controls;

namespace PanoramaApp1
{
    public partial class MainPage : PhoneApplicationPage
    {
        #region MVVM
        //// Constructeur
        //public MainPage()
        //{
        //    InitializeComponent();

        //    // Affecter l'exemple de données au contexte de données du contrôle ListBox
        //    DataContext = App.ViewModel;
        //    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        //}

        //// Charger les données pour les éléments ViewModel
        //private void MainPage_Loaded(object sender, RoutedEventArgs e)
        //{
        //    if (!App.ViewModel.IsDataLoaded)
        //    {
        //        App.ViewModel.LoadData();
        //    }
        //} 
        #endregion


        #region Classes
        private SPAuthentication auth;
        private Demos thedemos; 
        #endregion

        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            auth = new SPAuthentication();
            auth.AuthenticationTokenSet += new Authenticated(auth_AuthenticationTokenSet);//récupération du jeton
            auth.Authenticate();//authentification
        }

        //La classe DEMOS à besoin du cookie pour lancer ses appels ODATA sinon erreur
        void auth_AuthenticationTokenSet()
        {
            thedemos = new Demos(auth.CookiJar);
            thedemos.PropertyChanged += new PropertyChangedEventHandler(propertyChanged);
            thedemos.GetLists();

        }

        //Affichage dans une listbox
        void propertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.ToLowerInvariant() == "demolist")
            {
                Dispatcher.BeginInvoke(() => { MyListBox.ItemsSource = thedemos.DemoList; });
            }

        }
    
    }
}

Résultat final :

image

Partant de ce code nous pouvons créer une DLL d’authentification très simplement. L’aspect MVVM n’est abordé, il est simplement décommenté dans le MainPage. A partir de cet exemple plusieurs opportunité s’ouvre à nous.

Voici la solution dans un fichier zip : PanoramaApp1

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s