Create a jQuery accordion with a SharePoint CQWP

Today we’ll be creating 5 (don’t be scared!) small files that will result in you being able to add a Content Query Web Part to a page, and display the results in a jQuery accordion format. It should look something like this:

Click for a demo or download the source files

Steps to create your own:

Step 1: Add, then Export a standard CQWP

This method of editing a CQWP to do what you want involves no custom coding, no packaging, no .wsp files. All you need to do is go to any page and insert a CQWP Web Part (OOTB). Once it is on your page, click the little black arrow that displays the Web Parts context menu and select Export. This will download a .webpart file called Content. Open this file in your favorite code editor (at the moment mine are Notepad++ for Windows and TextMate for Mac), and go to step 2.

Step 2: Edit the .webpart file to read custom .xsl files

To see the full .webpart file, check out the source code, but basically we’re going to edit 4 lines and put links to our custom .xsl files:

  • Title
  • ItemXslLink
  • MainXslLink
  • XsL

Once you have edited these 4 lines, upload this webpart to the root Web Part gallery by clicking Site Actions > Site Settings > Web parts.

<property name="Title" type="string">Accordion</property>
<property name="ItemXslLink" type="string">/Style Library/XSL Style Sheets/AccordionStyle.xsl</property>
<property name="MainXslLink" type="string">/Style Library/XSL Style Sheets/AccordionCQWP.xsl</property>
<property name="Xsl" type="string">&lt;xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:cmswrt="http://schemas.microsoft.com/WebPart/v3/Publishing/runtime" exclude-result-prefixes="xsl cmswrt x" &gt; &lt;xsl:import href="/Style Library/XSL Style Sheets/Header.xsl" /&gt; &lt;xsl:import href="/Style Library/XSL Style Sheets/AccordionStyle.xsl" /&gt; &lt;xsl:import href="/Style Library/XSL Style Sheets/AccordionCQWP.xsl" /&gt; &lt;/xsl:stylesheet&gt;</property>

Step 3: Create AccordionStyle.xsl

Instead of using SharePoint’s ItemStyle.xsl (not best practice to edit that directly) we’re going to create our own stylesheet (the one we linked to in step 2). It has the same header as ItemStyle.xsl, so feel free to copy from there or from below. However, it only has one template. I’ve called the template Accordion, so when you’re editing the Web Part properties in the browser you’ll see this as the only selectable style in the Presentation pane.

Basically all this style does is wrap the header field (represented by @Title) in a div with a class of accordion-header, and the body field (represented by @Body) in a div with a class of accordion-content. We’ll use these classes later in the jQuery functions.

Once you’ve created this file, put it in /Style Library/XSL Style Sheets/ using SharePoint Designer.

<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">

<!-- Accordion Template -->
    <xsl:template name="Accordion" match="Row[@Style='Accordion']" mode="itemstyle">

       <div class="accordion-header"><xsl:value-of select="@Title"/></div>
	   <div class="accordion-content"><xsl:value-of select="@Body" disable-output-escaping="yes" /></div>

    </xsl:template>
</xsl:stylesheet>

Step 4: Create AccordionCQWP.xsl

The next page we’ll make starts off as a clone of ContentQueryMain.xsl, so go there real quick and copy all of that code into your new file.

There are three things we will do to this code.

  1. Replace dfwp-list with accordion (on lines 32 and 83)
  2. Replace all occurrences of dfwp-item with accordion-item (line 34)
  3. Add (using html character codes…annoying) “expand all” and “collapse all” divs before the list.

Come to think of it, you wouldn’t have to do #3 if you just used jQuery to prepend those buttons to the list. Yeah, that’s the easier way…I’ll update the post soon.

