Add a jQuery accordion with a SharePoint CQWP

Requirements:

  • Create a web part to display categories and procedures.
  • Group the procedures by category.
  • Can add, edit and delete categories and procedures.
  • Can view the full detail of a procedure

Final delivery:

result

Features:

  • No custom solution developed. Applied custom CSS and JavaScript to an OOTB content query web part.
  • Used jQuery accordion to toggle the categories and items belonging to the category.
  • Edit category and procedure in a modal dialog box.

Solution:

1. Create two lists, Category and Procedure

Category list columns:

Title: Single Line of Text

Description: Multiple Line of Text

                Procedure list columns:

Procedure Name: Single line of text

Category: Lookup (Lookup to Category list, tick the ID checkbox)

ProcID: Calculated (Formular: =TEXT(Created,”ddMMyyhhmmss”))

ProcLink: Calculate (Formular: =”/pages/DisplayFullProc.aspx?ProcID=”&ProcID)

 

2. Create DisplayFullProc.aspx page with a data view web part. The DVWP displays a single Procedure item. (This step is optional)

3. Go to a page and add a CQWP. Export it to your local desktop.

4. Open the exported .webpart file in a text editor.

5. Change the following code:

<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>
     <property name="UseCache" type="bool">False</property>

6. Upload the edited .webpart file to the Web Part Gallery in the root site.

7. Open your root site in SharePoint Designer. Navigate to All Files->Style Library->XSL Style Sheets. Create a new file and call it AccordionStyle.xsl.  This is the style file is for each procedure item in the list. Copy the following code to the document.

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

<!-- Procedure Item Template -->
    <xsl:template name="Accordion" match="Row[@Style='Accordion']" mode="itemstyle">
       <!--Need to pass the category ID value to the group header, but the ID doesn't need to be displayed-->          
                   <div style="display:none">
                                <xsl:value-of select="@CategoryID" />
                   </div>
       <div>
                  <!--Edit Proc Button-->
                      <a href="#">
                                                <xsl:attribute name="onclick">
                                                                javascript:portal_openEditProModalDialog('<xsl:value-of select="@ProcID"/>')
                                                </xsl:attribute>
                                             <img src="/Style Library/Images/edit-icon-thumb.png" border="0"></img>
                                  </a>

                                  <!--Delete Proc Button-->
                                  <a href="#">
                                                <xsl:attribute name="onclick">
                                                                deleteProc('<xsl:value-of select="@ProcID"/>')
                                                </xsl:attribute>
                                                <img src="/Style Library/Images/delete-icon-thumb.png" border="0"></img>
                                  </a>

                      <xsl:text>                </xsl:text>

                                  <a href="{@ProcLink}" target="_blank">
                                <xsl:value-of select="@Title"/>
                      </a>
       </div>
    </xsl:template>
</xsl:stylesheet>

8. In the XSL Style Sheets folder, make a copy of the ContentQueryMain.xsl file and rename it to AccordionCQWP.xsl. Make the following modifications:

  • Replace dfwp-list with accordion (Line 32 and 83)
  • Replace dfwp-item with accordion-item (Line 34)
  • Replace Line 83 with the following code:

This code displays the Expand All, Collapse All, Add Category and Add Procedure links before the list. To make it easier to see, visit this site and this site for conversion.

<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;div class=&quot;add-cat&quot;&gt;&lt;a href=&quot;/ippreserver/Lists/Category/NewForm.aspx?IsDlg=0&quot;&gt;Add Category&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;add-proc&quot;&gt;&lt;a href=&quot;/ippreserver/Lists/Procedure/NewForm.aspx?IsDlg=0&quot;&gt;Add Procedure&lt;/a&gt;&lt;/div&gt;&lt;ul class=&quot;accordion&quot; style=&quot;width:')" />

9. In the XSL Style Sheets folder, back up the Header.xsl file by making a copy of the original file and rename it to Header_original.xsl. The Header.xsl is the style file for each category group the list.  Edit the Header.xsl file. Paste the following code before </xsl:stylesheet>

<xsl:template name="IPCat" match="*[@GroupStyle='IPCat']" mode="header">
    <div>
      <a href="#">
              <xsl:attribute name="onclick">
                      javascript:portal_openEditCatModalDialog('<xsl:value-of select="@CategoryID"/>')
              </xsl:attribute>
              <img src="/Style Library/Images/edit-icon-thumb.png" border="0"></img>
      </a>                  
      <a href="#" >
              <xsl:attribute name="onclick">
                       deleteCategory('<xsl:value-of select="@CategoryID"/>')
              </xsl:attribute>
              <img src="/Style Library/Images/delete-icon-thumb.png" border="0"></img>
      </a>

      <xsl:text>                </xsl:text>

      <xsl:call-template name="OuterTemplate.GetGroupName">
        <xsl:with-param name="GroupName" select="@*[name()=$Group]"/>
        <xsl:with-param name="GroupType" select="$GroupType"/>
      </xsl:call-template>
    </div>
  </xsl:template>

