Archives de catégorie : SharePoint Foundation

Alternative à Silverlight pour afficher des vidéos dans SharePoint 2010

Introduction

Depuis un bon moment déjà, Chrome à cesser de supporter les plugins NPAPI. Ceci a pour effet que le composant WebPart multimédia qui utilise Silverlight dans SharePoint 2010 ne fonctionne plus dans Chrome. Puisqu’il fonctionne encore dans Internet Explorer, cela occasionne des comportements différents entre les navigateurs. Dans certaines entreprises, YouTube et Vimeo ne sont pas une option alors dans ce billet je vous propose une solution permettant l’affichage de vidéo autant dans Chrome que dans Internet Explorer.

Utilisation d’un lecteur vidéo qui imite celui de HTML 5

Le lecteur vidéo MediaElementJs permet d’afficher des vidéos dans un seul interface visuel dans tous les navigateurs avec uniquement du JavaScript/CSS.

Il utilise le HTML5 pour les navigateurs récents et Flash/SilverLight afin d’imiter les balises HTML5 pour les anciens navigateurs.

L’intégration du lecteur vidéo dans SharePoint est relativement simple via une WebPart éditeur de contenu. Référez-vous au site de MediaElementJs pour les balises à insérer.

Si jamais les boutons ne s’affiche pas dans le lecteur vidéo, il vous suffit d’ajouter un MIME dans IIS sur vos serveurs SharePoint :

MIME_SVG

  1. Ouvrir IIS Manager et sélectionner votre Application Web.
  2. Dans Features View, double cliquer sur MIME Types.
  3. Dans l’onglet Actions, cliquer sur Add.
  4. Dans la boîte de dialogue « Add MIME Type » inscrire « .svg » dans le champ « File name extension ».
  5. Inscrire « image/svg+xml » dans le champ « MIME type » .
  6. Cliquer sur  OK.Source : http://technet.microsoft.com/en-us/library/cc725608(v=ws.10).aspx

Résultat

Chrome (HTML 5 player):

mediaElementsChrome

Internet Explorer 8.0 (Flash Player) :

mediaElementIE8

Conclusion

Bien évidemment, il s’agit d’une solution de contournement pour SharePoint 2010 car déjà dans SharePoint 2013, le lecteur vidéo permet d’afficher des vidéos en HTML 5 puis dans SilverLight pour les anciens navigateurs.

Références

Mediaelementjs.com

Comparatif des lecteurs vidéo HTML 5

 

Advertisements

Comparatif des différentes possibilités pour afficher des tableaux de bord dans SharePoint

Introduction

Il existe plusieurs façons d’afficher des tableaux de bord dans SharePoint. Cependant, les possibilités peuvent varier selon la version (2010/2013/2016) et le type de déploiement sur site (On Premise) ou dans SharePoint Online. Il est donc facile de passer plusieurs heures avant de se rendre compte que la solution envisagée n’est pas compatible avec la version en place. Dans ce billet, je vais mettre en évidence les différentes possibilités pour afficher des tableaux de bords (dashboard) puis fournir un comparatif par fonctionnalité et par version.

Power BI

PowerBI

Source : PowerBI

Il s’agit de l’offre BI de Microsoft en tant que service hébergé dans le cloud. Malgré que la solution repose dans le cloud, il est possible de s’alimenter de données sur site (On Premise) et il est possible d’incorporer un tableau de bord dans presque toute les plateformes Web. Microsoft investit beaucoup d’argent dans cette plateforme et pour cette raison, il y a des mises à jours régulièrement. Une communauté très active permet entre autre d’avoir accès à une galerie de visuels pour vos tableaux de bord.

Outils tiers

sales-dashboard_Collabion

Source : Collabion

Plusieurs outils tiers sont disponibles pour ajouter la possibilité d’afficher des tableaux de bord dans SharePoint. Les fonctionnalités sont différente selon le produit sélectionné. Par exemple : le nombre de visualisation disponible ou le nombre de connexion à des sources de données. Pour n’en nommer que quelque uns : Collabion, Bamboo et Kwizcom.

