This is part 2 of an ongoing series of posts about my experience building a custom site on top of Office SharePoint Server 2007. If you haven't yet read part 1, I'd strongly suggest reading it first.
In my last post, I discussed planning and building out my site structure, as well as customizing my master page. In this post, I'll cover creating page layouts and their backing content types, and customizing the Content Query (CQWP) web part to roll up individual posts into a summary page.
Creating Content Types
The first thing I needed to do before I could go about creating my custom page layouts was create a content type. Because I wanted it to be used by pages in my site, I used the Page content type as the parent for my custom content type, which I called TBPost. I then added a few custom fields to my content type, with the following column types:
Person or Group
Date and Time
Most of these custom columns are straightforward, with the exception of the "Featured Post" and "Post Category" fields.
The Featured Post field is used to determine whether a given post is featured on my site. In practice, posts that are featured get displayed at the top of the rollup pages and have a slightly different style applied to them. I have three options for this field: Not Featured, Section Only, and Front Page. By setting this field on a given page, I can make it display at the top of either the front page of the site, or only on the rollup page for a specific section. If a post is set to be featured on the front page, it's implicitly meant to be featured within the section as well. I'll explain how I do this a bit later.
The Post Category field is a lookup field to a custom list that I created that contains all the categories that a post can be in. You may be wondering why I use a lookup field in this case, instead of just a choice field, like I did for the Featured Post field. Well, a given category has an image associated with it, and by using a custom list to store the categories, I can associate an image with each category as an additional column in the list. In the end, I have not been able to leverage this in the way I initially planned, but with this design I do have capability to associate additional data with a category and look it up using code if I need to.
I should also note that both the custom columns and content type I created are in the root site. This is so that the entire site hierarchy can use them, and so that the Content Query web parts can filter on them. But both content types and columns can be scoped to a subweb.
Building Page Layouts
With my new content type created, I set about building my page layouts. I started with the Post layout, which is used by every individual post in the site. Whenever I want to post something new, I simply create a new Page in the appropriate subweb using the Post page layout.
To build the layout, I followed pretty much the same steps that I did when building my master page: I opened up a page on my old site and copied the HTML source for the main post body into the
PlaceHolderMain content placeholder in my page layout in SharePoint Designer. Then I removed the content of the post, and dragged in field controls from the handy SharePoint Designer toolbox. Here's what the page layout looks like in SharePoint Designer:
This whole process was very easy, because SharePoint Designer automatically loads the appropriate fields and controls into the toolbox based on content type that I associated with the page layout when I created it. It really is as easy as dragging and dropping these fields into the right spot on the layout. One other thing I did was add an Edit Mode Panel to the top of the layout. This nifty little thing only displays its contents when the page is brought into edit mode, so it's perfect for controls that you want only your authors to see or edit. In my case, I put the Featured Post and Post Category field controls there. You can find the Edit Mode Panel in the toolbox, under SharePoint Controls -> Server Controls.
The trickiest problem I ran into was how to display the category image for a post. It couldn't be hard coded into the layout, because then I'd need a separate layout for every category, which would be a pain to maintain later. I could also have used an image field control, but then every time I created a new post I would have to select the image. I wanted it to be done automatically based on the category I selected.
My initial plan was to create a custom field control or web part that would render out the right image based on the category I'd selected on the page. However, I really wanted to avoid writing code, so I made a compromising solution. Every site in MOSS has an associated site image. This image is what's displayed in the top left-hand corner of the site when you're using default.master. I changed this for each of my subwebs by going to Site Settings -> Title, description, and icon. Then, in my page layout, I used some markup from default.master to insert that site image into the layout:
<SharePointWebControls:splinkbutton runat="server" NavigateUrl="~site/" id="onetidProjectPropertyTitle">
<SharePointWebControls:sitelogoimage id="onetidHeadbnnr0" runat="server"/>
As you can see, the
sitelogoimage control is wrapped in a
splinkbutton control that points to the subweb, not the site collection. This means that if I click the category image for a given post, I am taken to the subweb that that page is in, which essentially takes me to the rollup page for that category. This is the exact functionality I had on my old site. This solution is pretty elegant, and prevents me from having to use different page layouts on different sites or pick images manually for every post. It's not ideal, however, for reasons I'll point out a bit later.
Now that I had a layout for my individual posts, I wanted to make some layouts for my rollup pages that would aggregate all the pages in various categories, and across the whole site. I started with the individual site rollup page layout, since most of the rollup pages in the site would use this layout. My main home page would be slightly different, but I'll get to that in a minute.
I knew that I wanted to use Content Query web parts for rolling up the pages, so I put a web part zone in the center of my layout and added the web parts to the zone there. I added two web parts. The first CQWP was configured to roll up only pages that were featured on either the home page or the section (using the Featured Post field I mentioned earlier). I did this by configuring the part to look for Pages under the root site that had the content type TBPost. I also used the Filter properties, telling it to exclude pages that did not have their Featured Post property equal to "Front Page" or "Section Only." I also wanted to be sure that my rollup pages were excluded from the CQWP, so I set it to filter out pages whose category was "Rollup."
The second CQWP was configured to roll up all pages across the subweb, i.e. all pages in a given category. To accomplish this, I filtered the results only on the Rollup field and I excluded pages that had their Featured Post property equal to "Front Page" or "Section Only." This means that if a post is featured, it only shows up in one of the CQWPs. I also turned on the RSS feed for this second web part, but I'll discuss RSS at greater length later. Finally, I configured both web parts to only return 5 items apiece.
The other thing I needed to do was tell the CQWP to go and grab some of my extra fields. By default, the CQWP does not return all of the fields for a given page. However, you can override this behavior by setting the
CommonViewFields property. There is more information on doing this in George's post on the CQWP. In my case, I set the property as follows:
I also didn't want to have to use the annoying name
Post_x0020_Time in my XSL, so I used the
DataColumnRenames property to tell the CQWP to remap that field name to another name in my XSL:
At this point, my CQWP's were configured to grab the right data, but of course they didn't render the content quite like I wanted them to. In order to get things looking right, I had to edit the XSLT that was being applied to the items, thus changing how they got rendered out. I'll tackle that particular task in my next post. Until next time...