Wednesday, August 15, 2012

Show All Sites I Have Access to in SharePoint with Filtering using JQuery and Javascript

Suppose you want your users to see all the SharePoint sites that they have access to on one page, instead of having to drill down to each subsite to find their content. Instead of creating a site directory, I want to show them all the sites that they can access and allow them to filter based on the site name, site path and site description.

Laura Rogers has a blog post that goes through a step by step process of using the Search Core Results web part, which can be found here: http://sharepoint911.com/blogs/laura/Lists/Posts/Post.aspx?ID=90

However, I want to do this by using some scripting. By default, it will show all the sites the current user has access to (with a scroll bar on the right hand side if it is an extensive list). The search textbox will automatically filter the entire set of sites (with no post backs) on the site title, path and description. The end result will be similar to below:


First, we will want to use the SharePoint search service and get all the sites and webs using jquery/ajax. The query we will use gets only the indexed items that are of contentclass type "STS_SITE" or "STS_WEB:

SELECT Title, Rank, Size, Description, Path FROM Scope() WHERE "scope" = 'All Sites' AND (contentclass = 'STS_Site' OR contentclass = 'STS_Web') ORDER BY "Rank" DESC"
Next, we will call the query using the search service, where the url being passed in is the url to the search.asmx path.

 $.ajax({ 
url: "http://mydomain/_vti_bin/search.asmx" ,  
type: "POST",   
dataType: "xml",       
data: soapEnv,      
async:true,
complete: processResult,   
contentType: "text/xml; charset=\"utf-8\""
 });   
The processResult function will be executed to iterate through the results and create our table once the ajax call has been made.

Finally, we will use the dataTable js plugin to allow scrolling and filtering on the sites list
$('#mySitesTable').dataTable( {
"sScrollY": "300px",
"bPaginate": false,
"bSort": false
} );

Simply add this script to a SharePoint page and reference the jquery library, which you can grab from http://docs.jquery.com/Downloading_jQuery and the jquery.dataTables.min.js, which you can download from http://datatables.net/:

<div id="errorMsg"></div>
<div id="showQueryResults" style="display: none">false</div>
<div id="searchResults"></div>
<div id="testoutput" style="display: none"></div>

<script src="/assets/js/jquery/jquery-1.7.2.min.js" type="text/javascript"></script>
<script src="/assets/js/jquery/plugins/dataTables/jquery.dataTables.min.js" type="text/javascript"></script>

<style>
 div.table_Wrapper { border:10px solid blue; }
 .dataTables_filter 
 {
  width: 50%;
  float: right;
  text-align: right;
 }
 .dataTables_info
 {
  width: 100%;
  font-weight:bold;
  float: left;
  border: 2px solid #ddd;
  background-color: Gainsboro;
  color: #999;
  text-align: right;

 }
 
</style>


<script type="text/javascript">

var searchURL = "http://mydomain/_vti_bin/search.asmx";
var arraySearchResults = new Array();
var arrayListToSearch = new Array();
var displayQueryResults = false;
var searchCap = 5000;
 
//used to display the output of the query; 
//if you would like to see the query result, make set the innerHTML of showQueryResults to true
if( document.getElementById("showQueryResults").innerHTML == "true")
{
 displayQueryResults = true;       
}

