Saturday, May 21, 2011

Deploy Branding Solution

Visual Studio Package

If you are an experienced SharePoint 2007 professional you will know that to do custom development in Visual Studio 2007 required a lot of manual work to build up a deployable package. This usually resulted in developers created half-packaged solutions where they had tons of manual steps to deploy a solution.

With SharePoint 2010 the game has changed. Visual Studio 2010 is everything I ever wanted and I wish I was out doing consulting with it now because it is so easy to build a WSP and deploy it. On top of that, Sandbox Solutions make it every more easier to deploy solutions.

In this section I am going to take a much of the code I created in this series using SharePoint Designer 2010 and pull it in to Visual Studio so that I can make a re-deployable solution.

Getting Started

To test this out I did the following:

  • Created a new web application.
  • Created a team site, site collection.
  • Added a FAST Search Center sub site.
  • Turned on all publishing features at both the site collection and site levels.
  • Modified the navigation to show the FAST sub site.

The result was the following.

clip_image002

Next I need to create a deployment package that will move all the stuff I have worked on into this new web application. There are a bunch of things I need to move into this deployment package.

  • Master Page and all the files referenced by it. This includes image and css files.
  • Content types
  • Page Layouts
  • Content Query Web Part customizations

To get this started I simply opened up Visual Studio 2010 and created an empty SharePoint project. I also made this a Sandbox Solution.

Master Pages

Setting up the Master Pages is the easier one; plus it would be the first to logically do. Back in SharePoint and Visual Studio 2007 we spend a whole bunch of time trying to recreate the SharePoint folder structure and get it all worked out in the various XML files. Visual Studio 2010 with SharePoint 2010 is going to save us a ridiculous amount of time.

I took the following steps to set up the visual studio project.

  • I added a new module called “_catalogs”. I removed the sample.txt file.
  • Under the _catalogs module I created a folder called “masterpage”. In that folder I placed both of the master pages I had created earlier in SharePoint Designer.
  • I then created another module called “Style Library”. I removed the sample.txt file.
  • I created a folder called BrandingBlog and images to be the same as the folder structure that I had created in the master page.
  • I then moved in all the css and images into the appropriate folders.
  • Then under the Features folder, I renamed Feature1 to BrandingBlogMaster. I did this because I want my first feature of this deployment package to be deploying my master pages only.

clip_image003

  • Next I modified the Feature scope to be Site so that this is set at the site collection level.

clip_image005

  • Next I had to modify the Elements.xml file in the _catalogs module.
  • I changed the module to have both the URL and Path set to “_catalogs/masterpage”.

Then I modified each of the File elements as you see below. Note that I set the Type to GhostableInLibrary .

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="_catalogs" Url="_catalogs/masterpage" Path="_catalogs/masterpage">
<File Url="BrandingBlog.master" Type="GhostableInLibrary"/>
<File Url="BrandingBlog_searchcenter.master" Type="GhostableInLibrary"/>
</Module>
</Elements>



  • Next I went to the Style Library module and modified the Elements.xml file in a similar fashion.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Style Library" Url="Style Library/BrandingBlog" Path="Style Library/BrandingBlog">
<File Url="style.css" Type="GhostableInLibrary"/>
<File Url="images/splitter.gif" Type="GhostableInLibrary"/>
<File Url="images/sidebar_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/picture.jpg" Type="GhostableInLibrary"/>
<File Url="images/li.gif" Type="GhostableInLibrary"/>
<File Url="images/bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/content.gif" Type="GhostableInLibrary"/>
<File Url="images/footer_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/head_bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/menu_bckg.gif" Type="GhostableInLibrary"/>
</Module>
</Elements>


Now this is done, it is really simple to deploy.




  • I right clicked the solution and selected to build it. Even though there is no compiled code it is just a habit of mine.


  • Next I right clicked the solution and selected Package. This will generate a WSP package which in this case would be BrandingBlog.wsp. Since I am doing a debug build, the BrandingBlog.wsp file will be generated in the Debug folder.


  • Next I went to the Site Collection >> Site Settings >> Solutions to deploy my WSP into the Sandbox. I uploaded and activated it. This subsequently but all my files into the Master Pages gallery and into the Style Library folder.


  • Next I web to the Style Library >> BrandingBlog folder and checked in all the files in the directory.


  • Next I went to Site Settings >> Master Pages and changed the top site to use the BrandingBlog.master file. The following is the result.



clip_image002[6]




  • Next I went to the FAST Search Center site and set the master page to be BrandingBlog_searchcenter.master. Below is the branded FAST Search Center.



