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
- <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
- <ContentType ID="0x0100BEBD79039D12B646B28237616422B927"
- Name="Team"
- Group="World Cup 2010 ContentTypes"
- Version="1">
- <Folder TargetName="_cts/Team" />
- <XmlDocuments>
- <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
- <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
- <Display>ListForm</Display>
- <Edit>ListForm</Edit>
- <New>ListForm</New>
- </FormTemplates>
- </XmlDocument>
- </XmlDocuments>
- <FieldRefs>
- <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" />
- <FieldRef ID="{05a571ad-f9d1-4aab-a703-1af3ae393fbf}" Name="Country" Required="TRUE" />
- </FieldRefs>
- </ContentType>
- <ContentType ID="0x0100BEBD79039D12B646B28237616422B92701"
- Name="Team Player"
- Group="World Cup 2010 ContentTypes"
- Version="1">
- <Folder TargetName="_cts/Team Player" />
- <XmlDocuments>
- <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
- <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
- <Display>ListForm</Display>
- <Edit>ListForm</Edit>
- <New>ListForm</New>
- </FormTemplates>
- </XmlDocument>
- </XmlDocuments>
- <FieldRefs>
- <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" />
- <FieldRef ID="{05a571ad-f9d1-4aab-a703-1af3ae393fbf}" Name="Country" Required="TRUE" />
- <FieldRef ID="{04555083-ec04-4c20-a609-42a283428374}" Name="PlayerName" Required="TRUE" />
- <FieldRef ID="{2f2abeb9-ed3d-41d9-bbde-03d1150396a1}" Name="PlayerAge" Required="FALSE" />
- <FieldRef ID="{ea4b814a-a18b-403a-a33a-8ea5436cb540}" Name="Position" Required="TRUE" />
- </FieldRefs>
- </ContentType>
- <ContentType ID="0x0100BEBD79039D12B646B28237616422B92702"
- Name="Team Venue"
- Group="World Cup 2010 ContentTypes"
- Version="1">
- <Folder TargetName="_cts/Team Venue" />
- <XmlDocuments>
- <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
- <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
- <Display>ListForm</Display>
- <Edit>ListForm</Edit>
- <New>ListForm</New>
- </FormTemplates>
- </XmlDocument>
- </XmlDocuments>
- <FieldRefs>
- <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" />
- <FieldRef ID="{05a571ad-f9d1-4aab-a703-1af3ae393fbf}" Name="Country" Required="TRUE" />
- <FieldRef ID="{af008077-3a7e-41d9-aaca-2b0997bb5e25}" Name="HostCity" Required="TRUE" />
- <FieldRef ID="{9910fa0d-c7e8-464a-9607-caba1502b7dc}" Name="Arrival" Required="FALSE" />
- </FieldRefs>
- </ContentType>
- </ Elements >
Para adicionar os Content Types acima podemos utilizar o seguinte trecho de código:
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Xml;
- using System.IO;
- using Microsoft.SharePoint;
- using CommonLibrary;
-
- namespace ContentTypes
- {
- class Program
- {
- static void Main(string[] args)
- {
- using (SPSite site = new SPSite("http://localhost"))
- {
- using (SPWeb web = site.RootWeb)
- {
- XmlDocument xmlDoc = new XmlDocument();
-
- xmlDoc.Load(Path.GetFullPath("ContentTypes.xml"));
-
- foreach (XmlElement fieldNode in xmlDoc.DocumentElement.ChildNodes)
- {
- SPContentType ct = ContentTypesHelper.CreateEmptyContentType();
-
- XmlTextReader xmlReader = new XmlTextReader(fieldNode.OuterXml, XmlNodeType.Element, new XmlParserContext(xmlDoc.NameTable, null, "en", XmlSpace.Default));
-
- ContentTypesHelper.LoadContentTypeFromXml(ct, xmlReader);
-
- ContentTypesHelper.SetContentTypeScope(ct, web);
-
- site.RootWeb.ContentTypes.Add(ct);
-
- site.RootWeb.Update();
- }
- }
- }
- }
- }
- }
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:
Figura 1 - Classe SPContentType visualizada pelo .NET Reflector
Vejamos então o código da classe utilitária ContentTypesHelper:
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Reflection;
- using System.Xml;
- using Microsoft.SharePoint;
-
- namespace CommonLibrary
- {
- public class ContentTypesHelper
- {
- /// <summary>
- /// Creates an Empty Content Type
- /// </summary>
- public static SPContentType CreateEmptyContentType()
- {
- ConstructorInfo constructor = (typeof(SPContentType)).GetConstructor(
- BindingFlags.NonPublic | BindingFlags.Instance,
- null,
- Type.EmptyTypes,
- null);
-
- SPContentType ctype = (SPContentType)constructor.Invoke(new object[0]);
-
- return ctype;
- }
-
- /// <summary>
- /// Loads Content Type from XML
- /// </summary>
- public static void LoadContentTypeFromXml(SPContentType ctype, XmlTextReader xmlReader)
- {
- MethodInfo loadMethod = ctype.GetType().GetMethod("Load",
- BindingFlags.NonPublic | BindingFlags.Instance,
- null,
- new Type[] { typeof(XmlReader) },
- null);
-
- loadMethod.Invoke(ctype, new object[] { xmlReader });
- }
-
- /// <summary>
- /// Sets Content Type from XML
- /// </summary>
- public static void SetContentTypeScope(SPContentType ctype, SPWeb web)
- {
- string scope = web.ServerRelativeUrl.TrimStart('/');
-
- MethodInfo setCTScopeMethod = ctype.GetType().GetMethod(
- "SetScope",
- BindingFlags.NonPublic | BindingFlags.Instance,
- null,
- new Type[] { typeof(string) },
- null);
-
- setCTScopeMethod.Invoke(ctype, new object[] { scope });
-
- PropertyInfo fieldLinksScopeProperty = ctype.FieldLinks.GetType().GetProperty(
- "Scope",
- BindingFlags.NonPublic | BindingFlags.Instance,
- null,
- typeof(string),
- Type.EmptyTypes,
- null);
-
- fieldLinksScopeProperty.SetValue(ctype.FieldLinks, scope, new object[0]);
-
- PropertyInfo contentTypeWebProperty = ctype.GetType().GetProperty(
- "Web",
- BindingFlags.NonPublic | BindingFlags.Instance,
- null,
- typeof(SPWeb),
- Type.EmptyTypes,
- null);
-
- contentTypeWebProperty.SetValue(ctype, web, new object[0]);
- }
- }
- }
No final os Content Types serão criados conforme a Figura abaixo:
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.
Oi pessoal,
De agora em diante também postarei algumas notas rápidas com dicas de desenvolvimento. Acredito que serão bastante úteis no dia-a-dia de cada um.
Nesse post demonstro como criar Site Columns programaticamente via XML.
Considere o seguinte XML de Site Columns:
Code Snippet
- <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
- <Field Type="Choice" Description="List of countries" Required="TRUE" Format="Dropdown" FillInChoice="FALSE" Group="World Cup 2010 Columns" ID="{05A571AD-F9D1-4aab-A703-1AF3AE393FBF}" StaticName="Country" Name="Country" DisplayName="Country" Version="1">
- <CHOICES>
- <CHOICE>Algeria</CHOICE>
- <CHOICE>Argentina</CHOICE>
- <CHOICE>Australia</CHOICE>
- <CHOICE>Brazil</CHOICE>
- <CHOICE>Cameroon</CHOICE>
- <CHOICE>Chile</CHOICE>
- <CHOICE>Côte d'Ivoire</CHOICE>
- <CHOICE>Denmark</CHOICE>
- <CHOICE>England</CHOICE>
- <CHOICE>France</CHOICE>
- <CHOICE>Germany</CHOICE>
- <CHOICE>Ghana</CHOICE>
- <CHOICE>Greece</CHOICE>
- <CHOICE>Honduras</CHOICE>
- <CHOICE>Italy</CHOICE>
- <CHOICE>Japan</CHOICE>
- <CHOICE>Korea DPR</CHOICE>
- <CHOICE>Korea Republic</CHOICE>
- <CHOICE>Mexico</CHOICE>
- <CHOICE>Netherlands</CHOICE>
- <CHOICE>New Zealand</CHOICE>
- <CHOICE>Nigeria</CHOICE>
- <CHOICE>Paraguay</CHOICE>
- <CHOICE>Portugal</CHOICE>
- <CHOICE>Serbia</CHOICE>
- <CHOICE>Slovakia</CHOICE>
- <CHOICE>Slovenia</CHOICE>
- <CHOICE>South Africa</CHOICE>
- <CHOICE>Spain</CHOICE>
- <CHOICE>Switzerland</CHOICE>
- <CHOICE>Uruguay</CHOICE>
- <CHOICE>USA</CHOICE>
- </CHOICES>
- </Field>
- <Field Type="Choice" Description="List of host cities" Required="TRUE" Format="Dropdown" FillInChoice="FALSE" Group="World Cup 2010 Columns" ID="{AF008077-3A7E-41d9-AACA-2B0997BB5E25}" StaticName="HostCity" Name="HostCity" DisplayName="City" Version="1">
- <Default>Johannesburg</Default>
- <CHOICES>
- <CHOICE>Cape Town</CHOICE>
- <CHOICE>Durban</CHOICE>
- <CHOICE>Johannesburg</CHOICE>
- <CHOICE>Mangaung/Bloemfontein</CHOICE>
- <CHOICE>Nelson Mandela Bay/Port Elizabeth</CHOICE>
- <CHOICE>Nelspruit</CHOICE>
- <CHOICE>Polokwane</CHOICE>
- <CHOICE>Rustenburg</CHOICE>
- <CHOICE>Tshwane/Pretoria</CHOICE>
- </CHOICES>
- </Field>
- <Field Type="Choice" Description="List of players" Required="TRUE" Format="Dropdown" FillInChoice="FALSE" Group="World Cup 2010 Columns" ID="{EA4B814A-A18B-403a-A33A-8EA5436CB540}" StaticName="Position" Name="Position" DisplayName="Position" Version="1">
- <CHOICES>
- <CHOICE>Defensive Midfielder</CHOICE>
- <CHOICE>Forward</CHOICE>
- <CHOICE>Goalkeeper</CHOICE>
- <CHOICE>Left Back</CHOICE>
- <CHOICE>Left Midfielder</CHOICE>
- <CHOICE>Right Back</CHOICE>
- <CHOICE>Right Midfielder</CHOICE>
- <CHOICE>Stopper</CHOICE>
- <CHOICE>Striker</CHOICE>
- <CHOICE>Sweeper</CHOICE>
- </CHOICES>
- </Field>
- <Field Type="Text" Description="The name of the player" Required="TRUE" MaxLength="255" Group="World Cup 2010 Columns" ID="{04555083-EC04-4c20-A609-42A283428374}" StaticName="PlayerName" Name="PlayerName" DisplayName="Player Name"></Field>
- <Field Type="Number" Description="The age of the player" Required="FALSE" Decimals="0" Group="World Cup 2010 Columns" ID="{2F2ABEB9-ED3D-41d9-BBDE-03D1150396A1}" StaticName="PlayerAge" Name="PlayerAge" DisplayName="Player Age"></Field>
- <Field Type="DateTime" Description="The date of arrival" Required="FALSE" Format="DateOnly" Group="World Cup 2010 Columns" ID="{9910FA0D-C7E8-464a-9607-CABA1502B7DC}" StaticName="Arrival" Name="Arrival" DisplayName="Arrival" />
- </Elements>
Para adicionar os Site Columns acima podemos utilizar o seguinte trecho de código:
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Xml;
- using System.IO;
- using Microsoft.SharePoint;
-
- namespace SiteColumns
- {
- public class Program
- {
- public static void Main(string[] args)
- {
- using (SPSite site = new SPSite("http://localhost/"))
- {
- XmlDocument xmlDoc = new XmlDocument();
-
- xmlDoc.Load(Path.GetFullPath("SiteColumns.xml"));
-
- foreach (XmlElement fieldNode in xmlDoc.DocumentElement.ChildNodes)
- {
- site.RootWeb.Fields.AddFieldAsXml(fieldNode.OuterXml);
- }
- }
- }
- }
- }
No final os Site Columns serão criados conforme a Figura abaixo:
Figura 1 – Criação dos Site Columns
Baixe a solução aqui.
[]’s
Marcel Medina
Clique aqui para ler o mesmo conteúdo em Inglês.
Oi pessoal, tudo bem?
A integração Visual Studio 2010 x SharePoint Foundation 2010 no deploy de soluções do tipo BDC Model ainda apresenta problemas. O problema está no deploy pelo Visual Studio, justamente por não encontrar as DLLs necessárias para realizar a operação. Seguem mensagens de erro encontradas:
Problemas:
Error occurred in deployment step 'Add Solution': Failed to load receiver assembly "Microsoft.Office.SharePoint.ClientExtensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" for feature "<Sua Feature>: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Office.SharePoint.ClientExtensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' or one of its dependencies. The system cannot find the file specified.
File name: 'Microsoft.Office.SharePoint.ClientExtensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.Load(String assemblyString)
at Microsoft.SharePoint.Administration.SPFeatureDefinition.get_ReceiverObject()
Error occurred in deployment step 'Add Solution': Could not load file or assembly 'Microsoft.Office.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' or one of its dependencies. The system cannot find the file specified.
Solução:
Para eliminar esse problema é necessário a cópia de alguns arquivos do SharePoint Server 2010, encontrados no diretório 14\ISAPI:
- microsoft.office.sharepoint.clientextensions.dll
- Microsoft.Office.Sharepoint.ClientExtensions.xml
- Microsoft.Office.Server.dll
- Microsoft.Office.Server.xml
Registre as DLLs no GAC para eliminar o problema, simplesmente copiando as DLLs para o diretório C:\Windows\assembly.
Reinicie o Visual Studio 2010 e realize o deploy de sua solução do BDC Model sem problemas!
[]’s
Marcel Medina
Clique aqui para ler o mesmo conteúdo em Inglês.