Archives de mot-clé : javascript

Créer un add-in part SharePoint Hosted pour afficher vos nouvelles avec le nombre de commentaires et de mentions « J’aime »

Je viens tout juste de publier mon premier vidéo sur le site Channel 9 :

video

Code source :

Pour s’y faire les technologies suivantes seront utilisées :

  • JavaScript
  • jQuery
  • SharePoint Hosted Add-in Part
  • SharePoint REST API

Je vous inviter à commenter et à évaluer ce vidéo !

 

Advertisements

Créer des filtres dynamiques sur vos List View WebPart avec du JS + KnockoutJS

Introduction

Jusqu’à tout récemment, j’utilisait une WebPart de filtre avec une connexion afin de filtrer l’année de publication des nouvelles :

Actualites

Cependant, ça ne fonctionne pas lorsqu’on utilise la pagination ET le filtre.  Le problème c’est que la pagination ajoute les paramètres de filtre directement dans le Querystring Ex : FilterField1=PublishedDateYear&FilterValue1=2016. Par la suite, même si on change de valeur via la WebPart de filtre, la valeur qui sera appliqué sera toujours celle du Querystring.

Je n’ai malheureusement pas trouvé de solution élégante à cette problématique alors j’ai plutôt opté pour une solution en JavaScript qui m’offre plus de flexibilité.

Une solution 100 % JS

Je me suis basé sur un article que j’ai trouvé sur le blog de Phil Harding et je l’ai adapté à mon besoin. Le résultat est très intéressant et nous allons voir en détail comment mettre le tout en place dans ce billet. Voici tout d’abord un bref aperçu :

ActualitesFiltreDynamique

Le code peut être exécuté autant dans SP2010 que SP2013 mais il y a quelque subtilité au niveau du code.

Intégration du code

La solution utilise une WebPart Éditeur de contenu pointant vers un fichier « txt » qui contient le balisage HTML. Ce fichier « txt » contient des références vers un fichier JS, CSS et au Framework Knockout JS v3.1.0.

La structure des fichiers est la suivante :

  • /Style Library/txt/script_actualites_filtre_html.txt (Gist)
  • /Style Library/css/app_filtre.css (Gist)
  • /Style Library/scripts/app_filtre.js (Gist)
  • /Style Library/scripts/knockout-3.1.0.js (Source)

La première étape consiste à copier vos fichiers dans votre dossier Style Library et à ajuster les références.

Ensuite, dans une page contenant une List View WebPart ajouter une WebPart Éditeur de contenu avec un lien vers le fichier : /Style Library/txt/script_actualites_filtre_html.txt.

Puis, adapter le fichier app_filtre.js (Voir les TODO dans le code) afin que vos filtres correspondent à vos colonnes et à votre liste.

app_filtre.js