Solution personnalisé avec du code client

GoogleChart.PNG

Source : Google Charts

Le nombre de librairie client disponible pour afficher des tableaux de bord est grandissant. Une solution personnalisée peut ainsi facilement utiliser une de ces librairies afin d’afficher des tableaux de bord très intéressant via un Add-In ou une WebPart Éditeur de contenu. Pour n’en nommer que quelque unes : Google Charts, jqPlot Charts et Chartjs.

Solution personnalisé avec du code serveur

Telerik

 

Source : Telerik

Il existe aussi quelque librairies serveur pour afficher des tableaux de bord. On peut alors réaliser une solution personnalisée en utilisant une de ces librairies via une Visual WebPart et généralement un service Web. Pour n’en nommer que quelque unes : Telerik, ComponentOne et Crystal report.

 

SQL Reporting Services (SSRS)

ssrs

Source : SSRS

SSRS est un logiciel de génération de rapport serveur de Microsoft qui fait partit de la suite SQL Serveur. Un générateur de rapport peut permettre à quelqu’un qui n’est pas technique de réaliser des tableaux de bord. Autrement, des interfaces dans Visual Studio permettent de réaliser des rapports plus complexe. Il existe aussi des WebParts pour afficher les rapports directement dans une page SharePoint.

PerformancePoint

PerformancePoint

Source : cdhtalkstech.com

PerformancePoint est un outil de Microsoft disponible dans la version Entreprise de SharePoint 2010-2016 mais qui n’a pas vraiment évolué depuis la première version. Il est possible de créer des tableaux de bords à partir de quelque sources de données dont des cubes provenant de SSAS. Des WebParts spécifique à PerformancePoint sont aussi disponible pour un affichage directement dans SharePoint.

Excel BI / Power View

PowerView

Source : blogs.msdn.microsoft.com

Power View constitue une expérience d’exploration, de visualisation et de présentation interactives des données qui encourage la génération intuitive de rapports ad hoc. Power View est une fonctionnalité de Microsoft Excel 2013 et Microsoft SharePoint Server 2010 et 2013 disponible avec le complément SQL Server 2012 Service Pack 1 Reporting Services de Microsoft SharePoint Server Enterprise Edition.

PowerPivot

PowerPivot

Source : Youtube.com

PowerPivot est un complément pour Microsoft Excel 2010 qui vous permet d’importer des millions de lignes de données à partir de plusieurs sources de données dans un classeur Excel unique, de créer des relations entre les données hétérogènes, de créer des mesures et des colonnes calculées à l’aide de formules, de créer des tableaux croisés dynamiques et des graphiques croisés dynamiques, puis d’analyser ces données afin de pouvoir prendre des décisions opportunes sans nécessiter d’assistance informatique.

Visio Services dans SharePoint 2013

Visio.png

Source : Sharepointpromag.com

Visio Services permet aux utilisateurs de partager et d’afficher des diagrammes Visio dans SharePoint. Il permet également l’actualisation et la mise à jour des diagrammes Visio connectés à des données à l’aide de différentes sources de données. Visio Services s’exécute en tant qu’application de SharePoint Server 2013.

SharePoint 2010 Composant WebPart Graphique (Chart Web Part)

Pie ChartBar Chart

Source : Support.office.com

Le composant WebPart Graphique est un composant WebPart qui peut être créé et ajouté à un site SharePoint pour permettre aux utilisateurs d’afficher un tableau de bord. Cette WebPart n’a pas évolué depuis la version 2010 mais il est encore possible de l’utiliser dans SharePoint 2010-2016.

Comparatif des solutions par fonctionnalité