RunSearch(); 

   
function RunSearch()
{
 arraySearchResults = new Array();
 

 var myQuery = "<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'>";
 myQuery += "<Query>"; 
 myQuery += "<SupportedFormats><Format>urn:Microsoft.Search.Response.Document.Document</Format></SupportedFormats>";  
 myQuery += "<Range><Count>" + searchCap + "</Count></Range>";  
 myQuery += "<Context>";
 myQuery += "<QueryText language='en-US' type='MSSQLFT'>";
 myQuery += "SELECT Title, Rank, Description, Path FROM Scope() WHERE \"scope\" = 'All Sites' AND (contentclass = 'STS_Site' OR contentclass = 'STS_Web') ORDER BY \"Rank\" DESC";
 myQuery += "</QueryText>";
 myQuery += "</Context>"; 
 myQuery += "</Query>";
 myQuery += "</QueryPacket>";   

  
 var soapEnv = "<?xml version=\"1.0\"?>"+
 "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>" + 
 "<soap:Body>" +
 "<Query xmlns='urn:Microsoft.Search'>"+
 "<queryXml>" + escapeHTML(myQuery) + "</queryXml>"+   
 "</Query>"+    
 "</soap:Body>"+ 
 "</soap:Envelope>";


 $.ajax({  
  url: searchURL,   
  type: "POST",    
  dataType: "xml",        
  data: soapEnv,       
  async:true,

  complete: processResult,    
  contentType: "text/xml; charset=\"utf-8\""

 });         
 }
 
  

function processResult(xData, Status)
{
 if (Status == "error") 
 {
  DisplayErrorMesssage(Status, xData);
  return;
 }

// alert($(xData.responseXML).text());
 
   var queryResult = $(xData.responseXML).find("QueryResult").text();
   $("#testoutput").text(queryResult);
 
   if( displayQueryResults )
   {
       document.getElementById("testoutput").style.display = "";
   }
   else
   {
       document.getElementById("testoutput").style.display = "none";
   }
 
   $(xData.responseXML).find("QueryResult").each(function() {  
    var xml = $("<xml>" + $(this).text() + "</xml>");  
    xml.find("Document").each(function() 
    {  
  var curPath = $("Action>LinkUrl", $(this)).text();  
  curPath = curPath.toLowerCase();

  var curTitle = "";  
  var curDesc = "";
 
 
  $(this).find("Property").each(function() 
  {  
   if ($("Name", $(this)).text() == "TITLE") 
   {  
    curTitle = $("Value", $(this)).text(); 
   }  
   if ($("Name", $(this)).text() == "DESCRIPTION") 
   {  
    curDesc = $("Value", $(this)).text(); 
   }  
  });  
  
  arraySearchResults.push([curTitle, curPath, curDesc]);
    
    });  

  });
 
 PrintOutput();
}
 
 

function PrintOutput()
{ 
 arraySearchResults.sort(sortSearchResults);

 var output = "";

 output += '<table id="mySitesTable" cellpadding="0" cellspacing="0" border="0" class="display">';
 output += "<thead><tr><th align='left'><font color='steelblue' size='2pt'><u><b>Sites that I have access to</b></u></font><br/><br/></th></tr></thead>"; 
 output += "<tbody>";

 
 for( var x = 0; x < arraySearchResults.length; x++ )
 {
  var title = arraySearchResults[x][0];
  var path = arraySearchResults[x][1];
  var desc = arraySearchResults[x][2];
  
  output += PrintRow(path, title, desc);
 } 
 
 output += "</tbody></table>";
 
 document.getElementById("searchResults").innerHTML = output;
 
  
 $('#mySitesTable').dataTable( {
  "sScrollY": "300px",
  "bPaginate": false,
  "bSort": false
 } );
}
 
function PrintRow(path, title, desc)
{
 var output = "";

 if( desc != null && desc != "" )
  output += '<tr><td><a href="' +  path +  '" target="_blank">' + title   + '</a><br/><font color="green">' + path + '</font><br/>'+desc+'<br/><br/></td></tr>';
 else
  output += '<tr><td><a href="' +  path +  '" target="_blank">' + title   + '</a><br/><font color="green">'+path+'</font><br/><br/></td></tr>';

 return output; 
}
 
 
function DisplayErrorMesssage( Status, xData)
{
 document.getElementById("errorMsg").innerHTML = "Error occurred. " + Status ;

 if( xData.responseXML != null )
 {
  document.getElementById("errorMsg").innerHTML += $(xData.responseXML).text();
 }
}
 
 
function sortSearchResults(a, b){

 var aTitle = a[0]; 
 var bTitle = b[0];
  
  
 var x = aTitle.toLowerCase(), y = bTitle.toLowerCase();   
 
 return x < y ? -1 : x > y ? 1 : 0;   
}


