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.



Customizing Content Query Web Parts

Content Query Web Parts

I have to say the game has changed with content query web parts. When trying to work with them in SharePoint 2007 you ended up having to do a ton of work just so a custom field from a content type. Now it really very simple to go in and start working with content query web parts all inside of SharePoint Designer. In this example I am going to:

  • Create a content query web part that is going to show me the most popular articles that are related to an article that I created.
  • I will do some “conditional formatting” to display a confidentiality warning to the user based on the metadata associated to the article.

Approach

As I alluded creating your own xsl for content query web parts is much easier with SharePoint Designer 2010. To access and modify the style sheets select All Files in the Navigation. Then drill down to Style Library >> XSL Style Sheets. There are three style sheets that come out of the box ContentQuery.xsl, Header.xsl and ItemStyle.xsl. The one you will need to work with the most of the time is ItemStyle.xsl.

clip_image002

Next question you may have is how to create new xsl entries for the content query web part? Well I have found the simplest approach is pretty simple.

  • I open up ItemStyle.xsl.
  • I copy ones of the existing xsl templates. Specifically, I copy a full <xsl:template> and add it to the very bottom.
  • Then you need to modify the template tag and replace XXX values <xsl:template name="XXX" match="Row[@Style='XXX']" mode="itemstyle">. It is important that both values are identical. As well, they need to be unique to the xsl file.

That’s it. I am not going to give a full lesson here on all the cool things you can do once you are in there. I leave that up to you as a creative solution developer.

When doing this write up I refined my thoughts a little bit on how to do content query web parts. Specifically I think it is not best to the ItemStyle.xsl directly. Why? Well when you make a change to the ItemStyle.xsl (as I outlined above) you will get a warning saying “Saving your changes will customize this page so that is no longer based on the site definition. Do you want to continue?”

clip_image003

So you make this change you are customizing (unghosting) the ItemStyle.xsl which may not always be the best thing if you are planning for an Upgrade. YES YOU SHOULD BE THINKING ABOUT THE NEXT VERSION OF SHAREPOINT! Look at the forest; not the tree in front of you. Customized pages had to be dealt with when upgrading from SharePoint 2003 to 2007 as well as SharePoint 2007 to 2010. My response officially is it always “depends” but I try to avoid customizing (unghosting) as much as I can.

Knowing this I believe a best practice will be to create your own content query web parts that point to a custom ItemStyle.xsl file. Then your customizations will be managed separately and will not interfere with upgrades to OOB features. The solution is so easy as well; it is a no brainer. Let me walk through it.

  • First I took the existing ItemStyle.xsl in SharePoint Designer and copy pasted it within the same directory. I named it ItemStyle_BrandingBlog.xsl.
  • Next I went into it and stripped out all of the existing <xsl:template> tags and all the content within them. If those need to be used, I will use the OOB content query web part. Then saved it.
  • Next I just added an out of the box content query web part onto a web part page.
  • I made zero changes to the content query web part and exported the web part configuration. I saved the file locally. I named it Content_Query_Branding_Blog.webpart.
  • Next I opened the Content_Query_Branding_Blog.webpart (which is nothing more than a XML configuration file for the content query web part).
  • I modified the ItemXslLink to point to my new ItemStyle_BrandingBlog.xsl. <property name="ItemXslLink" type="string">/Style Library/XSL Style Sheets/ItemStyle_BrandingBlog.xsl</property>
  • I modified the Title to <property name="Title" type="string">Branding Blog Content Query</property>
  • Next I went to Site Settings >> Web Parts Gallery >> and then upload the Content_Query_Branding_Blog.webpart.

Done.

Now you have created your own XSL style sheet and you have created a content query web part configuration that uses that style sheet. We will later automate this as part of a WSP deployment.

Now let’s jump into the configuration of the two web parts that I need to create for my technology article publishing page.

Related Articles Content Query Web Part

As you may recall I need to support the ability show related technology articles. So I want to query for all articles that are related to the one being viewed.

Step 1 – Create the XSL for Content Query Web Part

The first thing I need to do is create a view of data that will show the following:

  • The title of the article
  • The rating of the article
  • The last modified date
  • The first 300 characters from the article