(function(module,$j) {
"use strict";

window.pd = window.pd || {};

pd.MeetingsViewModel = function() {
   /* observable state */
   this.FilterTextIm = ko.observable('');
   this.FilterText = ko.computed(this.FilterTextIm)
                        .extend({ throttle: 400 });
   this.LibraryTitle = ko.observable('');
   this.LibrarySubTitle = ko.observable('');
   this.FilterYears = new ko.observableArray([]);
   this.FilterMonths = new ko.observableArray(['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre']);
   this.SelectedYear = ko.observable('');
   this.SelectedMonth = ko.observable('');
   this.Working = ko.observable(true);

   /* populate years filter as Current Year..-=4 */
   for(var cy = parseInt(new Date().getFullYear()), y = cy; y > (cy-3); y--) { this.FilterYears.push(y.toString()); }

   /* model actions */
   this.filterByYear = function(year) {
      this.FilterTextIm('');   // zap the filter text
      year = (year === this.SelectedYear()) ? '' : year;
      this.SelectedYear(year);
   }.bind(this);
   this.filterByMonth = function(month) {
      this.FilterTextIm('');   // zap the filter text
      month = (month === this.SelectedMonth()) ? '' : month;
      this.SelectedMonth(month);
   }.bind(this);

   /* model behaviour */
   this.isSelectedYear = function (year) {
      var y = this.SelectedYear();
      return (y == year);
   }.bind(this);
   this.isSelectedMonth = function (month) {
      var m = this.SelectedMonth();
      return (m == month);
   }.bind(this);
}


pd.FilterLibraryApp = function() {
   var
      viewModel = new pd.MeetingsViewModel(),
      debug = true,
      wpCtx = null,
      wpEvent = null,
      
      filterControlId = null,
      listViewTableId = null,
      _module = {
         start: start
      };
   return _module;

   function locateListView() {
      var fn = function() {
         InitAllClvps();

         // find the CTX for the LVWP
         for(var k in g_ctxDict) {
           if (debug && window.console) console.log(g_ctxDict[k]);
           
           if (g_ctxDict[k].ListTitle === module.LibraryTitle) {
             wpCtx = window['ctx'+g_ctxDict[k].ctxId];
             break;
           }
         }
         if (debug && window.console) {
            console.log(wpCtx.clvp);
            console.log(wpCtx.clvp.ctx);
            console.log(wpCtx.clvp.ctx.view);
         }
         /* this isn't used in the 2010 version, not figured out if/how to use it yet 
         var clvp = (wpCtx && wpCtx.clvp) ? wpCtx.clvp : null;
         wpEvent = { clvp: clvp, event: { currentCtx: { clvp: clvp } } };
         */
         
         // find the LVWP and table containing the rows
         var 
            $wpnode = $j('#'+wpCtx.clvp.wpq).first(),
            $wpTableNode = $wpnode.find("table[id^='onetidDoclibViewTbl']");
         listViewTableId = $wpTableNode.attr('id');

		 //Il faut avoir préalablement coché la case "Afficher le bouton de raffraichisement" sur la LVWP

         // find the postback control id; e.g. ctl00$m$g_109629d4_d78b_4c9e_8ec0_90078b6e444e$ctl02
         var
            $wpinput = $wpnode.find('> :first-child > input:first-child'),
            ctrlid = $wpinput.attr('id');
         ctrlid = ctrlid && ctrlid.length
                     ?  ctrlid.replace(/^(ctl\d\d)_/gi,'$1$')
                               .replace(/_g_/g,'$g_')
                               .replace(/_(ctl\d\d)$/gi,'$$$1')
                     : '';
         if (debug && window.console) { console.log(">>locateListView: controlid="+ctrlid); }
         filterControlId = ctrlid;
         viewModel.LibraryTitle(wpCtx.ListTitle);
         viewModel.LibrarySubTitle('All');
         viewModel.Working(false);
      };
      EnsureScript("inplview", typeof InitAllClvps, fn);
   }

   function onFilterChange(filterType) {
      if (!filterControlId) {
         alert('Unable to filter: the list view webpart could not be located or the list view contains a Person/Group column!');
         return;
      }

      var
         filterColumn = null,
         filterValue = '';

      if (filterType.match(/year/gi)) {
         filterValue = this.SelectedYear();
         filterColumn = 'PublishedDateYear'; //TODO ajuster votre filtre
      } else if (filterType.match(/month/gi)) {
         filterValue = this.SelectedMonth();
         filterColumn = 'PublishedDateMonth'; //TODO ajuster votre filtre
      }

      if (!filterValue) filterValue = '##dvt_all##';
      var filterCall = "__doPostBack('"+filterControlId+"','NotUTF8;__filter={"+filterColumn+"="+filterValue+"}')"

      if (debug && window.console) {
         console.log(">>Filter by " + filterType + " on ["+filterColumn+"] = ["+filterValue+"]");
         console.log("  >> " + filterCall);
      }
      eval(filterCall);
   }

   function start() {
      ko.applyBindings(viewModel, $j('#cdtm').get(0));
      viewModel.SelectedYear.subscribe(onFilterChange.bind(viewModel, 'year'));
      viewModel.SelectedMonth.subscribe(onFilterChange.bind(viewModel, 'month'));

      locateListView();
   }
}();

$j(pd.FilterLibraryApp.start);

})({ Name: 'Module', LibraryTitle: 'Billets' },jQuery); //TODO Ajuster le titre de votre liste