Once you’ve done those 3 edits, upload this file to /Style Library/XSL Style Sheets/ (the same place as the file in step 3)

      <!-- beginning of code from ContentQueryMain.xsl goes here -->
  <xsl:variable name="BeginList" select="string('&lt;ul class=&quot;accordion&quot;&gt;&lt;div class=&quot;expand-all&quot;&gt;Expand All&lt;/div&gt;&lt;div class=&quot;collapse-all&quot;&gt;Collapse All&lt;/div&gt;')" />
  <xsl:variable name="EndList" select="string('&lt;/ul&gt;')" />
  <xsl:variable name="BeginListItem" select="string('&lt;li class=&quot;accordion-item&quot;&gt;')" />
  <xsl:variable name="EndListItem" select="string('&lt;/li&gt;')" />
  <xsl:template match="/">
        <xsl:call-template name="OuterTemplate" />
    </xsl:template>
    <xsl:template name="OuterTemplate">
        <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row" />
        <xsl:variable name="RowCount" select="count($Rows)" />
        <xsl:variable name="IsEmpty" select="$RowCount = 0" />
            <div id="{concat('cbqwp', $ClientId)}" class="cbq-layout-main">
                 <xsl:if test="$cbq_iseditmode = 'True' and string-length($cbq_errortext) != 0">
                    <div class="wp-content description">
                        <xsl:value-of disable-output-escaping="yes" select="$cbq_errortext" />
                    </div>
                  </xsl:if>
                  <xsl:choose>
                      <xsl:when test="$IsEmpty">
                           <xsl:call-template name="OuterTemplate.Empty" >
                               <xsl:with-param name="EditMode" select="$cbq_iseditmode" />
                           </xsl:call-template>
                      </xsl:when>
                      <xsl:otherwise>
                           <xsl:call-template name="OuterTemplate.Body">
                               <xsl:with-param name="Rows" select="$Rows" />
                               <xsl:with-param name="FirstRow" select="1" />
                               <xsl:with-param name="LastRow" select="$RowCount" />
                          </xsl:call-template>
                      </xsl:otherwise>
                  </xsl:choose>
            </div>
            <xsl:if test="$FeedEnabled = 'True' and $PageId != ''">
                <div class="cqfeed">
                    <xsl:variable name="FeedUrl1" select="concat($SiteUrl,$FeedPageUrl,'xsl=1&amp;web=',$WebUrl,'&amp;page=',$PageId,'&amp;wp=',$WebPartId,'&amp;pageurl=',$CBQPageUrl,$CBQPageUrlQueryStringForFilters)" />
                    <a href="{cmswrt:RegisterFeedUrl( $FeedUrl1, 'application/rss+xml')}"><img src="\_layouts\images\rss.gif" border="0" alt="{cmswrt:GetPublishingResource('CbqRssAlt')}"/></a>
                </div>
            </xsl:if>
    </xsl:template>
    <xsl:template name="OuterTemplate.Empty">
        <xsl:param name="EditMode" />
            <xsl:if test="$EditMode = 'True' and string-length($cbq_errortext) = 0">
                <div class="wp-content description">
                    <xsl:value-of disable-output-escaping="yes" select="$cbq_viewemptytext" />
                </div>
            </xsl:if>
    </xsl:template>
    <xsl:template name="OuterTemplate.Body">
      <xsl:param name="Rows" />
      <xsl:param name="FirstRow" />
      <xsl:param name="LastRow" />
      <xsl:variable name="BeginColumn1" select="string('&lt;div class=&quot;expand-all&quot;&gt;Expand All&lt;/div&gt;&lt;div class=&quot;collapse-all&quot;&gt;Collapse All&lt;/div&gt;&lt;ul class=&quot;accordion&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;')" />
      <!-- rest of code from ContentQueryMain.xsl goes here -->

Step 5: jQuery functions

Our jQuery code is going to do several things:

  • Start every content pane hidden and make sure all headers are deactivated
  • When a header is clicked, it toggles the “expanded” class on the header (giving it a different style) and uses slideToggle (a jQuery animation) to show or hide the content pane.
  • It adds functions to the “expand all” and “collapse all” buttons we made earlier

When you’re done copy and pasting (or modifying) this code, save it in /Style Library/scripts/accordion.js. If you’ve got other javascript files there you should consider combining and condensing your code for less calls, but for the sake of this tutorial, it will be a standalone .js file. Once you’ve dropped the file into your system, link to it from the Master Page. Here’s an article on how to call custom javascript from your Master Page.

function accordionLoad() {

	$(".accordion-header").removeClass("expanded");
	$(".accordion-content").hide();

	$(".accordion-header").bind("click", function(){
		$(this).toggleClass("expanded");
		$(this).siblings(".accordion-content").slideToggle();
	})

	$(".expand-all").bind("click",function(){
		$(this).siblings(".accordion").find(".accordion-content").slideDown();
		$(this).siblings(".accordion").find(".accordion-header").addClass("expanded");
	})

	$(".collapse-all").bind("click",function(){
		$(this).siblings(".accordion").find(".accordion-content").slideUp();
		$(this).siblings(".accordion").find(".accordion-header").removeClass("expanded");
	})
}

$(document).ready(function(){
	accordionLoad();
});

side note: I would love to rewrite this jQuery to be a controller, that way it can be re-used across the site anytime somebody adds .accordion() to an element using jQuery.

Step 6: CSS

This is the way I want my accordion to look, so feel free to modify. This is also the style used in the demo of this tutorial.

When you’re done, drop this file in /Style Library/css/accordion.css (again, you could also include this in you main css, but for the sake of the tutorial it’s standalone).