clip_image004



Man that was a little too easy.



But I will be honest, there were a few challenges I ran into when deploying the rest of the solution.



Deploying the Rest of the Solution



Now I do have some lessons learned in regards to the deployment of the rest of the solution – which I knew was going to run into. Let’s go ahead and discuss them out before we get started.



Content Type GUIDs Lessons Learned



If you are going to use Content Types and plan on doing a re-deployment, it is best to get those content types in Visual Studio and create a WSP package as soon as possible. This is because all the content types and columns have their own unique GUIDs which are generated when you build them in the UI. All of my Page Layouts are tied to those GUIDs.



So why does that cause me heart burn in this situation? Well it is common practice for UI designer to actually do a bunch of the work in SharePoint Designer 2010, and then pull them out and give to a developer. It would be good if both the designer and the developer are using the same GUIDs. Plus the GUIDs will be the same between development and production environments making deployments smoother.



Content Query Web Parts and Managed Metadata Columns Lessons Learned



I somewhat anticipated that there would run into some bumps with re-deploying content query web parts with managed metadata columns. If you have been reading along with this blog series, you will see I was able to get a pretty decent solution put together. Remember my goal is to create a solution that can be deployed to the Sandbox. Here is what I learned.




  • Content Query Web Parts that are pre-configured into Page Layouts will require some extra manual steps when redeployed across environments. This is mostly because there are several GUIDs that get tied into the configuration of the web part. So in this example I pre-configured content query web parts on my custom page layouts to show related articles and confidential visualization. When the page layouts are redeployed someone will have to go into SharePoint Designer and manually reconfigure the web parts.


  • Content Query Web Parts that use Management Metadata columns with multiple values require some extra work. For instance, the Technology and Enterprise Keyword columns I have are only available when I select the pages library as the Source. If I try to select the first two options neither of those two columns are available. The net affect it the GUID from the pages library that will be in the content query web part configuration will be different across environments. The resolution is again, I have to reconfigure that web part once it has been re-deployed.


  • The Microsoft.SharePoint.Taxonomy namespace is not available in the Sandbox. Why is that important to this particular solution? Well my vision was a 100% re-deployable solution in the sandbox. To achieve that vision I would want to add some code in the Feature activation event to generate to managed metadata that would be mapped back to my content types.


  • Another lesson learned was the managed metadata column defined declaratively in a Feature does not have the ability to map existing term sets. The resolution is to add some code to the Feature activation to map the column to pre-existing term sets in the destination environment. That code is pretty well defined but it again requires access to the Microsoft.SharePoint.Taxonomy namespace which is not supported in the Sandbox.



What are the ramifications of all this? Why am I trying to figure out these boundaries? Well if you are looking ahead to using Office365 we are pretty much contained to working with the Sandbox. There will be opportunities to get custom code deployed in dedicate Office365 but again there is a process to go through and best to build to the Sandbox.



Knowing what I have learned, I will be taking the following actions moving forward:




  • Managed Metadata – I am not going to give up on. There are major redeeming benefits of using them. So there are some simple manual steps that will be done which I will outline.


  • Content Query Web Parts – I will go through reconfiguration of them when they are embedded into page layouts. However I am going to consider writing simple web parts in the future using LINQ to SharePoint. This way I have much more flexibility to scale later and that will work in the Sandbox.



Adding Other Components



In the first part of this Visual Studio deployment I set up the project to deploy out the master page and associated files really quickly. In this part, I am going to capture how I pulled in all of the other components of this solution and changes I had to make based on the lessons learned. Specifically I will be adding:




  • Columns and Content Type Definitions


  • Page Layout


  • Content Query Web Part


  • Custom Content Query Web Part Styles



Below is a screenshot of the updated solution with all the added files. I will discuss each of these additions.



clip_image005



Columns and Content Type Definitions