Dernier détail

Afin de pouvoir lier les filtres à la vue, pour ma part j’ai été contraint de cocher la case suivante :

ActualitesFiltreDynamiqueLVWP

Sans cela, le controlid était toujours vide. Si vous avez une erreur vérifier tout d’abord votre console (F12) :

ActualitesFiltreDynamiqueLVWPConsole

Conclusion

Seulement avec du JS on a réussi à ajouter des filtres dynamiques sur une List View WebPart et cela fonctionne correctement avec la pagination. Donc, beaucoup de flexibilité au niveau du déploiement et de la personnalisation visuelle.

Référence

Dynamically Filtering SharePoint List Views using Javascript

Techniques pour récupérer les éléments HTML d’une WebPart en JavaScript

Introduction

Dans ce billet je vous propose quelque techniques de JavaScript pour pouvoir récupérer les éléments HTML d’une WebPart spécifique. Chez un de mes clients je me suis vite rendu compte qu’il n’était pas évident d’avoir une « poigné » fixe pour appliquer une personnalisation sur une seule WebPart. Le Client Side Rendering (CSR) est à la mode c’est temps-ci mais il comporte une certaine complexité en comparaison aux quelques lignes de JavaScript qui suivront. Pour ce qui est du CSS, il n’est pas évident de l’appliquer seulement à une WebPart.

Examinons le code généré d’une WebPart

En appuyant sur F12 dans une page contenant des WebParts, vous pourrez examiner le code HTML généré par celle-ci.

Voici un exemple :

WebPartByTitle_HTMLElement

Vous remarquerez qu’il y a un ID unique sur chacune des WebParts de la page.

L’élément DIV avec le ID « MSOZoneCell_WebPartWPQ6 » correspond à l’ensemble de la WebPart.

L’élément DIV avec le ID « WebPartWPQ6_ChromeTitle » correspond à l’entête de la WebPart.

L’élément SPAN avec le ID « WebPartTitleWPQ6 » correspond au titre de la WebPart.

Voyons maintenant quelque techniques pour les récupérer.

Technique 1 – Récupérer les éléments HTML d’une WebPart par son titre

Cette technique consiste à utiliser le titre de la WebPart comme poigné pour récupérer les éléments HTML d’une WebPart précise.

Voici le code pour y arriver :

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function()
{
     //Parcours tous les éléments SPAN ayant un ID qui commencent par WebPartTitleWPQ
     $("span[id^=WebPartTitleWPQ]").each(function()
     {
          //Si le titre de la WebPart commence par "Site Collection Documents"
          if($(this).attr('title').indexOf("Site Collection Documents") == 0){
          //Applique une personnalisation sur l'ensemble de la WebPart (Couleur rouge)
               $(this).parent().parent().css('background-color', 'red');
          }
    });
});
</script>

Résultat

Le background-color rouge est appliqué uniquement sur la WebPart avec le titre « Site Collection Documents » :

WebPartByTitle_After

Points à faire attention avec cette technique

  1. Assurez-vous d’avoir un titre unique pour vos WebParts
  2. Assurez-vous d’utiliser un type de chrome incluant le « Titre »

Au besoin, vous pouvez modifier le titre de votre WebPart ou le type de chrome :

WebPartByTitle_MenuWP

Technique 2 – Récupérer les éléments HTML d’une WebPart par son ID

Cette technique consiste à utiliser l’ID comme poigné pour récupérer les éléments HTML d’une WebPart précise.

Voici un exemple de code pour modifier une WebPart par sont ID :

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function()
{
 //Applique une personnalisation à l'ensemble de la WebPart (Couleur bleu) par son ID
 $("#MSOZoneCell_WebPartWPQ6").css('background-color', 'blue');
});
</script>