To accomplish I wrote the following xsl in the ItemStyle_BrandingBlog.xsl that I created in the previous step. I did all of this in SharePoint Designer 2010.

<xsl:stylesheet 
version="1.0"
exclude-result-prefixes="x d xsl msxsl cmswrt"
xmlns:x="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://schemas.microsoft.com/sharepoint/dsp"
xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime"
xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:param name="ItemsHaveStreams">
<xsl:value-of select="'False'" />
</xsl:param>
<xsl:variable name="OnClickTargetAttribute" select="string('javascript:this.target=&quot;_blank&quot;')" />
<xsl:variable name="ImageWidth" />
<xsl:variable name="ImageHeight" />
<xsl:template name="TitleDateAndRatings" match="Row[@Style='TitleDateAndRatings']" mode="itemstyle">
<xsl:variable name="SafeLinkUrl">
<xsl:call-template name="OuterTemplate.GetSafeLink">
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="DisplayTitle">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="@Title"/>
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<div>
<xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>
<table>
<tr>
<td width="500px" colspan="3">
<hr />
</td>
</tr>
<tr>
<td width="300px">
<a href="{$SafeLinkUrl}" title="" >
<xsl:if test="$ItemsHaveStreams = 'True'">
<xsl:attribute name="onclick">
<xsl:value-of select="@OnClickForWebRendering"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="$ItemsHaveStreams != 'True' and @OpenInNewWindow = 'True'">
<xsl:attribute name="onclick">
<xsl:value-of disable-output-escaping="yes" select="$OnClickTargetAttribute"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="$DisplayTitle"/>
</a>
</td>
<td nowrap="nowrap" width="100px">
<xsl:if test="@Ratings &gt;= 4.75">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_5" alt="Current average rating is 5 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &gt;= 4.25 and @Ratings &lt; 4.75">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_4_5" alt="Current average rating is 4.5 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &gt;= 3.75 and @Ratings &lt; 4.25">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_4" alt="Current average rating is 4 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &gt;= 3.25 and @Ratings &lt; 3.75">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_3_5" alt="Current average rating is 3.5 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &gt;= 2.75 and @Ratings &lt; 3.25">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_3" alt="Current average rating is 3 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &gt;= 2.25 and @Ratings &lt; 2.75">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_2_5" alt="Current average rating is 2.5 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &gt;= 1.75 and @Ratings &lt; 2.25">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_2" alt="Current average rating is 2 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &gt;= 1.25 and @Ratings &lt; 1.75">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_1_5" alt="Current average rating is 1.5 stars." /></a>
</span>
</xsl:if>
<xsl:if test="@Ratings &lt; 1.25">
<span>
<a class="ms-currentRating"><img src="/_layouts/Images/Ratings.png" class="ms-rating_1" alt="Current average rating is 1 star." /></a>
</span>
</xsl:if>
</td>
<td nowrap="nowrap" width="100px">
<xsl:value-of select="ddwrt:FormatDateTime(string(@Date), 1033, 'M/d/yyy h:mm tt')" />
</td>
</tr>
<tr>
<td colspan="3" width="500px">
<xsl:value-of select="substring(@Description, 1, 300)" disable-output-escaping="yes" />...
</td>
</tr>
</table>
</div>
</xsl:template>
</xsl:stylesheet>


Let me discuss several of things I did to accomplishing this:




  • First I created a little HTML table that will control the format of the data. Then I started to add in fields.


  • The link title is printed here - <xsl:value-of select="$DisplayTitle"/> I simply took an example from the out of the box ItemStyle.xsl.


  • Next I needed to print the star ratings. I would have never figured this out until I found this article by accident - blog. This smart person showed how to do it and I just had to make a few minor changes for myself. Basically it looks at the rating value tied to the page rating. Then using styles it will print the appropriate star image.


  • Next I needed to print the last modified date. Here is the code that accomplished that <xsl:value-of select="ddwrt:FormatDateTime(string(@Date), 1033, 'M/d/yyy h:mm tt')" />. One thing to note is I had to add this namespace to the main xsl tag xmlns:ddwrt=http://schemas.microsoft.com/WebParts/v2/DataView/runtime. If you forget to do this, you will get errors.


  • Finally print the description I used the following line of code - <xsl:value-of select="substring(@Description, 1, 300)" disable-output-escaping="yes" />. In it I only print the first 300 characters. Plus I using the disable-output-escaping attributed to remove the embedded HTML in the article.



