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

 

Advertisements

Passing parameter to a function for onclick in an XSLT RSS

Scenario:

I have a link in a xslt file and some javascript methods in another file. When I click on the link, the javascript method invokes. I need to pass in a xsl value to the js method. OK, this sounds really confusing. Here is some code:

In the xsl file:

<a href=”#” onclick=”javascript:portal_openDispCalModalDialog(‘@xslparameter’)”>

<xsl:value-of select=”@Title” />

</a>

In the js file:

function portal_openDispCalModalDialog(id){

var options = {url:”list/DispForm.aspx?ID=”+id+”&IsDlg=1“,

dialogReturnValueCallback: CalDialogCallback

};

SP.UI.ModalDialog.showModalDialog(options);     }

Solution:

I needed to build my onclick as an attribute and then add the data inside it.

<a href=”#”>
<xsl:attribute name=”onclick”>
javascript:portal_openDispCalModalDialog(‘<xsl:value-of select=”@ID”/>’)
</xsl:attribute>
<xsl:value-of select=”@Title” />

</a>

 

Reference:

http://forums.asp.net/t/1498719.aspx/1

XSLT displays a multi-select column

Thanks to Marc again for this helpful article.

Here is the xsl code:

<xsl:template name=”MultiSelectDisplay”>
<xsl:param name=”MultiSelectValue”/>
<xsl:param name=”MultiSelectSeparator”/>
<xsl:choose>
<xsl:when test=”contains($MultiSelectValue, ‘;’)”>
<xsl:value-of select=”concat(substring-before($MultiSelectValue, ‘;’), $MultiSelectSeparator)” disable-output-escaping=”yes”/>
<xsl:call-template name=”MultiSelectDisplay”>
<xsl:with-param name=”MultiSelectValue” select=”substring-after($MultiSelectValue, ‘;’)”/>
<xsl:with-param name=”MultiSelectSeparator” select=”$MultiSelectSeparator”/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select=”$MultiSelectValue” disable-output-escaping=”yes”/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Other useful SharePoint xsl templates written by Marc & Co can be found here.