Résultat

Le background-color bleu est appliqué uniquement sur la WebPart avec le ID MSOZoneCell_WebPartWPQ6 :

WebPartByID_After

Points à faire attention avec cette technique

  1. Assurez-vous d’ajuster votre code si une WebPart est déplacé,  ajouté ou supprimé

Que se passe-t’il si on déplace une WebPart ou qu’on en ajoute une nouvelle?

Le ID de la WebPart va tout simplement changer.

Avant la suppression d’une WebPart (2 WebParts) :

WebPartByTitle_HTMLElement_AvantDelete

WebPart 1 : WebPartWPQ4_ChromeTitle

 

Après la suppression d’une WebPart (1 WebPart) :

WebPartByTitle_HTMLElement_ApresDelete

WebPart 1 : WebPartWPQ2_ChromeTitle 

Voici un autre exemple qui utilise cette technique.

Conclusion

Les deux techniques présentés dans cet article peuvent vous permettre facilement d’appliquer des personnalisations sur un élément HTML d’une WebPart. La première technique utilisant le titre de la WebPart n’est pas sans faille si deux WebParts se retrouvent avec le même nom. Pour ce qui en est d’utiliser le ID de l’élément HTML de la WebPart, il faut faire attention si on déplace, supprime ou ajoute une WebPart dans la page car les ID sont susceptible de changer et ainsi de rendre votre code non fonctionnel. Ces techniques peuvent vous permettre d’appliquer des modifications rapidement lorsque ce n’est pas possible de le faire avec du CSS ou que c’est trop complexe avec du Client Side Rendering.

Personnaliser vos sites SharePoint 2013 avec le Client Side Rendering (CSR) et le JS Link

Introduction

Effectuer des personnalisations dans l’interface utilisateur de SharePoint a toujours été un défi pour bien des développeurs. Dans les versions 2007 et 2010, on utilisait majoritairement le XSLT pour faire des personnalisations bien que c’était relativement complexe à s’y familiariser. D’autres utilisaient des WebParts de type Éditeur de contenu pour effectuer des modifications JavaScript directement dans une page et dans ce cas la difficulté est de trouver une « poignée » pour appliquer les modifications sur un élément précis. La fonctionnalité JSLink est arrivé dans SharePoint 2013 pour simplifier la vie aux développeurs en leur permettant de faire des personnalisations avec des langages qu’ils connaissent déjà : HTML, JavaScript et CSS. Grâce à cette fonctionnalité, on peut contrôler via un fichier JavaScript le rendu visuelle des éléments suivant :

  • Champ
  • Élément
  • Formulaire de liste
  • Vue
  • WebPart

XSL/XSLT vs Client Side Rendering

Le tableau ici-bas résume bien les différences entre les deux fonctionnalités :

XSL/XSLT Client Side Rendering
Performance
  • Plus rapide lorsqu’il y a un grand nombre d’éléments à afficher.
  • Aucune charge additionnel dans le navigateur.
  • Plus lent lorsqu’il y a un grand nombre d’éléments à afficher.
  • La vitesse du navigateur client peut avoir un impact sur la performance.
  • Réduit la charge du serveur.
Complexité
  • Langage complexe à apprendre initialement.
  • Difficile à déboguer (Souvent par essaie-erreur).
  • Temps de développement plus grand.
  • Facile de déboguer du JavaScript, HTML et CSS avec les navigateurs modernes.
  • Temps de développement réduit.
Flexibilité
  • Limité aux fonctions XSLT 1.0 pour SharePoint 2010 et 2013.
  • Limité pour faire un rendu visuel complexe.
  • Très efficace pour faire des opérations coûteuse (ex : Count dans une vue).
  • Permet de cibler les personnalisations sur une vue, un champ, une WebPart, etc…
  • Possibilité d’utiliser des plugins (ex : JQuery).
  • Très adapté pour faire un rendu visuel complexe.
  • Inefficace pour faire des opérations coûteuses (ex : Count dans une vue).