10. Go back to the SharePoint site. Open the site in SharePoint Designer. Navigate to Site Pages. Upload an empty .txt file. This file will be used to store JavaScript and CSS. A content editor web part will reference this file. It actually can be uploaded to any location.

11. Paste the following code to the .txt file:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
 
<script type="text/javascript">
/*--------------------------Expand All, Collapse All and Category Header Click--------------------------*/
function accordionLoad() {
 
    $(".accordion-header").removeClass("expanded");
    $(".accordion-content").hide();
 
    $(".accordion-header").bind("click", function(){
        $(this).toggleClass("expanded");
        $(this).siblings(".accordion").find(".accordion-content").slideToggle();
        $(this).siblings(".accordion").find(".accordion-content").css("border-bottom","1px solid #ccc");
    })
 
    $(".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");
    })
}
/*--------------------------End Expand All, Collapse All and Category Header Click--------------------------*/
 
/*--------------------------Pop up IPPreserver Category Edit Form--------------------------*/
function portal_openEditCatModalDialog(id){
    var options = {
        url:"/ippreserver/Lists/Category/EditForm.aspx?ID="+id+"&IsDlg=1",
        dialogReturnValueCallback: CatDialogCallback
        };
 
    SP.UI.ModalDialog.showModalDialog(options);
}
 
function CatDialogCallback(dialogResult, returnValue){
                if (dialogResult != 0)
                {
                                document.location.reload(true);
                }
}
/*--------------------------End Pop up IPPreserver Category Edit Form--------------------------*/
 
/*--------------------------Delete the selected category--------------------------*/
var oListItem;
var categoryName;
var clientContext;
function deleteListItem(id) {
 
    this.itemId = id;
                var siteUrl = '/ippreserver';
 
    clientContext = new SP.ClientContext(siteUrl);
    var oList = clientContext.get_web().get_lists().getByTitle('Category');
                oListItem = oList.getItemById(id);
    
    clientContext.load(oListItem, 'Title');
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}
 
function onQuerySucceeded() {                 
                categoryName = this.oListItem.get_item('Title');
    oListItem.deleteObject();
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onSecondQuerySucceeded), Function.createDelegate(this, this.onSecondQueryFailed));    
}
 
