SharePoint 4 Developers

Additional reference guide in .NET / SharePoint Development

Rebranding branded SharePoint Publishing Sites - Dilemmas

If you are going to Rebrand a branded SharePoint site, and if the budget allows you to go further, plan for an upgrade before rebranding. This will minimize rework and hence costs in the future.

Hi folks,

Due to a recent project I worked on I have decided to write something about facing dilemmas, about to do or not to do? That is the question.

When you face situations that you don’t have an immediate answer for that, you need to put some thought on it.

dilemma

Rebranding branded sites

When a client comes to you telling they want to rebrand an existing SharePoint site, the first thing that comes to my mind... is this an upgrade?

It turns out that because I have always worked on the development of Custom Branded Sites, I used to think about that.

In general, rebranding a branded site comes after upgrading the environment, but this is not a rule.

To upgrade, or not to upgrade?

If you are going to Rebrand a branded SharePoint site, and if the budget allows you to go further, plan for an upgrade before rebranding. This will minimize rework and hence costs in the future.

But if the budget does not comprise an upgrade, it is up to you to tell the client that not upgrading the environment can lead to expensive costs in the future, especially if you need to jump platforms.  e.g. from MOSS 2007 to SP2013. These costs involve:

  • Infrastructure – New architecture, resources, hardware.
  • Solutions – Compatibility of customizations.

But it is up to the client to decide where to put the money. The client needs to be guided to what is best for its business.

In my case a client decided not to upgrade the environment, so my job was to rebrand branded sites in the same environment. Although it seems pretty straightforward, it is not, you need to consider:

  • Design / Look and Feel – Changes on site definitions, master pages and page layouts.
  • Content Types – New fields available in page layouts.
  • Web parts – Mapping existing x new.
  • Navigation – Top and left navigation changes.
  • Reusable content – Header and Footer sections.

To be, or not to be guided by mock-ups?

Before the mock-ups are created, talk to the business users about content, ask how the pages are going to display information created by content editors.

Don’t let yourself be driven by mock-ups, in general design agencies don’t have a clue about SharePoint, so the design they create probably will have some incompatibilities with SharePoint.

To update, or not to update site definitions?

Don’t ever touch the site definition schemas after site collections/sites were already provisioned. This is not supported! Here is the proof: http://support.microsoft.com/kb/898631

Instead use the object model to apply changes to the site definition. If you are provisioning the sub-sites add some code to the Feature Receiver (Activation or Deactivation) to modify the site definition.

Note: You could create a new site definition if you want a fresh start, but you would need to adapt existing code and migrate existing site collections that uses the old version of site definition. I personally don’t recommend this approach.

To create, or not to create a new master page?

It depends. I say yes if you need to keep the old version, but if you do that you will need to modify the feature receiver to assign the new master page programmatically when provisioning a site collection/site. Remember the old master page is bound to the site definition schema!

But if you don’t need to keep the master page I say no, update the existing master page. This is my recommended approach.

To create, or not to create new page layouts?

Probably there will be layout / field changes as a result of the new mock-ups provided.

You are free to modify or create new page layouts, what you cannot do is to delete existing page layouts. They can be referenced by the site definition schema, so in this case you need to keep them available.

I don’t mean available for users to select when creating pages (you can hide them), but just as part of the code as a legacy.

Note: Content types can be bound to page layouts, so when you create pages, fields from content types are displayed on pages.

To preserve, or not to preserve webparts?

Do whatever you want with them. You can delete a web part descriptor from the Web part gallery and still have a reference to a page, so it means you can modify or delete them.

It all depends on the look and feel / functionalities required by the Rebranding of the web site. If it fits to the purpose of the new Rebranding, keep them, otherwise remove them.

To create, or not to create custom navigations?

It is up to you to decide what goes on the top bar or quick launch. Obviously you don’t want users to see Lists/Libraries displayed on Custom Navigation. So you can modify it to not to display these objects by default.