Compatibilité
  • Indépendant du navigateur.
  • Supporté dans SP2010 et SP2013.
  • Fonctionne même si le JavaScript est désactivé dans le navigateur.
  • Génère du contenu statique qui est bien interprété par les robots d’indexation.
  • Adapté pour des sites publiques.
  • Dépendant du navigateur.
  • Supporté dans SP2010 et SP2013
  • Requiert que le JavaScript soir activé dans le navigateur.
  • Génère du contenu dynamique qui n’est pas bien interprété par les robots d’indexation.
  • Non-adapté pour des sites publiques.

Comment configurer le JSLink?

Pour référencer vos fichiers dans la propriété JSLink vous pouvez spécifier un jeton de remplacement (Token) ainsi qu’un séparateur (|) si vous avez plusieurs fichiers.

Les jetons disponibles sont les suivants :

  • ~site : correspond au URL du site Web courant.
  • ~sitecollection : correspond au URL de la collection de site parent du site Web courant.
  • ~layouts : correspond au dossier « _layouts/15 » de la web application.
  • ~sitelayouts – correspond au dossier layouts du site courant (ex : site/monsite/monsoussite/_layouts/15).
  • ~sitecollectionlayouts : correspond au dossier layouts de la collection de site courante (Ex:  /sites/monsite/_layouts/15).
  • ~sitelayouts – correspond au dossier layouts du site courant (Ex: /site/monsite/monsoussite/_layouts/15).

En utilisant ces jetons avec le séparateur on peut donc référencer plusieurs fichiers de scripts dans le JSLink.

Ex : ~site/style library/­monScript1.js|~sitecollection/style library/monScript2.js.

On peut spécifier la propriété JSLink en utilisant ces méthodes :

  • PowerShell
  • Code serveur C#/VB en utilisant l’API
  • Code serveur déclaratif XML
  • Interface utilisateur

Pour simplifier ce billet, je vous propose la méthode utilisant l’interface utilisateur :

jslink_link

Pour plus de détail sur les autres méthodes permettant de spécifier la propriété JSLink, je vous recommande le billet de Tobias Zimmergren.

Fonctionnement technique

À l’intérieur de ces fichiers, le JavaScript est utilisé pour « overrider » :

  • la manière dont les données sont affichés
  • le contenu à afficher

Voici un exemple de code permettant de modifier la valeur d’un champ dans une vue :

// Créer un namespace pour la fonction afin d'éviter des collisions avec les autres fonctions
var sharepointerie = sharepointerie || {};

(sharepointerie.CustomizeFieldRendering = function () {

// Initialise les variables pour overrider les objets
 var overrideCtx = {};
 overrideCtx.Templates = {};

 /*
 * Force la valeur "SharePointerie" dans le titre de tous les éléments.
 */
 overrideCtx.Templates.Fields = {
 'LinkTitle': { 'View' : 'SharePointerie' }
 };

 /*
 * Enregistre l'override du template.
 */
 SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
})();

Techniquement, on construit un objet qui sera ensuite utilisé afin d’enregistrer l’override du template. L’objet « overrideCtx » peut ensuite interagir avec ces propriétés :

  • View
  • Header
  • Body
  • Footer
  • Group
  • Item
  • Fields
  • OnPreRender
  • OnPostRender

Dans l’exemple on utilise la propriété Fields avec ce format :

overrideCtx.Templates.Fields = {NomDuChamp: {Porté : Override}}

  • NomDuChamp : Le nom interne du champ que l’on veut overrider.
  • Porté : Les choix possibles « View », »DisplayForm », »EditForm », et « NewForm » permettent de spécifier l’endroit ou l’on veut faire l’override.
  • Override : Contient une chaîne HTML ou une fonction à exécuter à l’intérieur de tags « <#= #> ».

Résultat (avant l’ajout du CSR) :

jslink_technique_avant

