SharePoint 4 Developers

Guia de referência adicional em desenvolvimento .NET / SharePoint

Criando Content Types Programaticamente via XML

Nesse post demonstro como criar Content Types programaticamente via XML (Nota Rápida)

Oi pessoal,

Segue aqui mais uma nota rápida, agora para a criação de content types via XML. Esse tipo de abordagem não é comum, pois exige Reflection para a utilização de métodos de escopo Internal da classe SPContentType do namespace Microsoft.SharePoint.

Podemos criar Content Types utilizando XML se criarmos uma Feature para tal, porém se você possui o XML e não quer utilizar uma Feature, a única saída é utilizar Reflection como solução.

Considere o seguinte XML:

Code Snippet
  1. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  2.   <ContentType ID="0x0100BEBD79039D12B646B28237616422B927"
  3.                Name="Team"
  4.                Group="World Cup 2010 ContentTypes"
  5.                Version="1">
  6.     <Folder TargetName="_cts/Team" />
  7.     <XmlDocuments>
  8.       <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
  9.         <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
  10.           <Display>ListForm</Display>
  11.           <Edit>ListForm</Edit>
  12.           <New>ListForm</New>
  13.         </FormTemplates>
  14.       </XmlDocument>
  15.     </XmlDocuments>
  16.     <FieldRefs>
  17.       <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" />
  18.       <FieldRef ID="{05a571ad-f9d1-4aab-a703-1af3ae393fbf}" Name="Country" Required="TRUE" />
  19.     </FieldRefs>
  20.   </ContentType>
  21.   <ContentType ID="0x0100BEBD79039D12B646B28237616422B92701"
  22.                Name="Team Player"
  23.                Group="World Cup 2010 ContentTypes"
  24.                Version="1">
  25.     <Folder TargetName="_cts/Team Player" />
  26.     <XmlDocuments>
  27.       <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
  28.         <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
  29.           <Display>ListForm</Display>
  30.           <Edit>ListForm</Edit>
  31.           <New>ListForm</New>
  32.         </FormTemplates>
  33.       </XmlDocument>
  34.     </XmlDocuments>
  35.     <FieldRefs>
  36.       <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" />
  37.       <FieldRef ID="{05a571ad-f9d1-4aab-a703-1af3ae393fbf}" Name="Country" Required="TRUE" />
  38.       <FieldRef ID="{04555083-ec04-4c20-a609-42a283428374}" Name="PlayerName" Required="TRUE" />
  39.       <FieldRef ID="{2f2abeb9-ed3d-41d9-bbde-03d1150396a1}" Name="PlayerAge" Required="FALSE" />
  40.       <FieldRef ID="{ea4b814a-a18b-403a-a33a-8ea5436cb540}" Name="Position" Required="TRUE" />
  41.     </FieldRefs>
  42.   </ContentType>
  43.   <ContentType ID="0x0100BEBD79039D12B646B28237616422B92702"
  44.                Name="Team Venue"
  45.                Group="World Cup 2010 ContentTypes"
  46.                Version="1">
  47.     <Folder TargetName="_cts/Team Venue" />
  48.     <XmlDocuments>
  49.       <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
  50.         <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
  51.           <Display>ListForm</Display>
  52.           <Edit>ListForm</Edit>
  53.           <New>ListForm</New>
  54.         </FormTemplates>
  55.       </XmlDocument>
  56.     </XmlDocuments>
  57.     <FieldRefs>
  58.       <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" />
  59.       <FieldRef ID="{05a571ad-f9d1-4aab-a703-1af3ae393fbf}" Name="Country" Required="TRUE" />
  60.       <FieldRef ID="{af008077-3a7e-41d9-aaca-2b0997bb5e25}" Name="HostCity" Required="TRUE" />
  61.       <FieldRef ID="{9910fa0d-c7e8-464a-9607-caba1502b7dc}" Name="Arrival" Required="FALSE" />
  62.     </FieldRefs>
  63.   </ContentType>
  64. </ Elements >

 

Para adicionar os Content Types acima podemos utilizar o seguinte trecho de código:

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Xml;
  6. using System.IO;
  7. using Microsoft.SharePoint;
  8. using CommonLibrary;
  9.  
  10. namespace ContentTypes
  11. {
  12.     class Program
  13.     {
  14.         static void Main(string[] args)
  15.         {
  16.             using (SPSite site = new SPSite("http://localhost"))
  17.             {
  18.                 using (SPWeb web = site.RootWeb)
  19.                 {
  20.                     XmlDocument xmlDoc = new XmlDocument();
  21.  
  22.                     xmlDoc.Load(Path.GetFullPath("ContentTypes.xml"));
  23.  
  24.                     foreach (XmlElement fieldNode in xmlDoc.DocumentElement.ChildNodes)
  25.                     {
  26.                         SPContentType ct = ContentTypesHelper.CreateEmptyContentType();
  27.  
  28.                         XmlTextReader xmlReader = new XmlTextReader(fieldNode.OuterXml, XmlNodeType.Element, new XmlParserContext(xmlDoc.NameTable, null, "en", XmlSpace.Default));
  29.  
  30.                         ContentTypesHelper.LoadContentTypeFromXml(ct, xmlReader);
  31.  
  32.                         ContentTypesHelper.SetContentTypeScope(ct, web);
  33.  
  34.                         site.RootWeb.ContentTypes.Add(ct);
  35.  
  36.                         site.RootWeb.Update();
  37.                     }
  38.                 }
  39.             }
  40.         }
  41.     }
  42. }

 

