SharePoint 2007 : Activation de Features et ActivationDependencies

Une rapide recherche sur ActivationDependencies vous permettra de comprendre que c'est un élément du schéma assigné aux features qui vous permet :

  • Activer un sous ensemble de features (si elles ont l'attribut Hidden == True)
  • Empecher l'activation d'une feature si une autre n'est pas activée (faire dépendre une feature de scope Web, d'une feature de scope Site par exemple)

Je ne vais pas insister sur cette partie là qui a déjà été revue maintes et maintes fois sur plusieurs blogs mais si vous voulez plus d'informations, voici quelques liens :

Grosso modo, ca ressemble à ça :

   1:  <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
   2:           Id="349C60C9-1B08-4C66-BDF2-ACCBBC46D9FF"
   3:           Title="$Resources:FeatureTitle;"
   4:           Description="$Resources:FeatureDescription;"
   5:           Scope="Site"
   6:           Hidden="False"
   7:           DefaultResourceFile="_Res"
   8:           Version="1.0.0.0">
   9:    <ActivationDependencies>
  10:      <ActivationDependency FeatureId="FBC83A15-59B6-4F52-9863-893A3BBA63AD"/>
  11:      <ActivationDependency FeatureId="BA0AD5C7-C8A4-413C-8DE2-578634474470"/>
  12:      <ActivationDependency FeatureId="08BF6137-EDD2-4401-A536-2A37766D49AC"/>
  13:      <ActivationDependency FeatureId="36541092-DC4F-42AC-820D-0B82E6A45E6B"/>
  14:    </ActivationDependencies>
  15:  </Feature>

Bien que ce mécanisme puisse se révêler être très interessant dans certains cas, je me retrouve de plus en plus souvent à ne pas l'utiliser pour une raison toute simple : Je souhaite activer mes features dans une ordre donné et les désactiver dans l'ordre inverse...

