Archives du mot-clé type de contenu externe

Ajout d’un filtre de recherche pour un type de contenu externe SharePoint 2013


Cet article fait partie d’une série concernant la mise en oeuvre de liste externes et de workflows avec SharePoint. Les étapes seront passées en revue :

_______________________________________________________________________________________________

Lors du déploiement d’un type de contenu externe par programmation, dans une colonne de données externes, si aucun filtre de recherche n’est créé, il sera compliqué voire impossible de sélectionner certains éléments. En effet si le nombre d’élément est trop grand tous ne peuvent être disponible à la sélection.

external column sharepoint

Un filtre de recherche est alors indispensable. Celui-ci peut être créé via SharePoint Designer avec certaines limites ou par code. C’est cette 2e solution qui fait l’objet de cet article.

Pour cela prenons l’exemple d’une recherche par nom de personne. Il faut :

  • ajouter un nouveau paramètre sur la méthode de recherche de liste
  • ajouter un descripteur de filtre sur cette méthode

1 – Sélectionner l’entité BDC et la méthode ReadList.

méthode BDC sharepoint

2 – Ajouter un paramètre pour le nom de type String.

descripteur de filtre sharepoint

3 – Ajouter un descripteur de filtre (étendre ce noeud puis cliquer sur ajouter), le renommer en « NomPersonne » et le typer « Comparison ». Sélectionner « NomPersonne » pour filtre associé dans la fenêtre propriétés.

descripteur de filtre

Les types disponibles sont les suivants, on voit dans ce cas qu’il va surement falloir créé d’autres fitres afin de fournir un mécanisme complet aux utilisateurs :

Types de filtres pris en charge par le service Business Data Connectivity

4 – Modifier la méthode avec le code suivant :

public static IEnumerable<PERSONNE> ReadList(string Nom)
        {
            string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
            AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

            if (string.IsNullOrEmpty(Nom))
            {
                IEnumerable<PERSONNE> pList =
                from p in dataContext.PERSONNE.Take(1000)
                select p;
                return pList;
            }
            else
            {
                IEnumerable<PERSONNE> pList =
                    from p in dataContext.PERSONNE
                    where p.LastName.Contains(Nom)
                    select p;
                return pList;
            }
        }

5 – déployer le filtre de recherche et le tester.

search filter external content type sharepoint 2013

Créer une colonne de données externes et la référencer dans une liste


Cet article fait partie d’une série concernant la mise en oeuvre de liste externes et de workflows avec SharePoint. Les étapes seront passées en revue :

_______________________________________________________________________________________________

Dans certains cas il peut être nécessaire d’utiliser un lien vers un type de contenu externe dans un autre type de contenu. Cela permet, entre autre, de créer un formulaire avec une liste de choix dynamique liée à un système tiers.

L’idée est la même que pour un champ de recherche (lookupField) mais pointant sur une source de données externes.

Par exemple il est possible d’utiliser le type de contenu externe « Personne » précédemment créé dans une liste d’annonce.

Pour cela il existe 2 méthodes :

  • Mode déclaratif
  • Utilisation du modèle objet

Avant de les passer en revue, commençons par voir ce que cela donne dans l’IHM native SharePoint 2013.

Via l’IHM

La procédure est faisable via l’IHM en ajoutant une colonne de type « Données Externe » dans une liste existante (vous ne verrez pas ce type de colonne lors de la création d’un type de contenu).

1 – Par exemple dans une liste d’annonce, ajouter une colonne de type « Données Externes ».

colonne de données externe

Sélectionner le type de contenu et le(s) champ(s) à afficher.

type de contenu externe sharepoint 2013

2 – Une fois la colonne ajoutée, créer une nouvelle annonce dans la liste.

colonne de données externe sharepoint 2013

3 – Sélectionner la personne liée à votre annonce dans la popup.

colonne de données externe sharepoint 2013

A ce moment-là si la liste de personne est trop longue et que vous n’avez aucun filtre de recherche vous ne pourrez pas sélectionner une personne au-delà de celles qui sont affichées (200 résultats dans mon cas). Il faudra donc créer un filtre de recherche (article à venir) via Visual Studio ou SharePoint Designer permettant de paginer les résultats mais aussi de les filtrer en fonction des noms / prénoms par exemple.

Mode déclaratif

Cette méthode n’est pas privilégiée, en effet, aucun wizzard Visual studio n’offre ce type de fonctionnalité. Nous allons tout de même la passer en revue. Pour tester cette méthode il faudra suivre les étapes suivantes :

1 – Créer un nouveau modèle + instance de liste de type « Annonces » via Visual Studio 2012.