function onQueryFailed(sender, args) {
 
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
 
function onSecondQuerySucceeded() {                                  
    alert('Category deleted: ' + categoryName);
    location.reload();
}
 
function onSecondQueryFailed(sender, args) {
 
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
 
 
function deleteCategory(id)
{
                var x;
                var r=confirm("Are you sure you want to delete this category?");
                if (r==true)
                {
                                deleteListItem(id);                           
                }              
}
/*--------------------------End Delete the selected category--------------------------*/
 
/*--------------------------Pop up IPPreserver Procedure Edit Form--------------------------*/
function portal_openEditProModalDialog(id){
    var options = {
        url:"/ippreserver/Lists/Procedure/EditForm.aspx?ID="+id+"&IsDlg=1",
        dialogReturnValueCallback: ProDialogCallback
        };
 
    SP.UI.ModalDialog.showModalDialog(options);
}
 
function ProDialogCallback(dialogResult, returnValue){
                if (dialogResult != 0)
                {
                                document.location.reload(true);
                }
}
/*--------------------------End Pop up IPPreserver Category Edit Form--------------------------*/
 
/*--------------------------Delete the selected procedure--------------------------*/
var pListItem;
var procName;
var pContext;
function deleteProcItem(id) {
 
    this.itemId = id;
                var siteUrl = '/ippreserver';
 
    pContext = new SP.ClientContext(siteUrl);
    var oList = pContext.get_web().get_lists().getByTitle('Procedure');
                pListItem = oList.getItemById(id);
    
    pContext.load(pListItem, 'Title');
    pContext.executeQueryAsync(Function.createDelegate(this, this.onPQuerySucceeded), Function.createDelegate(this, this.onPQueryFailed));
}
 
function onPQuerySucceeded() {                              
                procName = this.pListItem.get_item('Title');
    pListItem.deleteObject();
    pContext.executeQueryAsync(Function.createDelegate(this, this.onSecondPQuerySucceeded), Function.createDelegate(this, this.onSecondPQueryFailed));    
}
 
function onPQueryFailed(sender, args) {
 
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
 
function onSecondPQuerySucceeded() {                                                
    alert('Procedure deleted: ' + procName);
    location.reload();
}
 
function onSecondPQueryFailed(sender, args) {
 
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
 
 
function deleteProc(id)
{
                var x;
                var r=confirm("Are you sure you want to delete this procedure?");
                if (r==true)
                {
                                deleteProcItem(id);                         
                }              
}
/*--------------------------End Delete the selected category--------------------------*/
 
$(document).ready(function(){
    accordionLoad();
       
});
 
</script>
 
/*--------------------------Style Sheet--------------------------*/
<style>
 
ul.accordion {
                list-style:none;
                margin:0px;
                padding:0px;
}
.accordion-item {
/*           border-top:1px solid #ccc;*/
}
.accordion-header {
                font-size:1.3em;
                cursor:pointer;
                padding:10px;
                border-bottom:1px solid #ccc;
}
.accordion-header:hover {
                background:#efefef;
}
.accordion-header.expanded {
                background:#dfdfdf;
}
.accordion-content {
                padding:5px 20px;
}
.expand-all,
.collapse-all {
                display:inline-block;
                cursor:pointer;
                padding:5px 10px;
zoom:1; *display: inline; _height: 30px;
 
}
.expand-all:hover,
.collapse-all:hover {
background:#efefef;
}
 
.add-cat,
.add-proc {
                display:inline-block;
                cursor:pointer;
                padding:5px 10px;
zoom:1; *display: inline; _height: 30px;
 
}
.add-cat:hover,
.add-proc:hover {
background:#efefef;
}
 
.editIcon
{
                vertical-align:middle
}
 
.deleteIcon
{
                vertical-align:middle;
                width:20px;
}
</style>
/*--------------------------End Style Sheet--------------------------*/

12. Upload the edit and delete icon to Style Library-> Images folder in SharePoint Designer

13. Go to the SharePoint page. Add a CEWP and enter the .txt file’s link to the Content Link field.

14. Add the uploaded CQWP from Step 6.

15. Edit the CQWP’s properties.

Browse to the Procedure list.

1

2

3

4

16. Overall structure:

  • Category and Procedure lists
  • OOTB CQWP, CEWP
  • AccordionStyle.xsl
  • AccordionCQWP.xsl
  • Header.xsl
  • text file to store CSS and JavaScript

References:

Demo: http://www.isb.bj.edu.cn/admissions/Pages/Frequently-Asked-Questions.aspx

Source file: https://bentedder.qx.ly/2yPB

Steps to create your own: http://www.bentedder.com/create-a-jquery-accordion-with-a-sharepoint-cqwp/

HTML Encoder/Decoder:

http://www.web2generators.com/html/entities

http://www.textfixer.com/html/html-character-encoding.php

 

Custom rich text editor styles for Content editor web part

I need to restrict site editor not to use OOTB fonts and styles while they edit the contents of “Content editor web part” in SharePoint 2010.

I created custom text styles and added these styles to my custom page layout’s style sheet only. So if an editor creates any page using my custom page layout, he/she will only see custom text styles while editing the “content editor web part” contents.

This article was very helpful!

In his first step, he added the css to the whole site’s master page’s css. I added these css to my layout’s css instead.

Use jQuery to customize Quick Launch in SharePoint 2010

I needed to customize my quick launch so that

  1. The very top level has a different style from its children
  2. Only display one level down at once, other nodes are collapsed
  3. Add indent between levels.
  4. When a link is selected, it will lose the URL and toggled when clicked

Things I had to do to get this:

SharePoint Navigation Setting, CSS and jQuery

Final look:

SharePoint Navigation Setting:

Change the navigaion settings at Site Actions->Site Settings->Look and Feel->Navigation->Current Navigation

Top navigation setting: (E.g “Our Science” level)

Second level setting: (E.g “Animal performance, Plant & forage, Land & environment” level)

Third level setting: (E.g. “Reproduction”, “Genomics”, “Growth & development” level)
In this case, the next level below this are all pages, not subsites, so no need to change the settings for the next level down.

CSS used to style the quick launch:

/* |--------- Quicklaunch -----------| */ 
.s4-ql ul.root ul { margin-bottom:5px; } 

/* Outher nav element */ 
#s4-leftpanel-content{ background-color:#fff!important; border:1px #d8d6d7 solid!important; border:0px!important } 

/* Inner nav element */ 
.menu-vertical { padding-right:10px!important; padding-left:10px!important; } 

/* Inner Inner nav element */ 
.menu-vertical > ul.root { padding-top:3px; } 

/* Level 1 static item and hover state */ 
.menu-vertical > ul.root > li.static > .menu-item{padding-left:0px!important; padding:1px!important; color:#333!important; } 
.menu-vertical > ul.root > li.static > .menu-item:hover{  text-decoration:none;  color:#8cc63e!important; }

/* Level 2 static item and hover state */ 
.menu-vertical > ul.root > li.static > ul.static > li.static >.menu-item{ padding-left:0px!important; padding:1px!important; color:#333!important;  font-weight:bold; } 
.menu-vertical > ul.root > li.static > ul.static > li.static >.menu-item:hover{  text-decoration:none;  color:#8cc63e!important; }

/* Level 3 static item and hover state */ 
.menu-vertical > ul.root > li.static > ul.static > li.static > ul.static > li.static >.menu-item{ padding-left:0px!important; padding:1px!important; color:#333!important; } 
.menu-vertical > ul.root > li.static > ul.static > li.static > ul.static > li.static >.menu-item:hover{  text-decoration:none;  color:#8cc63e!important; }

/*Level 1 selected item - Level1 include Our Science, Animal performance, Plant & forage etc */ 
.menu-vertical > ul.root > li.selected > .menu-item{ background-color:transparent!important; border:0px; margin:0px; padding:0px;  color:#8cc63e!important; }

/*Level 2 selected item*/ 
.menu-vertical > ul.root > li.static > ul.static >li.selected > .menu-item{ background-image:none!important; background-color:transparent; border:0px; margin-top:1px;  color:#8cc63e!important; }

/*Level 3 selected item*/ 
.menu-vertical > ul.root > li.static > ul.static >li.static > ul.static >li.selected >.menu-item{ background-image:none!important; background-color:transparent!important; border:0px; margin:0px; padding:0px;  color:#8cc63e!important; }

/* Sublinks margins */ 
.menu-vertical > ul.root > li.static > ul.static  { margin-top:5px; margin-bottom:5px; }

/* no border for the QL when no links */ 
.s4-specialNavLinkList { border:0px } 

/* Liststyle square */ 
.s4-ql ul.root ul > li  { list-style:square inside !important; list-style-position:outside !important; margin-left: 23px !important; padding-top:0px; } 

/* Liststyle square links */ 
.s4-ql ul.root ul > li > a { display: inline-block!important; padding-top:0px; padding-bottom:5px!important; padding-left:0px!important; vertical-align:top!important; }

jQuery used:

$(document).ready( 
function() {
/*Toggle quick launch*/
//Hide all children         
$('.s4-ql li ul').hide(); 
                  
//Remove the url from the selected item 
$('.s4-ql .selected').removeAttr('href');

//Add class to selected item so it can be toggled when click
$('.s4-ql .selected a').addClass('min');

//Display the selected items parents nodes 
$('.s4-ql .selected').parent().parent().show();
$('.s4-ql .selected').parent().parent().parent().show();

//Display the selected item's immediate child
$('.s4-ql ul li.selected').find('ul:first').show();

//In the root level, the heading and it's children are considered to be the same level, 
//to distiguish them, the first child will have bold font and black bottom border
//and other children will have a left indent and a black arrow in front of them.
$('.s4-ql ul.root').find('li:first').css({'font-weight':'bold', 'border-bottom':'1px black solid'});
$('.s4-ql ul.root').find('li:first').siblings('li').css({'padding-left':'20px', 'background':'url(\'/Style Library/Ag_Custom/images/arrow.png\') no-repeat 5px 5px;'});

//Setup Click Hanlder for min class       
$('.min').click(function() { 
//Traverse the DOM to find the child UL node             
var subList = $(this).siblings('ul'); 

//Toggle the UL             
subList.toggle();                  
});  
/*End toggle quick launch*/
});

Here is how I used the Developer’s Tool in IE9 to select the items:

Adding a jQuery image carousel to SharePoint 2010 using content editor web part

I couldn’t find a carousel web part for SharePoint 2010 so decide to use a content editor web part to include some html and JavaScript to display an image carousel. Initially, found a jQuery plugin called jCarousel Lite which didn’t provide any css so looked for another one which is called Infinite Carousel. This one worked for me.

Final look:

  • Clicked on the demo link in the article and “View Source”ed it. All the code are laid out nicely.
  • Copy and paste everything to a text editor and remove the unwanted parts and change the image urls, save it as a .txt file. Upload the text file to SharePoint (In this case, Style Library/custom_scripts)
  • Downloaded the “arrow” image and added to Style Library/Images
  •  On the SharePoint page where I need this carousel, add a content editor web part, click on “Edit Web Part”.
  • In the content link box, paste the url of the text file I uploaded to SharePoint before.

  • Change Appearance->Chrome Type to “None” and click OK

Here’s the code I used, unfortunately wordpress doesn’t let me upload text files, so I had to copy and paste it to a Word document.