Sharepoint Developer Tools

Hi,

in this article will be presented some tools that are essential for every SharePoint developer.

SharePoint Manager 2013

SharePoint Manager is like the toolbox that Microsoft would have forgotten to provide with its product.

It will allow you to have a quick view over all your Web Applications, your sites collections, your associated/activated features on all spaces, lists, document libraries, every element properties.
You will certainly gain some time using it !
SPManager

A quick example of the features described above

You will have ton install and run SP Manager on the server where SharePoint is installed.
It may require Administrator rights, depending on which user you are currently logged on.

If you had the choice of one tool in addition to Visual Studio, download SharePoint Manager 2013 !! (also exists in version 2010)

——————————————————————————-

ULS Viewer

Once the SharePoint developments started, you will soon realize that when SP is not happy with what you did he almost never says it.

The only solution is to go in the log directory and open the latest. First mistake is to open it with Notepad or another text editor, you may get scared and never want to do it again.

Microsoft has developed a tool that will provide a quick and friendly way of reading logs.

ULS Viewer

Run ULS-Viewer, select the logs location (C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\LOGS\ for SP 2013, \14 for SP2010 etc) and you will realize that lots of things are happening (event if you don’t do anything in the web interface)

Fortunately, Microsoft is providing filtering capabilities for all the log activity.
ULS Viewer2

You will be able to filter by severity, then on defined text strings .

A real example is filtering on field Correlation when SharePoint will pop an error with an associated Guid, you will only see the errors related to the given problem.

This is, in my opinion, the second must-have tool, on my development machine it is always opened.

——————————————————————————-

SharePoint Feature Administration and Clean Up Tool

This is a tool that I use more often but that is also useful.

More focused on the features management in your farm, it will allow you to find in one click all defective features (after a migration, for example) or to quickly see the features enabled at a given scope (Farm / Site / Web)

FeatureAdmin for SharePoint 2013 - v2.3

——————————————————————————-

ILSpy

Sometimes in a developer’s life you might want to get your hands dirty and see what Microsoft’s guys have been doing in SharePoint code.

ILSpy spy will allow you to load and decompile assemblies that you used without knowing its content.

ILSpy
Above : Microsoft.SharePoint.Portal.CommunityEventReceiver class methods

Lots of DDls used by SharePoint are stored in C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI (\14 for SP2010 etc.), you will probably find what you are looking for there.

——————————————————————————-

CAML Designer for SP 2013

If you are experiencing the need to develop customized views or retrieve specific items in lists you may most likely use CAML queries.

With CAML Designer you can quickly generate the correct syntax to get the desired result without the need to deploy lots of times your solution.

CAML Designer for SharePoint 2013.

Complete information about using it can be found on the developer’s site.

——————————————————————————-

Sharepoint Color Palette Tool

If Microsoft’s blue is not really your favorite color and you want to enjoy the new features of SP2013 branding, then this tool is for you.

It will allow you to generate a color palette file (.Spcolor) in two clicks, you can import it into the gallery themes (http://url/_catalogs/theme/15) and apply it either through the interface (Change the look Menu) or through code.

SharePoint Color Palette Tool
Here is a shimmering green

——————————————————————————-

This is a good overview of what you can find as SharePoint development assistance, this list is not exhaustive and will be completed when I find new tools.

Etendre ou modifier le Portail des communautés

Bonjour,

j’ai récemment rencontré plusieurs problématiques avec le nouveau template de site « Portail des communautés » / « Community Portal ».

Pour rappel, un site communautaire est un template qui fournit de base un liste de discussions (sorte de forum) avec un système assez poussé de gestion des membres (possibilité de s’inscrire, gestion de badges pour les membres les plus actifs etc.)

Voilà ce que Microsoft dit à propos des communautés et du portail associé

Créer un portail des communautés :

comPort1

Le portail des communautés OOTB :

comPort2

L’affichage est plutôt sympa, avec les informations principales (Titre, Description, Nombre de membres, Nombre de topics et de réponses), on a aussi accès à plus d’informations avec une popup

comPort4

De base nous obtenons donc un site qui diffère un peu des autres templates standard par le fait qu’il ne possède pas de Logo, pas de colonne de menu gauche.

Le contenu principal est géré par une webPart de type Content Search, préconfigurée pour afficher toutes les communautés accessibles (et par accessibles j’entend à minima que l’utilisateur courant ait un droit Visitor sur les communautés ou la collection qui les contient)

Les communautés sont triées par popularité (formule barbare incluse ci dessous ;-) )