Par exemple, je me retrouve dans ce cas là, lorsque j'ai besoin de créé une liste "Catégorie" dont dépendra un Champ de type "Lookup" que j'intègre à un ContentType "Quizz" qui sera lui même utilisé dans une autre liste (Oui, je sais ca à l'air compliqué comme ça). Pour que tout fonctionne, il faut, bien sur, que je crée tout dans un certain ordre lors de l'activation : Liste Categorie > Lookup > ContentType > Liste Quizz.

Mais lors de la désactivation, c'est l'inverse : Suppréssion de la liste Quizz > ContentType > Lookup > Liste Catégorie.

Ce qui m'oblige à utiliser mon propre système...

Voyons un peu comment j'ai fait :

image La Feature Quizz est la feature qui englobera l'ensemble des autres Features que j'ai décidé de cacher. Elle activera à tour de rôle QuizzListCategory, QuizzFields, QuizzContentType et QuizzListAnimals.

QuizzFields est un peu particulière car elle va à la fois créee des champs "classiques" et un champs de type Lookup, je m'attarderai sur cette fonctionnalité dans un autre post.

QuizzListAnimals et QuizzListCategory ne sont rien d'autre que des fichier XML faisant appel à des FeatureReceiver qui créeront les listes et les paramètreront.

QuizzContentType est très classique et défini le contentType via un fichier XML.

Finalement, voyons en détails la feature Quizz...

Quizz > Feature.xml

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
   3:           Id="321BBA1F-47A9-4B0F-BB17-3A34CFD58CCE"
   4:           Title="$Resources:FeatureTitle;"
   5:           Description="$Resources:FeatureDescription;"
   6:           Scope="Site"
   7:           Hidden="False"
   8:           DefaultResourceFile="_Res"
   9:           ReceiverAssembly="Demo.SharePoint.Quizz.Global, Version=1.0.0.0, Culture=neutral, PublicKeyToken=36d82c640787533c"
  10:           ReceiverClass="Demo.SharePoint.Quizz.Global.FeatureReceiver.QuizzFeatureReceiver"
  11:           Version="1.0.0.0">
  12:    <Properties>
  13:      <Property Key="QuizzListCategory" Value="FBC83A15-59B6-4F52-9863-893A3BBA63AD"/>
  14:      <Property Key="QuizzFields" Value="BA0AD5C7-C8A4-413C-8DE2-578634474470"/>
  15:      <Property Key="QuizzContentType" Value="08BF6137-EDD2-4401-A536-2A37766D49AC"/>
  16:      <Property Key="QuizzListAnimals" Value="36541092-DC4F-42AC-820D-0B82E6A45E6B"/>
  17:    </Properties>
  18:  </Feature>

Je stocque dans les propriétés de la feature, la liste de toute les autres features que je veux activées puis dans le featureReceiver...

Quizz > QuizzFeatureReceiver

   1:          public override void FeatureActivated(SPFeatureReceiverProperties properties)
   2:          {
   3:              SPSite site = properties.Feature.Parent as SPSite;
   4:   
   5:              try
   6:              {
   7:                  foreach (SPFeatureProperty featureProp in properties.Feature.Properties)
   8:                  {
   9:                      Guid featureGuid = new Guid(featureProp.Value);
  10:                      site.Features.Add(featureGuid);
  11:                  }
  12:              }
  13:              catch (Exception ex)
  14:              {
  15:                  ULS.WriteError("Error activating Quizz Features. Message : " + ex.Message, LOGCATEGORY);
  16:                  ULS.WriteError("Rolling back...", LOGCATEGORY);
  17:   
  18:                  site.Features.Remove(properties.Feature.DefinitionId);
  19:              }
  20:          }

Le comportement est "on ne peut plus simple", j'itère donc sur chacune des propriétés et m'assure d'ajouter la feautre à la collection de features de la collection de site. On pourrait s'assurer du bon format de featureProp.value afin de ne pas se retrouver avec un Guid == null.

Mais le plus important est de bien s'occuper de la gestion d'erreur. Si jamais l'activation d'une feature génère une exception, je log l'erreur (ULS.WriteError est une méthode fournie par SharePointOfView sur Codeplex (www.codeplex.com/sharepointofview))  et lance la désactivation de la feature Quizz afin de me retrouver dans un environnement stable.

   1:          public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
   2:          {
   3:              SPSite site = properties.Feature.Parent as SPSite;
   4:   
   5:              try
   6:              {
   7:                  for (int i = properties.Feature.Properties.Count - 1; i >= 0; i--)
   8:                  {
   9:                      SPFeatureProperty featureProp = properties.Feature.PropertiesIdea as SPFeatureProperty;
  10:   
  11:                      Guid featureGuid = new Guid(featureProp.Value);
  12:   
  13:                      SPFeature feature = site.Features[featureGuid];
  14:   
  15:                      if (feature != null)
  16:                      {
  17:                          site.Features.Remove(featureGuid);
  18:                      }
  19:                  }
  20:              }
  21:              catch (Exception ex)
  22:              {
  23:                  ULS.WriteError("Error de-activating Quizz Features. Message : " + ex.Message, LOGCATEGORY);
  24:                  throw ex;
  25:              }
  26:          }

J'itère donc à l'envers sur chacune des propriétés et les désactive au fur et à mesure si elles ont bien réussi à s'activer précédemment.

Bien entendu, pour que tout celà fonctionne, il faut que chaque feature se charge de faire le "nettoyage" de sa partie, par exemple, pour la feature QuizzListCategory :

Quizz > QuizzListCategoryFeatureReceiver

   1:      public override void FeatureActivated(SPFeatureReceiverProperties properties)
   2:          {
   3:              SPSite site = properties.Feature.Parent as SPSite;
   4:   
   5:              SPWeb web = site.RootWeb;
   6:   
   7:              try
   8:              {
   9:                  string quizzListCategoryTitle = Localization.GetResource("Quizz_List_QuizzCategory_Title", RESFILE, web.Language);
  10:                  string quizzListCategoryDescription = Localization.GetResource("Quizz_List_QuizzCategory_Description", RESFILE, web.Language);
  11:   
  12:                  web.Lists.Add(quizzListCategoryTitle,
  13:                      quizzListCategoryDescription,
  14:                      "Lists/QuizzCategory",
  15:                      "00BFEA71-DE22-43B2-A848-C05709900100",
  16:                      100,
  17:                      "100",
  18:                      SPListTemplate.QuickLaunchOptions.On);
  19:              }
  20:              catch (Exception ex)
  21:              {
  22:                  ULS.WriteError(ex.Message, LOGCATEGORY);
  23:                  site.Features.Remove(properties.Feature.DefinitionId);
  24:                  throw ex;
  25:              }
  26:   
  27:              web.Dispose();
  28:          }
  29:   
  34:          public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
  35:          {
  36:              SPSite site = properties.Feature.Parent as SPSite;
  37:              SPWeb web = site.RootWeb;
  38:   
  39:              try
  40:              {
  41:                  string quizzListCategoryTitle = Localization.GetResource("Quizz_List_QuizzCategory_Title", RESFILE, web.Language);
  42:                  SPList quizzListCategory = web.Lists[quizzListCategoryTitle];
  43:                  quizzListCategory.Delete();
  44:              }
  45:              catch (Exception ex)
  46:              {
  47:                  ULS.WriteError(ex.Message, LOGCATEGORY);
  48:              }
  49:   
  50:              web.Dispose();
  51:          }

Au niveau de l'activation, j'essaye d'ajouter une liste "custom" mais si jamais il y a un problème, je logue l'erreur, j'essaye de désactiver la feature qui va essayer de supprimer la liste créer, puis je préviens la feature "Globale" en lui passant de nouveau l'exception. La désactivation essaye tout simplement de supprimer la liste.

Je vais améliorer un peu la gestion de cette feature d'activation et je la publierai sur notre projet commun SharePointOfView sous peu.

Je reviendrai dans les semaines à venir sur cette exemple de solution basée sur un Quizz (que j'expliquerais en détails à l'occasion) et en profiterai pour détailler deux trois fonctionnalité que je vais implementer.

<Philippe/>

Publié lundi 1 décembre 2008 11:00 par phil
Classé sous , , , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

About phil

Philippe Sentenac est Consultant SharePoint à Wygwam en région Parisienne. Il intervient essentiellement sur des missions liées à SharePoint (2007 et 2010 ) mais aussi autour du Web 2.0. Plus généralement, il s'intéresse à l'ASP.Net (MVC) , à Silverlight, et à tout ce qui est orienté Web en rapport avec les nouvelles technologies, qu'il pratique depuis 2006. Féru de développement, il est passionné par les problématiques de méthodologies et d'industrialisation du développement.

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01