liste sharepoint 2013

2 – Modifier le fichier schema.xml de ce modèle de liste afin d’ajouter de nouveaux champs à la section « Fields ». Et là cela devient complexe car il n’existe pas de règle simple concernant les propriétés des champs à créer. Afin de simplifier la procédure nous allons récupérer le code XML de la colonne « Personne » créée via l’IHM (cf. chapitre précédent) avec SharePoint Manager 2013.

–          Retrouver sa liste cible dans son site dans SharePoint Manager 2013

sharepoint manager 2013

–          Retrouver les colonnes permettant de constituer la colonne de données externe « Personne » :

  • Personne : Champs de type BusinessData qui est la colonne de données externe en elle-même
  • Personne_ID : champs caché qui permet de créer un pointeur vers la personne sélectionnées
  • Personne : FirstName : champs en lecture seule qui affiche le nom de la personne sélectionnée dans la liste
  • Personne : LastName : champs en lecture seule qui affiche le prénom de la personne sélectionnée dans la liste

sharepoint manager 2013

–          Récupérer le code XML de ces champs via l’onglet « Schema XML »

sharepoint manager 2013

–          Copier puis coller l’XML pour ces 3 champs dans la section <Fields> du schema.xml du votre template de liste.

A titre d’information voici le code XML des champs récupéré :

<Field Type="BusinessData" DisplayName="Personne" Required="FALSE" EnforceUniqueValues="FALSE" ID="{f4194a0c-b0d6-4f12-b3e9-9ade42f2cb6d}" SourceID="{11411064-1db6-4071-ada7-b87308d09c5f}" StaticName="Personne" BaseRenderingType="Text" Name="Personne" ColName="nvarchar3" RowOrdinal="0" SystemInstance="AdventureWorksPerson" EntityNamespace="SPAlex.Labs.ContentTypes.AdventureWorksPerson" EntityName="Personne" BdcField="BusinessEntityID" Profile="" HasActions="True" SecondaryFieldBdcNames="10%209%20FirstName%20LastName%205" RelatedField="Personne_ID" SecondaryFieldWssNames="32%2031%20Personne%5Fx003a%5F%5Fx0020%5FFirstName%20Personne%5Fx003a%5F%5Fx0020%5FLastName%206" RelatedFieldBDCField="" RelatedFieldWssStaticName="Personne_ID" SecondaryFieldsWssStaticNames="32%2031%20Personne%5Fx003a%5F%5Fx0020%5FFirstName%20Personne%5Fx003a%5F%5Fx0020%5FLastName%206" AddFieldOption="AddToDefaultContentType, AddFieldToDefaultView" Version="1" />
      <Field Type="Note" DisplayName="Personne_ID" Hidden="TRUE" ReadOnly="TRUE" BdcField="Personne_ID" ID="{c639810f-4582-4807-9cb9-5fa05e33a77a}" SourceID="{11411064-1db6-4071-ada7-b87308d09c5f}" StaticName="Personne_ID" Name="Personne_ID" ColName="ntext3" RowOrdinal="0" />
      <Field Type="Note" DisplayName="Personne: FirstName" ReadOnly="TRUE" BdcField="FirstName" ID="{9283024f-2d09-4fc5-99ef-9e7c5652de19}" SourceID="{11411064-1db6-4071-ada7-b87308d09c5f}" StaticName="Personne_x003a__x0020_FirstName" Name="Personne_x003a__x0020_FirstName" ColName="ntext4" RowOrdinal="0" Version="1" />
      <Field Type="Note" DisplayName="Personne: LastName" ReadOnly="TRUE" BdcField="LastName" ID="{92cb9e80-3012-43ea-9739-e92f2a5c0caf}" SourceID="{11411064-1db6-4071-ada7-b87308d09c5f}" StaticName="Personne_x003a__x0020_LastName" Name="Personne_x003a__x0020_LastName" ColName="ntext5" RowOrdinal="0" Version="1" />

Il faut bien comprendre ici que ces champs s’appuient sur une liste externe existante déjà déployée sur mon site. On reprend donc le principe du lookupfield.

–          Ajouter ces champs à la vue par défaut (propriété defaultview à TRUE) dans le schema.xml du template de liste.

<ViewFields>
<FieldRef Name="LinkTitle"></FieldRef>
<FieldRef Name="Personne_x003a__x0020_FirstName"></FieldRef>
<FieldRef Name="Personne_x003a__x0020_LastName"></FieldRef>
<FieldRef Name="Modified"></FieldRef></ViewFields>