/* Accordion Style
********************************************************** */
ul.accordion {
	list-style:none;
	margin:0px;
	padding:0px;
}
.accordion-item {
	border-top:1px solid #ccc;
}
.accordion-header {
	font-size:1.2em;
	cursor:pointer;
	padding:10px;
}
.accordion-header:hover {
	background:#efefef;
}
.accordion-header.expanded {
	background:#dfdfdf;
}
.accordion-content {
	padding:20px;
}
.expand-all,
.collapse-all {
	display:inline-block;
	cursor:pointer;
	padding:5px 10px;
}
.expand-all:hover,
.collapse-all:hover {
	background:#efefef;
}

Step 7: Putting it all together

So, once you’ve made all of the above files, your structure should look like this:

  • /Style Library/XSL Style Sheets/AccordionStyle.xsl
  • /Style Library/XSL/Style Sheets/AccordionCQWP.xsl
  • /Style Library/scripts/accordion.js
  • /Style Library/css/accordion.css
  • Accordion.webpart (uploaded to the Web Part Gallery)

Now go to your page, add the Accordion Web Part, and use it to display a list of items (FAQs and announcements work great in this format). It should be quite easy to read in the is jQuery Accordion format.

Click for a demo or download the source files

Sharepoint 2010 + SPServices + jQuery Mobile

Here we will setup a SharePoint 2010 page that utilizes both jQuery Mobile as well as the jQuery SPServices library. Note: This post was updated Feb 21 to reflect an issue I’ve documented here with IE8.

Create a new page

Create a new blank .aspx page in the “Site Pages” library (using SharePoint Designer 2010). I like to use a new .aspx page like this because I can create a document from scratch, I don’t have to deal with page layouts, master pages, etc. The same principals can be applied elsewhere, but this gives me a clean page to start from (which is way easier to debug).

Setup your html

Note: I’ve created my document using html5 standards (as does jQuery Mobile).

<!DOCTYPE html>
<html>
<head>
<meta name="WebPartPageExpansion" content="full" />
<title>SharePoint, SPServices, and jQuery Mobile!</title>
	<link rel="apple-touch-icon" href="/Style Library/Images/apple-touch-icon.png"/>
	<meta name="viewport" content="user-scalable=yes, width=device-width, initial-scale=1"> 
	<link rel="stylesheet" href="/Style Library/scripts/jquery.mobile-1.0.1/jquery.mobile-1.0.1.min.css" />
	<script src="/Style Library/scripts/jquery-1.6.4.min.js"></script>
	<script src="/Style Library/scripts/jquery.SPServices-0.7.0.min.js"></script>
<script type="text/javascript">
 //we'll put the SPServices functions here
 //note the placement of this script tag BEFORE the jQuery Mobile javascript link
</script>
	<script src="/Style Library/scripts/jquery.mobile-1.0.1/jquery.mobile-1.0.1.min.js"></script>

<!--[if lt IE 9]>
<script src="/Style Library/scripts/html5.js"></script>
<![endif]-->

</head>
<body>
<!--- we'll put the jQuery Mobile and HTML here --->
</body>
</html>

Create jQuery Mobile layout

Now let’s setup our html within the body tag. We’ll use the standard setup you can find in the jQuery Mobile documentation.

<section id="page1" data-role="page">
 <header data-role="header" data-position="fixed">
  <h1>First Page</h1>
 </header>
  <div data-role="content">
   Here's your first name: <span class="first-name" style="font-weight:bold;"></span><br/>
   <a href="#page2">Click here</a> to see your last name
  </div>
</section>

Here’s our first pagelet. It has an ID of “page1″, and a title of “First page”. If you have everything setup right so far, you should see an image like this:

Now let’s setup page 2. You’ll notice above that we’ve used a link tag with a href=”#page2″. jQuery Mobile takes this and navigates us to “page” 2, even though it is simply another section on the same aspx page. Here’s what you’ll have, and the image follows:

<section id="page2" data-role="page">
 <header data-role="header" data-position="fixed">
  <h1>Second Page</h1>
 </header>
  <div data-role="content">
   Here's your last name: <span class="last-name" style="font-weight:bold;"></span><br/>
   <a href="#page1">Go back</a> to see your first name
  </div>
</section>

Create functions

Now that we’ve created our pages, it’s time to create the functions. We’re going to create two functions. One will call the first name of the user, and one will call the last name. Once it gets the first/last name, it appends the value of that variable to the empty ‘span’ tags we created in the step above. This is a basic example, but you’ll get the point at least.

So the first thing we need to do is create the functions on document ready. But note that we won’t CALL the functions just yet.

First function (to get first name of the logged in user):