Visualisation personnalisée
Incorporer dans d’autres outils
Impression et exportation
Connexion à plusieurs source de données
Aucun codage requis
Power BI ~20 types Mark Mark ~ 60 sources Mark
Outils tiers ~1-50 types Mark Mark ~ 10 sources Mark
Solution personnalisé avec du code client warning-icon warning-icon
Solution personnalisé avec du code serveur warning-icon warning-icon
SQL Reporting Services (SSRS) 12 types Mark Mark ~10 sources warning-icon
PerformancePoint 8 types Mark  ~5 sources
Excel BI / Power View ~11 types Mark Mark 5 sources
PowerPivot ~10 types Mark Mark 4 sources Mark
Visio Services dans SharePoint 2013 Mark warning-icon 5 sources Mark
SharePoint 2010 Composant WebPart Graphique (Chart Web Part)  13 types ~3 sources Mark

 Mark Supporté

warning-icon Possible avec contournement

∞   Illimité

Comparatif des solutions par version

SP2010 SP2013 SP2016 SharePoint Online
Power BI Mark Mark Mark Mark
Outils tiers Mark Mark Mark Mark
Solution personnalisé avec du code client Mark Mark Mark Mark
Solution personnalisé avec du code serveur Mark Mark Mark warning-icon
SQL Reporting Services (SSRS) Mark Mark Mark
PerformancePoint Mark Mark Mark
Excel BI / Power View Mark Mark
PowerPivot Mark Mark Mark
Visio Services dans SharePoint 2013 Mark Mark Mark Mark
SharePoint 2010 Composant WebPart Graphique (Chart Web Part) Mark warning-icon warning-icon

Légende

 Mark Supporté

warning-icon Possible avec contournement

∞    Illimité

Conclusion

Les possibilités sont nombreuses pour afficher des tableaux de bord dans SharePoint. Certaines solutions n’ont pas évoluées depuis quelques versions ce qui en dit long concernant leur pérennité. Dans ce sens, je crois que PowerBI se démarque du lot au niveau des fonctionnalités et en second je considère que les outils tiers comme Collabion sont une bonne alternative au développement personnalisé.

Les solutions évoluent très rapidement alors si vous voyez des erreurs n’hésitez pas à m’en faire part et j’apporterai les ajustements.

Quelles solutions de tableau de bord utilisez-vous dans votre entreprise ?

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

Envoyer des courriels périodiques pour SharePoint OnPrem avec du PowerShell

Introduction

SharePoint dispose d’un mécanisme d’alerte assez puissant. Celui-ci permet d’envoyer à l’abonné une alerte par courriel basé sur une action qui se produit sur l’élément (il est créé, modifié, etc…). Il comporte cependant des limitations :

  • On ne peut pas recevoir une alerte périodique (ex : tous les trimestres, tous les premiers Lundi du mois, etc…);
  • La modification du gabarit de courriel est complexe;
  • Il n’est pas possible de modifier l’adresse de réponse et le sujet pour une alerte en particulier;
  • Vous devez être que propriétaire du site afin d’envoyer des alertes à d’autres personnes. Sans cela, la seule personne à qui vous pouvez envoyer des alertes à est vous-même.

Alors que faire si on veut contourner ces limitations?

Il y a quelques solutions possibles :

  1. Créer un timer job SharePoint (Exemple)
  2. Créer un flux de travail (Exemple)
  3. Utiliser un logiciel tiers (Exemple)
  4. Créer un script PowerShell s’exécutant dans une tâche planifié (Source)

Je vais mettre l’emphase sur le point 4 dans ce billet car à mon avis c’est la meilleure alternative lorsque les alertes « out-of-the-box » ne répondent pas à votre besoin.

Comparaison des différentes solutions

Alertes SharePoint out-of the-box Timer Job personnalisé

 

Flux de travail personnalisé Outil tiers

Script PowerShell

