Si vous ne connaissez pas encore notre projet Codeplex "SharePointOfView :
http://blogs.developpeur.org/phil/archive/2008/06/30/sharepoint-2007-projet-codeplex-sharepointofview.aspx
Dans le dernier post, je vous ai expliqué comment créé simplement des colonnes de site en exportant les informations depuis SharePoint grâce à une application très intuitive : Imtech FieldsExplorer.
Néanmoins, il y a un petit souci lors de l'export sur les champs Lookup.
0:
1: <Field Type="Lookup" DisplayName="Catégorie" Required="FALSE" List="b23273d2-946b-41e8-81cb-8911dadc2f92" ShowField="Title" Sortable="FALSE" UnlimitedLengthInDocumentLibrary="FALSE" ID="{48b006bf-5e14-43cb-b5cb-9a3dae4f0646}" Group="Demo" StaticName="QuizzCategory" Name="QuizzCategory" SourceID="http://schemas.microsoft.com/sharepoint/v3" xmlns="http://schemas.microsoft.com/sharepoint/" WebId="1483aa7c-a45b-410e-977c-45174ccbce5d" Version="1" />
2:
Ce bout d'XML précise à SharePoint que le champ Catégorie recupère la valeur "Title" de chaque éléments de la liste qui est référencé par le Guid : b23273d2-946b-41e8-81cb-8911dadc2f92 dans le Web référencé par le Guid : 1483aa7c-a45b-410e-977c-45174ccbce5d de la collection courante....
Or comme ces GUIDs sont différents entre chaque environnement SharePoint (celle des developpeurs, celle d'intégration, recette, prod), il n'est pas possible de se baser sur ce type d'information.
Si vous commencez à vous creuser la tête et à fouiller la toile à la recherche d'une solution (http://delicious.com/philippesentenac/lookup), vous trouverez qu'un certain Chris O'Brien a utilisé la solution suivante :
Placer dans un fichier XML, le texte suivant qui ressemble enormement à une déclaration de Field standard si ce n'est le List="Lists/QuizzAnimaux".
1: <?xml version="1.0"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3: <Field Type="Lookup" DisplayName="$Resources:Quizz,Quizz_Field_Category;" Required="FALSE"
4: List="Lists/QuizzAnimaux" ShowField="Title" Sortable="FALSE" UnlimitedLengthInDocumentLibrary="FALSE"
5: ID="{48b006bf-5e14-43cb-b5cb-9a3dae4f0646}" Group="Demo"
6: StaticName="QuizzCategory" Name="QuizzCategory" SourceID="http://schemas.microsoft.com/sharepoint/v3"/>
7: </Elements>
L'idée est de récupérer le GUID correspondant à cette liste et de remplacer la valeur de l'attribut List lors de l'activation d'une Feature via un FeatureReceiver.
Voyons en détails :
Après export des fields avec l'outil cité précédemment, j'organise la Feature "QuizzFields" comme ci-dessous.
feature.xml
1: <?xml version="1.0"?>
2: <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
3: Id="BA0AD5C7-C8A4-413C-8DE2-578634474470"
4: Title="$Resources:FeatureTitle;"
5: Description="$Resources:FeatureTitle;"
6: Scope="Site"
7: Hidden="True"
8: DefaultResourceFile="_Res"
9: ReceiverAssembly="SharePointOfView, Version=1.1.0.0, Culture=neutral, PublicKeyToken=85a56337686e234b"
10: ReceiverClass="SharePointOfView.EventReceivers.LookupCreationFeatureReceiver"
11: Version="1.0.0.0">
12: <ElementManifests>
13: <ElementManifest Location="elements.xml" />
14: </ElementManifests>
15: <Properties>
16: <Property Key="lookupFile" Value="lookupFields.xml"/>
17: <Property Key="resourceFile" Value="Quizz"/>
18: </Properties>
19: </Feature>
Vous remarquerez l'utilisation d'un nouveau FeatureReceiver de SharePoint : le LookupCreationFeatureReceiver.
elements.xml
1: <?xml version="1.0"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3: <Field Type="Note" DisplayName="$Resources:Quizz,Quizz_Field_Answers;" Required="FALSE" NumLines="6"
4: RichText="TRUE" RichTextMode="Compatible" Sortable="FALSE" Group="Demo" ID="{2a3f841f-ff0a-4348-9028-cf5e0a16a089}"
5: StaticName="QuizzAnswers" Name="QuizzAnswers" SourceID="http://schemas.microsoft.com/sharepoint/v3"/>
6: <Field Type="Note" DisplayName="$Resources:Quizz,Quizz_Field_Choices;" Required="FALSE" NumLines="6"
7: RichText="TRUE" RichTextMode="Compatible" Sortable="FALSE" Group="Demo" ID="{71d78a3d-eaad-4e6e-b905-e183cf774693}"
8: StaticName="QuizzChoices" Name="QuizzChoices" SourceID="http://schemas.microsoft.com/sharepoint/v3"/>
9: <Field Type="Note" DisplayName="$Resources:Quizz,Quizz_Field_Question;" Required="FALSE" NumLines="6"
10: RichText="TRUE" RichTextMode="Compatible" Sortable="FALSE" Group="Demo" ID="{3bfd9f63-3c57-4a0d-b22a-3ed324150348}"
11: StaticName="QuizzQuestion" Name="QuizzQuestion"/>
12: <Field ... />
13: <Field ... />
14: </Elements>
Je reviendrai dans un précédent post sur les deux autres champs qui ne sont pas définis dans cet extrait...
lookupFields.xml
1: <?xml version="1.0"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3: <Field Type="Lookup" DisplayName="$Resources:Quizz,Quizz_Field_Category;" Required="FALSE"
4: List="$Resources:Quizz,Quizz_List_QuizzCategory_Title;" ShowField="Title" Sortable="FALSE" UnlimitedLengthInDocumentLibrary="FALSE"
5: ID="{48b006bf-5e14-43cb-b5cb-9a3dae4f0646}" Group="Demo"
6: StaticName="QuizzCategory" Name="QuizzCategory" SourceID="http://schemas.microsoft.com/sharepoint/v3"/>
7: </Elements>
SharePointOfView.EventReceivers.LookupCreationFeatureReceiver
1: namespace SharePointOfView.EventReceivers
2: {
3: public class LookupCreationFeatureReceiver : SPFeatureReceiver
4: {
5: /// <summary>
6: /// Occurs after a Feature is activated.
7: /// Get values for properties named 'lookupFile' and 'resourceFile'. The key 'lookupfile' is mandatory
8: /// This code is based on the solution provided by Chris O'Brien at http://www.codeplex.com/SP2007LookupFields
9: /// </summary>
10: /// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
11: public override void FeatureActivated(SPFeatureReceiverProperties properties)
12: {
13: SPSite site = properties.Feature.Parent as SPSite;
14: SPWeb web = site.RootWeb;
15:
16: try
17: {
18: string filePath = Path.Combine(properties.Definition.RootDirectory, properties.Feature.Properties["lookupFile"].Value);
19: string resourceFile = string.Empty;
20:
21: try
22: {
23: resourceFile = properties.Feature.Properties["resourceFile"].Value;
24: }
25: catch { }
26:
27: XmlDocument xmlDoc = new XmlDocument();
28: xmlDoc.Load(filePath);
29:
30: XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable);
31: nsMgr.AddNamespace("def", "http://schemas.microsoft.com/sharepoint/");
32:
33: XmlNodeList fields = xmlDoc.SelectNodes(".//def:Field", nsMgr);
34:
35: foreach (XmlNode field in fields)
36: {
37: string listName = string.Empty;
38:
39: foreach (XmlAttribute attribute in field.Attributes)
40: {
41: if (attribute.Value.StartsWith("$Resources:") && !string.IsNullOrEmpty(resourceFile))
42: {
43: attribute.Value = Localization.GetResource(attribute.Value.Replace("$Resources:", string.Empty), resourceFile, web.Language);
44: }
45:
46: if (attribute.Name == "List")
47: {
48: listName = attribute.Value;
49: }
50: }
51:
52: SPList referencedList;
53:
54: if (listName != String.Empty && web.Lists.SovTryGet(listName, out referencedList))
55: {
56: field.Attributes["List"].Value = referencedList.ID.ToString();
57: string realFieldToCreate = field.OuterXml;
58:
59: string lookupColumnName = web.Fields.AddFieldAsXml(realFieldToCreate);
60:
61: SPFieldLookup lookupColumn = web.Fields.GetFieldByInternalName(lookupColumnName) as SPFieldLookup;
62:
63: lookupColumn.LookupWebId = web.ID;
64: lookupColumn.Update();
65: }
66: }
67: }
68: catch (Exception ex)
69: {
70: ULS.WriteError("Error activating lookup creation feature. Message : " + ex.Message, "SharePointOfView");
71: site.Features.Remove(properties.Feature.DefinitionId);
72: throw ex;
73: }
74: finally
75: {
76: web.Dispose();
77: }
78: }
79:
80: /// <summary>
81: /// Occurs when a Feature is deactivated.
82: /// </summary>
83: /// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
84: public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
85: {
86: SPSite site = properties.Feature.Parent as SPSite;
87: SPWeb web = site.RootWeb;
88:
89: try
90: {
91: string filePath = Path.Combine(properties.Definition.RootDirectory, properties.Feature.Properties["lookupFile"].Value);
92:
93: XmlDocument xmlDoc = new XmlDocument();
94: xmlDoc.Load(filePath);
95:
96: XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable);
97: nsMgr.AddNamespace("def", "http://schemas.microsoft.com/sharepoint/");
98:
99: XmlNodeList fields = xmlDoc.SelectNodes(".//def:Field/@ID", nsMgr);
100:
101: Guid lookupfieldGuid;
102: foreach (XmlNode field in fields)
103: {
104: lookupfieldGuid = new Guid(field.Value);
105:
106: SPFieldLookup lookupColumn = web.Fields[lookupfieldGuid] as SPFieldLookup;
107: lookupColumn.Delete();
108: }
109: }
110: catch (Exception ex)
111: {
112: ULS.WriteInformation("Error de-activating lookup creation feature. Message : " + ex.Message, "SharePointOfView");
113: }
114:
115: web.Dispose();
116: }
117:
118: #region Not Used
119: public override void FeatureInstalled(SPFeatureReceiverProperties properties) { }
120: public override void FeatureUninstalling(SPFeatureReceiverProperties properties) { }
121: #endregion
122: }
123: }
124:
Ce bout de code se résume ainsi :
- On récupère le chemin complet vers le fichier spécifié dans la propriété lookupFile et eventuellement les fichier ressource
- Pour chaque champ "Field" spécifié dans le fichier, on traduit les valeurs des attributs commencant par $Resources et on récupère l'url de la liste.
- Si la liste existe dans le site alors on fait le remplacement de l'url en Guid et on crée le Field
La désactivation fait grosso modo la même chose mais supprime le champ au lieu de le créer :)
Vous pourrez retrouver ce FeatureReceiver dans les derniers bouts de code archivés dans le projet codeplex SharePointOfView : http://codeplex.com/SharePointOfView
Je n'ai pas encore créé de nouvelle release mais ca ne saurait tarder :)
Dans le prochain post, je vous expliquerais comment obtenir rajouter dans proprement dans une liste, un menu sur une colonne de site personnalisé :
A la semaine prochaine :)
<Philippe/>