Résultat (après l’ajout du CSR) :

jslink_technique_apres

Des exemples concrets

1) Limiter la taille du texte dans le champ « Corps » d’une liste d’annonces

Cet exemple de code, réalisé par Muawiyah Shannak, permet d’afficher un résumé du corps d’une annonce et d’afficher la version intégrale dans une infobulle.

Pour déployer la solution :

  1. Télécharger le fichier CSR.
  2. Ajouter le fichier sous /Style Library/.
  3. Créer une liste de type Annonces
  4. Créer ou modifier une vue afin d’afficher le champ Corps.
  5. Modifier la page contenant la List View WebPart.
  6. Aller dans les propriétés de la WebPart et ajouter le fichier JSLink (~sitecollection/Style%20Library/SubstringLongText.js) dans la propriété Lien Js (JSLink) sous l’onglet Divers
  7. Cliquer sur Appliquer et quitter l’édition de la page.

Résultat (avant l’ajout du CSR) :

jslink_limite_texte_avant

Résultat (après l’ajout du CSR) :

jslink_limite_texte_apres

2) Ajouter des onglets dans un formulaire

Cet exemple de code, réalisé par Muawiyah Shannak, permet d’utiliser JQuery Tabs pour afficher des onglets dans un formulaire.

Pour déployer la solution :

  1. Télécharger le fichier CSR.
  2. Ajouter le fichier sous /Style Library/.
  3. Créer une liste personnalisé
  4. Ajouter des colonnes à la liste
  5. Modifier l’objet tabsObj selon vos besoin.
  6. Créer une nouvelle vue contenant le champ Lien
  7. Modifier les pages « newform » et « editform » de la liste
  8. Aller dans les propriétés de la WebPart et ajouter le fichier JSLink (~sitecollection/Style%20Library/Tabs.js) dans la propriété Lien Js (JSLink) sous l’onglet Divers
  9. Cliquer sur Appliquer et quitter l’édition de la page.

Résultat (avant l’ajout du CSR) :

jslink_tabs_avant

Résultat (après l’ajout du CSR) :

jslink_tabs_apres

Points à faire attention

  • Il peut y avoir des conflits avec le CSR si vous utilisez la fonctionnalité « Minimal download strategy ». Pour corriger ce problème consulter ce billet.
  • Le code de vos fonctions CSR ne devrait pas être défini dans le Namespace globale.
  • Le nom interne des colonnes est utilisé dans le code JavaScript des fonctions CSR alors favoriser des noms de colonnes sans espaces et sans caractères accentués.
  • Le CSR, contrairement au XSLT, dépend du navigateur alors assurez-vous de tester dans plusieurs navigateurs.

Conclusion

Pour conclure, le CSR comporte plusieurs avantages au niveau de la flexibilité et de la complexité comparativement au XSLT. Cependant, il vient aussi avec certaines lacunes en lien avec la performance et la compatibilité. Il y a donc énormément de potentiel à utiliser le CSR pour vos personnalisations (Validations, AJAX, etc…) mais comme il y a des limitations, je crois que le XSLT va continuer d’exister en parallèle. Je vous invite donc à l’essayer et à me donner vos commentaires!

Références

Voici quelques liens pour approfondir vos connaissance sur le CSR / JSLink.

Exemples de codes CSR réalisé par Muawiyah Shannak

Perfomance de l’utilisation du CSR via le JSLink

5 faits concernant l’utilisation du JSLink

Comment : personnaliser un type de champ à l’aide de rendu côté client

Comment : personnaliser le mode Liste dans les apps pour SharePoint à l’aide du rendu côté client

Introduction au CSR par Ross Bradbrook

Bon point de départ sur l’utilisation de l’API CSR

Utiliser jQuery UI autocomplete et l’API REST de SharePoint pour obtenir des résultats de recherche de personne

Introduction