function getFirstName() {
  firstname = $().SPServices.SPGetCurrentUser({fieldName: "FirstName"})
  $(".first-name").append(firstname);
}	

Second function (to get last name of the logged in user):

function getLastName() {
  lastname = $().SPServices.SPGetCurrentUser({fieldName: "LastName"})
  $(".last-name").append(lastname);
}

Call functions on page change

Right now these two functions are just sitting in the script tag, but we want to call them. You can do one of two things:

  1. call the functions right away
  2. call whenever the new pagelet loads

Because of the functions I am calling in one of my other projects, I’ve decided to call the function each time each pagelet loads. That means that even though jQuery Mobile loads the new pagelet without reloading the url, my data will still load in asynchronously. So when we click on “click here to see your last name”, it will then call the function when that pagelet comes into view.

The other way is fine as well, it just means that while you are using your new web app, the data only loads when the document is ready. This means if data changes while you are navigating, you won’t see the change until you refresh the page.

Note: If you call the functions each time the page is loaded, your script must remain above the jquery mobile js reference (see step 2). This because you need to tweak the DOM before jQuery Mobile applies its classes and functionality to the page.

So onto the code! We’re going to use the jQuery function “on” and pass the event “pagecreate”. Like I mentioned above, this means the function will only run when the page is created (every time).

$("section#page1").live("pagecreate", function(event){
  getFirstName();
});
$("section#page2").live("pagecreate", function(event){
  getLastName();
});

Now you should see the following when you load each page:

Full Code

And for your copying and pasting pleasure, here’s the full code!

<!DOCTYPE html>
<html>
<head>
<meta name="WebPartPageExpansion" content="full" />
<title>SharePoint, SPServices, and jQuery Mobile!</title>
	<link rel="apple-touch-icon" href="/Style Library/Images/apple-touch-icon.jpg"/>
	<meta name="viewport" content="user-scalable=yes, width=device-width, initial-scale=1"> 
	<link rel="stylesheet" href="/Style Library/scripts/jquery.mobile-1.0.1/jquery.mobile-1.0.1.min.css" />
	<script src="/Style Library/scripts/jquery-1.6.4.min.js"></script>
	<script src="/Style Library/scripts/jquery.SPServices-0.7.0.min.js"></script>

<!--[if lt IE 9]>
<script src="/Style Library/scripts/html5.js"></script>
<![endif]-->

	<script type="text/javascript">
		$(document).ready(function(){
			
function getFirstName() {
	firstname = $().SPServices.SPGetCurrentUser({fieldName: "FirstName"})
	$(".first-name").append(firstname);
}		
function getLastName() {
	lastname = $().SPServices.SPGetCurrentUser({fieldName: "LastName"})
	$(".last-name").append(lastname);
}
			
$("section#page1").live("pagecreate", function(event){
  getFirstName();
});
$("section#page2").live("pagecreate", function(event){
  getLastName();
});		})
	</script>
	<script src="/Style Library/scripts/jquery.mobile-1.0.1/jquery.mobile-1.0.1.min.js"></script>
</head>
<body>

<section id="page1" data-role="page">
 <header data-role="header" data-position="fixed">
  <h1>First Page</h1>
 </header>
  <div data-role="content">
   Here's your first name: <span class="first-name" style="font-weight:bold;"></span><br/>
   <a href="#page2">Click here</a> to see your last name
  </div>
</section>

<section id="page2" data-role="page">
 <header data-role="header" data-position="fixed">
  <h1>Second Page</h1>
 </header>
  <div data-role="content">
   Here's your last name: <span class="last-name" style="font-weight:bold;"></span><br/>
   <a href="#page1">Go back</a> to see your first name
  </div>
</section>

</body>

</html>

Enjoy!

Ben

jQuery and SharePoint 2010: the Basics

SharePoint out-of-the-box is functional, but fairly lacking when it comes to really wanting to extract and beautify your data. This is where jQuery comes in. It’s light, easy to implement, and actually hooks up with a bunch of SharePoint services when you have the right plugins. Consider this a basic intro to what you can do in jQuery and SharePoint. It’s really the tip of the iceberg, so keep exploring!

Setup jQuery for SharePoint

First things first, let’s make sure we’re properly setup. If you are using SharePoint behind a secure server (as part of an intranet possibly), then you’ll probably want to load up a local copy of the jQuery script instead of grabbing it from jquery.com each time the page loads. So grab the latest build from jquery.com and dump it in “Style Library/scripts/jquery.js”.

Next, go to your master page and put the following script (depending on where your file is) anywhere within the HEAD section:

 <script type="text/javascript" src="/Style Library/scripts/jquery.js"></script>