Flexibilité Mark Mark
Permet de modifier le contenu dynamique envoyé facilement Mark Mark Mark Mark
Permet de modifier le visuel du gabarit sans avoir besoin de programmation Mark Mark Mark
Envoyer des courriels à des utilisateurs externes Mark Mark
Nécessite peu d’efforts de configuration Mark Mark
Permet d’envoyer un courriel basé sur un intervalle de temps Mark Mark Mark
Gratuit Mark Mark Mark Mark

 

Utiliser un script PowerShell

Le script pour SharePoint 2013 sur site (OnPrem) est disponible ici :

telecharger-bouton

Assurez-vous de modifier la section « Constantes » et d’y inscrire les valeurs correspondants à votre environnement.

Capture

Celui-ci va créer une liste EmailTemplate :

EmailTemplate

Cette liste permet à un utilisateur de piloter les différentes valeurs sans avoir besoin d’effectuer de la programmation lorsque vient le temps de changer le contenu, le sujet, etc… Bien évidemment, si vous ajoutez d’autres modèles, vous devrez ajuster votre code afin de les utiliser correctement.

Le script va aussi créer une liste Project :

ProjectListView

Celle-ci est uniquement présente à des fins de démonstration. C’est à partir de cette liste que nous allons envoyer du contenu dynamique par courriel. Vous pouvez donc la supprimer et vous alimenter de votre propre liste si vous le souhaitez.

Voici un exemple de courriel envoyé par le script :

EmailResult

Exécution du script PowerShell dans une tâche planifiée

Démarrer -> Exécuter -> Taskschd.msc

Créer une nouvelle tâche :

ScheduledTaskPowerShell_create

Spécifier un compte ayant les droits d’administrateur de la ferme SharePoint.

Spécifier un déclencheur :

ScheduledTaskPowerShell_declencheur

Ajouter une nouvelle action :

ScheduledTaskPowerShell_action

Programme/Script : Powershell.exe

Ajouter des arguments : -ExecutionPolicy Bypass C:\Sources\SharePointerie.OnPrem.SendTimeBasedEmail.ps1

En prenant soin d’adapter le chemin vers votre script.

Cliquer sur OK pour enregistrer la tâche.

Outils de développeur pour faciliter le travail avec les courriels

Pour ma part, j’utilise l’outil SMTP4DEV afin de travailler en mode local. Il suffit de démarrer l’application et celle-ci se chargera d’intercepter les envois de courriel sur le port défini.

smtp4Dev

Source : https://smtp4dev.codeplex.com/

Conclusion

L’utilisation d’un script PowerShell pour envoyer des courriels périodiques permet un maximum de flexibilité autant au niveau du déploiement qu’au niveau du pilotage. Il n’y a pas de période d’indisponibilité lors du déploiement comparativement au déploiement d’un Timer Job. Je crois donc que c’est la meilleure alternative lorsque les alertes out-of-the-box ne répondent pas à votre besoin.

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.

Message d’erreur lors de la publication d’un flux de travail SharePoint 2010 contenant plus de 5 processus d’approbation

Introduction

Dernièrement, nous avons réalisé un flux de travail assez complexe en terme de logique pour un de nos client. Il contient 7 formulaires Infopath et il utilise plusieurs processus d’approbation. Le diagramme Visio entre difficilement dans dans une page 8½ x 11 !

Malheureusement, nous avons rencontré un problème majeur lors de la publication de ce flux de travail.

Voici le contexte de l’environnement :

  • Flux de travail de liste
  • Flux de travail SharePoint 2010
  • Créé à partir de SharePoint Designer 2013
  • Ferme SharePoint 2013

Lors de nos tentatives de publication on obtient toujours ce message d’erreur :

« Erreur lors de la compilation du flux de travail »

« Erreur inattendue sur le serveur associant le flux de travail »

wf

Investigations et problématique

On a alors investigué plusieurs pistes sans succès :

  • Augmenter les « timeout » dans les fichiers web.config et dans les pool applicatifs
  • Investiguer les fichiers de Logs ULS / Obsérvateur d’événements
  • Essayer sur d’autres postes
  • Vider la cache de SharePoint Designer 2013
  • Vider la cache du serveur
  • Redémarrer le serveur