[formula:((CommunityMembersCount*0.5)+CommunityTopicsCount+(CommunityRepliesCount*0.5))/(log(abs(Created-{0})+1)+log(abs(LastModifiedTime-{0})+1)+1)]

La paramètre {0} étant généré par la méthode suivante

private static ulong NowInTicks()
{
    DateTime utcNow = DateTime.UtcNow;
    checked
    {
        ulong num = (ulong)utcNow.Second * 10000000uL;
        num += (ulong)utcNow.Minute * 600000000uL;
        num += (ulong)utcNow.Hour * 36000000000uL;
        num += (ulong)utcNow.DayOfYear * 864000000000uL;
        num += (ulong)utcNow.Year * 316224000000000uL;
        if (utcNow.Month > 2 && !DateTime.IsLeapYear(utcNow.Year))
        {
            num += 864000000000uL;
        }
        return num;
    }
}

N’ayant pas encore acquis la médaille Fields j’ai encore un peu de mal à décortiquer la totalité, ce que j’en ait retenu c’est que le nombre de membres et le nombre de topics (et réponses) sont importants dans le classement. Les dates de création de dernière modification jouent aussi.


Le portail des communauté est donc un site qui peut être très utile OOTB, et qui se met en place en quelques clics.
Par contre si vous avez besoin de modifier ce site vous allez au devant de quelques problèmes.

    1 – Pas de bandeau ni de possibilité d’insérer des WebParts supplémentaires

comPort3

Concrètement la div qui affiche normalement le ruban est volontairement cachée, ce qui vous en conviendrez ne facilite pas l’ajout de nouvelles WebParts par exemple.