And that’s it! Now every time a page loads, you’re pulling in all of jQuery’s power. Minified, the file is only about 31kb, so (considering everything else SharePoint is loading) this isn’t that much of a burden. But you can also just load the jQuery script in page layouts, or even within individual pages.

Now that you’ve got jQuery loaded, it might be in your best interest to create another .js file that holds all of your custom scripts. You can choose to do scripts on a per page or page layout basis as well, but this keeps things more organized. So create a “myscripts.js” file in “Style Library/scripts” (the same folder we put jQuery.js in). And don’t forget to reference it in your master page, right under the jquery code. So now the above code is amended to read:

 <script type="text/javascript" src="/Style Library/scripts/jquery.js"></script>
 <script type="text/javascript" src="/Style Library/scripts/myscripts.js"></script>

Now that we’ve got everything loaded, what can we do?

What can you do with jQuery and SharePoint?

Here are just a few things:

Markup your body content and make it interactive

I use this for something like an FAQ section. We’ll put every question in an H4 tag with a class of “question” and every answer in a span with a class of “answer” directly after the h4. That’s all we have to do to our HTML source in the page. Then in our custom scripts file (above) we add the following jQuery code:

$(document).ready(function(){ 
 $("span.answer").slideUp(); // hides all of the answers
 $("h4.question").click(function(){ // when you click an h4
   $(this).next("span.answer").slideToggle('fast') //the answer slides down
 })
});

Keep in mind you may want to add some CSS that make the h4 seem more like a link (cursor:pointer, etc.) so people know they should click.

Make an “instant” search/filter box


Check out this post about how to create this. You can create a filter, and then append it to any list across your site.

Create a slideshow of content from a picture library


I’ve written a separate post with precise instructions about how to do this, but here’s the general idea:

  1. Create a picture library
  2. Make a new page
  3. Put a CQWP in the page, and wrap it in some divs with specific classes
  4. Use the tinycarousel plugin to convert that Content Query WebPart into a slideshow/carousel
  5. Style with CSS

Create tabs (not the jQuery UI kind)

Check out this post on creating tabs using jQuery, but without using the jQuery UI. It’s a bit easier with the UI plugin, but again, the idea is:

  1. Create the tabs in HTML
  2. Create each panel in HTML
  3. Add the jQuery
  4. Style it with CSS

Use SPServices to really hook into SharePoint with jQuery

I’ve started writing what is turning into a small series on using jQuery with SPServices. Some of the things you can do:
Rollup items from multiple lists
Rollup from multiple lists and put the results in a new list
Rollup articles across a site collection

SPServices and jQuery: pull items from multiple lists in SharePoint 2010

Let’s say you want to display all of the titles of every page library in your site collection. Using jQuery and SPServices, it’s a breeze it’s definitely possible.

What we have here are three calls of the SPServices function. First, we call the GetAllSubWebCollection operation. This will bring us all sites (webs) within a certain site collection. Once we have that, we’ll then iterate through each web to find all lists (2nd SPServices call) using the GetListCollection function. Once we have all lists, we’ll iterate through each list to look for any lists with the title “Pages”. This will give us every page library of every site in our site collection. Then we we will call our 3rd and final SPServices function, GetListItems to bring us all of the titles of all the items (in every list, in every site of this site collection). Finally, for our last act, we’ll then link each title of each item of each list of each subsite of the site collection to it’s specific URL.

But then we have to do something with that chunk of data. The last line in our code is a simple jQuery function that appends an LI with each title inside an empty UL tag with the ID of #sitelist (somewhere in your page).

Phew!

…ok, here’s the code (I just tested it, it works!):

$().SPServices({
	operation: "GetAllSubWebCollection",
	webURL: "/",
	async:false,
	completefunc: function(xData, Status) {
		$(xData.responseXML).find("Web").each(function(){
$().SPServices({
 operation: "GetListCollection",
 webURL: $(this).attr("WebFullUrl"),
 async:false,
 completefunc: function(xData, Status) {
  $(xData.responseXML).find("List").each(function(){
  //function to get all items inside every list called "Pages"
  if($(this).attr("Title") == "Pages") {
   $().SPServices({
    operation: "GetListItems",
    webURL: $(this).attr("WebFullUrl"),
    async: false,
    listName: $(this).attr("Title"),
    CAMLViewFields: "<ViewFields><FieldRef Name='EncodedAbsUrl' /><FieldRef Name='Title' /></ViewFields>",
     completefunc: function (xData, Status) {
      $(xData.responseXML).SPFilterNode("z:row").each(function() {
       var url = $(this).attr("ows_EncodedAbsUrl") + "";
       var liHtml = "<li><a href='" + url + "'>" + $(this).attr("ows_Title") + "</a></li>";
       $("#sitelist").append(liHtml);
      });
     }
    });
   }
  })
 }
});
		})
	}
})