Step 2 – Test It



I saved my work and the next thing I did was test it to make sure the formatting looked good. To accomplish this I just went to a web part page. Here is a view of it with some test data in there. You can see I am getting the article title, start rating, a nicely formatted date and the article content. Now I just need to configure this into my Page Layout.



clip_image002[5]



Step 3 – Add and Configure the Content Query Web Part to the Page Layout



The last step is to configure this web part into the page layout. When I do this, now every page provisioned using this layout will have this web part pre-configured. What I did was:




  • Go back to my page layout.


  • Added a new row for Related Articles.


  • Open up the split view.


  • Then I will select Insert tab on the ribbon.


  • Then select the web part I deployed in the previous section.



clip_image004



Next I right click on the web part and select Web Part Properties which will open the same web part configuration panel I would use in the SharePoint UI.



clip_image006



I make the following configurations to the content query web part.



First I set up the query, pretty basics stuff. I set it to go against the pages library and only retrieve the Technology Article type.



clip_image007



Next I set up the Filters for the query. I only want to get articles that match on both technology and keywords. I also want to make sure to not get the same article.



For both the Technology and Enterprise Keywords I use [PageFieldValue:XXX] to retrieve metadata values from the article as part of the query. I will also note that if you have values in the URL querystring, you can use [PageQueryString:XXX] to access those values to use in the Filters.



To ensure I do not have the article itself appear in the query, I exclude it by the Title of the article which is a unique value for me.



clip_image008



Next I set it up to sort by star ratings.



Then in the Styles section I select TitleAndDateRatings style I created in ItemStyle_BrandingBlog.xsl.



clip_image009



The last part of the configuration is to set up the fields in the Technology Article to feed into the TitleAndDateRatings style.



clip_image010



I also set the Chrome to None to hide the web part header.



Now that is done I have the below results. I provisioned a Technology Article page, set the Technology and Keywords value. When I saved the article, the content query web part I pre-configured into the page layout will now be visible.



clip_image012



One important note it is not possible to change the configuration of the web part in the provisioned web page (like above). So if I went to edit the web page, I would not have the ability to edit the web part. If you want to support changing the web part configuration after page provisioning you will need to add a web part zone into the page layout and then put the web part into that zone. This can all be done in SharePoint Designer.



Just remember if you put a web part zone into the page layout, this will allow users to put in web parts in an ad-hoc fashion. So there is a real trade-off you have to consider. In my case, I actually used a web part zone at first, I trained users, and they still did not listen and started to add their own web parts. I ended up removing all the web part zones from the page layouts. This becomes pretty important in a real controlled publishing scenario.



Another note you should be aware of is if you make a change to the web part configuration in the page layout those configurations will not be pushed into provisioned pages either. This behavior is different that adding new fields or changing the presentation of the page layout which will change provisioned pages. This behavior makes sense and is expected; though all is not lost. If you really need to make a web part configuration change to all the provisioned pages, you can write some code to iterate through the pages and web parts to make changes. That is beyond the scope of my article but the SharePoint API supports it.



Confidentiality Web Part



The next thing I wanted to accomplish is create some sort of simple solution where I could visually indicate to the user that the data on the article is confidential. The environment I am deploying to does not have Sandbox Solutions enabled so writing a web part was not an option. To accomplish this, I was able to write a content query web part style.



Please note that this solution is not really secure. Page level security can be implemented but still users can accidently copy and paste content and email the information by accident. Web pages cannot be locked down with Information Rights management either. So a user can still copy and paste the information and email. The only real way to ensure this information is not sent out is to create a document library, configure information rights management in that library, load content into that library and then reference a link to the locked down content from the publishing page.



Step 1 – Create the XSL for Content Query Web Part



I followed the same steps and created a template in ItemStyle_BrandingBlog.xsl. The logic of this one is pretty simple. I basically have an If statement that checks to see what level of confidentiality the article has and then show visual indicator.




    <xsl:template name="Confidential" match="Row[@Style='Confidential']" mode="itemstyle">