Analisando o código acima, podemos perceber que os métodos CreateEmptyContentType, LoadContentTypeFromXml e SetContentTypeScope são responsáveis pela criação dos Content Types e estão contidos na classe utilitária ContentTypesHelper. Esses métodos utilizam Reflection e foram criados por Robert Fridén em seu blog há anos atrás e resolvi reutilizá-los.

Se analisarmos a classe Microsoft.SharePoint.SPContentType pelo .NET Reflector conseguiremos ver seus métodos de escopo Internal, conforme Figura abaixo:

reflector
Figura 1 - Classe SPContentType visualizada pelo .NET Reflector


Vejamos então o código da classe utilitária ContentTypesHelper:

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Reflection;
  6. using System.Xml;
  7. using Microsoft.SharePoint;
  8.  
  9. namespace CommonLibrary
  10. {
  11.     public class ContentTypesHelper
  12.     {
  13.         /// <summary>
  14.         /// Creates an Empty Content Type
  15.         /// </summary>
  16.         public static SPContentType CreateEmptyContentType()
  17.         {
  18.             ConstructorInfo constructor = (typeof(SPContentType)).GetConstructor(
  19.                 BindingFlags.NonPublic | BindingFlags.Instance,
  20.                 null,
  21.                 Type.EmptyTypes,
  22.                 null);
  23.  
  24.             SPContentType ctype = (SPContentType)constructor.Invoke(new object[0]);
  25.  
  26.             return ctype;
  27.         }
  28.  
  29.         /// <summary>
  30.         /// Loads Content Type from XML
  31.         /// </summary>
  32.         public static void LoadContentTypeFromXml(SPContentType ctype, XmlTextReader xmlReader)
  33.         {
  34.             MethodInfo loadMethod = ctype.GetType().GetMethod("Load",
  35.                 BindingFlags.NonPublic | BindingFlags.Instance,
  36.                 null,
  37.                 new Type[] { typeof(XmlReader) },
  38.                 null);
  39.  
  40.             loadMethod.Invoke(ctype, new object[] { xmlReader });
  41.         }
  42.  
  43.         /// <summary>
  44.         /// Sets Content Type from XML
  45.         /// </summary>
  46.         public static void SetContentTypeScope(SPContentType ctype, SPWeb web)
  47.         {
  48.             string scope = web.ServerRelativeUrl.TrimStart('/');
  49.  
  50.             MethodInfo setCTScopeMethod = ctype.GetType().GetMethod(
  51.               "SetScope",
  52.               BindingFlags.NonPublic | BindingFlags.Instance,
  53.               null,
  54.               new Type[] { typeof(string) },
  55.               null);
  56.  
  57.             setCTScopeMethod.Invoke(ctype, new object[] { scope });
  58.  
  59.             PropertyInfo fieldLinksScopeProperty = ctype.FieldLinks.GetType().GetProperty(
  60.               "Scope",
  61.               BindingFlags.NonPublic | BindingFlags.Instance,
  62.               null,
  63.               typeof(string),
  64.               Type.EmptyTypes,
  65.               null);
  66.  
  67.             fieldLinksScopeProperty.SetValue(ctype.FieldLinks, scope, new object[0]);
  68.  
  69.             PropertyInfo contentTypeWebProperty = ctype.GetType().GetProperty(
  70.               "Web",
  71.               BindingFlags.NonPublic | BindingFlags.Instance,
  72.               null,
  73.               typeof(SPWeb),
  74.               Type.EmptyTypes,
  75.               null);
  76.  
  77.             contentTypeWebProperty.SetValue(ctype, web, new object[0]);
  78.         }
  79.     }
  80. }

 

No final os Content Types serão criados conforme a Figura abaixo:

cttypes
Figura 2 – Criação dos Content Types


Baixe a solução aqui.

Considerações

  • Os Site Columns não são herdados automaticamente ao utilizarmos essa abordagem. Observem que os Content Types do XML estão listando todas as colunas, o que não seria necessário se a herança funcionasse corretamente.
  • A adição da propriedade Inherits=TRUE não modifica esse comportamento.

Referências:
http://msdn.microsoft.com/en-us/library/system.reflection.bindingflags.aspx
http://www.15seconds.com/issue/050602.htm
http://vspug.com/bobsbonanza/2007/12/17/deploying-site-columns-and-content-types-to-sharepoint-webs
http://www.red-gate.com/products/reflector


[]’s

Marcel Medina

Clique aqui para ler o mesmo conteúdo em Inglês.

blog comments powered by Disqus