Puis on est tombé sur cette article à propos du même message d’erreur lorsqu’on a plusieurs processus d’approbation. On a donc réalisé que notre flux de travail complexe contenait trop d’étape d’approbation (8) ! On en a alors enlevé (3) et suite à cela on a été en mesure de le publier sans problème.

Solutions possibles

  • Rendre le flux de travail moins complexe en retirant des processus d’approbation
  • Séparer le flux de travail en plusieurs flux de travail
  • Remplacer quelque processus d’approbation par des processus de tâche personnalisée qui sont moins complexe

Reproduire le problème

Pour reproduire le problème, suivre les étapes suivantes :

1. Créer un flux de travail de liste sur une liste de votre choix :

wf_step1

2. Donner un nom à votre flux et sélectionner « Flux de travail SharePoint 2010 » :

wf_step2

3. Ajouter 5 processus d’approbation avec le menu « Action -> Démarrer le processus d’approbation » :

wf_processus

4. Choisir au moins un utilisateur dans le champ « Utilisateur » :

wf_5processus

5. Publier le flux de travail

wf_step5

6. La publication se déroulera alors sans problème.

7. Ajouter un autre processus d’approbation :

wf_6processus

On obtient alors le message d’erreur.

Conclusion

Ce problème ne semble pas être documenté auprès de Microsoft et à mon avis, il s’agit d’une limitation au niveau de la complexité des flux de travail. Je vous suggère donc de faire plusieurs flux de travail simple au lieu d’un seul gros flux de travail complexe qui est susceptible de se déployer difficilement.

Utiliser les Patterns and Practices et le modèle des Apps pour déployer vos sites dans SharePoint Online ou on-premise

Introduction

Créer efficacement des sites personnalisées pour répondre aux besoins de nos clients c’est une des raisons de l’existence de SharePoint. Microsoft se concentre de plus en plus vers des déploiements dans le cloud, alors nous aurons inévitablement besoin d’avoir un modèle de déploiement qui est viable et facilement réalisable à la fois sur O365 et on-premise.

Dans cette optique, je me suis penché sérieusement sur la question afin de mieux comprendre les différentes techniques de déploiement de sites ainsi que leurs avantages et inconvénients.

Le déploiement de sites personnalisés dans SharePoint a évolué au cours des dernières versions et avec l’arrivé du modèle des Apps, il est évident qu’une façon de faire uniformisé s’impose. Une des techniques consiste à utiliser le modèle de Apps pour déployer vos sites.

Différentes techniques de déploiement de sites personnalisés

Avant d’entrer dans le vif du sujet, voyons un bref résumé des techniques qui s’offrent à nous pour effectuer le déploiement :

  • Modèles Web (Web Templates)
    • Seulement disponible au niveau de la collection de site dans O365
    • Difficile à maintenir à jour
  • Définitions de site (Site Definitions)
    • Non supporté dans O365
    • Beaucoup d’impact lors des mises à jours
  • Solutions de ferme personnalisées
    • Non supporté dans O365
    • Complexe à mettre en place et à maintenir
  • Création de structure de site avec Powershell
    • Complexe à mettre en place et à maintenir
  • Agrafage de fonctionnalités (Feature stappling)
    • Seulement disponible au niveau de la collection de site dans O365

Changement d’orientation de la part de Microsoft

Les modèles web (Web Templates) ont été, jusqu’à dernièrement, un excellent mécanisme fournis par Microsoft pour créer des centaines de sites d’équipes avec leurs propres personnalisations (bibliothèques, vues, WebPart, etc…) en addition à un modèle de site « out of the box ».

