SharePoint 2007 : Les champs Lookup et SharePointOfView

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.

FeatureQuizzFields

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 :

  1. On récupère le chemin complet vers le fichier spécifié dans la propriété lookupFile et eventuellement les fichier ressource
  2. 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.
  3. 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é :

CustomFieldMenu

A la semaine prochaine :)

<Philippe/>

Publié jeudi 18 décembre 2008 14: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

# re: SharePoint 2007 : Les champs Lookup et SharePointOfView @ mardi 23 décembre 2008 19:46

Attention avec ce code, dans certaines condition la fonction utilisée "GetList" peut planter et provoquer de grosses erreurs sur la ferme SharePoint.

Il suffit simplement de modifier le code en utilisant la seconde "GetListFromURL" :

- http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.getlistfromurl.aspx

(en ajoutant /AllItemsp.aspx dans l'url) et le tour est joué.

Fabrice

ROMELARD Fabrice

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

- 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

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01