First I created a new Feature for deploying my content types called BrandingBlogContentTypes. I decided to put this into a separate Feature because I like to keep data definitions separate from functionality; just like with apps and SQL. Basic steps to create are:




  • Right clicked Features and added a new Feature called BrandingBlogContentTypes.


  • Changed the scope of BrandingBlogContentTypes to Site.


  • Right clicked the project and added content type. As part of the configuration you will be asked what your content type will inherit from. In this blog, my custom Technology Article content type inherits from Article Page, so I selected that.


  • Then I went to the elements.xml and added columns and content type definitions.


  • Below is the code that created for my Technology Article content type and column definitions.


  • First I added both the Confidentiality and Technology columns which will use managed metadata. Note that the type is TaxonomyFieldType and TaxonomyFieldTypeMulti. Reminder to generate new GUIDs. Otherwise this is the same as we did in SharePoint 2007.


  • A content type will be generated for you and you will need to add your columns to it. As you can see it took the GUID off SharePoint and then appended 00 plus new GUID. That simple little feature makes my day J


  • Next I added the Confidentiality and Technology columns. Same as in the SharePoint 2007 days.


  • Next you see I added some strange columns called TaxCatchAll, TaxCatchAllLabel, TaxKeywordTaxHTField. If you do not add these four hidden columns, your managed metadata columns will not work in a list or library. I figured this out by exporting the library definition in my development environment plus confirmed it on some readings out on web. These all get added behind the scenes for you when you create content types through the SharePoint Site Collection Admin.


  • Next I added TaxKeyword to bring in the existing Enterprise Keywords column.



Finally I added AverageRating and RatingCount so the ratings would be part of my content type definition. You have to add these two columns even though it is presented to the user as single field. Both are used in the calculation of the presentation.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

<Field ID="{C578CFD4-4A0A-4E38-B057-0A602704B317}"
Name="Confidentiality"
StaticName="Confidentiality"
DisplayName="Confidentiality"
Group="BrandingBlog"
Type="TaxonomyFieldType"
ShowField="Term1033"
EnforceUniqueValues="FALSE">
</Field>

<Field ID="{F3F1CD61-ACE3-40D1-8F4D-F6618FC09E23}"
Name="Technology"
StaticName="Technology"
DisplayName="Technology"
Group="BrandingBlog"
Type="TaxonomyFieldTypeMulti"
ShowField="Term1033"
EnforceUniqueValues="FALSE">
</Field>

<!-- Parent ContentType: Article Page (0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D) -->
<ContentType ID="0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D00a88b4089676141a2baada81b07a8c125"
Name="Technology Article"
Group="BrandingBlog"
Description="Technology Article for Wiki."
Inherits="TRUE"
Version="0">
<FieldRefs>
<FieldRef ID="{C578CFD4-4A0A-4E38-B057-0A602704B317}" Name="Confidentiality"/>
<FieldRef ID="{F3F1CD61-ACE3-40D1-8F4D-F6618FC09E23}" Name="Technology"/>
<FieldRef ID="{f3b0adf9-c1a2-4b02-920d-943fba4b3611}" Name="TaxCatchAll" Hidden="TRUE" />
<FieldRef ID="{8f6b6dd8-9357-4019-8172-966fcd502ed2}" Name="TaxCatchAllLabel" Hidden="TRUE" />
<FieldRef ID="{1390a86a-23da-45f0-8efe-ef36edadfb39}" Name="TaxKeywordTaxHTField" Hidden="TRUE" />
<FieldRef ID="{23f27201-bee3-471e-b2e7-b64fd8b7ca38}" Name="TaxKeyword" />
<FieldRef ID="{5a14d1ab-1513-48c7-97b3-657a5ba6c742}" Name="AverageRating" />
<FieldRef ID="{b1996002-9167-45e5-a4df-b2c41c6723c7}" Name="RatingCount" />
</FieldRefs>
</ContentType>
</Elements>


With that, I am done with create the content types.



Page Layout Definitions



Next I needed to bring over the Page Layout I had created for the Technology Article.




  • I went to my development environment and exported the Technology Article.


  • I added TechnologyArticle.aspx to the _catalogs >> masterpage folder.


  • Then in the page layout I removed both of the content query web part configurations based on one of the limitations captured earlier. I will reconfigure it later.


  • I modified the Confidentiality and Technology GUIDs Taxonomy:TaxonomyFieldControl to match the GUIDs I had defined in my content type Feature.


  • Next I modified the elements.xml.



I made the following changes to the existing elements.xml which already had the master pages. This is because master pages and page layouts reside in the same directory.




  • I moved the TechnologyArticle.aspx File tag to be with the master pages.


  • I made it GhostableInLibrary.


  • I added a property to make sure it is a Page Layout content type entry.



I added a property that maps the Page Layout to the content type that I had just created. Notice the GUID matches.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="_catalogs" Url="_catalogs/masterpage" Path="_catalogs/masterpage">
<File Url="BrandingBlog.master" Type="GhostableInLibrary"/>
<File Url="BrandingBlog_searchcenter.master" Type="GhostableInLibrary"/>
<File Url="TechnologyArticle.aspx" Type="GhostableInLibrary">
<Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
<Property Name="PublishingAssociatedContentType" Value=";#Technology Article;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D00a88b4089676141a2baada81b07a8c125;#" />
</File>
</Module>
</Elements>