<div>
<table width="100%" height="50px">
<tr>
<xsl:if test="@Conf = 'Confidential'">
<td style="background-color:red; color:black; text-align:center; font-size:large"><xsl:value-of select="@Conf" /> </td>
</xsl:if>
<xsl:if test="@Conf = 'NDA'">
<td style="background-color:yellow; color:black; text-align:center; font-size:large"><xsl:value-of select="@Conf" /></td>
</xsl:if>
</tr>
</table>
</div>
</xsl:template>


Step 2 – Test It



Then I test it with the web page I already configured.



Step 3 – Add and Configure the Content Query Web Part to the Page Layout



Then just like the last time, I opened the page layout in SharePoint designer and inserted the web part into the top of the page. I right clicked the web part and selected web part properties.



I set it query for the Technology Article type.



clip_image001



For the parameters I set it to only find articles based on the unique title.



clip_image002



For the presentation I set it to use the new Confidential style.



clip_image003[5]



I then provisioned a page, set the confidentiality and I had the following result. As you can see a big red banner is displayed to the user indicating that the content on this page should be created as confidential.



clip_image005



Conclusions



This pretty much concludes my exploration into creating custom content query web parts with SharePoint Designer 2010. I do recognize that if the queries for data become more complex, I recommend creating custom web parts and use LINQ to SharePoint to get out the data and present it. However I really like this solution because I was able to accomplish this without having to write a ton of complex code.



Next



We are going to explore how to redeploy this entire solution using Visual Studio.

Creating a Page Layout

Page Layouts

The next thing you will need to do as part of any major web content management project is create Page Layouts. Again, I am not going to provide a robust definition on what a Page Layout is because it is pretty well defined in books and on MSDN. What I am going to do in this example is create a simple content type and then create a page layout for the content type.

In this example I am going to create a Technology Article Page which will inherit from the out of the box Article Page. I will am going to add a couple custom fields to this content type like Confidentiality and Technology. I will later use these to do some fun stuff with content query web parts.

1 – Create a Content Type

Like I said the first thing I am going to do is create a content type based on the Article Page content type and add some of my own fields. For both Confidentiality and Technology, I am going to use the managed metadata service and term storm.

So first I created some terms:

clip_image001[4]

Then I created my new Technology Article content type which inherits from Article Page. Below is a screenshot of the new content type.

clip_image003[4]

Then you I created Confidentiality and Technology columns using the managed metadata. I also added the Enterprise Keywords column which allows for folksonomy tagging to be applied to my article. As well I have added Rating (0-5). Here is a screenshot of all the columns.

clip_image005[4]

2 – Create Page Layout

To create a new Page Layout:

  • Open SharePoint Designer 2010 and go to Page Layouts.
  • In the ribbon select new Page Layout.
  • Select the Technology Article content type. I will subsequently be taken to a page where I will be editing the page in advanced mode.

Now all I need to do is start adding in controls and HTML to build up the page layout. As you can see below, I have turned on the split view and the branding from the master page is being applied. Towards the bottom of the split view you notice the PlaceHolderMain outlined in light purple. That is where all the modifications will be made. You may not recall but this corresponds back to the Master Page. So when a page is rendered in SharePoint, both the Master Page and Page Layout are merged together to create the web page that is ultimately rendered.

clip_image007[4]

Within this screen I was able to compose a web page using the fields I have available to me from my content type. Here is the resulting screen that I created. Looks pretty basic but I have pulled in some core data fields, I am displaying managed metadata, and I am displaying the rating for the article.

clip_image009[4]

Here is a view of that same article in edit mode.

clip_image011[4]

Now to achieve this I did have to do several things in SharePoint Designer 2010.

clip_image013[4]

The process for creating the page was simple. First I created a HTML layout with some CSS embedded page layout to control the presentation. I put in a HTML table and then started dragging and dropping fields into the table. Now there are a couple little things you should know.

Metadata fields will not work when you drop them in. You need to add the following to the top of the page which will then allow the managed metadata fields to render on the page layout.

<%@ Register Tagprefix="Taxonomy" Namespace="Microsoft.SharePoint.Taxonomy" Assembly="Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


The Rating (0-5) control will not work either unless the following is registered at the very of the page layout.




<%@ Register Tagprefix="SharePointPortalControls" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


