Thursday, September 12, 2013

How to create a FAQ in SharePoint with OOB features

There are lots of stuff in the web around how to create a FAQ in SharePoint.

In this post I'll show you some tricks what you can do with additional OOB SharePoint tools to have also some search and some style within your FAQ to impress your customer. It also shows you the power of SharePoint as a developer platform.

The requirements for our FAQ are:
  • Easy to maintain
  • Easy to search content
  • Add documents as links in FAQ
  • Grouped FAQ content
  • Export FAQ when needed into a report

To understand this post you should have some basic know-how about:
  • SharePoint Lists
  • SharePoint List Views
  • HTML, CSS
  • JavaScript (SharePoint Client API)
  • SharePoint WebParts
  • CAML
or you just follow the instructions.

The following image shows us the final page.

Final FAQ Pag

Our FAQ page has a FAQ list and a document library. This is useful if you want to link on some documents in your FAQ. The page has a list WebPart with a special grouped view and also2 quick searches for documents and for the FAQ list.

I also use a category for my FAQ. To manage the category I've an own list "Basic themes", where the FAQ owner can manage his categories.


Steps to create the FAQ



1. STEP : Create a site

Create a own site collection for your FAQ. Use the blank site template.

2. STEP : Create the Basic Themes List

Use the "Custom List Template" to create the Basic themes List. Name the list "Basicthemes". This list
will be referenced by the FAQ list for categorize the FAQ. This list does need nothing but a title. This titles will categorize our FAQ.

3. STEP : Create a document library

Create a standard document library and call it "Documents". This library will contain documents referenced by the FAQ.

4. STEP : Create the FAQ List

Use the "Custom List Template" to create the FAQ List. Name the list "FAQ".

Our FAQ needs now some fields. You can easily add more fields if you want but for my FAQ I took the following fields:

Field nameField type
TitleSingle line of text
ThemeLookup to Basic Themes List on Title
QuestionMultiple line of text
AnswerMultiple line of text

5. STEP: Create a group view

Go to the FAQ list and create a new view. Call it "WebPart View". Use following settings

View option Option values
Fields : Question, Answer
Group by : Theme than title
Format: Magazine

6. STEP: Create an asset library

Create a document or asset library and name it "Site Assets". This library will contain some scripts we need for the site.

7. STEP Upload Quick Search Scripts

Upload the following scripts to the "Site Assets" library. Adapt the list/site names in the scripts with the names of your list/site.

Search FAQ Script

Script to search FAQ content. Copy the code into a .txt file and upload it to the "Site Assets" library.
Adapt the list url and site url settings in the code. Check in the "BAR.CreateXML" function that the field names are set correct like in your library.