As usual, thanks to Marc Anderson and his SPServices library

Create jQuery Tabs in Sharepoint 2010

This post will show you how to create a tabbed interface using Sharepoint Designer and some basic jQuery skills. I do not profess to know everything about jQuery, but this is getting the job done hopefully without too sloppy of code. Watch the screencast (no audio), then follow the instructions below.

Click to skip ahead:
Setup
Create the Tab Structure
Create the Panel Structure
Add the jQuery
Full Code + CSS


Setup


Create a blank aspx page in Sharepoint Designer. Other than that top 5 lines of Sharepoint code that all begin with “<%", take everything else out. The two things you will need are 1) the placeholder for additional page head for holding any css and the jQuery scripts,

<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">

and 2) the main placeholder, for holding the actual tabs.

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">


Create the Tab Structure


You'll create the tabs with an unordered list. It's important to give each tab two classes. One that designates it as a tab (mine is "choice"); one that designates which division it is (mine is "hs" or "ms", etc.); and you'll also want to make one tab activated by default. This is done by adding the CSS class "activated". You could also do this with jQuery if you wanted. Check the code at the bottom for all of the wrapping divs that will work with jQuery.

<ul class="div-nav">
<li class="choice show-hs activated">High School</li>
<li class="choice show-ms">Middle School</li>
<li class="choice show-es">Elementary School</li>
</ul>


Create the Panel Structure


Each panel needs two classes as well. One will designate it as a panel (mine is "webpart"); and one will designate which division it is (again, "hs", "ms", etc.). For testing purposes just put some plain text in each panel so you can confirm the tabbing is working. After that, you can put anything inside. For instance, on my production page I am loading 16 (yes, a bit worrisome) Content Query WebParts, yet only a few are showing at a time because of my tab system. Again, check the code at the bottom for all of the wrapping divs that will work with jQuery.

<div class="webpart hs">High School Stuff</div>
<div class="webpart ms">Middle School Stuff</div> 
<div class="webpart es">Elementary School Stuff</div>


Add the jQuery


First, make sure you're calling the jQuery script (I have a local copy installed). Then I'll split up my code into several parts and explain it.

I've wrapped everything in a function that runs when the tabs are loaded and ready to go:

$("ul.div-nav").ready(function(){
});

Then I hide all of the panels, and fade in the first one (in this case the High School panel, as I have the High School tab activated).

$("#daily-wrapper .webpart").hide();
$("#daily-wrapper .webpart.hs").fadeIn('slow');

Next I set the tab magic. Each tab needs to have a toggleClass attribute attached to it so you'll know which tab you're on. Then you'll want to deactivate the activated class on all other tabs.

$("ul.div-nav li.choice").click(function(){$(this).toggleClass("activated")});
$("ul.div-nav li.choice").click(function(){$(this).siblings("ul.div-nav li").removeClass("activated")});

Finally, anytime a tab is clicked I fade everything out, and then only fade in the particular panel that the tab calls for.

$("ul.div-nav li.choice").click(function(){$("#daily-wrapper .webpart").fadeOut('fast')});
$("ul.div-nav li.show-hs").click(function(){$("#daily-wrapper .webpart.hs").fadeIn('slow')});
$("ul.div-nav li.show-ms").click(function(){$("#daily-wrapper .webpart.ms").fadeIn('slow')});
$("ul.div-nav li.show-es").click(function(){$("#daily-wrapper .webpart.es").fadeIn('slow')});

As you can see, it's actually quite basic. I'm sure there are some things here against best practice, and definitely let me know if you find a better way of accomplishing this. But this seems to work for us.


Full Code (CSS Below)