–          Ouvrir maintenant l’instance de liste, les colonnes ajoutées sont maintenant visibles dans le designer.  Modifier les paramètres de visibilité de celles-ci afin de décider d’afficher ou non ces colonnes sur le formulaire de création/édition/affichage etc. Dans mon cas la colonne « Personne » est visible dans le newForm/editForm/displayForm, les colonne Personne8ID reste cachée et les autres sont visibles dans le displayForm.

lite instance property sharepoint 2013

Cette solution est plutôt simple mais difficilement lisible et maintenable. De plus ce n’est pas une bonne pratique mais elle a le mérite d’exister J

Une fois ce modèle et l’instance de liste déployés sur mon site via une feature le résultat suivant est alors visible :

colonne de données externe sharepoint

external data column sharepoint

Via le modèle objet

Créer une liste de la même manière que dans la méthode précédente. Puis via un feature receiver déclencher le code suivant :

//le scope de la feature est "web"
            SPWeb currentWeb = (SPWeb)properties.Feature.Parent;
            if (currentWeb != null)
            {
                SPList myList = currentWeb.Lists.TryGetList("MesAnnonces");
                if (myList != null)
                {
                    SPBusinessDataField myPerson = myList.Fields.CreateNewField("BusinessData", "Personne2") as SPBusinessDataField;
                    myPerson.SystemInstanceName = "AdventureWorksPerson";
                    myPerson.EntityNamespace = "SPAlex.Labs.ContentTypes.AdventureWorksPerson";
                    myPerson.EntityName = "Personne";
                    myPerson.HasActions = false;
                    myPerson.BdcFieldName = "BusinessEntityID";
                    string[] myArray = new string[]{"FirstName", "LastName"};
                    myPerson.SetSecondaryFieldsNames(myArray);

                    myList.Fields.Add(myPerson);
                    myList.Update();
                }
            }
 

Même résultat à l’écran qu’avec la méthode précédente sauf que celle-ci est plus « propre » 🙂

Création d’un type de contenu externe et utilisation du secure store service via Visual Studio 2012


Cet article fait partie d’une série concernant la mise en oeuvre de liste externes et de workflows avec SharePoint. Les étapes seront passées en revue :

_______________________________________________________________________________________________

Après avoir vu comment créer une application dans le secure store service puis s’en servir pour créer un type de contenu externe avec l’interface utilisateur SharePoint Designer 2013, voyons maintenant comment faire de même en programmation avec Visual Studio 2012.

Pour cela nous allons créer un accès complet à la table « Person » de la base « AdventureWorks2012 » avec le  Secure Store Service  de la manière suivante  :

Création du modèle BDC avec classes LINQ d’accès aux données

1 – Créer un projet de type SharePoint 2013 Vide

Visual Studio 2012 SharePoint 2013

2 – Sélectionner « déployer en tant que solution de batterie » (en effet un type de contenu externe ne se déploie qu’au niveau « farm »).

SharePoint 2013 - spalexandre

3 – Ajouter un nouvel élément de type « Modèle de connectivité de données métiers » au projet.

 Modèle de connectivité de données métiers (solution de batterie uniquement) spalexandre

4  – Se connecter à la base de données cible (ici de type SQL Server pour AdventureWorks2012).spalexandre sharepoint 2013

SQL Server datasource visual studio 2012

connexion base de données sharepoint 2013

5 – Ajouter une classe d’accès aux données au projet (Ajouter nouvel élément -> données -> classes Linq To SQL). Ceci dépend du type de source de données auquel vous avez affaire. Choisissez celui qui correspond ou créer votre propre classe d’accès aux données.classes Linq to SQL

6 – une fois le .dbml ouvert, ouvrir l’explorateur de serveur, sélectionner celui qui concerne la connexion ajoutée puis la(les) table(s) qui vous intéresse et glisser la(les) sur le designer.

visual studio 2012 designer LINQ entity

Dans mon cas, 2 classes ont été créées dans le fichier AdventureWorks.deigner.cs  : DataContext et Person. Le modèle de données de la table a été transformé en modèle objet, la table « Person » pourra donc être manipulé comme un objet C#, le type de ses propriétés étant mappé sur le type de colonne de la table en base de données. Ceci permet de simplifier l’accès aux données et va permettre de construire facilement via des requête LINQ des méthodes de CRUD pour notre entité.

7 – Supprimer les entités par défaut du modèle BDC. Pour cela ouvrir le designer en double cliquant sur « AventureWorksPerson » puis clic droit sur l’entité -> supprimer. Supprimer également les fichier Entity1.cs et Entity1Service.cs dans l’explorateur de solutions.