This script contains also some css to make the list view more readable.

 <style type="text/css">  
 #SearchResults ul { padding-left:10px }  
 #SearchResults li { margin-bottom:5px }  
 #SearchResults span { color:#666 }  
 .biggySearcher div.s4-search { width:100%; height:30px }  
 .biggySearcher .ms-sbplain { width:150px }  
 .ms-listviewtable tbody tr+tr+tr .ms-rtestate-field {  
  font-weight:bold;  
  font-size: 14px;  
  background-color:#eee;  
  padding:2px 0;  
  border-bottom:1px solid #de3400  
 }  
 .ms-listviewtable tbody tr+tr+tr+tr .ms-rtestate-field {  
  font-weight:normal;  
  font-size: inherit;  
  background-color:#fff  
 }  
 </style>  
 <script type="text/javascript">  
 if (typeof BAR == "undefined") {  
   BAR = {};    
 }  
 BAR.siteUrl   = "/sites/claims";  
 BAR.listName  = "FAQ"; // Displayname  
 BAR.listUrl    = BAR.siteUrl + '/Lists/faq'  
 BAR.resultItems = null;

 BAR.StartQuery = function() {  
   var text = document.getElementById("QueryText").value;  
   text = text.replace(/^\s+|\s+$/g, ""); // Trim  
   var result = BAR.CreateQuery(text);  
   BAR.QueryList(result);  
 }  

 BAR.CreateQuery = function( pSearchText )   
 {  
   var result = "";  
   var searchWords = pSearchText.split(" ");  
   for (var i=0;i<searchWords.length;i++){  
     if (searchWords[i] != "") {  
       if (i>0) result = "<And>" +result;  
       result += BAR.CreateXML(searchWords[i]);  
       if (i>0) result += "</And>";  
     }  
   }  
   return result;  
 }  

 BAR.CreateXML = function(pSearchText) {  
 return ' <Or>'  
   + ' <Or>'  
   + '  <Contains>'  
   + '    <FieldRef Name="Title" />'  
   + '    <Value Type="Text">'+ pSearchText +'</Value>'  
   + '  </Contains>'  
   + '  <Contains>'  
   + '    <FieldRef Name="Question" />'  
   + '    <Value Type="Note">'+ pSearchText +'</Value>'  
   + '  </Contains>'  
   + ' </Or>'  
   + '  <Contains>'  
   + '    <FieldRef Name="Answer" />'  
   + '    <Value Type="Note">'+ pSearchText +'</Value>'  
   + '  </Contains>'  
   + ' </Or>'  
 }

 BAR.QueryList = function(pSearchXML){  
   var context = new SP.ClientContext(BAR.siteUrl);  
   var oList = context.get_web().get_lists().getByTitle(BAR.listName);  
   var camlQuery = new SP.CamlQuery();  
   camlQuery.set_viewXml("<View><Query>"  
   + '<Where>'  
   + pSearchXML  
   + '</Where>'  
   + '</Query><RowLimit>10</RowLimit></View>');  
   BAR.resultItems = oList.getItems(camlQuery);  
   context.load(BAR.resultItems, 'Include(Id, Title, Frage, Basisthema)');  
   context.executeQueryAsync(  
     Function.createDelegate(this, BAR.onQuerySucceeded),   
     Function.createDelegate(this, BAR.onQueryFailed)  
   );  
 }

 BAR.onQuerySucceeded = function(sender, args) {  
   var resultHTML = [];  
   var listItemEnumerator = BAR.resultItems.getEnumerator();  
   var i = 0;    
   while (listItemEnumerator.moveNext()) {  
     i++;  
     var oListItem = listItemEnumerator.get_current();      
     var id  = oListItem.get_id();  
     var theme = oListItem.get_item('Basisthema').get_lookupValue();  
     var title = oListItem.get_item('Title');  
     var quest = oListItem.get_item('Frage').substr(0,120);  
     resultHTML.push('<li>');      
     resultHTML.push('<a href="javascript:EditItem2(null,\''+ BAR.listUrl +'/DispForm.aspx?ID='+ id +'\')">' + title + '</a>');  
     resultHTML.push('<br/>('+ theme +')');  
     resultHTML.push('<br/><span>'+ quest +' ...</span>');  
     resultHTML.push('</li>');  
   }  
   if (i == 0)  
     resultHTML.push('<li><span>No FAQ entry found.</span></li>');  
   document.getElementById("SearchResults").innerHTML = resultHTML.join("");  
 }

 BAR.onQueryFailed = function(sender, args) {  
   alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());  
 }  
 </script>  

 <div class="biggySearcher">  
   <div class="s4-search">      
     <table class="ms-sbtable s4-search"><tr>  
     <td class="ms-sbcell">  
       <input type="text" class="ms-sbplain" id="QueryText" />  
     </td>  
     <td class="ms-sbgo ms-sbcell">  
       <img src="/_layouts/images/gosearch15.png" alt="Search" class="srch-gosearchimg"   
         onmouseout="this.src='\u002f_layouts\u002fimages\u002fgosearch15.png'"   
         onmouseover="this.src='\u002f_layouts\u002fimages\u002fgosearchhover15.png'" title="Search"  
         onclick="BAR.StartQuery()" />  
     </td>  
     </tr></table>  
   </div>  
   <ul id="SearchResults"></ul>  
 </div>  

Script to search the document library

The second script is to set a search. The search results appear in a dialog box. I find this very handy. Copy now the following script into a .txt file and upload it also to your site assets library. Adapt the list and site parameters in the script to search the correct site and library .

 <script type="text/javascript">  
 if (typeof BAR == "undefined") {  
   BAR = {};    
 }  
 BAR.siteUrl   = '/sites/myfaq';  
 BAR.documentLibUrl = 'http://sharepoint/sites/myfaq/documents'  
 BAR.QueryDocs = function() {    
   var text = document.getElementById("QueryTextDocs").value;  
   text = text.replace(/^\s+|\s+$/g, ""); // Trim  
   text = "*" + text + "*"    
   EditItem2(null, BAR.siteUrl + '/_layouts/OSSSearchResults.aspx?k=' + text + '&cs=This%20List&u=' + encodeURIComponent(BAR.documentLibUrl) );  
 }  
 </script>  
 <div class="biggySearcher">  
   <div class="s4-search">  
     <table class="ms-sbtable s4-search"><tr>  
     <td class="ms-sbcell">  
       <input type="text" class="ms-sbplain" id="QueryTextDocs" />  
     </td>  
     <td class="ms-sbgo ms-sbcell">  
       <img src="/_layouts/images/gosearch15.png" alt="Search" class="srch-gosearchimg"   
         onmouseout="this.src='\u002f_layouts\u002fimages\u002fgosearch15.png'"   
         onmouseover="this.src='\u002f_layouts\u002fimages\u002fgosearchhover15.png'" title="Search"  
         onclick="BAR.QueryDocs()" />  
     </td>  
     </tr></table>  
   </div>    
 </div>  

