SharePoint : ajouter un webpart depuis une feature ?

July 16, 2010

Pour un de mes clients, j’ai été confronté à la difficulté suivante : comment, depuis une feature, instancier un web part sur la page d’accueil ?

En effet, un de mes features fournit web part spécifique, et je souhaite, à l’activation de la feature, qu’un exemplaire de ce dernier soit disponible sur la page d’accueil.

Tout aurait été simple si je déployait un nouvelle page. En effet, lorsque vous déployez une page, vous le faite via un module. Et dans un module, vous définissez un fichier à déployer :

<Module Name="Dashboard"
    SetupPath="sitetemplates\sts"
    Url="">
        <File Url="Dashboard.aspx"
              Path="default.aspx"
              Type="Ghostable">
            <AllUsersWebPart WebPartOrder="1"
                             WebPartZoneID="Right">
              <![CDATA[
  <WebPart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/WebPart/v2">
    <Title>Besoin d'aide ?</Title>
    <FrameType>Standard</FrameType>
    <Assembly>Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
    <TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart</TypeName>
    <ContentLink xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
    <Content xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor">
    exemple de contenu </Content>
    <PartStorage xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
  </WebPart>
  ]]>
        </AllUsersWebPart>
    </File>
</Module>

Ici le noeud AllUsersWebPart permet d’ajouter un webpart à un page que vous êtes entrain de déployer. Comment faire alors pour une page existante, comme par exemple le fichier default.aspx ?

Le seul moyen que j’ai trouvé est de passer par un FeatureReceiver. En gros, on capture l’activation de la feature pour aller, en code, chercher la page visée et lui ajouter le web part :

public override void FeatureActivated(SPFeatureReceiverProperties properties) {
    SPWeb web = null;
    SPSite site = null;
    try {
        if (properties.Feature.Parent is SPWeb) {
            web = properties.Feature.Parent as SPWeb;
            site = web.Site;
        } else {
            site = properties.Feature.Parent as SPSite;
            web = site.RootWeb;
        }
        OnActivated(web, site, properties);
    } finally {
        if (web != null) {
            web.Dispose();
        }
        if (site != null) {
            site.Dispose();
        }
    }
}

Ce bout de code permet de récupérer les objets essentiels de SharePoint, en respectant la disposition de ces objets.

Pour implémenter la méthode OnActivated, il faudra :

  1. Récupérer notre page :

    var defaultPage = web.GetFile("default.aspx");
  2. Obtenir l’instance du gestionnaire de webpart`

    var manager = defaultPage.GetLimitedWebPartManager(PersonalizationScope.Shared);`
  3. Créer le webpart. Ici j’utilise un fichier .webpart qui contient le webpart au format xml. Vous pouvez assez facilement le générer en exportant un fichier .webpart depuis l’interface utilisateur de SharePoint

    string errorMessage;
    
    var fileName = Path.Combine(
        properties.Definition.RootDirectory,
        "mywebpart.webpart"
    );
    
    using(var webPartDefinition = File.OpenRead(fileName)) {
        using(var xr = XmlReader.Create(webPartDefinition)) {
            var webPart = manager.ImportWebPart(
            xr,
            out errorMessage);
        }
    }
  4. L’ajouter à la page

    manager.AddWebPart(webPart, "Right", 0);
  5. Sauvegarder la page

    defaultPage.Update();

Ou en mettant tout bout à bout :

protected virtual void OnActivated(SPWeb web, SPSite site, SPFeatureReceiverProperties properties) {
    var defaultPage = web.GetFile("default.aspx");
    var manager = defaultPage.GetLimitedWebPartManager(PersonalizationScope.Shared);
    string errorMessage;
    var fileName = Path.Combine(
    properties.Definition.RootDirectory,
        "mywebpart.webpart"
        );
    using(var webPartDefinition = File.OpenRead(fileName)) {
        using(var xr = XmlReader.Create(webPartDefinition)) {
            var webPart = manager.ImportWebPart(
            xr,
            out errorMessage);
            manager.AddWebPart(webPart, "Right", 0);
        }
    }

    defaultPage.Update();
}

Je trouve un peu dommage d’avoir à implémenter cet ajout en code, mais le code reste léger donc cela répondra à la problématique !