Cependant, lors d’une conférence au TechEd Europe 2014 (30 Octobre) Microsoft a annoncé ceci :

  • Éviter d’utiliser des définitions de site et des modèles Web
  • Éviter les pages maîtres personnalisées
  • Éviter d’utiliser le code XML déclaratif

siteProvisioning_NoSiteDef

Lien vers le vidéo

La raison derrière ce changement est, à mon avis, que Microsoft tente de faire d’Office 365 une plate-forme de développement qui n’est pas onéreuse à faire fonctionner et en même temps de fournir un service qui n’est pas trop complexe.

Maintenant, si on utilise des modèles Web nous ne bénéficieront pas des derniers changements pour chaque mise à jour de Microsoft. On parle ici d’une « taxe de personnalisation » que vous devez payer si vous utilisez des modèles Web.

Réaction face à ce changement d’orientation

J’évite volontairement de vous parler de l’orientation qui consiste à éviter les pages maître personnalisés et le code XML déclaratif car je ne suis pas complètement en accord avec ce changement. Je partage l’opinion de Chris O’Brien, il y aura toujours place à la personnalisation dans un site Intranet et le code XML déclaratif est encore supporté à 100% dans O365!

Tout comme nous n’avons pas immédiatement abandonné les solutions de fermes lors de l’arrivé du modèle des Apps, les modèles Web continueront sans doute d’être utilisé pour plusieurs scénarios pour où vous ne voudriez PAS de ces derniers changements provenant des mises à jour de Microsoft.

Cependant, je considère que pour les site d’équipe ainsi que pour les sites de projets, il n’est plus favorable d’utiliser un modèle Web et il serait plus approprié d’utiliser le modèle des Apps pour déployer vos sites afin d’éviter cette taxe de personnalisation!

Le modèle des Apps pour déployer vos sites

Le diagramme suivant illustre le déploiement de collections de site en utilisant le modèle des Apps ainsi qu’une approche asynchrone :

 

siteProvisioning_Remote

  1. Lorsqu’un utilisateur a besoin d’une nouvelle collection de site, il remplis les métadonnées requises dans le formulaire de création
  2. L’information est stockée dans un endroit centralisé
  3. Si requis, on peut y associer des flux d’approbation
  4. Les requêtes approuvées sont traitées par une tâche planifié distante. Ex : Une tâche planifié hébergé dans un serveur applicatif qui lance un script PowerShell ou une application console
  5. Le déploiement du site est réalisé en fonction des métadonnées fournis en utilisant le CSOM
  6. Une notification est envoyé à l’utilisateur pour lui informer de la création de la collection de site.

Le principe de cette technique consiste à sortir le code permettant d’effectuer le déploiement à l’extérieur de SharePoint afin d’avoir un faible couplage avec la plateforme. Cette technique fonctionne dans O365 ainsi qu’on-premise. Un des avantages est qu’il n’est pas nécessaire de déployer de fonctionnalités dans la ferme pour effectuer le déploiement. En plus de cela, il n’y a plus de dépendances avec des packages de solutions à l’intérieur des bases de données de contenu alors on pourrait uniquement restaurer celles-ci en cas de disaster recovery.

Fonctionnement technique

  1. Aller sur le site GitHub de OfficeDevPnp à l’adresse https://github.com/OfficeDev/PnP
  2. Cliquer sur le bouton « Download zip » et choisir un emplacement
  3. Extraire le Zip et aller dans le répertoire PnP-master\PnP-master\Samples\Provisioning.OnPrem.Async
  4. Ouvrir Provisioning.OnPrem.Async.Console.sln avec Visual Studio
  5. Vous retrouverez la méthode principale ProcessSiteCreationRequest
[...]