<%@ Register tagprefix="WebControls" namespace="Microsoft.SharePoint.Publishing.WebControls" assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%-- _lcid="1033" _version="14.0.4762" _dal="1" --%>
<%-- _LocalBinding --%>
<%@ Page language="C#" MasterPageFile="~masterurl/default.master"    Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document"  %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, 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" %>
<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">
<script type="text/javascript" src="/Style Library/scripts/jquery.js"></script>
<script type="text/javascript">
$("ul.div-nav").ready(function() {
	$("#daily-wrapper .webpart").hide();
	$("#daily-wrapper .webpart.hs").fadeIn('slow');
	$("ul.div-nav li.choice").click(function(){$(this).toggleClass("activated")});
	$("ul.div-nav li.choice").click(function(){$(this).siblings("ul.div-nav li").removeClass("activated")});
	$("ul.div-nav li.choice").click(function(){$("#daily-wrapper .webpart").fadeOut('fast')});
	$("ul.div-nav li.show-hs").click(function(){$("#daily-wrapper .webpart.hs").fadeIn('slow')});
	$("ul.div-nav li.show-ms").click(function(){$("#daily-wrapper .webpart.ms").fadeIn('slow')});
	$("ul.div-nav li.show-es").click(function(){$("#daily-wrapper .webpart.es").fadeIn('slow')});
});
</script>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<div id="daily-wrapper">
<ul class="div-nav">
<li class="choice show-hs activated">High School</li>
<li class="choice show-ms">Middle School</li>
<li class="choice show-es">Elementary School</li>
</ul>
<div class="content">
<div class="calendar">
<h2>Calendar</h2>
<div class="webpart hs">High School Stuff</div>
<div class="webpart ms">Middle School Stuff</div> 
<div class="webpart es">Elementary School Stuff</div>
</div>
</div>
</div>
</asp:Content>

Full CSS


#daily-wrapper ul.div-nav li {
	font-size:1.2em;
	margin:0px;
	border-right:1px solid #fff;
}
#daily-wrapper .calendar {
	position:relative;
	border:0px solid #333;
	width:67%;
	float:left;
}
#daily-wrapper .calendar {
	margin-right:19px;
	width:30%
}

#daily-wrapper .content .calendar h2 {
	color:#fff;
	text-indent:-9999px;
	height:64px;
	background: #fff url('/Style Library/images/calendar-header.jpg') no-repeat top left;
	margin-bottom:20px;
}

