Tag: web parts

Using the SharePoint image description field in the CQWP

The problem

I have a list with an Image column. When adding items to the list, you get the ability to type a description for the image. Seems like this would be used as “alt” descriptive text, right?

Image information in list

Well, let’s pull this field into a Content Query Web Part. I’ll use the default of “image on left” just to see what happens. In the Fields to display section, I put in my “Image” column name, as shown below. I’m hoping that the image will be displayed on the page, and that my image description will show up as “alt” or “title” attributes.

CQWP settings

But nothing shows up! When I view the HTML, there is no “alt” attribute, and the “title” attribute is blank.

No title

Let’s check out the XSL to see what’s going on. Opening SharePoint Designer, go to Style Library > XSL Style Sheets > ItemStyle.xsl. Look for

<xsl:template name="Default" ...>

(which should be near the top) to find the default style. Going down further in the code, you should then be able to locate the <img> tag, which looks like this:

<img src="{$SafeImageUrl}" title="{@ImageUrlAltText}">

For some reason, the @ImageUrlAltText just isn’t showing up. Rather than fight with SharePoint to figure out why, let’s create our own custom Item Style.

The Solution

The first step is to make a new item style. You may find it easiest to find the chunk of code that correlates with an existing item style that is close to what you already want to use. For example – if “Image on left” suits your purposes, copy the “Default” chunk of code (it’s around 65 lines), which begins and ends with:

<xsl:template name="Default" ...>
...
</xsl:template>

Or, if you want to use the “Clickable Image” style, then look for this:

<xsl:template name="ClickableImage" ...>
...
</xsl:template>

Copy that code and paste it to create a new section. Edit the opening xsl:template tag attributes to look like this:

<xsl:template name="YourCustomName" match="Row[@Style='YourCustomName']" mode="itemstyle">

There are probably some xsl:variable elements immediately underneath the xsl:template tag. Let’s make a new variable to hold our image descriptive text. Add this code:

<xsl:variable name="ImageAltText">
<xsl:value-of select="substring-after(@ImageUrlAltText, ', ')" />
</xsl:variable>

(Brief explanation: After some testing, I noticed that SharePoint holds the image URL and alt text in the same field, which looks something like this if you pull it out: “imageurl, This is the descriptive text here.” So I just select the substring after the comma to grab the alt text, and pull that into my new custom variable, “ImageAltText.”)

Now, look for that <img> tag, and change the code to look like this — basically, change the “@” symbol into the “$.”

<img src="{$SafeImageUrl}" title="{$ImageAltText}">

You can keep this as the “title” attribute, or change it to “alt,” depending on what you want.

<img src="{$SafeImageUrl}" alt="{$ImageAltText}">

Save ItemStyle.xsl and check it in. (You have check in ItemStyle.xsl or else the changes won’t show up.) In your CQWP, change the item style dropdown to your new custom style. If all has gone well, you should see your image pulled in with the proper image description as your title (or alt) text!

Advertisements

Adding rounded corners to web part header for SharePoint 2010

I wanted to add rounded corners to the out-of-the-box web part header in a SharePoint 2010 site. Looking around in Google’s initial results, I found a couple references to Heather Solomon’s article about adding rounded corners to a web part header in a 2007 site, but I didn’t especially love that the rounded corner didn’t go all the way to the right edge:

At this point, I have to admit that I didn’t dive much further into Google results, but decided to have a go at it on my own. If I had spent a little bit of extra time, I might have found Rich Whitworth’s article about using jQuery to add classes to the table cells of the web part header which would then give you an easy way to hook in the CSS. Or I might have seen Kyle Schaeffer’s article on using CSS and images to add rounded corners, which is pretty close to what I ended up doing. … But I didn’t, so here’s what I ended up doing on my own.

I’m going to go through a couple different examples:

  • A gradient rounded header bar using images and CSS
  • A rounded header bar using CSS3

These will all be based on the same general principle, but perhaps the slight differences and examples will be helpful.

The basic idea