private static string ProcessSiteCreationRequest(ClientContext ctx, ListItem listItem)
{

 //get the base tenant admin urls
 string tenantStr = ConfigurationManager.AppSettings["SiteCollectionRequests_SiteUrl"];

 // Resolve root site collection URL from host web. We assume that this has been set as the "TenantAdminSite"
 string rootSiteUrl = tenantStr.Substring(0, 8 + tenantStr.Substring(8).IndexOf("/"));

 //Resolve URL for the new site collection
 var webUrl = string.Format("{0}/sites/{1}", rootSiteUrl, listItem["SiteUrl"].ToString());
 var tenantAdminUri = new Uri(rootSiteUrl);
 string realm = TokenHelper.GetRealmFromTargetUrl(tenantAdminUri);
 var token = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, tenantAdminUri.Authority, realm).AccessToken;
 using (var adminContext = TokenHelper.GetClientContextWithAccessToken(tenantAdminUri.ToString(), token))
 {
 // Set the time out as high as possible
 adminContext.RequestTimeout = int.MaxValue;

 var tenant = new Tenant(adminContext);
 var properties = new SiteCreationProperties()
 {
 Url = webUrl,
 Owner = listItem["AdminAccount"].ToString(),
 Title = listItem["Title"].ToString(),
 Template = listItem["Template"].ToString(),
 };

 //start the SPO operation to create the site
 SpoOperation op = tenant.CreateSite(properties);
 adminContext.Load(op, i => i.IsComplete);
 adminContext.RequestTimeout = int.MaxValue;
 adminContext.ExecuteQuery();
 }

 // Do some branding for the new site
 SetThemeToNewSite(webUrl);

 return webUrl;
}

[...]

Afin d’uniformiser la manière de faire entre O365 et on-premise, vous remarquerez qu’on utilise la même notion de « Tenant« .

Une fois le code déployé, vous aurez un interface comme celui-ci pour saisir l’information concernant la collection de site à créer :

siteProvisioning_UI

Cette information sera ensuite sauvegardée dans une liste :

siteProvisioning_List

Finalement, l’application console que vous aurez lancé manuellement ou via une tâche planifié se chargera de créer la collection de site à partir de cette liste.

Pour plus d’information consulter ce vidéo qui présente en détail le fonctionnement du code et du processus.

Office Development Patterns & Practices (PnP)

En plus de fournir le code pour déployer des sites, le site d’Office PnP présente une série d’exemples et de scénarios sur les bonnes pratiques qui utilise le modèle des Apps.

Les exemples et scénarios utilisent les méthodes qui sont recommandées par l’équipe Office 365 et la majorité s’applique à :

  • Office 365 Multi Tenant (MT)
  • Office 365 Dedicated (D)
  • SharePoint 2013 on-premises

Voici quelque références pour les différents sites relié à Office PnP :

Conclusion

Le modèle des Apps pour déployer vos sites peut être la bonne solution dans une situation et peut ne pas l’être dans une autre. Le plus important est d’avoir une bonne compréhension des diverses techniques pour ensuite prendre une décision éclairé selon vos besoins. Le choix de votre technique de déploiement de site aura un impacte à long terme sur vos coûts de mise en place et de maintenance. J’aime bien la technique asynchrone proposé par Office PnP car il est possible de l’utiliser dans O365 ainsi que on-premise et de plus, l’utilisateur n’a pas besoin d’attendre dans son navigateur car il reçoit une notification à la fin du traitement.

Qu’est-ce que vous pensez de cette technique de déploiement de sites ?

Références

Building a async provisioning model par Office PnP

Office365 Developer Patterns and Practices par Vesa Juvonen

Provisioning site collections using SP App model in on-premises with just CSOM par Vesa Juvonen

Site provisioning techniques with SharePoint apps par Bert Jansen et Vesa Juvonen

Transforming Your SharePoint Full Trust Code to the SharePoint App Model  par Vesa Juvonen et Steve Walker

Developing for Office 365 – thoughts on use of custom master pages and web templates par Vesa Juvonen

Site provisioning techniques and remote provisioning in SharePoint 2013 par Chris O’Brien

L’effort de Microsoft au sujet de l’extensibilité d’Office 365 par Sébastien Levert