-->

04/12/2013

SharePoint - Copy Files / Documents from Search screen using JavaScript

One of the major enhancement of SharePoint 2010 is "Content Organizing" which makes it more strong in "Document Management System (DMS)" race.

Content Organizing means, the content automatically routed to different destinations based up on various factors.

Best part of it, content is organized without any manual intervention.

Worst part of it, Data / Docs are scattered across site collection or whole web application.

Lets consider a scenario, where the user uploaded 20 documents which were organized into multiple libraries in a site collection. Now i want to bring all that 20 docs to one single library where the user can edit them and resubmit.

Let me show you what i am aiming for.


First Question "Why Search?"
As we discussed content organizer will route the documents in all directions which will change in future, so we cannot rely on "SPSiteDataQuery". SO search will fetch the docs where ever they are across farm.

Now second Question "Why OOTB stuff, we can design a beautiful Custom page with Search API?"
Yeah its not that you cannot, but you cannot imagine how painful it will be while migrating to next version of SharePoint with all the customization.

We are doing all of it using JavaScript, as it will be easy to migrate to SharePoint 2013 apps with no server code.

# Create a site collection which is search enabled and the content of it is searchable.

# Create a webpart page and add below webparts in respective order, so that they appear in reverse order on screen.
         1. Search Core Results webpart.
         2. Search Box webpart.
         3. Content Editor webpart.

# Edit "Search Box" webpart and go to "Miscellaneous" section of its properties and ensure that Target search results page pointing to self. Else the search action will take user to default search results page.

 # Edit "Search Core Results" webpart, go to Display Properties section of its properties and  un-check "Use Location Visualization", now XSL Editor button will be enabled.

# Click "XSL Editor" button and start editing XSLT of the webpart. Search for "srch-Icon", and add below piece of markup.

<!-- THIS IS WHERE WE MODIFIED TO EMBEDED CHECKBOX-->
<input type="checkbox" id="{concat($currentId,'_Checkbox')}" align="absmiddle" class="documentcheckbox"></input> <label style="display:none"><xsl:value-of  select="$url"/></label>

This will add a visible check-box and an invisible url of the document. 

# Consider a standard place to keep your Script files. I choose "Style Library", and create a folder "JSApps" and then add below mentioned files.
             1. Jquery.min.js
             2. Script file in txt format, used to load in Content Editor webpart.
Don't worry about the content of the SPScript.txt file now.

# Edit "Content Editor" webpart and go to Content Link section in properties and give url for the SPScript.txt file. Apply the changes and whatever you put in that SPScript.txt file will start effecting the search page.

# Now add below content tp SPScript file and the page will turn just like how it appears on the very first snapshot provided.
<html><head>
<script src="/sites/TestSC/Style Library/JSApps/jquery-1.10.1.min.js"></script>
</head>

<body>
Enter Universal processing Id:<input type="text" id="UniversalProcessID" class="txtprocessid"/>
<button type="button" class="btnprocess">Copy to Destination Library</button>
</body>
</html>
<script type="text/javascript">
var foo = ExecuteOrDelayUntilScriptLoaded(useClientContext, "sp.js");
var docarray="";

function useClientContext() {

 this.ctx = new SP.ClientContext.get_current(); 
 this.web = this.ctx.get_web(); 
 this.ctx.load(this.web);
 this.ctx.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}

function CopyFiles()
{ 
 var filearray=docarray.split('$');
 var ctx, file, notifyId;
 
    ctx = new SP.ClientContext.get_current();  
  
 ctx.executeQueryAsync(
  function (sender, args) {
   // Set a notification to the user that we are going to copy the file.
   notifyId = SP.UI.Notify.addNotification('Copying files...', true);
   for(i=0;i<(filearray.length)-1;i++)
   {  
    debugger;
    file = ctx.get_web().getFileByServerRelativeUrl(filearray[i]);
    ctx.load(file);
    var fileparts= filearray[i].split('/');
    var filename= fileparts[(fileparts.length)-1]
    
    // File loaded. Now we want to copy the file. We'll use a nested AJAX call
    file.copyTo("/sites/TestSC/TestDestLib/"+filename, true);
   }
    ctx.executeQueryAsync(
     function (sender, args) {
      
      // File copied successfully!
      SP.UI.Notify.removeNotification(notifyId);
      // Let the user know that the operation was successful
      SP.UI.Notify.addNotification('Files copied successfully', false);
     },
     function (sender, args) {
      
      // Unable to copy file.
      SP.UI.Notify.removeNotification(notifyId);

      showError(args.get_message());
     });
   }
  ,
  function (sender, args) {
   
   // Unable to locate file.
   SP.UI.Notify.removeNotification(notifyId);

   showError(args.get_message());
  });
 
}

function onQuerySucceeded() {
 //alert('Title: ' + this.web.get_title());
}
 
function onQueryFailed(sender, args) {
 alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
$(document).ready(function(){
 
 $(".documentcheckbox").change(function(){
   var url=$($(this)[0]).next()[0].firstChild.data.replace(/^(?:\/\/|[^\/]+)*\//, "");
   if($(this)[0].checked)
   {
    docarray=docarray+"/"+url+"$"; 
   }
   else
   {
    docarray=docarray.replace("/"+url+"$","");
   }
   alert(docarray);
        });
  
 $(".btnprocess").click(function(){
  CopyFiles();
 });
});

</script>

To make it sweet and short, all i did is,
    1. When a User selects a file, i keep track of the relative url of the selected file.
    2. When a user un-check a file, it will remove the pertaining url from the track.
    3. Once the user selects all the files to be copied, and when he clicks the "Copy To Destination Library", using JavaScript (ECMA), the selected files will be copied to destination library.
   4. User will be notified while copying the files and after successful copying.
   5. If the content type of Source and destination libraries match, it will even copy the metadata.

The only piece of code here is that SPScript File which you can download from this link. Search for "SPScript.zip" file.

Note: This piece of code works only to copy files with-in the Site collection. If you intend to copy across different site collections, well that's my next article.

1 comment:

  1. Great script - works like a charm! I've been trying to tweak it so that it works on folders too but I'm having no luck so far.

    ReplyDelete