8. STEP: Insert the FAQ List into your page

Goto your home site and click on edit. Add your FAQ list to the middle WebPart section. Click on "Add WebPart" and choose the FAQ list. Choose the WebPart View from the WebPart settings.

9. STEP: Insert FAQ Document Search

Goto your home site and click on edit. Add a "Content Viewer WebPart" to the right WebPart section. Go to the WebPart settings and put into the top text box the path to the uploaded txt file, which contains the documents query.

10. STEP: Insert FAQ Quick Search

Do the same for the FAQ query file.

11. STEP: Adding category filter WebPart

In this step we want to add the category filter:


Go to your home site and click on edit.Click on "Add WebPart" on the right section and choose the "HtmlFormsWebPart" from "Forms" category.

The cool thing about the html forms WebPart is that you can connect to other WebParts with it.

Open the settings of this WebPart and click on "Sourcecode editor". Put the following source code in it. Check if the list name in the code "Basicthemes" matches your list name, else change it to your list name.

This code loads the entries from the "Basicthemes" list into a dropdown menu, which can be used for filtering.

 <div onkeydown="javascript:if (event.keyCode == 13) _SFSUBMIT_" style="margin-bottom:5px">  
   <select id="selThemes" name="T1"></select>  
   <input type="button" value="Filter" onclick="javascript:_SFSUBMIT_"/>  
 </div>  
 <span style="height:11px;width:11px;position:relative;display:inline-block;overflow:hidden;">  
   <img style="position:absolute;border:0;left:0;top:-584px" src="/_layouts/images/fgimg.png" />  
 </span>  
 <a href="javascript:location.href=location.href">Reset Filter</a>  
 <script type="text/javascript">  
  function LoadSelectThemes() {  
   var context = SP.ClientContext.get_current();  
   var list = context.get_web().get_lists().getByTitle('Basicthemes');  
   var camlQuery = SP.CamlQuery.createAllItemsQuery();  
   var items = list.getItems(camlQuery);    
   context.load(items, 'Include(Title, Id)');  
   context.executeQueryAsync(  
     function(){        
       var listItemEnumerator = items.getEnumerator();  
       var oSelect = document.getElementById("selThemes");  
       while (listItemEnumerator.moveNext()) {          
         var oListItem = listItemEnumerator.get_current();  
         var title = oListItem.get_item('Title');          
         var option = document.createElement("option");  
         option.innerHTML = title;  
         option.value = title;  
         if (oSelect != null){            
           oSelect.appendChild(option);  
         }  
       }  
     },  
     function(sender, args) {  
       alert('List Data fetch failed. ' + args.get_message() + 'n' + args.get_stackTrace());  
     }  
   );  
  }  
  window.onload = function(){ ExecuteOrDelayUntilScriptLoaded(ViewItem, "sp.js"); };
 </script>  

12. STEP: Connecting the filter

In this step we connect the Form WebPart with the list view WebPart on the site. Edit you home site. Go to your WebPart Menu of your HTML Form WebPart and click on connections and choose FAQ.


In the upcoming Window choose :
  • Source field = T1
  • Consumer field = Themes (The Lookup field)


Click on "Save" and save your WebPart Settings. The connection should be etablished now.

13. STEP: Testing

Most things should be straight forward. The HTMLFormWebPart caused me sometimes that the whole site stopped working because of wrong JavaScript code. In this case remove the WebPart by adding ?contents=1 to the URL. You will be redirected to the WebPart maintanance page.
Other problems can be that the list or site names are wrong in the scripts. Pay attention that you change every setting. Search & replace is a good tactic in such a case. You can also edit the .txt file in the site assets library in Explorer View directly in NotePad++. So don't have to down- and upload everytime.

No comments: