SharePoint 4 Developers

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

Rebranding branded SharePoint Publishing Sites - Dilemas

Se você for fazer o Rebrand de um site no SharePoint que já possui um branding aplicado, e se o orçamento lhe permitir ir mais longe, faça o planejamento de um upgrade antes de aplicar o rebranding. Isto vai minimizar qualquer retrabalho e, consequentemente, custos no futuro.

Oi pessoal,

Decidi escrever algo a respeito sobre alguns dilemas, devido a um projeto que trabalhei recentemente. Fazer ou não fazer? Eis a questão.

Se você enfrentar situações para as quais não tem uma resposta imediata, vai precisar despender um tempo pensando nisso.

dilema

Rebranding branded sites

Quando um cliente vem até você dizendo que quer fazer o rebrand de um site SharePoint já existente, a primeira coisa que vem à minha mente ... isso é um upgrade?

Acontece que, pelo fato de sempre ter trabalhado no desenvolvimento de Custom branded sites, sempre pensei dessa maneira.

Geralmente se faz o Rebrand de um site depois de atualizar o ambiente, mas isso não é uma regra.

Atualizar, ou não atualizar?

Se você for fazer o Rebrand de um site no SharePoint que já possui um branding aplicado, e se o orçamento lhe permitir ir mais longe, faça o planejamento de um upgrade antes de aplicar o rebranding. Isto vai minimizar qualquer retrabalho e, consequentemente, custos no futuro.

Mas se o orçamento não incluir o upgrade, é sua tarefa dizer ao cliente que por não fazer o upgrade do ambiente, os custos podem ficar caros no futuro, especialmente se você precisar pular plataformas. Ex: do MOSS 2007 para SP2013. Estes custos envolvem:

  • Infra-estrutura - A nova arquitetura, recursos, hardware.
  • Soluções - Compatibilidade de customisações.

Porém cabe ao cliente decidir onde investir o capital. O cliente precisa ser guiado para o que for melhor para o seu negócio.

No meu caso o cliente decidiu não fazer o upgrade do ambiente, por isso meu trabalho se resumiu em fazer o rebrand do branded site no mesmo ambiente. Embora possa parecer simples, não é, é preciso considerar:

  • Design / Look and Feel – Alterações de site definitions, master pages e page layouts.
  • Content Types – Novos campos (fields) disponíveis em page layouts.
  • Web parts – Mapeamento do existente x novo.
  • Navigation – Mudanças na navegação Top e da Esquerda.
  • Reusable content – Seções de Cabeçalho e Rodapé.

Ser ou não ser guiado por mock-ups?

Antes dos mock-ups serem criados, converse com os business users sobre conteúdo, pergunte como as páginas vão exibir as informações criadas por content editors.

Não seja guiado por mock-ups, em geral agências de design não têm a menor idéia sobre SharePoint, muito provável o design que for criado pode ter algumas incompatibilidades com o SharePoint.

Atualizar ou não atualizar site definitions?

Nunca modifique schemas de site definitions depois que site collections / sites já tiverem sido provisionados. Isto não é suportado! Aqui está a prova: http://support.microsoft.com/kb/898631

Utilize o object model para alterar site definitions. Se você estiver provisionando sub-sites, adicione código à feature receiver (ativação ou desativação) para modificar site definitions.

Obs: Você pode criar um novo site definition se você quiser um novo começo, mas você precisará adaptar o código existente e migrar site collection existentes que usam a versão antiga do site definition. Pessoalmente não recomendo essa abordagem.

Criar ou não criar uma nova master page?

Depende. Sim, se você precisar manter a versão antiga, mas se você fizer isso vai precisar modificar a feature receiver para atribuir a nova master page programaticamente durante o provisionamento de um site collection / site. Lembre-se que a antiga master page ainda está atrelada ao schema do site definition!

Mas se você não precisar manter a master page eu digo que não, atualize a master page existente. Esta é a abordagem que recomendo.

Criar ou não criar novos page layouts?

Provavelmente existirão mudanças de layout / field como resultado dos novos mock-ups fornecidos.

Sinta-se à vontade em modificar ou criar novos page layouts, o que você não pode fazer é excluir page layouts existentes. Eles podem estar referenciados pelo schema do site definition, portanto, neste caso, você precisa mantê-los disponíveis.

Não quero dizer disponível aos usuários, para que eles selecionem quando criarem páginas (você pode escondê-los), mas apenas como parte do código, como um legado.

Obs: Os content types podem estar acoplados a page layouts, então, assim que criar páginas, os campos (fields) de content types são exibidos nas páginas.

Preservar ou não preservar webparts?

Faça o que quiser com elas. Você pode excluir web part descriptors da galeria de Web Parts e ainda possuir referências em páginas, o que significa que você pode modificá-las ou excluí-las.

Tudo depende do look and feel / funcionalidades requeridas pelo Rebranding do web site. Se a webpart ainda fazer parte do propósito do novo Rebranding, mantenha-a, caso contrário remova-a.

Criar ou não criar navegações customisadas?

Cabe a você decidir o que vai na barra superior ou de inicialização rápida (quick launch). Claro que você não quer que os usuários vejam listas / bibliotecas (libraries) exibidas na navegação. Assim, você pode modificá-la para não exibir esses objetos por padrão.

Existem melhores práticas na criação de custom navigations. Você não quer iterar manualmente pelos sites e sub-sites para isso.

Use os objetos sob o namespace Microsoft.SharePoint.Publishing.Navigation, pois proporcionam um desempenho muito melhor ao manusear itens de navegação.

No MOSS 2007 e SP2010 a classe PortalSiteMapProvider deve ser usada para percorrer os nodes (que representam sites, páginas, links), enquanto que no SP2013 a classe TaxonomySiteMapProvider deve ser usada por se tratar do Metadata Service Application.

Usar ou não usar conteúdo reutilizável?

Claro que você pode usar um conteúdo reutilizável. Isto é totalmente recomendado em publishing sites!

Usando a lista de Reusable Content você pode criar HTML reutilizáveis ou textos reutilizáveis.

Na maioria dos sites as seções de cabeçalho e rodapé são compartilhadas e exibidas em todas as páginas, então por que não usar esse recurso?

Aderir ou não aderir a essa abordagem?

Não é raro se encontrar travado em dilemas quando customisar o SharePoint. Baseado na minha experiência, a minha recomendação é aderir às melhores práticas de SharePoint e recomendações do pessoal que realmente trabalha com a plataforma.

Dê preferência à artigos da Microsoft que mostrem como fazer. Se você não encontrar nada específico, converse com outros profissionais sobre o problema que você tem.

Espero que isso ajude. Caso você tenha um ponto de vista diferente, compartilhe.

[]’s,

Marcel

Referências:
http://support.microsoft.com/kb/898631
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.publishing.navigation.aspx
http://office.microsoft.com/en-nz/sharepoint-server-help/use-reusable-content-HA010163838.aspx

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

The reCAPTCHA server is unavailable

Se você obtiver a mensagem “The reCAPTCHA server is unavailable”, significa que a tentativa de se conectar ao Google para validar a Resposta (o texto que você digitou) falhou.

Oi pessoal,

Dessa vez quero falar rapidamente sobre o anti-spam Recaptcha. Sim, aquele criado pelo Google.

Recaptcha1

Quando funciona é excelente, mas pode se transformar em pesadelo se você tiver restrições no ambiente (especialmente se você está trabalhando com SharePoint) Smile

Problema

Se você obtiver a mensagem “The reCAPTCHA server is unavailable”, significa que a tentativa de se conectar ao Google para validar a Resposta (o texto que você digitou) falhou. Se você está refenciando o binary Recaptcha.dll na sua solução eu afirmo que vai ser bem difícil identificar a raiz do erro.

Solução

Vários fatores podem causar tal erro. O mais comum é que você pode ter um proxy em seu ambiente, que você precisará referenciá-lo no controle Recaptcha.

Para identificar o problema, verifique primeiramente o EventViewer, pois haverá uma mensagem no source “Äpplication”.

Outros fatores desconhecidos também podem afetar, então eu recomendo obter o código fonte do Google:

http://code.google.com/p/recaptcha/source/checkout

Instale o TortoiseSVN e obtenha o código fonte, então adicione-o a sua solução. Ao fazer isso você poderá debugar e verificar o que está acontecendo, do contrário vai ser bem difícil identificar a mensagem genérica do EventViewer.

Espero que esse post ajude.

[]’s,

Marcel Medina

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

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.