function escapeHTML (str) 
{  
 return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');   
}     
 </script>



Tuesday, August 7, 2012

Track SharePoint Attachments Uploaded and Deleted in the EditForm using JavaScript

To track the changes to attachments in a SharePoint item for auditing/record keeping purposes:

1. Create a new SharePoint edit form for the list using SharePoint Designer
2. Make sure that your Edit Form is updated and can upload attachments (use this MSDN article if you are having issues http://support.microsoft.com/kb/953271/en-us)

3. **Updated step**

Find <tr id="idAttachmentsRow" > and directly underneath of that closing </tr> add the following:


<tr style="display:none">
      <td colspan="2" valign="top" class="ms-formbody" nowrap="" height="20px">
       <H3 class="ms-standardheader">
              <SharePoint:AttachmentsField ControlMode="Display" FieldName="Attachments" runat="server" Visible="true"/>
       </H3>
      </td>
     </tr>
(By adding this row and this field, it will retain the name of the attachment)
    
4. To get the newly added attachments, you will want to grab the "attachmentsOnClient" element. This is where the references to the newly added attachments are stored. Next, you will want to iterate through all the INPUT tags and get the "value" attribute to get the names of the files that were uploaded.

5. To get the removed attachments from the SharePoint list item, we will grab the "attachmentsToBeRemovedFromServer" element. All of the GUIDs of the list item attachments to be removed are stored in this element. We will parse out each guid and use it to identify the attachmentRow found under the attachmentsTable. This row will have a reference to the removed items.

6. To view the changes that have been made when a user clicks to save the form, add the script below. Of course, instead of alerting the user of the changes made, you can record it in a status or audit trail field to make it more seamless!


 
<script type="text/javascript">

function GetNewlyUploadedAttachments()
{
 var uploadedAttachments = new Array();
 var oAttachments = document.getElementById("attachmentsOnClient");
 if( oAttachments.innerHTML != null )
 {
  var attachmentTable = oAttachments.getElementsByTagName("INPUT");
  for (var i = 0; i < attachmentTable.length; i++) 
  {    
   var value = attachmentTable[i].getAttribute("value");    
   if ( value != null && value.length > 0 ) 
   {   
    var lastIndex = value.lastIndexOf("\\");
    var fileName = value.substring(lastIndex+1);
    uploadedAttachments.push(fileName); 
   } 
  } 
 }

 return uploadedAttachments.toString();
}

function GetRemovedAttachments()
{
 var removedAttachments = new Array();
 var attachmentsToBeRemoved = document.getElementsByName("attachmentsToBeRemovedFromServer").item(0).value;

 if( attachmentsToBeRemoved != null && attachmentsToBeRemoved != "")
 {
  var array = attachmentsToBeRemoved.split(';');
  for(var i =0; i < array.length; i++ )
  {
   attachmentGuid = array[i];
   if( attachmentGuid != null && attachmentGuid != "" )
   {
    var attachmentRow =  document.getElementById(attachmentGuid);
    var span = attachmentRow.getElementsByTagName('span')[0];
    var links = span.getElementsByTagName("a");
    for (z = 0; z < links.length; z++) 
    {
     removedAttachments.push(links[z].firstChild.nodeValue);
    }
   }
  }
 }
 return removedAttachments.toString();
}

function PreSaveAction()
{
 var newUploadedAttachments = GetNewlyUploadedAttachments();
 var removedAttachments = GetRemovedAttachments();
 if( removedAttachments != null && removedAttachments != "" )
 {
  alert("User removed attachments: " + removedAttachments );
 }
 if( newUploadedAttachments != null && newUploadedAttachments != "" )
 {
  alert("User uploaded attachments: " + newUploadedAttachments );
 }
}
</script>