ul.div-nav {
	width:100%;
	background:#999;
	float:left;
	text-align:center;
}
ul.div-nav li {
	display:block;
	float:left;
	color:#fff;
	text-decoration:none;
	padding:10px;
}
ul.div-nav li:hover {
	background:#b80000;
	cursor:pointer;
}
ul.div-nav li.activated {background:#b80000;}

Create a feedback button for Sharepoint 2010

Ever want to get feedback from your users without making them go through some laborious process of filling out a survey or sending an email to the helpdesk?

Introducing: the feedback button. It allows your users, no matter what page they are on, to submit their feedback. I’ve found this to be an extremely useful tool for gathering, you guessed it, feedback. And now I’ll teach you how to create one.

Click below to skip ahead:
Create a new list: feedback
Apply the workflow
Edit your master page


Create a new list: feedback


The first step in making this feedback button is to make a custom list into which your feedback will be input. For my list I have two columns. The first column (renamed the required “title” column) is “What page are you on?” I haven’t made the system intelligent, I’m leaving it up to the user to tell me where they are on the site. You never know, it could provide a little hint if they have no idea where they are, or they think they are somewhere they are not. The second column I create is a multi-line textbox column called “Comments”. That’s all I want from my users. Their feedback. Simple.

List Settings

  • Turn approval off, turn off version history
  • Item-level read permissions: read items that were created by the user
  • Item-level create/edit permissions: create items and edit items that were created by the user
  • Disable items to show in search results

Permissions
You’ll want to break permissions on this specific list and allow your users to have contribute permissions on this list alone. Otherwise they will not be able to submit items when they click the feedback button.


Apply the workflow


Create a new workflow on this list called “Feedback Workflow”. I always turn the start options to “start automatically when item is created”. That is really the only scenario with this list.

There is only one step in the workflow, and it should be an “email” step. If you check out my settings, I have put the contents of the comment box, including who wrote it, when they created it, and the page they were on.

Don’t forget to save and publish the workflow!


Edit your master page


This bit is slightly tricky. Not only do you need to put the button on your master page sidebar, but it needs to open the form in a modal dialog box window (at least that’s my preference). So below you’ll see the javascript I use to enable that dialog box on the link:

<div class="feedback"><a href="javascript;" onClick="javascript:SP.UI.ModalDialog.showModalDialog({ url: '/Lists/Feedback/NewForm.aspx', title: 'Add item' }); return false;"><h4>Questions?</h4><span>Click to send feedback</span></a></div>

Also, for some reason you want to style your feedback button like mine is style, here’s my CSS:

.feedback {
	margin-top:10px;
	text-align:center;
}
.feedback a {
	text-decoration:none;
	color:inherit;
	background:transparent url('/Style Library/images/feedback.png') no-repeat top center;
	display:block;
	height:40px;
}
.feedback a:hover {
	background:transparent url('/Style Library/images/feedback.png') no-repeat bottom center;
}
.feedback a h4 {
	color:#FFFF99;
	font-family:'Ubuntu Bold';
	font-weight:bold;
	font-size:1.3em;
	padding-top:2px;
}
.feedback a span {
	color:#ffffff;
	font-size:0.9em;
}

Itemstyle.xsl in SharePoint 2010: A guide for designers

Arguably the most useful tool in SharePoint 2010 is the Content Query WebPart (CQWP). This little webpart allows you rollup content from anywhere in the site collection and display it however you like. Yes, it has limitations, but this post is about the things you can do with it. But of course the CQWP is incomplete without a working knowledge of xsl styles. In SharePoint, all styles for the CQWP are stored in itemstyle.xsl. I’m not going to go into creating your own stylesheets, linking to CQWPs, etc. You can figure that out. We will dive into what is possible with styling CQWP results.

We’ll be creating something like this:

Skip ahead:

Create your list

Before we begin, create a list with the following columns:
Title (standard text field)
Body (rich text area)
SiteLink (hyperlink column)

Declare a new style and insert fields

Every new style in itemstyle.xsl must be wrapped in the following tags:

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

Inside this tag, we’ll begin by simply adding in our fields. I like to bring in all my fields, confirm they work on the page, and then begin styling.

<xsl:value-of select="@Title"/>
<xsl:value-of select="@Body"/>
<xsl:value-of select="@Author"/>
<xsl:value-of select="@SiteLink"/>

When you call a variable, it is either already built into the system (Title, Author), or you will need to customize it in the browser to add in your specific fields into those value placeholders.

The first thing we’ll do is format the hyperlink field to make it a friendly URL. The field, by default, puts the URL and then the friendly name concatenated together, but separated by a column. So what we do is create two variables in xsl, one that pulls everything before the comma, one that pulls everything after the comma. Then we will put the URL into an “a” tag.

<xsl:variable name="SiteLink" select="substring-before(@SiteLink,',')"/>
<xsl:variable name="SiteName" select="substring-after(@SiteLink,',')"/>
Link: <a href="{$SiteLink}"><xsl:value-of select="$SiteName"/></a>

Link Author to their mysite

Next, I want to take the Author’s name and link it to their MySite profile. To do that we are going to create a new variable that takes the login ID of the user (using ddwrt:UserLookup on the @Author tag). Once we have that ID we’re going to pass it into a parameter of the mysite URL.

<xsl:variable name="authoremail">
<xsl:value-of select="ddwrt:UserLookup(string(@Author) ,'Login')" />
</xsl:variable>
Questions about this? Contact <a href="https://m.domainname.com/Person.aspx?accountname={$authoremail}"><xsl:value-of select="@Author" /></a>

Format the Body to use markup

Your body content probably includes all the markup at the moment. The quick fix to that is adding in a disable-output-escaping=”yes” attribute.

<xsl:value-of select="@Body" disable-output-escaping="yes"/>

Final Code

Finally, just add some divs with classes to style. Here is my final code.

<xsl:template name="testannouncement" match="Row[@Style='testannouncement']" mode="itemstyle">
<xsl:variable name="SiteLink" select="substring-before(@SiteLink,',')"/>
<xsl:variable name="SiteName" select="substring-after(@SiteLink,',')"/>
<xsl:variable name="authoremail">
<xsl:value-of select="ddwrt:UserLookup(string(@Author) ,'Login')" />
</xsl:variable>
<div id="DailyAnnouncement">
<div class="title"><h4><xsl:value-of select="@Title"/></h4></div>
<div class="details">
<xsl:value-of select="@Body" disable-output-escaping="yes"/>
<xsl:if test="@SiteLink != ''">
<br/>Link: <a href="{$SiteLink}"><xsl:value-of select="$SiteName"/></a>

</xsl:if>
</div>
 <div class="contactemail">Questions about this? Contact
	<a href="https://m.domainname.com/Person.aspx?accountname={$authoremail}">
	<xsl:value-of select="@Author" />
	</a>
 </div>
</div>
</xsl:template>

Here’s my CSS for the itemstyle.xsl styles

#DailyAnnouncement {
	display:block;
	border:1px solid #fff;
	padding-right:5px;
}
#DailyAnnouncement div.title h4 {
	margin:0px;
	padding:0px;
	line-height:normal;
	background:#efefef;
	border:1px solid #ccc;
	padding:4px;
	background:#efefef url('/PublishingImages/Logos/announcement.gif') no-repeat 0px 0px;
	padding-left:35px;
	display:block;
}

#DailyAnnouncement div.details {
	border:1px solid #ccc;
	border-top:0px;
	padding:10px;
	display:block;
}

#DailyAnnouncement div.contactemail {
	background:#efefef;
	padding:4px;
	text-align:right;
	border:1px solid #ccc;
	border-top:0px;
}