Le complètement automatique, ou encore par anglicisme l’autocomplétion, est une fonctionnalité informatique permettant à l’utilisateur de limiter la quantité d’informations qu’il saisit, en se voyant proposer un complément qui pourrait convenir à la chaîne de caractères qu’il a commencé à taper. Dans SharePoint, ce type de fonctionnalité n’existe pas nativement pour obtenir des résultats de recherche de personnes et pourtant ça semble être très en demande par plusieurs clients qui veulent offrir une expérience utilisateur similaire à Google.

Prérequis

  • Avoir SharePoint 2013 sur site (On-Premise) ou SharePoint 2013 Online (O365)
  • Avoir configuré votre service de recherche ainsi que la recherche de personnes
  • Avoir une référence aux librairies tierces suivantes : jQuery, jQuery.UI et jQuery.Ui.Autocomplete

Librairies tierce

Il y a quelque années, j’avais réalisé une composante permettant d’utiliser l’autocomplétion en utilisant la composante RadAutoCompleteBox de Telerik. Cette composante payante avait requis relativement beaucoup d’effort afin d’être intégré dans SharePoint en plus de nécessiter le déploiement et l’hébergement d’un service Web spécifique pour fournir les résultats de recherche.

Maintenant, il existe plusieurs librairies tierces gratuites permettant d’effectuer le travail en s’intégrant dans SharePoint beaucoup plus facilement et en interrogeant l’API REST de recherche natif. Une des librairies populaires est le plugin Autocomplete de jQuery.

Intégrer le plugin AutoComplete

Je m’inspire du code provenant du billet de Murad qui nous offre un bon point de départ pour intégrer le plugin. Le code intègre les librairies tierces requises pour cette démonstration en plus de fournir un champ texte pour effectuer la recherche. Il suffit donc d’insérer le code suivant dans une WebPart de type Éditeur de Script :

<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css" />
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery.ui.autocomplete.css" />
<script src="http://code.jquery.com/jquery-1.8.3.js"></script>
<script src="http://code.jquery.com/ui/1.10.0/jquery-ui.js"></script>
//[...]
//Code retiré pour simplifier l'exemple
//[...]
<table>
<tbody>
	<tr>
		<td>REST Search</td>
		<td>
 		<div id="menu-container" style="width:600px">
			<input type="text" id="restSearch" size="50"/>
			</div>
		</td>
	</tr>
</tbody>
</table>

Construire une requête pour obtenir des résultats de recherche de personnes

Tout d’abord, il faut construire une requête sur l’API REST de recherche. Ce site est une excellente référence sur les divers points d’entrés ainsi que sur les paramètres disponibles pour construire notre requête.

Ex : https://<NomWebApplication>/_api/search/query?querytext=’lev*’&sourceid=’B09A7990-05EA-4AF9-81EF-EDFAB16C4E31’&clienttype=’Custom’

  • Le paramètre querytext représente le terme que l’on recherche
  • Le paramètre SourceId indique le GUID du résultat Source à utiliser pour l’exécution de la requête de recherche

Remarquez la propriété SourceId qui fait partie de la requête. On aurait pu utiliser le paramètre « ContentClass=urn:content-class:SPSPeople » à la place, mais SharePoint 2013 expose le SourceId des résultats. Le SourceId pour les résultats de personnes semble être le même pour toutes les installations soit « B09A7990-05EA-4AF9-81EF-EDFAB16C4E31 ». Alors je ne me sens pas coupable de hardcoder ce GUID.

On peut tester cette requête en l’exécutant directement dans le fureteur  qui nous affichera le résultat sous une forme XML :

rest_people_search_results_xml

Ajouter le code pour exécuter la requête et afficher les résultats de recherche de personnes

Afin de retourner des résultats de recherche de personne, il faut modifier le code initial afin de lancer un appel asynchrone lorsqu’un certain nombre de caractères seront saisie par l’utilisateur. La requête construite précédemment sera par la suite exécuté et finalement on affichera les résultats en dessous du champ texte recherche.

<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css" />
 <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery.ui.autocomplete.css" />
 <script src="http://code.jquery.com/jquery-1.8.3.js"></script>