Delete entity visual studio 2012

8 – Ajouter une nouvelle entité sur le designer via la boite à outil en glisser déposer puis la renommer via la fenêtre de propriétés et définir un identificateur via clic droit sur le modèle puis définir son type.

Business data connectivity SharePoint 2013

propriété visual studio 2012

Propriétés visual studio 2012 BDCM

visual studio 2012 bcs

Accès au Secure Store  Service et récupération des informations d’authentification

1 – Ajouter un nouvel élément au projet de type Code -> Classe C#.

secure store service programmatically

2 – référencer les DLL suivantes :

  • Microsoft.Office.SecureStoreService située dans « C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.Office.SecureStoreService\v4.0_15.0.0.0__71e9bce111e9429c »
  • Microsoft.BusinessData située dans « C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\ »

3 – Ajouter le code suivant au fichier :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.BusinessData.Infrastructure.SecureStore;
using Microsoft.Office.SecureStoreService.Server;

namespace SPAlex.Labs
{
    public static class SecureStoreUtils
    {
        public static Dictionary<string, string> GetCredentials(string applicationID)
        {
            var credentialMap = new Dictionary<string, string>();
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SPServiceContext serviceContext = SPServiceContext.Current;
                var secureStoreProvider = new SecureStoreProvider { Context = serviceContext };
                using (var credentials = secureStoreProvider.GetCredentials(applicationID))
                {
                    var fields = secureStoreProvider.GetTargetApplicationFields(applicationID);
                    for (var i = 0; i < fields.Count; i++)
                    {
                        var field = fields[i];
                        var credential = credentials[i];
                        var decryptedCredential = ToClrString(credential.Credential);

                        credentialMap.Add(field.Name, decryptedCredential);
                    }
                }
            });
            return credentialMap;
        }

        public static string ToClrString(this SecureString secureString)
        {
            var ptr = Marshal.SecureStringToBSTR(secureString);

            try
            {
                return Marshal.PtrToStringBSTR(ptr);
            }
            finally
            {
                Marshal.FreeBSTR(ptr);
            }
        }

        public static string getSQLConnectionString(string applicationID, string serverName, string dataBaseName)
        {
            Dictionary<string, string> creds = GetCredentials (applicationID);
            string connectionString = string.Format(@"Data Source={0};Initial Catalog={1};User Id={2};Password={3};",
                serverName,
                dataBaseName,
                creds["Nom d'utilisateur SQL"],
                creds["Mot de passe SQL"]);

            return connectionString;
        }
    }
}

Attention à utiliser les mêmes noms de champs que ceux renseignés lors de la création de votre application dans le Secure Store Service pour récupérer les credentials dans le méthode getConnectionString.

Création des méthodes de CRUD pour le type de contenu externe

Recherche d’élément spécifique (readItem)

1 – Afin de permettre au service SharePoint de pouvoir faire des opérations de CRUD sur notre base de donnée externe, il faut définir les méthodes qui permettent d’accéder à ces données via le modèle de données créé précédemment et basé sur notre connexion. Dans notre cas nous allons créer des méthodes de recherche. Pour cela cliquer sur la partie « méthodes » de votre entité afin de voir apparaître la fenêtre ‘« détails de la méthode BDC » puis sélectionner « Créer une méthode de recherche spécifique » comme suit :

détails de la méthode BDC Visual Studio 2012

2 – Modifier le type de paramètre de retour de cette méthode afin de décrire l’ensemble des propriétés de l’objet « personne ».

modifier le descripteur de type visual studio 2012

Dans la fenêtre propriétés pour la valeur « Nom de type » sélectionner projet actif -> PERSONNE afin de mapper le type d’objet retourné avec l’entité de mapping LINQ.

descripteur de type business data connectivity

3 – Ajouter tous vos descripteurs de types correctement typés 🙂 via l’explorateur de BDC.

explorateur bdc visual studio 2012 - descripteur de type

Ajouter le premier en modifiant son nom / son type et en le sélectionnant en tant qu’identifiant dans la fenêtre propriétés.

identificateur explorateur de BDC visual studio 2012

Ajouter tout ceux qui correspondront aux éléments qui vous voudrez utiliser dans votre liste externe SharePoint 2013. Ne pas oublier de les typer.

Descripteur de type visual studio 2012

4 – Dans le fichier PersonneServices.cs déclarer les constantes suivantes (qui peuvent être stockées en SPPersistedObject ou dans le propertyBag niveau Ferme via un featureReceiver) :


private const string serverName = @"SPALEXANDRE\SQLSHAREPOINT";
private const string dataBaseName = "AdventureWorks2012";
private const string sssAppId = "AdventureWorks2012";

