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 :
 |
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.Properties
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/>