<script src="http://code.jquery.com/ui/1.10.0/jquery-ui.js">
 </script>

<script type="text/javascript">
 // Parse an item and create an title/value dictionary with all the properties available
 function getFields(results) {
 r = {};
 for(var i = 0; i<results.length; i++) {
 if(results[i] != undefined && results[i].Key != undefined) {
 r[results[i].Key] = results[i].Value;
 }
 }
 return r;
 }

 $(document).ready(function() {

 var autocomplete = $( "#restSearch" ).autocomplete({
 minLength: 3,
 source: function( request, response ) {
 $.ajax({
 beforeSend: function (request)
 {
 request.setRequestHeader("Accept", "application/json;odata=verbose;charset=utf-8");
 },
 url: "/_api/search/query?sourceid='B09A7990-05EA-4AF9-81EF-EDFAB16C4E31'&querytext='" + request.term + "*'",
 dataType: "json",
 success: function( data ) {
 response( $.map( data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function( item ) {
 return {
 fields: getFields(item.Cells.results)
 }
 }));
 },
 error: function( data ) {
 alert('search error');
 }
 });
 },
 // Run this when the item is in focused (not selected)
 focus: function( event, ui ) {
 //$( "#restSearch" ).val(ui.item.value );
 return false;
 },
 // Run this when the item is selected
 select: function( event, ui ) {
 location.href = ui.item.fields.Path;
 },
 appendTo: $('#menu-container')
 }).data( "uiAutocomplete" )._renderItem = function( ul, item ) {
 return $("<li>").append('<a>' + item.fields.PreferredName + '</a>' )
 .appendTo( ul );
 };
 });
</script>

<table>
 <tbody>
 <tr>
 <td>REST Search:</td>
 <td>
 <div id="menu-container" style="width:600px">
 <input type="text" id="restSearch" size="50"/>
 </div>
 </td>
 </tr>
 </tbody>
 </table>

Résultat :

rest_people_search_results

Dans de grandes entreprises on peut retrouver plusieurs personnes ayant le même nom  et prénom. Pour éviter la confusion, il est facile d’ajouter la photo, le titre ou la direction de la personne.

Pour s’y faire, simplement modifier la ligne suivante :

return $("<li>").append('<a><img style="margin-right:3px;top:3px;" src="/_layouts/15/images/'+ img +'">' + item.fields.Title + '</a>' ) .appendTo( ul );

Par ceci :

var hasPicture = item.fields.PictureURL;
var currentItem;
 if(hasPicture){
 currentItem = '<a><img src="' + item.fields.PictureURL + '"/>' + item.fields.PreferredName + ' - ' + item.fields.JobTitle + '</a>'
 }
 else{
 currentItem = '<a><img src="/_layouts/images/o14_person_placeholder_96.png"/>' + item.fields.PreferredName + ' - ' + item.fields.JobTitle + '</a>'
 }
return $("<li>").append(currentItem).appendTo( ul );

Résultat :

rest_people_search_results_with_img

Conclusion

On peut réaliser une fonctionnalité d’autocomplétion des résultats de recherches de personnes très rapidement en intégrant le plugin gratuit jQuery Autocomplete et en utilisant les API REST de recherche natif. Cette solution fonctionne pour les environnements SharePoint 2013 sur site (On-Premise) ainsi que dans SharePoint 2013 Online (O365). Le code ne nécessite pas de déploiement de solution et il pourrait facilement être amélioré pour ajouter toute autres propriétés que vous jugeriez pertinente (Ex : Département, intérêts, etc…).

Cliquez ici pour télécharger le code source

Citation

Murad Sæter. (2013, 20 février). Using jQuery UI autocomplete with the REST API to get search results in the search box [Billet de blogue]. Repéré à http://blogs.msdn.com/b/murads/archive/2013/02/20/using-jquery-ui-autocomplete-with-the-rest-api-to-get-search-results-in-the-search-box.aspx