5 – Transformer les méthode ReadItem créée automatiquement avec le code suivant :

public static PERSONNE ReadItem(int BusinessEntityID)
{
string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

PERSONNE myPersonne =
(from p in dataContext.PERSONNE.AsEnumerable()
where p.BusinessEntityID == BusinessEntityID
select p).Single();
return myPersonne;
}

Cette méthode récupère un enregistrement à partir de la table personne de la BDD AdventureWorks  en fonction de son ID. La chaîne de connexion du dataContext Linq  est construite avec les credentials du l’utilisateur SQL stocké dans le Secure Store Service pour cette application.

Résultat :

recherche élément type de contenu externe

Recherche globale (ReadList)

1 – ajouter une nouvelle méthode de recherche.

méthode de recherche BDC

2 – remplacer le code de la méthode générée par le code suivant :


public static IEnumerable ReadList()
{
string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

IEnumerable pList = from p in dataContext.PERSONNE.Take(1000) select p;
return pList;
}

Cette méthode récupère les 1000 premières lignes de la table SQL (pour rappel au delà de 2000 ça n’est pas possible).

Une fois les méthodes de recherche créée la solution peut être compilée, déployée et testé en créant une liste externe basée sur ce nouveau type de contenu manuellement dans le site cible. Les données devrait être accessible sauf problème de droits d’accès (plutôt récurrents dans ce cas).

Résultat :

Recherche type de contenu externe

Création (Create)

1 – Ajouter une méthode de création dans la fenêtre « Détails de la méthode BDC »  comme suit :

BCS create

2 – remplacer le code de la méthode par le code suivant :


        public static PERSONNE Create(PERSONNE newPersonne)
        {
            string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
            AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

            PERSONNE myPers = new PERSONNE();
            myPers.BusinessEntityID = newPersonne.BusinessEntityID;
            myPers.FirstName = newPersonne.FirstName;
            myPers.LastName = newPersonne.LastName;

            //propriétés annexes ajoutées de par les contraintes de base de données
            myPers.ModifiedDate = DateTime.Now;
            myPers.PersonType = "EM";
            myPers.EmailPromotion = 0;
            myPers.NameStyle = false;
            myPers.rowguid = new Guid();

            dataContext.PERSONNE.InsertOnSubmit(myPers);
            dataContext.SubmitChanges();
            return myPers;
        }

Si vous aviez déjà créé une liste externe dans SharePoint afin de tester les éléments de recherche, il faudra la supprime et la recréer pour tester cette méthode sinon vous aurez l’erreur suivante : « Impossible de trouver le formulaire de création par défaut pour la liste XXX ».

résultat : newform BCS

Mise à jour (Update)

1 – Ajouter une méthode de mise à jour dans la fenêtre « Détails de la méthode BDC »  comme suit :

BCS update

2 – Modifier les propriété du paramètre en INPUT si le champs ID n’est pas en lecture seule (ce qui est mon cas) afin de cocher la prorpiété champs de pré-mise à jour (PreUpdaterField)  à TRUE.

PreUpdaterField true

3 – remplacer le code de la méthode par le code suivant :

public static void Update(PERSONNE personne)
 {
 string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
 AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

PERSONNE myPersonneToUPdate =
 (from p in dataContext.PERSONNE.AsEnumerable()
 where p.BusinessEntityID == personne.BusinessEntityID
 select p).Single();

myPersonneToUPdate.FirstName = personne.FirstName;
 myPersonneToUPdate.LastName = personne.LastName;
 dataContext.SubmitChanges();

}

résultat :

update type de contenu externe programmation

Suppression (Delete)

1 – Ajouter une méthode de suppression dans la fenêtre « Détails de la méthode BDC »  comme suit :

BCS Delete

2 – remplacer le code de la méthode par le code suivant :


public static void Delete(int businessEntityID)
        {
            string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
            AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

            PERSONNE myPersonneToDelete =
             (from p in dataContext.PERSONNE.AsEnumerable()
              where p.BusinessEntityID == businessEntityID
              select p).Single();

            dataContext.PERSONNE.DeleteOnSubmit(myPersonneToDelete);
            dataContext.SubmitChanges();
        }
    }

résultat :

Delete external content type

N.B : Cette méthode n’est pas fonctionnelle aujourd’hui pour ce type d’élément dans le cas de la base AdventureWorks2012 car il faudrait supprimer toutes les dépendances associées. Mais le principe est là 🙂

Pour déploiement et test via une liste externe créé par programmation c’est par ici.