To display the created and modified date you will need to again add the following registration to the top of the page layout.




<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


Lastly you may have noticed that the big wave logo is missing from this page layout. This is because I made a decision that the wave branding was taking up too much real-estate for an article. A little trick I did was adding some CSS to the page layout to hide it. I like this approach because I really want this logo to be part of the master page but just for this particular page layout I do not want to display it.




<asp:Content ContentPlaceholderID="PlaceHolderAdditionalPageHead" runat="server">
<style type="text/css">
#logo { display: none; }
</style>
</asp:Content>


Page Layout Code



The following is the resulting code I created for the page layout.




<%@ Page language="C#"   Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full" %>
<%@ Register Tagprefix="SharePointWebControls" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Taxonomy" Namespace="Microsoft.SharePoint.Taxonomy" Assembly="Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePointPortalControls" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ContentPlaceholderID="PlaceHolderAdditionalPageHead" runat="server">
<style type="text/css">
#logo {
display: none;
}
.TechArticleTitle {
font-weight:bold;
vertical-align:top;
width: 10%;
padding-right: 10px;
padding-left: 10px;
}
.TechArticleValue {
vertical-align:top;
}
</style>
</asp:Content>

<asp:Content ContentPlaceholderID="PlaceHolderPageTitle" runat="server">
Technology Article -
<SharePointWebControls:FieldValue id="PageTitle" FieldName="Title" runat="server"/>
</asp:Content>
<asp:Content ContentPlaceholderID="PlaceHolderMain" runat="server">

<table>
<tr>
<td class="TechArticleTitle">Title:</td>
<td class="TechArticleValue">
<SharePointWebControls:TextField FieldName="fa564e0f-0c70-4ab9-b863-0177e6ddd247" runat="server"></SharePointWebControls:TextField>
</td>
<td class="TechArticleTitle">Modified:</td>
<td class="TechArticleValue" nowrap>
<SharePoint:FieldValue FieldName="Modified" runat="server" id="modified" ControlMode="display"/>
</td>
</tr>
<tr>
<td></td>
<td class="TechArticleValue" nowrap>
<SharePointPortalControls:AverageRatingFieldControl FieldName="5a14d1ab-1513-48c7-97b3-657a5ba6c742" runat="server"></SharePointPortalControls:AverageRatingFieldControl>
</td>
<td class="TechArticleTitle">Created:</td>
<td class="TechArticleValue" nowrap>
<SharePoint:FieldValue FieldName="Created" runat="server" id="created" ControlMode="display"/>
</td>
</tr>
<tr>
<td class="TechArticleTitle">Confidentiality:</td>
<td class="TechArticleValue" colspan="3">
<Taxonomy:TaxonomyFieldControl FieldName="a472e313-fd5f-4423-89b3-bdd721461eac" runat="server"></Taxonomy:TaxonomyFieldControl>
</td>
</tr>
<tr>
<td class="TechArticleTitle">Technology:</td>
<td class="TechArticleValue" colspan="3">
<Taxonomy:TaxonomyFieldControl FieldName="9129feba-3b4a-4dd7-82cc-0822730d16c8" runat="server"></Taxonomy:TaxonomyFieldControl>
</td>
</tr>
<tr>
<td class="TechArticleTitle">Keywords:</td>
<td class="TechArticleValue" colspan="3">
<Taxonomy:TaxonomyFieldControl FieldName="23f27201-bee3-471e-b2e7-b64fd8b7ca38" runat="server"></Taxonomy:TaxonomyFieldControl>
</td>
</tr>
<tr>
<td class="TechArticleTitle">Article:</td>
<td class="TechArticleValue" colspan="4">
<PublishingWebControls:RichHtmlField FieldName="f55c4d88-1f2e-4ad9-aaa8-819af4ee7ee8" runat="server"></PublishingWebControls:RichHtmlField>
</td>
</tr>
</table>
</asp:Content>


Conclusions



With that I hope that I have demonstrated how quick and easy it is to create some page layouts with data coming off my content types. I really think this is just such an awesome way and easy way to do web content management.



In the next part of this series I am going to further extend this page layout with content query web parts.



Next



We are going to explore content query web parts and how to integrate them with the page layout we just created.