Content Query Web Part Definition



Next I needed to bring in my custom content query web part definition.




  • I created a new folder called “wp” under _catalogs. Then I went to the web part library of my development environment, downloaded the Content_Query_Branding_Blog.webpart file and added to the wp folder of my project.


  • Next I modified the Module entry in the elements.xml file to be following.


  • Note do not forget to add the Property for group. Otherwise the web part will not appear.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="_catalogs" Url="_catalogs/masterpage" Path="_catalogs/masterpage">
<File Url="BrandingBlog.master" Type="GhostableInLibrary"/>
<File Url="BrandingBlog_searchcenter.master" Type="GhostableInLibrary"/>
<File Url="TechnologyArticle.aspx" Type="GhostableInLibrary">
<Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
<Property Name="PublishingAssociatedContentType" Value=";#Technology Article;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D00a88b4089676141a2baada81b07a8c125;#" />
</File>
</Module>
<Module Name="_catalogs" Url="_catalogs/wp" Path="_catalogs/wp">
<File Url="Content_Query_Branding_Blog.webpart" Type="GhostableInLibrary">
<Property Name="Group" Value="Branding Blog" />
</File>
</Module>
</Elements>


Content Query Web Part Styles Definition



The last thing I need to move over is the ItemStyle_BrandingBlog.xsl I created for the content query web part.




  • First I added a folder called “XSL Style Sheets” under Style Library.


  • I then exported ItemStyle_BrandingBlog.xsl from my development environment and added to XSL Style Sheets folder.


  • Then I modified the existing elements.xml file.


  • I just added a new Module entry for the XSL Style Sheets folder.


  • Added ItemStyle_BrandingBlog.xsl file.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Style Library" Url="Style Library/BrandingBlog" Path="Style Library/BrandingBlog">
<File Url="style.css" Type="GhostableInLibrary"/>
<File Url="images/splitter.gif" Type="GhostableInLibrary"/>
<File Url="images/sidebar_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/picture.jpg" Type="GhostableInLibrary"/>
<File Url="images/li.gif" Type="GhostableInLibrary"/>
<File Url="images/bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/content.gif" Type="GhostableInLibrary"/>
<File Url="images/footer_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/head_bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/menu_bckg.gif" Type="GhostableInLibrary"/>
</Module>
<Module Name="Style Library" Url="Style Library/XSL Style Sheets" Path="Style Library/XSL Style Sheets">
<File Url="ItemStyle_BrandingBlog.xsl" Type="GhostableInLibrary" />
</Module>
</Elements>


Manual Deployments Steps



Now you can see how easy it is to create a package with all these files and then deployed. Given some of the lessons I learned, I will have to perform a few simple manual steps to get everything running after activation. I know perfectly well that I could add some custom code to the Feature activation, change to custom web parts with LINQ and even use different column types. However I wanted to push through the solution as is because I wanted to keep this as a Sandbox solution.



Earlier I showed how to deploy the master page. Now we have added several other things to this deployment package. Here are a couple extra steps that you will need to do.




  • Again build and package the solution in Visual Studio.


  • The upload the WSP into the Sandbox solutions.


  • Go to the Style Library >> XSL Style Sheets and check in ItemStyle_BrandingBlog.xsl.


  • Go to Site Settings >> Site Columns and then modify the both the Confidentiality and Technology columns to the appropriate Term Set. As well for Technology you will have to check the Allow Multiple Values checkbox.


  • Open up SharePoint Designer >> Page Layouts >> TechnologyArticle.aspx and remove the existing content query web parts. Then add back the two content query web parts using the same steps defined earlier in this blog.


  • Go the pages library and add the Technology Article content type so users can create those types of pages.



In the grand scheme of things, I really did not find that these few manual steps that big of deal given the advantages of using the Sandbox and giving me the possibility of deploying to the Office365 cloud.



Conclusions



This has been a fun experience but glad I am done typing this up. I really hope this is helpful to get you started on doing branding and web content management in SharePoint 2010.



References



The following are all the references I used to help me along the way.



2 comments:

Edgard said...

Great write up! I find it challenging that most of the write ups for custom master pages involve one time deployment but ,to date, I've yet to find a resource about handling updates to components within a feature (images, masterpages,etc) through sandbox solutions.

Jason Apergis said...

Edgard,

THANKS for the feedback.

I may be confused on your last comment but this solution is deployable through the Sandbox!

Jason