<div id="s4-ribbonrow" style="height: 0px"> &amp; Display à none
    2 – Les possibilités de modification de la WebPart « Communautés populaires » sont limitées
  • Il est impossible de modifier le titre de la WebPart, ce sera toujours « Communautés Populaires » / « Popular communities »
  • Par défaut vous ne disposez que des templates d’affichages définis avec la WebPart
  • Les besoins exprimés par le client n’étant pas satisfait par le template OOTB il a fallu composer avec ces problématiques.


      Problématique 1 : Le titre de la WebPart ne convient pas

    Ok, donc là pas de solution miracle il va falloir aller fouiller dans les entrailles SP, à l’aide d’ILSpy

    Un export de la WebPart nous donne le type Microsoft.SharePoint.Portal.WebControls.ExistingCommunitiesWebPart

    Un rapide aperçu de la classe en question
    comPort5

    On remarque qu’elle est sealed (donc pas possible d’en hériter) et qu’elle est basée sur la ContentBySearchWebPart (ça on s’en doutait au vu de l’interface)

    Allons voir ce qui se cache sous notre problème de titre non modifiable
    comPort6

    Hum… mystère résolu, pas de setter et le getter renvoie une chaîne vide, mais alors comment est généré notre titre par défaut ?

    comPort7

    Ok donc tout est géré une méthode spécifique appelée dans le Render via une ressource multilangues.

    Pour modifier le titre il va donc falloir développer notre propre WebPart en s’inspirant de l’existante.


      Problématique 2 : Je dois afficher les communautés dont je suis membre (et pas uniquement celles auxquelles j’ai accès)

    La requête KQL passée pour récupérer les communautés est la suivante : WebTemplate:Community

    Comme dit plus haut elle va remonter toutes les communautés auxquelles j’ai possibilité d’accéder, mais pas forcément celles dont je suis membre (pour rappel le fait d’être membre d’une communauté est conditionné par la présence d’un objet SPUser dans la liste Community Members

    Après quelques recherches j’ai trouvé la méta donnée qui permet d’obtenir ces informations : MemberOWSUSER:{User.Name}
    Si vous exécutez cette requête dans une WebPart vous constaterez que les résultats remontés ne sont hélas pas des communautés mais items de listes (plus précisément les liens vers les objets SPUser dans chaque communauté où l’utilisateur est membre)

    Le problème est que l’on souhaite remonter des communautés (afin que le template fourni affiche correctement les informations)

    Même constatation que pour la problématique 1, sans développement ça ne sera possible.


      Problématique 3 : Par défaut toutes les communautés sont privées, pourtant je dois pouvoir afficher les communautés populaires pour pouvoir demander à les rejoindre

    Etant donné que le moteur de recherche est par défaut « bridé » sur les objets auxquels l’utilisateur a accès, obtenir le résultat en standard ne semble pas possible à priori.

  • Hypothèse 1 : Créer un Result Source retournant les communautés avec un « Credential Informations » ayant des droits élevés

  • Sur papier la solution semble idéale, le Result Source renverrait toutes les communautés et donc on pourrait tout simplement les afficher à partir de là.
    Bon il s’avère que dans les faits je n’ai jamais réussi à obtenir le résultat attendu, le compte utilisé pour le Result Source a pourtant les privilèges qu’il faut (si je lance la requête via ce user j’obtiens bien des résultats corrects)

  • Hypothèse 2 : Créer une WebPart à partir de la WebPart ExistingCommunitiesWebPart dans laquelle j’exécute la requête de récupération des communautés dans un bloc de privilèges élevés (afin de retourner toutes les communautés)
  • Cette solution fonctionne et c’est ce que je vais présenter ci dessous.


    Création de la WebPart vide
    comPort8

    Créer un élément de type WebPart, dans le fichier .cs collez le code ci dessous (code d’ExistingCommunitiesWebPart)

    using Microsoft.Office.Server.Search.Query;
    using Microsoft.Office.Server.Search.WebControls;
    using Microsoft.SharePoint.Security;
    using Microsoft.SharePoint.Utilities;
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls.WebParts;
    namespace Microsoft.SharePoint.Portal.WebControls
    {
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true), AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public sealed class ExistingCommunitiesWebPart : ContentBySearchWebPart
        {
            private bool _includeSubSites;
            public override string Title
            {
                get
                {
                    return string.Empty;
                }
                set
                {
                }
            }
            public override PartChromeType ChromeType
            {
                get
                {
                    return PartChromeType.None;
                }
                set
                {
                }
            }
            public bool IncludeSubSites
            {
                get
                {
                    return this._includeSubSites;
                }
                set
                {
                    this._includeSubSites = value;
                }
            }
            public ExistingCommunitiesWebPart()
            {
                base.BypassCBSFeatureCheck = true;
                this._includeSubSites = true;
            }
            protected override bool RequiresWebPartClientScript()
            {
                return false;
            }
            private static ulong NowInTicks()
            {
                DateTime utcNow = DateTime.UtcNow;
                checked
                {
                    ulong num = (ulong)utcNow.Second * 10000000uL;
                    num += (ulong)utcNow.Minute * 600000000uL;
                    num += (ulong)utcNow.Hour * 36000000000uL;
                    num += (ulong)utcNow.DayOfYear * 864000000000uL;
                    num += (ulong)utcNow.Year * 316224000000000uL;
                    if (utcNow.Month &gt; 2 &amp;&amp; !DateTime.IsLeapYear(utcNow.Year))
                    {
                        num += 864000000000uL;
                    }
                    return num;
                }
            }
            protected override void OnLoad(EventArgs e)
            {
                if (base.DataProvider != null)
                {
                    string propertyName = string.Format(CultureInfo.InvariantCulture, "[formula:((CommunityMembersCount*0.5)+CommunityTopicsCount+(CommunityRepliesCount*0.5))/(log(abs(Created-{0})+1)+log(abs(LastModifiedTime-{0})+1)+1)]", new object[]
                    {
                        ExistingCommunitiesWebPart.NowInTicks()
                    });
                    if (this._includeSubSites)
                    {
                        base.DataProvider.QueryTemplate = "WebTemplate:Community";
                    }
                    else
                    {
                        base.DataProvider.QueryTemplate = "WebTemplate:Community AND contentclass:STS_Site";
                    }
                    base.DataProvider.SourceID = "8413CD39-2156-4E00-B54D-11EFD9ABDB89";
                    base.DataProvider.FallbackSort = new List();
                    base.DataProvider.FallbackSort.Add(new ResultSort(propertyName, SortDirection.Descending));
                }
                base.ResultsPerPage = 30;
                base.RenderTemplateId = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Control_SearchResults.js";
                base.ItemTemplateId = "~sitecollection/_catalogs/masterpage/Display Templates/System/Item_CommunityPortal.js";
                base.ShowAdvancedLink = false;
                base.ShowPreferencesLink = false;
                base.ShowAlertMe = false;
                base.ShouldHideControlWhenEmpty = false;
                base.EmptyMessage = StringResourceManager.GetString(LocStringId.CommunityPortalNoCommunities);
                base.OnLoad(e);
            }
            private static void RenderTitle(HtmlTextWriter writer)
            {
                string localizedString = SPUtility.GetLocalizedString("$Resources:spscore,CommunityPortal_WebPart_Title", null, checked((uint)CultureInfo.CurrentUICulture.LCID));
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "ms-webpart-chrome-title");
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "ms-webpart-titleText-withMenu ms-webpart-titleText");
                writer.AddAttribute(HtmlTextWriterAttribute.Title, localizedString);
                writer.RenderBeginTag(HtmlTextWriterTag.H2);
                writer.Write(localizedString);
                writer.RenderEndTag();
                writer.RenderEndTag();
            }
            protected override void Render(HtmlTextWriter writer)
            {
                ExistingCommunitiesWebPart.RenderTitle(writer);
                base.Render(writer);
            }
        }
    }

    Modifiez tout d’abord le nom de la classe ainsi que le namespace afin qu’ils correspondent à votre besoin / votre projet

    La méthode qui va principalement nous intéresser est OnLoad car c’est dans celle ci que sont définis la requête et les templates d’affichage des résultat.

    Par rapport à la problématique 3 (l’exécution de la requête avec des privilèges élevés) le code suivant remontera correctement toutes les communautés (que l’utilisateur y ait accès ou non)

    protected override void OnLoad(EventArgs e)
            {
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    if (base.ActiveDataProvider != null)
                    {
                        string propertyName = string.Format(CultureInfo.InvariantCulture, "[formula:((CommunityMembersCount*0.5)+CommunityTopicsCount+(CommunityRepliesCount*0.5))/(log(abs(Created-{0})+1)+log(abs(LastModifiedTime-{0})+1)+1)]", new object[]
                    {
                        PopularCommunitiesWP.NowInTicks()
                    });

                        base.ActiveDataProvider.QueryTemplate = "WebTemplate:Community";
                        // Local Sharepoint Results
                        base.ActiveDataProvider.SourceID = "8413CD39-2156-4E00-B54D-11EFD9ABDB89";
                        base.ActiveDataProvider.FallbackSort = new List();
                        base.ActiveDataProvider.FallbackSort.Add(new ResultSort(propertyName, Microsoft.Office.Server.Search.Query.SortDirection.Descending));
                    }

                    base.ResultsPerPage = 30;
                    base.RenderTemplateId = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Control_SearchResults.js";
                    base.ItemTemplateId = "~sitecollection/_catalogs/masterpage/Display Templates/System/Item_CommunityPortal.js";
                    base.ShowAdvancedLink = false;
                    base.ShowPreferencesLink = false;
                    base.ShowAlertMe = false;
                    base.ShouldHideControlWhenEmpty = false;
                    base.EmptyMessage = "No communities";
                    base.OnLoad(e);
                });
            }

    Il est bien évidemment possible de raffiner la requêtes suivant vos problématiques (collections spécifiques etc.)

    Le sourceID est le GUID par défaut du scope « Local SharePoint Results », scope par défaut du moteur de recherche FAST interne.

    Le FallbackSort est le mode de tri par défaut (formule détaillée plus haut), vous pouvez le modifier pour afficher par ordre alphabétique ou autre…

    Le RenderTemplateId et le ItemTemplateId sont les templates d’affichages globaux et de chaque item, vous pouvez récupérer les existants et les adapter à vos besoins.

    Par rapport à la problématique 1 (Modification du titre de la WebPart) vous avez plusieurs possibilités.
    Si votre site est multilangue alors vous devez probablement utiliser les fichiers de ressources pour gérer les libellés.
    Dans ce cas modifiez dans la méthode RenderTitle la ligne suivante avec vos informations

    string localizedString = SPUtility.GetLocalizedString("$Resources:NomFichierResource,nomChaineResource", null, checked((uint)CultureInfo.CurrentUICulture.LCID));

    Dans le cas où vous n’auriez qu’une langue à gérer vous pouvez tout simplement définir la variable localizedString.
    Il est aussi possible de supprimer l’override du Title afin de redonner à l’utilisateur la possiblité de définir lui même le titre de sa WebPart. (pensez à supprimer l’appel à RenderTitle dans ce cas)

    Il nous reste à voir la réponse à la problématique 2 (Afficher les communautés dont je suis membre)

    Nous avons vu tout à l’heure que la requête MemberOWSUSER:{User.Name} ne remontait que des items de liste et non des communautés, par contre ce qui peut nous être utile est le chemin url de la communauté.
    Une solution est donc d’exécuter une 1ère requête interne qui récupérera tous les chemins des communautés dont l’utilisateur est membre, puis ensuite une requête qui va récupérer les objets communautés pour affichage.

    private string GetFinalQueryFullCommunitiesPath()
            {
                StringBuilder fullQuery = new StringBuilder();

                string curentUser = SPContext.Current.Web.CurrentUser.Name;

                ResultTableCollection myResultCol = SPSearchUtils.ExecKQLQuery(SPContext.Current.Site, "MemberOWSUSER:\"" + curentUser + "\"");

                if (myResultCol.Count &gt; 0)
                {
                    DataTable resultTables = myResultCol.Filter("TableType", KnownTableTypes.RelevantResults).FirstOrDefault().Table;
                    List lCommunityImMember = resultTables.AsEnumerable().ToList();

                    List lCommPath = new List();
                    foreach (DataRow dr in lCommunityImMember)
                    {
                        // Colonne 19 champ ParentLink
                        string listURL = dr.ItemArray[19].ToString();
                        string webURL = listURL.Substring(0, listURL.IndexOf("Lists") - 1);

                        StringBuilder searchPath = new StringBuilder();

                        searchPath.AppendFormat(" Path:{0}", webURL);
                        lCommPath.Add(searchPath.ToString());
                    }
                    fullQuery.Append("(");
                    foreach (string str in lCommPath)
                    {
                        fullQuery.Append(str);
                    }
                    // Filtrage par communautés
                    fullQuery.Append(") AND WebTemplate:Community");
                }
                return fullQuery.ToString();
            }

    et ensuite de passer la requête générée au moteur de la WebPart (dans OnLoad)

    base.ActiveDataProvider.QueryTemplate = GetFinalQueryFullPopularCommunitiesPath();

    Voilà j’espère que cette note un peu détaillée vous a permis d’appréhender un peu mieux les possibilités du Portail des communautés et de sa WebPart associée.

    N’hésitez pas à revenir vers moi pour plus de détails ou si des points sont peu compréhensibles.

    Des bons outils du travailleur SharePoint

    Bonjour,

    je présenterai ici les divers outils qui sont plus ou moins indispensable à tout développeur SP.

    SharePoint Manager 2013

    SharePoint Manager est un peu la boite à outils que Microsoft aurait oublié de fournir avec sa construction.

    Il va vous permettre d’avoir une vue rapide sur toutes vos Web Applications, vos collections de sites, les features associées/activées à chaque espace, les listes, les bibliothèques, les propriétés de chaque élément bref vous allez obligatoirement gagner du temps à vous en servir.SPManager
    Un rapide exemple des fonctionnalités décrites plus haut

    SP Manager est à installer et exécuter sur le serveur où est bien évidemment installé SP.
    Il se peut qu’il vous demande de l’exécuter en mode administrateur cela dépendra de l’utilisateur que vous utilisez.

    Si vous n’aviez le choix que d’un outil en plus de Visual Studio alors n’hésitez pas téléchargez SharePoint Manager 2013 (existe aussi en version 2010)

    ——————————————————————————-

    ULS Viewer

    Une fois les développements SP commencés vous allez vite vous apercevoir que lorsque SP n’est pas content il n’est pas forcément très prolixe en explications.
    La seule solution qui s’impose est d’aller faire un tour dans le répertoire des logs et d’ouvrir le dernier en date.
    La première erreur est de l’ouvrir avec notepad (ou un autre éditeur texte) vous risquez de prendre peur et de ne plus jamais vouloir refaire ça.

    Microsoft dans sa grande bonté a développé un outil qui facilite (autant que faire se peut) la lecture des logs en question.
    ULS Viewer

    Lancez l’outil, sélectionnez l’emplacement des logs (C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\LOGS\ pour SP 2013, \14 pour SP2010 etc) et vous vous rendrez vite compte qu’il se passe énormément de choses (même sans action de votre part au niveau de l’interface web)

    Heureusement Microsoft avait prévu le coup et a fourni des fonctionnalités de filtrage de l’activité.
    ULS Viewer2

    Ainsi vous serez en mesure de filtrer par sévérité, puis sur des chaines de texte définies.

    Un exemple concret sera le filtrage par le champ Correlation lorsque SP vous indiquera un Guid d’erreur vous verrez uniquement les erreurs liées au problème remonté.

    C’est à mon avis le 2ème outil supplémentaire indispensable, sur ma machine de développement il est ouvert continuellement.

    ——————————————————————————-

    SharePoint Feature Administration and Clean Up Tool

    Voilà un outil dont je me sers plus rarement mais qui a aussi son utilité.

    Plus axé sur la gestion des features au sein de votre ferme il vous permettra en un clic de trouver les features défectueuses (après une migration par exemple) ou bien de voir rapidement les features activées pour un scope donné (Farm / Site / Web)
    FeatureAdmin for SharePoint 2013 - v2.3

    ——————————————————————————-

    ILSpy

    Parfois dans la vie d’un développeur il arrive que vous ayez envie de mettre un peu plus les mains dans le cambouis et de voir ce que font réellement les méthodes que les ptits gars de chez Microsoft vous ont pondu.

    Pour ça ILSpy va vous permettre d’aller charger et de dé-compiler les assemblies qui jusque là restaient opaque à vos yeux.

    ILSpy
    Ci dessus les méthodes de la classe Microsoft.SharePoint.Portal.CommunityEventReceiver

    Une grande partie des DLLs utilisées par SP sont stockées dans C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI (\14 pour SP2010 etc.) vous devriez donc y trouver votre bonheur.

    ——————————————————————————-

    CAML Designer for SP 2013

    Si d’aventure vous éprouvez le besoin de développer des vues personnalisées ou bien de récupérer des items spécifiques dans des listes vous risquez très probablement d’en passer par des requêtes CAML.

    CAML designer vous permettra rapidement de générer la syntaxe correcte pour arriver au résultat voulu, vous gagnerez du temps en déploiements et tests divers.
    CAML Designer for SharePoint 2013.

    Toutes les infos sur son utilisation se trouvent sur le site des développeurs.

    ——————————————————————————-

    Sharepoint Color Palette Tool

    Si le bleu Microsoft n’est pas vraiment votre couleur favorite et que vous souhaitez profiter des nouvelles fonctionnalités de branding SP2013 alors cet outil est fait pour vous.

    Il va vous permettre en 2 clics de générer une palette de couleurs (fichier .spcolor) qu’il sera ensuite possible d’importer dans la gallerie de thèmes (http://url/_catalogs/theme/15) et d’appliquer soit via l’interface (Menu Modifier l’apparence / Change the look) soit via du code.

    SharePoint Color Palette Tool
    Voici un vert des plus chatoyants

    ——————————————————————————-

    Voilà déjà un bon tour d’horizon de ce qui se fait comme aide au développement SP, cette liste n’est bien entendu pas exhaustive et sera complétée au fur et à mesure de mes découvertes.