There are best practices when creating custom navigations. You don’t want to iterate manually through sites and sub-sites for that.

Use the objects under the namespace Microsoft.SharePoint.Publishing.Navigation as they provide much better performance when handling navigation items.

In MOSS 2007 and SP2010 the PortalSiteMapProvider class should be used to iterate through nodes (that represent sites, pages, links), while in SP2013 the TaxonomySiteMapProvider class should be used as it comes from the Metadata Services Application.

To use, or not to use reusable content?

Of course you can use reusable content. This is totally recommended in publishing sites!

By using the Reusable Content List you can either create reusable HTML or reusable Text.

In most of sites you have the Header and Footer sections that are shared and displayed in all pages, so why not to use this feature?

To go, or not to go for it?

It is not rare to find yourself stuck with dilemmas when customizing SharePoint. Based on my experience, my recommendation is to stick to the SharePoint best practices and recommendations from the field.

Preferably look for Microsoft articles that show how to do it. If you don’t find anything specific, talk to other professionals about the problem you have.

I hope it helps. In case you have different point of view, please share it.

Cheers,

Marcel

References:
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

Click here to read the same content in Portuguese.

The reCAPTCHA server is unavailable

If you get the message “The reCAPTCHA server is unavailable”, it means that the attempt to connect to Google to validate the Response (the text you have inputted) failed.

Hi everyone,

This time I want to talk about the Recaptcha anti-spam. Yes, the one created by Google.

Recaptcha

It is great when it works, but it may become a nightmare if you have restrictions in your environment (especially if you are working on SharePoint) Smile

Problem

If you get the message “The reCAPTCHA server is unavailable”, it means that the attempt to connect to Google to validate the Response (the text you have inputted) failed. If you are referencing the binary Recaptcha.dll in your solution I assure you it is going to be very difficult to identify the source of the error.

Solution

Lots of factors may lead to errors. The most common thing is that you may have a proxy in your environment, so you need to set the proxy as a reference in the Recaptcha control.

To troubleshoot that, start by checking the EventViewer, as there will be a message under the “Application” source.

Other unknown factors may possibly affect that, so I recommend you to get the source code from Google:

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

Install TortoiseSVN and get the source code, then add it to your solution. By doing this you can debug and check what is going on, otherwise it is going to be very difficult to understand this generic message from the EventViewer.

I hope it helps.

Cheers,

Marcel Medina

Click here to read the same content in Portuguese.

Creating Content Types Programmatically via XML

In this post I demonstrate how to create Content Types Programatically via XML (Quick Note)

Hi folks,

Here we go with one more quick note, the approach now is creating content types programmatically via XML. This approach is not usual, because it requires Reflection to use some Internal methods of the SPContentType class (Microsoft.SharePoint namespace).

It is possible to create Content Types using XML in case a Feature is created for that, however if you have a XML and don’t want to create a Feature, the only way is using Reflection as solution.

Consider the following 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 >

 

In order to add the Content Types above, the code snippet below must be used:

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. }

 

When analising the code above, it is possible to note that the methods CreateEmptyContentType, LoadContentTypeFromXml and SetContentTypeScope are resposible for the Content Types creation, and are contained in the class ContentTypesHelper. These methods use Reflection and were created by Robert Fridén in his blog few years ago, now I am reusing them.

Through the .NET Reflector it is possible to identify Internal methods of the Microsoft.SharePoint.SPContentType class, according the Figure below:

reflector  Figure 1 – SPContentType class visualised through the .NET Reflector


Check out the code of the ContentTypesHelper class:

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. }

 

In the end the Content Types will be created according the Figure below:

cttypes  Figure 2 – Content Types Creation


Download the solution here.

Considerations

  • The Site Columns are not inherited automatically in this approach. Note that the Content Types of the XML are listing all the columns, which wouldn’t be necessary if the inheritance was working properly.
  • The addition of the property Inherits=TRUE does not change this behaviour.

References:
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


Cheers,

Marcel Medina

Click here to read the same content in Portuguese.