Here is the HTML for the table that holds the web part header. As you can see, the row has a class of .ms-WPHeader. Then, there are up to five table cells involved:

  • td.ms-wpTdSpace – the first spacer cell on the left
  • td.ms-WPHeaderTd – the cell that holds the title
  • td.ms-WPHeaderTdMenu – the cell with the dropdown arrow; only shows if you have editing privileges
  • td.ms-WPHeaderTdSelection – the cell with the select checkbox; only shows if you have editing privileges and if it’s available
  • td.ms-wpTdSpace – the last spacer cell on the right

In order to attach a background image or color, I’ll apply styles to all the table cells by using .ms-WPHeader td. To add on the rounded corners, I’ll apply styles to td.ms-wpTdSpace – the spacer cells on the left and right – and use the :first-child pseudo-class to target the left spacer cell and modify it as much as I need to. Finally, I’ll work with .ms-WPHeaderTdMenu, which has some border styles applied by the default core CSS and a hover background effect, so that it doesn’t clash with my new header bar styles.

Ready? Let’s go!

A gradient rounded header bar using images and CSS

Let’s start with a gradient rounded header bar using images and CSS, which will compatible across most browsers, including IE7 and 8. Here are the two images I’ll be using – “bg-bar.png” for the repeated background, and “bg-barcorners.png” for the left and right corners.

Now, let’s add the CSS. I’ll start with applying the repeated background image to all the table cells:

.ms-WPHeader td { background: url('../images/bg-bar.png') repeat-x; border-bottom: none !important;  }

Here’s what it looks like:

That little gap on the right has to do with some of the border settings on the .ms-WPHeaderTdMenu cell — we’ll deal with that later.

For now, let’s apply the rounded corners to the left and right table cells. I’ll apply the background image to any table cell with .ms-wpTdSpace and position it to show the top right, then use the :first-child pseudoclass to move the background image to the top left so that the corner shows up properly.

.ms-WPHeader td.ms-wpTdSpace { background: url('../images/bg-barcorners.png') top right; }
.ms-WPHeader td:first-child.ms-wpTdSpace { background-position: top left; }

Wasn’t that easy?

The rest of this is just fine-tuning. I’m going to change the color of the title text to be white (both the normal title text and any linked title text) and remove the border from .ms-WPHeaderTdMenu to get rid of that gap. I’ll also add some styling to the .ms-WPHeaderTdMenu hover state so that the hover effect goes more with my color scheme.

.ms-WPHeader h3, .ms-WPHeader h3 a { color: #fff; }
.ms-WPHeaderTdMenu { border: none !important; }
.s4-wpcell:hover .ms-WPHeader .ms-WPHeaderTdMenu:hover { background: #c3e298; }

Here’s how it looks now:

Here’s the complete CSS code for those of you who like to just copy and paste:

.ms-WPHeader td { background: url('../images/bg-bar.png') repeat-x; border-bottom: none !important;  }
.ms-WPHeader td.ms-wpTdSpace { background: url('../images/bg-barcorners.png') top right; }
.ms-WPHeader td:first-child.ms-wpTdSpace { background-position: top left; }
.ms-WPHeader h3, .ms-WPHeader h3 a { color: #fff; }
.ms-WPHeaderTdMenu { border: none !important; }
.s4-wpcell:hover .ms-WPHeader .ms-WPHeaderTdMenu:hover { background: #c3e298; }

A rounded header bar using CSS3

For an even quicker solution that doesn’t involve using images, you can use CSS3 to add rounded corners to the header bar. This solution won’t work in IE7 or IE8 but should work in IE9, Firefox, Safari, and Chrome.

If you look at the code below, I used a background color in place of a background image, then rounded the corners using CSS3 properties.

.ms-WPHeader td { background: #7bb42d; border-bottom: none !important;  }
.ms-WPHeader td.ms-wpTdSpace { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; }
.ms-WPHeader td:first-child.ms-wpTdSpace { -moz-border-radius-topright: 0px; -webkit-border-top-right-radius: 0px; border-top-right-radius: 0px; -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; }
.ms-WPHeader h3, .ms-WPHeader h3 a { color: #fff; }
.ms-WPHeaderTdMenu { border: none !important; }
.s4-wpcell:hover .ms-WPHeader .ms-WPHeaderTdMenu:hover { background: #c3e298; }

Here’s what it looks like:

You can even use CSS3 to add a background gradient to duplicate the look from my first example.

.ms-WPHeader td { background: #7bb42d; background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, rgb(124,180,45)), color-stop(1, rgb(176,210,128))); background-image: -moz-linear-gradient( center bottom, rgb(124,180,45) 0%, rgb(176,210,128) 100%); border-bottom: none !important;  }
.ms-WPHeader td.ms-wpTdSpace { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; }
.ms-WPHeader td:first-child.ms-wpTdSpace { -moz-border-radius-topright: 0px; -webkit-border-top-right-radius: 0px; border-top-right-radius: 0px; -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; }
.ms-WPHeader h3, .ms-WPHeader h3 a { color: #fff; }
.ms-WPHeaderTdMenu { border: none !important; }
.s4-wpcell:hover .ms-WPHeader .ms-WPHeaderTdMenu:hover { background: #c3e298; }

Just for an extra variation and a slightly different look, you can add rounded corners to the bottom edges of the web part header by using more border-radius properties. Notice that I also added .ms-WPBorder { border: none; } to remove the border from any web parts that might have the border showing.

.ms-WPHeader td { background: #7bb42d; background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, rgb(124,180,45)), color-stop(1, rgb(176,210,128))); background-image: -moz-linear-gradient( center bottom, rgb(124,180,45) 0%, rgb(176,210,128) 100%); border-bottom: none !important;  }
.ms-WPHeader td.ms-wpTdSpace { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; }
.ms-WPHeader td:first-child.ms-wpTdSpace { -moz-border-radius-topright: 0px; -webkit-border-top-right-radius: 0px; border-top-right-radius: 0px; -moz-border-radius-bottomright: 0px; -webkit-border-bottom-right-radius: 0px; border-bottom-right-radius: 0px; -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; }
.ms-WPHeader h3, .ms-WPHeader h3 a { color: #fff; }
.ms-WPHeaderTdMenu { border: none !important; }
.s4-wpcell:hover .ms-WPHeader .ms-WPHeaderTdMenu:hover { background: #c3e298; }
.ms-WPBorder { border: none; }

Hope you find this helpful!

References and additional resources

Customizing the HTML code of a Content Query Web Part

I’m learning all about SharePoint branding as part of my new job at Synteractive, so this post started out as a reference file and notes on the steps I was taking. It ended up being an insanely long blog draft and I decided to throw it out there to see if more experienced SharePoint superstars can give me some tips on how I could have done this more easily or maybe to help other newbie SharePoint developers.

Some facts and disclaimers:

  • I’m working with a SharePoint 2010 Publishing site and using SharePoint Designer 2010 for modifying some of the site files.
  • I’m not going to go into CSS in this article, so I won’t talk about how I style the web part. This article is really just about getting the HTML I want to be generated from a customized Content Query Web Part.
  • At this point, I’m literally just under three weeks into learning about SharePoint branding, customizing master pages, working with XSL, etc. I have a lot to learn.

Here is the challenge that I had:

First, here’s a screenshot of my custom query web part:

  • The CQWP pulls from a list of publishing pages and displays the title, a description, an image, and links to the page.
  • Of the three dummy data items there, #1 only has a title and link, #2 has a title, description, image, and link, and #3 only has the title, description, and link.
  • The CQWP is set up to use the “image on left” Item Style.

If you view the HTML source, there is a TON of HTML code that goes into the above; there are several nested tables and stuff for allowing you to click on the header to edit it. Here is just a small sampling…

<table class="s4-wpTopTable" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td><table border="0" cellpadding="0" cellspacing="0" width="100%">
			<tr class="ms-WPHeader">

				<td align="left" class="ms-wpTdSpace"> </td>
				<td title="Recent News - Displays a dynamic view of content from your site." id="WebPartTitleWPQ2" class="ms-WPHeaderTd"><h3 style="text-align:justify;" class="ms-standardheader ms-WPTitle"><nobr><span>Recent News</span><span id="WebPartCaptionWPQ2"></span></nobr></h3></td>

The actual data part – the list of news items – has HTML code that looks like this:

<ul class="dfwp-column dfwp-list" style="width:100%" >
<li class="dfwp-item"><div class="item"><div class="link-item"><a href="#" title="">New SPDevWiki site released!</a><div class="description"></div></div></div></li>
<li class="dfwp-item"><div class="item"><div class="link-item"><a href="#" title="">SharePoint 2010 SDK released</a><div class="description">SharePoint 2010 SDK released for RTM</div></div></div></li>
<li class="dfwp-item"><div class="item"><div class="link-item"><a href="#" title="">Announcing Community Kit for SharePoint</a><div class="description">CodePlex projects teams get together to release CKS:Dev!</div></div></div></li>
</ul>

There are plenty of classes to work with… but if I were to modify the .dfwp-xxx classes, those would affect all of the web parts using that type of display, and in my specific situation, I couldn’t throw the web part into my own custom <div> with an ID or class that would allow me to target only that web part.

But I’m getting ahead of myself. Let me show you what I was shooting for. Here’s my HTML that I would ideally want:

<div class="blogfeed-wrapper">
	<div class="blogfeed">
		<h2>Recent News</h2>
		<ul>
			<li>
				<p class="date">Jun 5, 2010</p>
				<p class="image"><img src="images/pic.png" alt="image" width="50" height="50" /></p>
				<h3><a href="#">Article title</a></h3>
				<p class="blurb">Article blurb</p>
			</li>
			<!-- repeat LI for each data item -->
		</ul>
	</div>
</div>

To accomplish this, I would have to:

  1. Figure out how to wrap the DIVs and UL around the repeated list items, along with the H2 heading
  2. Modify the code for the repeated list items to use the HTML code I want (and to show the additional “article date” field)

I looked around for information about customizing the Content Query Web Part and found this article, “Display Content Query Web Part Results in a Grid/Table” by Paul Galvin which seemed like it might do the trick. He references Heather Solomon’s blog post, “Customizing the Content Query Web Part and Custom Item Styles.” Here are the step-by-steps of what I did:

  1. I followed Heather Solomon’s article, steps 5-11, and Paul Galvin’s step 4 to create a test item style in ItemStyle.xsl in order to list out the data already being pulled into the web part. I named my custom style “TestList” – I’ll keep it around for testing other web parts.
    1. In SharePoint Designer 2010, open Style Library/XSL Style Sheets/ItemStyle.xsl. Note that this file is only available in a root or top-level SharePoint site, not in subsites.
    2. Click the “Edit” link (in the “Customization” section).
    3. In ItemStyle.xsl, you’ll see a few <xsl:variable /> tags at the top, followed by sections of <xsl:template /> code. Each <xsl:template /> has a name associated with it which corresponds to the Item Style dropdown in the Web Part editor.
    4. Here’s the code I added – just to keep it easy to find, I put it under the “Default” template section. Also notice that the value for name and @Style must be identical.
      <xsl:template name="Default" match="*" mode="itemstyle">
      ....
      </xsl:template>
      <!-- custom item style template for listing out fields -->
      <xsl:template name="TestList" match="Row[@Style='TestList']" mode="itemstyle">
      	<xsl:for-each select="@*">
      		<xsl:value-of select="name()" /><br />
      	</xsl:for-each>
      </xsl:template>
    5. Back in the browser, click to edit the web part. In the web part editor, under Presentation > Styles, pick the new “TestList” from the list of Item Styles:
    6. Here’s what I ended up with:
    7. If you want, copy and paste the list into a text editor for reference just in case.
  2. Now, following steps 12-13 in Heather’s article, I went to find the specific column “Internal Names” for fields that I wanted to add to the web part.
    1. Go to Site Settings > Site Administration: Site Libraries and Lists. This page shows a list of the various types of lists and libraries in your site.
    2. I picked the “Pages” list because that’s what my Content Query Web Part is pulling, but you’ll want to go to whichever list you’re trying to pull data from. This takes you to the List Information page.
    3. Scroll down to the Columns section to view all the columns that are available for that type of content.
    4. Hover or click on the link for the columns that you want to add to the Content Query Web Part results. I’m primarily interested in the “Article Date” column. At the very end of the link is the “Field” parameter, followed by the internal column name. In my case, the Article Date column’s internal name is ArticleStartDate.
  3. Following steps 16-18 in Heather’s article, I export the CQWP, modify the code to include the extra column(s), then import it back in as a new web part.
    1. Back on the web site, expand the edit menu and choose “Export.”
    2. Save the .WEBPART file to your computer, then open it in a text editor.
    3. Look for “CommonViewFields” and you’ll see something like this:
      <property name="CommonViewFields" type="string" />
    4. Following Heather’s instructions, I modify the tag to include my additional ArticleStartDate column.
      <property name="CommonViewFields" type="string">ArticleStartDate, DateTime</property>

      Note: From this MSDN article, “How to Display Custom Fields in a Content by Query Web Part,” there’s an additional step (Step 4) to map the new column to existing/available fields in the web part. This only works if your custom field can replace the Title, Description, LinkURL, or ImageURL web part fields – which could save you a few steps if, say, you wanted to show a different text column in place of the “description” field. In this case, though, since I want to display the article date, I’m going to have to do some actual customization to the XSL files, described below.

    5. Save the file. Back in the browser, click the “Edit” button to edit the page so that you can add more web parts to the page.
    6. Click “Add a Web Part”, then click “Upload a Web Part” and browse to your .WEBPART file, then click Upload.
    7. You’ll have to click “Add a Web Part” again, but this time you’ll see an additional folder for “Imported Web Parts” with your new web part listed. Add this to your page. (Mine is called “Recent News” because it exported my original Content Query Web Part, which had “Recent News” as the title.)
    8. The new web part will show up on the page. Notice that the new column is included with the other data:
    9. Delete the original web part just to get it off the page.
  4. Now, I wanted to figure out how to modify the HTML to display what I wanted: Some outer wrapper DIVs for styling, an H2 heading for the “Recent News” title, a wrapper UL for the list items, and then using a simple LI for each list item. Bear with me as I explain the overall approach:
    1. First – I’m going to get rid of the existing “Recent News” heading by changing the Chrome of the web part to hide the current title and ONLY show the list itself. This will allow me to get rid of the funky tables and code that are really difficult to style. (I’m going to be inserting my own heading in the HTML instead.)
    2. Then – I’m going to do the “easy” part, which is to edit ItemStyle.xsl with my HTML for each list item (the <LI> and everything in between from the top of this page in my “ideal” HTML code). This will allow me to get rid of the various DIVs from the default “Image on Left” code, remove displayed fields that I don’t need, and add in the additional fields that I want to display (in this example, ArticleStartDate). Both Paul and Heather’s articles cover this part. Note: As mentioned in Heather’s article, ItemStyle.xsl “repeats” itself for each data item, which is why I would only put in the <LI> code. If I added <UL>, I would get a separate UL list for each item.
    3. Finally – the “hard” part, which is to edit ContentQueryMain.xsl to put in my wrapper DIVs, H2 heading, and UL, which will go around the stuff being generated in ItemStyle.xsl and make everything look pretty. This is where I turned to Paul’s article.
  5. Here we go! Starting with 4a: Hide the web part title bar by editing the web part and changing the Chrome Type to “None.”

    Note: If you were to save/close or check in the page, you’ll just see the list of data fields without the title bar.

    If you’re like me, you may freak out initially: “What have I done?! Now I can’t edit the web part!” No worries – as soon as you click “Edit page,” you’ll see your web part zones and the title bar will reappear for editing.
  6. Pulling from Heather’s article (steps 19-24), I open ItemStyle.xsl (again) in SharePoint Designer for editing.
    1. At this point, I already have an idea of the Item Style that I want to start from – the “Image on Left” style, because it already will contain the code I need for the image, title, and description, so all I need to do is move things around and add the date. You will need to figure out which Item Style you want to start with and find the corresponding code.
    2. In this case, “Image on Left” happens to be the same as the “default” template, so I find the 60 or so lines of code that make up the Default template, copy, and paste it under my TestList template.
      <xsl:template name="Default" match="*" mode="itemstyle">
      ....
      </xsl:template>
    3. My first step is to replace <xsl:template name=”Default” match=”*” mode=”itemstyle”> with something that looks more like my TestList template line, but with yet another custom Item Style name. I’ll call mine “News”:
      <xsl:template name="News" match="Row[@Style='News']" mode="itemstyle">

      In other words, replace name=”YourCustomStyleName” and match=”Row[@Style=’YourCustomStyleName‘]” in your <xsl:template> tag.

    4. Now, each xsl:template section has some variables or parameters at the top, followed by recognizable HTML code mixed in with various xsl tags for testing conditionals or displaying data. I copy the “ideal HTML” that I want to have and paste it into the top of that section to start me off.
      <li>
      	<p class="date">Jun 5, 2010</p>
      	<p class="image"><img src="images/pic.png" alt="image" width="50" height="50" /></p>
      	<h3><a href="#">Article title</a></h3>
      	<p class="blurb">Article text</p>
      </li>
      
    5. Then, start moving the relevant XSL code into your HTML tags.You can see what I did below. I had to add the ArticleStartDate field (see Step 20 in Heather’s blog) and formatted it by following the steps at Josh Gaffey’s blog for Custom Date Formats in SharePoint so that the date is displayed as m/d/yyyy. (Note that I put stuff I don’t really care about… or don’t understand what they do… in a comment tag to hide them.)
      <!-- custom item style template for recent news -->
      <xsl:template name="News" match="Row[@Style='News']" 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="SafeImageUrl">
              <xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
                  <xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>
              </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>
      
      	<li>
      		<!-- Here is where I add in my ArticleStartDate field. It would typically look like
      		<xsl:value-of select="@ArticleStartDate" />
      		but the extra stuff is to format the date. -->
      		<p class="date"><xsl:value-of select="ddwrt:FormatDate(@ArticleStartDate,1033,1)" /></p>
      
      		<xsl:if test="string-length($SafeImageUrl) != 0">
                  <p class="image">
                      <a href="{$SafeLinkUrl}">
                        <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>
                        <img class="image" src="{$SafeImageUrl}" title="{@ImageUrlAltText}">
                          <xsl:if test="$ImageWidth != ''">
                            <xsl:attribute name="width">
                              <xsl:value-of select="$ImageWidth" />
                            </xsl:attribute>
                          </xsl:if>
                          <xsl:if test="$ImageHeight != ''">
                            <xsl:attribute name="height">
                              <xsl:value-of select="$ImageHeight" />
                            </xsl:attribute>
                          </xsl:if>
                        </img>
                      </a>
                  </p>
              </xsl:if>
      
      		<h3><a href="{$SafeLinkUrl}" title="{@LinkToolTip}"><xsl:value-of select="$DisplayTitle"/></a></h3>
      		<p class="blurb"><xsl:value-of select="@Description" /></p>
      
      		<!--
      		<xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>
                  <a href="{$SafeLinkUrl}" title="{@LinkToolTip}">
                    <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>
      
                  </a>
                  -->
      	</li>
      </xsl:template>
      
    6. When you’re done, go back into the browser to edit the web part. You should see your new custom item style in the Item Style select box. In the screenshot below, you can see that “News” (the name of my custom Item Style) showed up properly, so I was able to pick it and apply it. Of course, I’m still missing the wrapper HTML code (for the UL, etc.) so I moved on to the next step…
  7. Now I want to get the wrapper HTML code (below) around the LIs.
    <div class="blogfeed-wrapper">
    	<div class="blogfeed">
    		<h2>Recent News</h2>
    		<ul>
    		...
    		</ul>
    	</div>
    </div>
    

    At first glance, Paul’s article seems perfect for what I need to do. His method is to edit ContentQueryMain.xsl to pass in parameters that identify the “current position” and “last row” so that ItemStyle.xsl can put in special code at the start of the list (when current position = 0) and the end of the list (current position = last row). Unfortunately, after following his detailed and clear instructions, I found that it didn’t work for me. I’m not sure if it’s because of a difference in SharePoint 2007 and 2010, but basically what was going on was that the .dfwp-xxxx UL and LIs were still being wrapped around my code. So while Paul’s technique “worked” in the sense that the wrapper code showed up before the first item and after the last item, the other stuff was getting in the way and creating havoc with the code:

    <ul class="dfwp-column dfwp-list" style="width:100%" >
    <li class="dfwp-item">
    	<div class="blogfeed-wrapper">
    		<div class="blogfeed">
    			<h2>Recent News</h2>
    			<ul>
    			<li>...</li>
    </li>
    <li class="dfwp-item">
    			<li>...</li>
    </li>
    <li class="dfwp-item">
    			<li>...</li>
    			</ul>
    		</div>
    	</div>
    </li>
    </ul>
    

    Diving deep into the ContentQueryMain.xsl file, I was able to locate various variable definitions that hold the HTML code for the UL and LI tags. There were some variables near the top of the file and more inside the OuterTemplate.Body template.

    <xsl:variable name="BeginList" select="string('&lt;ul class=&quot;dfwp-list&quot;&gt;')" />
    <xsl:variable name="EndList" select="string('&lt;/ul&gt;')" />
    <xsl:variable name="BeginListItem" select="string('&lt;li class=&quot;dfwp-item&quot;&gt;')" />
    <xsl:variable name="EndListItem" select="string('&lt;/li&gt;')" />
    
    <xsl:template name="OuterTemplate.Body">
    	...
    	<xsl:variable name="BeginColumn1" select="string('&lt;ul class=&quot;dfwp-column dfwp-list&quot; style=&quot;width:')" />
    	<xsl:variable name="BeginColumn2" select="string('%&quot; &gt;')" />
    	<xsl:variable name="BeginColumn" select="concat($BeginColumn1, $cbq_columnwidth, $BeginColumn2)" />
    	<xsl:variable name="EndColumn" select="string('&lt;/ul&gt;')" />
    

    If you do a quick search in the code for $BeginList, $EndList, etc., you’ll see that those variables are printed out several times in different places. Not being at all an expert, though, it was pretty hard for me to figure out what was going on, so I resorted to an elegant solution to find the places to target. And by “elegant,” I mean “amateurish and hackish.” If you aren’t too embarrassed to follow my example, here’s what you do…

  8. Go through all of the code, looked for all the instances where those variables are printed out, and add test text before them that will print out in the HTML and show which conditionals were being fulfilled. Here’s one example:
    TEST1
    <xsl:value of disable-output-escaping="yes" select="$BeginColumn" />
    

    You’ll want to repeat that for each time you see an <xsl:value /> tag for $BeginList, $EndList, $BeginListItem, $EndListItem, $BeginColumn, and $EndColumn.

    When I saved and refreshed my browser, this is what I saw:

  9. Now you can go through the code, find the places where your test code was added, and replace them with your custom code. Below are the steps that I took, but you’ll need to figure out what will work for you.
    1. My first step was to set up my own custom variables for the wrapper. Inside the OuterTemplate.Body, under the existing variable definitions for BeginColumn1, BeginColumn2, etc., I added this custom code below. Since I didn’t feel like dealing with lots of &lt; and &gt;, I put my HTML code inside a CDATA section:
      <!-- my custom variables for the news section -->
      <xsl:variable name="newsStart">
      	<![CDATA[
      		<div class="blogfeed-wrapper">
      			<div class="blogfeed">
      				<h2>Recent News</h2>
      				<ul>
      
      	]]>
      </xsl:variable>
      <xsl:variable name="newsEnd">
      	<![CDATA[
      				</ul>
      			</div>
      		</div>
      
      	]]>
      </xsl:variable>
      
      
    2. Okay, NOW I’m ready to replace my “TEST#” stuff with actual useful code. I looked for TEST1 (start of list), TEST9 (start of list item), TEST10 (end of list item), and TEST6 (end of list) in the ContentQueryMain.xsl file. I added <xsl:choose /> and <xsl:when /> statements to put in my custom variables when the Item Style = “News” (the name of my custom item style).Here is the original code (with the first TEST1 stuck in there):
      <xsl:choose>
          <xsl:when test="$cbq_isgrouping != 'True'">
              <xsl:if test="$CurPosition = $FirstRow">
      		TEST1
      		<xsl:value-of disable-output-escaping="yes" select="$BeginColumn" />
              </xsl:if>
          </xsl:when>
      

      And here’s my edited code:

      <xsl:choose>
          <xsl:when test="$cbq_isgrouping != 'True'">
              <xsl:if test="$CurPosition = $FirstRow">
              <!-- customized stuff -->
      		<xsl:choose>
              	<xsl:when test="@Style='News'">
              		<xsl:value-of disable-output-escaping="yes" select="$newsStart" />
      			</xsl:when>
      			<xsl:otherwise>
      				<!-- this was the original code -->
      				<xsl:value-of disable-output-escaping="yes" select="$BeginColumn" />
      			</xsl:otherwise>
              </xsl:choose>
              <!-- end customized stuff -->
      
              </xsl:if>
          </xsl:when>
      
    3. I repeated the process for the other sections. Because my <li> and </li> is inside the ItemStyle.xsl file, I didn’t need to define variables for those and left those “when” statements empty.
      <!-- customized stuff -->
      <xsl:choose>
      	<xsl:when test="@Style='News'">
      		<xsl:value-of disable-output-escaping="yes" select="$newsEnd" />
      	</xsl:when>
      	<xsl:otherwise>
      		<!-- this was the original code -->
      		<xsl:value-of disable-output-escaping="yes" select="$EndColumn" />
      	</xsl:otherwise>
      </xsl:choose>
      <!-- end customized stuff -->
      
      ...
      
      <xsl:template name="OuterTemplate.CallItemTemplate">
          <xsl:param name="CurPosition" />
          <xsl:param name="LastRow" />
          <!-- customized stuff -->
      	<xsl:choose>
          	<xsl:when test="@Style='News'" />
      	<xsl:otherwise>
      		<!-- this was the original code -->
      		<xsl:value-of disable-output-escaping="yes" select="$BeginListItem" />
      	</xsl:otherwise>
          </xsl:choose>
          <!-- end customized stuff -->
      
      ...
      
      <!-- customized stuff -->
      <xsl:choose>
      	<xsl:when test="@Style='News'" />
      	<xsl:otherwise>
      		<!-- this was the original code -->
      		<xsl:value-of disable-output-escaping="yes" select="$EndListItem" />
      	</xsl:otherwise>
      </xsl:choose>
      <!-- end customized stuff -->
      
      
    4. And after saving the page, going back to the browser, and refreshing, I finally have my desired result! (Note that the colors and layout are all part of my stylesheet, which I’m not delving into for this article.)

      Here is what the HTML looks like (I took out the URLs and added line breaks for legibility). It’s much cleaner than the original generated HTML and includes the classes that I need to hook in my CSS!

      <div class="blogfeed-wrapper">
      	<div class="blogfeed">
      		<h2>Recent News</h2>
      		<ul>
      			<li xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
      				<p class="date"></p>
      				<h3><a href="..." title="">New SPDevWiki site released!</a></h3>
      				<p class="blurb"></p>
      			</li>
      			<li xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
      				<p class="date">8/3/2010</p>
      				<p class="image"><a href="..."><img class="image" src=".../image.png" title="" /></a></p>
      				<h3><a href="..." title="">SharePoint 2010 SDK released</a></h3>
      				<p class="blurb">SharePoint 2010 SDK released for RTM</p>
      			</li>
      			<li xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
      				<p class="date">6/4/2010</p>
      				<h3><a href="..." title="">Announcing Community Kit for SharePoint</a></h3>
      				<p class="blurb">CodePlex projects teams get together to release CKS:Dev!</p>
      			</li>
      		</ul>
      	</div>
      </div>
      

Whew! That was really long. Here’s a summary of the steps that I took:

  1. Created a custom “test” Item Style to list out the fields being passed to the web part, which also helped after Step 3 to see if the new fields were added in properly or not.
  2. Figured out the internal column name(s) for the additional field(s) to add to the web part.
  3. Exported the Custom Query Web Part that was on the page, modified the code to add the fields, and imported the modified web part back in and added it to the page.
  4. Changed the Chrome type to “none” to hide the title bar.
  5. Created a custom “News” Item Style and modified the code to get the HTML that I wanted to display for each item.
  6. Hacked ContentQueryMain.xsl to figure out where to modify the code, then added custom code specific to the “News” Item Style for my wrap-around code.

I definitely appreciate any comments that more experienced SharePoint developers have about how to modify the ContentQueryMain.xsl file more elegantly or other ways to accomplish this! If you have questions, please feel free to post them but remember that I’m new to this as well and may not be able to answer them. 😉

References and acknowledgements