Updating an expired Client Secret of SharePoint Add-in


Updated with Azure-AD / AZ Cli here https://cann0nf0dder.wordpress.com/2019/11/25/updating-an-expired-client-secret-of-a-sharepoint-add-in-using-azure-ad-az-cli/ 

Been working with SharePoint Add-in tokens for a while now, but this week has been the first time I’m still working with an add-in longer than a year in one environment. My application start throwing the error message:

Error:Token request failed., InnerMessage:System.Net.WebException: The remote server returned an error: (401) Unauthorized.”

I knew that they did expire after a year, but never really thought about (until now) how to go about renewing them. Luckily Microsoft documentation nowadays is a lot better than it used to be. I found this walk through https://msdn.microsoft.com/en-us/library/office/dn726681.aspx. In case Microsoft takes the link down, or changes the URL, I will explain the steps below. I have also changed the code slightly for the Report on Client ID expiry dates, as the Microsoft one didn’t return the results I expected.

Report on Client ID expiry dates.

  • Open Windows Powershell and run the following cmdlet:
Connect-MsolService
  • A login prompt will appear, here enter the tenant-administrator credentials for the Office 365 tenancy where the add-in was registered with AppRegNew.aspx
  • You can generate a report that list each add-in in the tenant with the date that the secret expires with the following PowerShelll code.

    $applist = Get-MsolServicePrincipal -all  |Where-Object -FilterScript { ($_.DisplayName -notlike "*Microsoft*") -and ($_.DisplayName -notlike "autohost*") -and  ($_.ServicePrincipalNames -notlike "*localhost*") }
    $output = "";
    foreach ($appentry in $applist)
    {
        $principalId = $appentry.AppPrincipalId
        $principalName = $appentry.DisplayName
    
        $results =  Get-MsolServicePrincipalCredential -AppPrincipalId $principalId -ReturnKeyValues $false | Where-Object { ($_.Type -ne "Other") -and ($_.Type -ne "Asymmetric") }
        if($results.count -gt 0)
        {
             $output += "PrincipalId:`t$principalId`n"
             $output += "PrincipalName:`t$principalName`n"
             $output += "Keys`n" foreach($result in $results) { 
             $output += "Type:`t" + $result.Type + "`n" 
             $output += "Value:`t" + $result.Value + "`n" 
             $output += "KeyId:`t" + $result.KeyId + "`n" 
             $output += "StartDate:`t" + $result.StartDate + "`n" 
             $output += "EndDate:`t" + $result.EndDate + "`n" 
             $output += "Usage:`t" + $result.Usage+ "`n" $output += "`n"        
         } 
          $output += "-----------------------------------------------`n" } 
    } 
          $output | Out-File "c:\temp\appsec.txt" 
    
    • The above code first filters out Microsoft’s own applications, add-ins still under development (and a now-deprecated type of add-in that was called autohosted).
    • Filters out non-SharePoint add-ins and add-in like workflow.
  • Open the file at c:\temp\appsec.text to see the report.

An example of the report below:

Note: The PrincipalID is your Client ID

Generate a new secret for another year.

To generate a new secret, you just need to run the following PowerShell script:

$clientId = <#Replace with your ClientID of the add-in>
$bytes = New-Object Byte[] 32
$rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rand.GetBytes($bytes)
$rand.Dispose()
$newClientSecret = [System.Convert]::ToBase64String($bytes)
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Sign -Value $newClientSecret
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Verify -Value $newClientSecret
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Password -Usage Verify -Value $newClientSecret
$newClientSecret

The output of the above PowerShell file will give you a new Client Secret, take note of this:

Generate a new secret for 3 years.

It is possible to create a secret that will last 3 years, the PowerShell script is very similar to the above script, but now it has a start and end date.

$clientId = <#Replace with your ClientID of the add-in>
$bytes = New-Object Byte[] 32
$rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rand.GetBytes($bytes)
$rand.Dispose()
$newClientSecret = [System.Convert]::ToBase64String($bytes)
$dtStart = [System.DateTime]::Now
$dtEnd = $dtStart.AddYears(3)
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Sign -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Verify -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Password -Usage Verify -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
$newClientSecret

Updating the remote web application using the new secret

  • If you are redeploying from Visual Studio you will need to update the client Secret in your Web.Config
  • If you are just updating directly in Azure, you can just go to the configurations and update the new secret there.

If you are using the default TokenHelper.cs file in your project, and it’s not the prerelease version then you can add a second app setting called SecondaryClientSecret. Here you would put in your old secret, and in the ClientSecret put in the new one. This is so if your token is going to expire soon, the application will still work as it will try one first then the other.

Time to propagate Client Secret to SharePoint

According the Microsoft link you should wait at least 24 hours to propagate the ClientSecret. However, I found as soon as I changed the secret I could use it straight away. After changing your Client Secret if you run the ‘Report on Client ID expiry dates’ powershell again, those dates didn’t update for me until the following day.

I ran the report the following day, and as you can see below, Demo App 1 which was shown in the screen shot above now has 3 new keys with new dates.

Deleting expired client secrets

As expired client secrets do not seem to get removed, it is recommended to delete them. The code below will grab any keys that are expired for a ClientID and delete them.

$clientId = <#Replace with your ClientID of the add-in>;
$keys = Get-MsolServicePrincipalCredential -AppPrincipalId $clientId -ReturnKeyValues $false
$dtNow = [System.DateTime]::Now
foreach($key in $keys)
{
 if($key.EndDate -lt  $dtNow)
 {
   write-host $key.KeyId "Expired"
   Remove-MsolServicePrincipalCredential -KeyIds @($key.KeyId) -AppPrincipalId $clientId
 }
}

Using ITextSharp to create basic PDF page from item property


  1. Setting up Word Automation Services for SharePoint 2013
  2. Setting up PowerPoint Automation Services for SharePoint 2013
  3. Converting a Library’s Word Documents to PDF
  4. Converting a Library’s PowerPoint documents to PDF
  5. Using ITextSharp to Merge PDF’s into one
  6. Using ITextSharp to create basic PDF page from item property

This is the final post in this series, I’ve shown you how to set up Word and PowerPoint Automation, how to convert Word and PowerPoint to PDF, and in the last blog post I showed you how to merge all your pdf’s into one. This post is to demonstrate how to create a PDF page using ITextSharp. ITextSharp is a port of the iText open source java library for PDF generation written entirely in C# for the .NET platform. http://itextpdf.com. Please see my previous blog on how to add the itextsharp dll to the Visual Studio project.

In this demonstration, I will read in the first item of a list, and then copy all the visible columns by creating a table like structure which will be saved in my document library as a PDF file. The purpose of this post is to introduce to you what you can do with ITextSharp, there is plenty I don’t know about ITextSharp, and I’ve only learnt enough to solve my proof of concept I was doing for a client. If you are interested in ITextSharp, I’d recommend you look up books by Bruno Lowagie.

First, I have created a contact list, and added a single person to this list.

Continuing with my previous Visual Studio solution, I have added a new visual web part. It simply has a text box for the user to enter the list which it will take the first item from and convert to PDF in the Shared document library, and a button that will fire off the event to convert the item.

Below is the code and comments explaining what is happening at different stages of the code.

//Will require these using statements
using iTextSharp.text;
using itextSharp.text.pdf;

protected void btnlistItemToPDF_Click(object sender, EventArgs e)
{
  //Get Current Web context and get Document and user chosen list.
  var web = SPContext.Current.Web;
  SPList doclibrary = web.Lists.TryGetList("Documents");
  SPList list = web.Lists.TryGetList(txtList.text);
 
  //Check if lists are not null, then call code to convert.
  if(doclibrary == null || list == null)
  {
     ListItemToConvertoToPDF(list, doclibrary);
  }
}

private void ListItemToConvertoToPDF(SPList list, SPList doclibrary)
{
   if (list.ItemCount == 0)
        throw new Exception("Unfortunately there are not any items in your list to convert");
 
   Byte[] result = null;
   String returnString = "";

   //Perform Query to return 1 item.
   SPQuery query = new SPQuery();
   query.RowLimit = 1;
   query.ViewFields = list.DefaultView.ViewFields.SchemaXml;

   try
   {
      var collection = list.GetItems(query);
      SPListItem item = collection[0];

      //Get Title name, using default Guid for Title, to create a filename.
      var filename = item[new Guid("fa564e0f-0c70-4ab9-b863-0177e6ddd247")] + ".pdf";

      //Creating the Document in memory first.
      using(MemoryStream ms = new MemoryStream())
      {
       //Using the ItextSharp Document
         using(Document document = new Document())
         {
           //Using the ItextSharp PdfWriter to create an instance of the document using the memory stream.
           PdfWriter.GetInstance(document, ms).SetFullCompression();

           //Open the document otherwise you won't be able to add to it.
           document.Open();

           //Create a PDF table with Two Columns
           PdfPTable table = new PdfPTable(2);

           //Create the first Cell, make it span across 2 columns.
           PdfPCell cell = new PdfPCell(new Phrase("Contact Details"));
           cell.Colspan = 2;
           cell.HorizontalAlignment = 1; //0=Left, 1=Centre, 2=Right

           //Add the cell to the table.
           table.AddCell(cell);

           //Loop through all the fields in the collection.
           foreach (SPField f in collection.Fields)
           {
               //Obtain the value of the given field.
               var value = item[f.Id];
               //We are only showing if there is a value and the field/column isn't hidden.
               if (value != null && !f.Hidden)
               {
                  //Create a cell and just put the title of the column in it.
                  table.AddCell(f.Title);
                  //Depending on the type of file, depends how we want to add it to the pdf.
                  switch (f.TypeAsString)
                  {
                         //URL to display within the PDF as a URL.
                         case "URL":
                           //Get the field value as a SPFieldURLValue
                           SPFieldUrlValue urlV = new SPFieldUrlValue(value.ToString());
                           //create a new Cell.
                           cell = new PdfPCell();
                           //Use Chunk to add the text
                           var chunk = new Chunk(urlV.Description);
                           //A chunk can have an Anchor tag added to it.
                           chunk.SetAnchor(urlV.Url);
                           //add chunk to the cell.
                           cell.AddElement(chunk);
                           //add the cell to the table. Because this is the second cell added to the table, it is aware that it's the right cell.
                           table.AddCell(cell);
                           break;

                         //Date Time to display a valid date time format.
                         case "DateTime":
                           //Parse the date time to full date and time.
                           var dateV = DateTime.Parse(value.ToString()).ToString("R");
                           //add the cell to the table.
                           table.AddCell(dateV);
                           break;
 
                        //Default captures all other type of field types, numbers, text, etc.
                        default:
                           table.AddCell(value.ToString());
                           break;
                        }
                     }
                  }
                  //Add the table to the document and close the document.
                 document.Add(table);
                 document.Close();
              }
             //Convert the final memory stream into a byte array.
             result = ms.ToArray();
          }
 
          //Write file to document library
          SPFolder rootFolder = doclibrary.RootFolder;
          SPFile newFile = rootFolder.Files.Add(filename, result, true);
      }
      catch (Exception Ex)
      {
           Throw new Exception("Error trying to convert item to pdf. Message: " + Ex.Message);
      }
 }

Once the code is deployed, web part added to the page and ran, I get a pdf file named after the contact’s surname.

If I open the file, I have a table like structure within my PDF displaying all the columns I expected. I’m not going to win any awards for style, however check out the resource link at the bottom of this post to learn ways of jazzing up your tables.

Link to Sample code : OneDrive

 

Resources: http://www.mikesdotnetting.com/article/86/itextsharp-introducing-tables

Using ITextSharp to Merge PDF’s into one


  1. Setting up Word Automation Services for SharePoint 2013
  2. Setting up PowerPoint Automation Services for SharePoint 2013
  3. Converting a Library’s Word Documents to PDF
  4. Converting a Library’s PowerPoint documents to PDF
  5. Using ITextSharp to Merge PDF’s into one
  6. Using ITextSharp to create basic PDF page from item property

In the last couple of posts I’ve shown you how to convert Word and PowerPoint documents into PDF. In this post I’m going to show you how to use ITextSharp to merge your PDF’s together. iTextSharp is a port of the iText open source java library for PDF generation written entirely in C# for the .NET platform. http://itextpdf.com

Following on from my last Farm Solution, I have extended the solution by adding another button that when clicked, it looks up the document library and grabs all pdf files. Looping through each file, copies each pdf to a memory stream, once it have completely looped through each file, it then saves the file back to the SharePoint library. The advantage of using ITextSharp dll, is that you could create a provider hosted SharePoint App, and therefore create this merge functionality in Office 365. Unfortunately, I’m not showing you how to do this in this demo.

The Shared Document library for my site, I already have pdf’s files from my previous demos.

First thing we need to do is add the itextsharp to the Visual Studio project. With NuGet this is really simple. Right click your project and select Manage NuGet Packages…

This will open the Manage NuGet Packages dialog. Type in the Search Online Box for ITextSharp, the top result should be ITextSharp. Just click the Install button, and accept the License Acceptance.

This will add the itextsharp reference to your project. All that remains is to ensure that this dll is deployed to the GAC. Double Click on the Package.Package file in your project.

Select the Advanced tab. Then click the Add to add Existing assemblies. You will find the dll sitting in a package folder at the same location you find the .sln file for your solution. Navigate down through the folder to Packages\iTextSharp.5.5.4\lib\itextsharp.dll. Select the Deployment Target as GlobalAssemblyCache and the location is itextsharp.dll

Click OK.

Below is the code and comments explaining what is happening at different stages of the code.

//Will require these using statements
using iTextSharp.text;
using iTextSharp.text.pdf;

protected void btnPDFMerge_Click(object sender, EventArgs e)
{
  //Get current web context
   var web = SPContext.Current.Web;
   var list = web.Lists.TryGetList(libraryName.Text)
   if (list != null)
   {
       MergePdfs(list);
   }
 
 private void MergePdfs(SPList library)
{
  byte[] result = null;
  SPFolder merged = null;
  //Using helper method (explained further down, look for folder called Merged)
  if (!DoesFolderExist(library.RootFolder, "Merged"))
  {
     //Folder not found so create it.
     merged = library.RootFolder.SubFolders.Add("Merged");
     library.Update();
  }
  else
  {
    //Using helper method (explained further down, Get folder called Merged)
    merged = GetFolder(library.RootFolder, "Merged");
  }
    //CAML Query to return all pdf file types.
   SPQuery query = new SPQuery();

   //Include all subfolders so include Recursive Scope
   //TODO: Ignore Merged folder.
    query.ViewXml = @"<View Scope='Recursive'>
         <Query>
            <Where>
                <Contains>
                    <FieldRef Name='File_x0020_Type'/>
                    <Value Type='text'>pdf</Value>
                </Contains>
            </Where>
        </Query>
      </View>";

   //Get Documents
   SPListItemCollection listItems = library.GetItems(query);
   //Check that there are any documents to merge
   if (listItems.Count > 0)
   {
      //Perform the conversion in memory first
      using (MemoryStream ms = new MemoryStream())
      {
         //Using the ItextSharp Document
         using (Document document = new Document())
         {
           //Using the ITextSharp PdfCopy to create a PDF document in the memory stream
           using (PdfCopy copy = new PdfCopy(document, ms))
           {
             //Open the document before any changes can be made.
             document.Open();

             //Loop through each file
             foreach (SPListItem li in listItems)
             {
               // Convert the BinaryStream of the file to a ITexSharp PdfReader
               PdfReader reader = new PdfReader(li.File.OpenBinaryStream());
               int n = reader.NumberOfPages;
               //Loop through each page in the current PDF file
              for (int page = 0; page < n; )
              {
                 //Import the page to the PDF document in the memory stream.
                 copy.AddPage(copy.GetImportedPage(reader, ++page));
              }
             }
          }
       }

      //Convert the final memory stream into a byte array.
      result = ms.ToArray();
   }
    //Create a new file in the Merged folder called combined.pdf from the byte array.
    SPFile newFile = merged.Files.Add("combined.pdf", result, true);
  }
 }
}

I used a helper method to check if folder exists and another method to return the found folder. I have pinched this method and converted to work on an on-premise code from the OfficeDevPnP code. (I can’t take credit for this).

//Pass in folder and folder we are looking for.
private bool DoesFolderExist(SPFolder parentFolder, string folderName)
{
  //Check that foldername
  if(string.IsNullOrEmpty(folderName))
 {
    throw new ArgumentException("folderName");
 }
   
 //Get collection of the sub folders
 var folderCollection = parentFolder.SubFolders;
 //Call the worker code
 var exists = FolderExistsImplementation(folderCollection, folderName);

  return exists;
}

private bool FolderExistsImplementation(SPFolderCollection folderCollection, string folderName)
{
    //Validate inputs
    if(folderCollection == null)
    {
       throw new ArgumentNullException("folderCollection");
    }

    if(string.IsNullOrEmpty(folderName))
    {
       throw new ArgumentException("folderName");
    }

    //Loop each folder for the folderName
    foreach(SPFolder f in folderCollection)
    {
       if(f.Name.Equals(folderName, StringComparison.InvariantCultureIgnoreCase))
       {
         //If found return true
         return true;
       }
    }

   //not found return false
   return false;
 }
 
 //This code is almost identical to Does folder exist, except instead of return true/false it returns the folder or null.
//In fact these I could just use this method for both and check for null instead of true/false
private SPFolder GetFolder(SPFolder parentFolder, string folderName)
{
   if (string.IsNullOrEmpty(folderName))
   {
       throw new ArgumentException("folderName");
   }

   var folderCollection = parentFolder.SubFolders;
   var folder = GetFolderImplementation(folderCollection, folderName);
   return folder;
}

private SPFolder GetFolderImplementation(SPFolderCollection folderCollection, string folderName)
{
   if (folderCollection == null)
   {
      throw new ArgumentNullException("folderCollection");
   }
   if (string.IsNullOrEmpty(folderName))
   {
     throw new ArgumentException("folderName");
   }

   foreach (SPFolder f in folderCollection)
   {
      if (f.Name.Equals(folderName, StringComparison.InvariantCultureIgnoreCase))
      {
          return f;
      }
   }

  return null;
}

As you can see from my screen shot, I now have a document called combined.pdf sitting in my Merged folder inside my Shared Document library.

Link to Sample code : OneDrive

Converting a Library’s PowerPoint Documents to PDF using PowerPoint Automation Services for SharePoint 2013


  1. Setting up Word Automation Services for SharePoint 2013
  2. Setting up PowerPoint Automation Services for SharePoint 2013
  3. Converting a Library’s Word Documents to PDF
  4. Converting a Library’s PowerPoint documents to PDF
  5. Using ITextSharp to Merge PDF’s into one
  6. Using ITextSharp to create basic PDF page from item property

In my last post, I showed you how to convert a Word document to PDF. In this post I’m going to walk through the code that you can use to convert your PowerPoint document to PDF.

Following on from my last Farm Solution, I have extended the solution by adding another button that when clicked, it looks up the document library and grabs all the PowerPoint documents. Looping through each PowerPoint document it finds, it converts them to PDF and then saves them back to the same library. As before, there is no current way of performing this in Office 365.

The Shared Document library for my site, I have added two PowerPoint documents to my library.

Test 3 is a simple PowerPoint slide that has some text.


And the second PowerPoint document is a “Create an Office Mix” Template created in PowerPoint. As with my word demo, this is just so you can see pictures and layouts converted correctly.


Below is the code and comments explaining what is happening at different stages of the code.

//Will require this using statement
using Microsoft.Office.Server.PowerPoint.Conversion;

protected void btnPowerPointConverttoPDF_Click(object sender, EventArgs e)
{
   //Get current site context
   var site = SPContext.Current.Site;
   var list = web.Lists.TryGetList(libraryName.Text)
   if (list != null)
   {
       PowerPointDocsToConvertToPdf(list);
   }
}

 private void PowerPointDocsToConvertToPdf(SPList library)
 {
    //Perform a SPQuery that returns only PowerPoint Documents.
    SPQuery query = new SPQuery();
    query.Folder = library.RootFolder;

    //Include all subfolders so include Recursive Scope.
    query.ViewXml = @"<View Scope='Recursive'>
       <Query>
            <Where>
                <Or>
                   <Contains>
                     <FieldRef Name='File_x0020_Type'/>
                     <Value Type='Text'>ppt</Value>
                   </Contains>
                   <Contains>
                     <FieldRef Name='File_x0020_Type'/>
                     <Value Type='Text'>pptx</Value>
                   </Contains>
                </Or>
           </Where>
        </Query>
   </View>";

//Get Documents
SPListItemCollection listItems = library.GetItems(query);
//Check that there are any documents to convert.
if (listItems.Count > 0)
{
       foreach (SPListItem li in listItems)
       {
             try
             {
             //Perform the conversion in memory first
             using (MemoryStream destinationStream = new MemoryStream())
             {
                  //Create the conversion request, configure settings.
                 Microsoft.Office.Server.PowerPoint.Conversion.FixedFormatSettings settings = new Microsoft.Office.Server.PowerPoint.Conversion.FixedFormatSettings();
                 settings.BitmapUnembeddableFonts = true;
                 settings.FrameSlides = true;
                 settings.IncludeDocumentProperties = true;
                 settings.IncludeDocumentStructureTags = true;
                 settings.IncludeHiddenSlides = true;
                 settings.OptimizeForMinimumSize = true;
                 settings.UsePdfA = true;
                 settings.UseVerticalOrder = true;

                 //Perform conversion, opening stream, passing in settings and destination stream.
                 PdfRequest request = new PdfRequest(li.File.OpenBinaryStream(), ".pptx", settings, destinationStream);

                 //Send the request synchronusly, passing in null value for the callback parameter and caputring the response in result object.
                 IAsyncResult result = request.BeginConvert(SPServiceContext.GetContext(library.ParentWeb.Site), null, null);

                //Use the EndConvertMethod;
                request.EndConvert(result);

                //Add the converted file to the document library
                var fileName = Path.GetFileNameWithoutExtension(li.File.Name) + ".pdf";
                SPFile newFile = library.RootFolder.Files.Add(fileName, destinationStream, true);
            }
          }
          catch(Exception ex)
          {
            throw new Exception("Error processing PowerPoint to PDF " + ex.message);
          }
     }
  }
}

After typing in the Document Library name and clicking the button, the PowerPoint documents have been converted to PDF.

Link to Sample code : OneDrive

 

Converting a Library’s Word Documents to PDF using Word Automation Services for SharePoint 2013


  1. Setting up Word Automation Services for SharePoint 2013
  2. Setting up PowerPoint Automation Services for SharePoint 2013
  3. Converting a Library’s Word Documents to PDF
  4. Converting a Library’s PowerPoint documents to PDF
  5. Using ITextSharp to Merge PDF’s into one
  6. Using ITextSharp to create basic PDF page from item property

In my previous posts, I showed you how to set up your environment so that you could perform Word or PowerPoint conversion to PDF. In this post I’m going to walk through the code that you can use to convert your word document to PDF.

Back in SharePoint 2010, when you converted a Word Document to PDF you created a Conversion Job, this was to accommodate bulk file operation scenarios. The job was placed in a queue within the Word Automation Services and ran by a SharePoint Timer Job. Due to it being ran on a SharePoint Timer Job, there wasn’t a simple way of determine when a conversion had completed. Developers who wanted to perform a conversion synchronously, and get a response if the file conversion was successful or not just couldn’t. Part of the SharePoint 2013 version of Word Automation Services, you can now call the Word Automation Services on demand. Meaning you can now perform synchronous file conversions. If you wish to perform Asynchronous Word to PDF conversion, there is a MSDN walkthrough article how you might achieve this. https://msdn.microsoft.com/en-us/library/office/ff181518(v=office.14).aspx

I have put together a simple Farm Solution that although it is performing a bulk conversion of Word Documents to PDF, it does it synchronously. There is currently no possible way of doing this process within Office 365, well not without using an on premise farm to perform the actual conversion for you.

I have built a simple visual web part (maybe in a real world scenario this might be a button on the ribbon of the library, or you perform a event receiver on the list that converts any word document to pdf), that has textbox for a user to enter the document library they wish to convert Word Documents to PDF, and a button. The button click calls then looks up the document library, grab all word documents then loops through each word document converting to PDF and then save it back to the same library.

image

The Shared Document library for my site, I have added two word documents to my library.

image

One is a simple test document that says “hello you”.

image

And a second document that is actually an “Event Flyer” Template created in Microsoft Word, and edited slightly. This is just so that you can see pictures and layouts conversion too.

image

Below is the code and comments explaining what is happening at different stages of the code.

//Will require this using statement
using Microsoft.Office.Word.Server.Conversions;

//This is the name of your Word Automation Service within Central Admin. If you have followed my setup yours will be called WAS too.
public static string WORD_AUTOMATION_SERVICE = "WAS";

//Click handler
protected void btnWordConvertToPDF_Click(object sender, EventArgs e)
{
  //Get current site context         
  var web = SPContext.Current.Web;
  var list = web.Lists.TryGetList(libraryName);

 if(list != null)
 {
    WordDocsToConvertToPdf(list);
 }
} 

private void WordDocsToConvertToPdf(SPList library)
{
   //Perform a SPQuery that returns only Word Documents.
   SPQuery query = new SPQuery();
   query.Folder = library.RootFolder;
   //Include all subfolders so include Recursive Scope.
    query.ViewXml = @"<View Scope='Recursive'>
                                <Query>
                                   <Where>
                                        <Or>
                                            <Contains>
                                                <FieldRef Name='File_x0020_Type'/>
                                                <Value Type='Text'>doc</Value>
                                            </Contains>
                                            <Contains>
                                                <FieldRef Name='File_x0020_Type'/>
                                                <Value Type='Text'>docx</Value>
                                            </Contains>
                                        </Or>
                                    </Where>
                                </Query>
                            </View>"; 

  //Get Documents
  SPListItemCollection listItems = library.GetItems(query);

  //Check that there are any documents to convert.
            if (listItems.Count > 0)
            {
                foreach (SPListItem li in listItems)
                {
                    //Perform the conversion in memory first, therefore we require a MemoryStream.
                    using (MemoryStream destinationStream = new MemoryStream())
                    {
                        //Call the syncConverter class, passing in the name of the Word Automation Service for your Farm.
                        SyncConverter sc = new SyncConverter(WORD_AUTOMATION_SERVICE);
                        //Pass in your User Token or credentials under which this conversion job is executed.
                        sc.UserToken = SPContext.Current.Site.UserToken;
                        sc.Settings.UpdateFields = true;

                        //Save format
                        sc.Settings.OutputFormat = SaveFormat.PDF; 

                        //Convert to PDF by opening the file stream, and then converting to the destination memory stream.
                        ConversionItemInfo info = sc.Convert(li.File.OpenBinaryStream(), destinationStream);

                        var filename = Path.GetFileNameWithoutExtension(li.File.Name) + ".pdf";
                        if (info.Succeeded)
                        {
                            //File conversion successful, then add the memory stream to the SharePoint list.
                            SPFile newfile = library.RootFolder.Files.Add(filename, destinationStream, true);
                        }
                        else if (info.Failed)
                        {
                            throw new Exception(info.ErrorMessage);
                        }
                     }
                }
            }
        }

After typing in the Document library name and clicking the button, the word document have been converted to PDF.

image

image

 

When building this demo, I did discover that if you have your Active Directory on the same box as your SharePoint then Word Automation Service will throw an error message as you try to convert documents, saying that:

“The file could not be converted; it may be corrupt or otherwise invalid (the conversion process failed). Please try opening the file in Microsoft Word, resaving it, and then resubmitting the file for conversion. If this does not resolve the issue, contact your system administrator.”

So firstly, I’m sorry to any developers out there that have used my earlier blogs to build themselves a Single SharePoint 2013 server farm. And secondly thank you to Karsten Pohnke who blog post confirmed why I was getting the error message. http://www.ilikesharepoint.de/2014/07/sharepoint-word-automation-service-does-not-work-file-may-be-corrupted/

Link to Sample code : OneDrive

Setting up PowerPoint Automation Services for SharePoint 2013


  1. Setting up Word Automation Services for SharePoint 2013
  2. Setting up PowerPoint Automation Services for SharePoint 2013
  3. Converting a Library’s Word Documents to PDF
  4. Converting a Library’s PowerPoint documents to PDF
  5. Using ITextSharp to Merge PDF’s into one
  6. Using ITextSharp to create basic PDF page from item property

Microsoft PowerPoint Automation Services is a new feature of Microsoft SharePoint Server 2013. Using PowerPoint Automation Services, you can convert PowerPoint (.pptx and .ppt) file to supported document formats (.pptx, .pdf, .xps, .jpg and .png).This blog post will walk you through the steps of setting up your SharePoint 2013 on premise farm to use PowerPoint Automation Services. Unlike with Word Automation Services, this cannot be configured from inside Central Admin.

clip_image002

To create this service PowerShell must be used. This feature is not available with SharePoint Online.Before you start, ensure in your AD you have created a service account. Mine is called PowerPointAutomation.

  1. Open up a PowerShell command window or PowerShell ISE.
  2. First we need to create the Managed Account in SharePoint. This can either be done by directly in SharePoint Central Admin by registering a new Managed Account https://<CentralAdminUrl>/_admin/ManagedAccounts.aspx
    clip_image004
    clip_image006
    Or we can do it with PowerShell

    add-pssnapin Microsoft.SharePoint.Powershell
    
    $cred = Get-Credential
    New-SPManagedAccount -Credential $cred
    

    When run, this will display a Credential box, put in here your Service account and password

    clip_image008

    Your Managed Account will now be registered with SharePoint.

  3. Now we can run the following script to create the PowerPoint Automation Service and Proxy. (Remember to change your service account)
    add-pssnapin Microsoft.SharePoint.Powershell
    
    #Create a new Service Application Pool Account
    New-SPServiceApplicationPool -Name "PowerPointAutomationAppPool" -Account cc\PowerPointAutomation
    #Get the Service Application pool
    $pool = Get-SPServiceApplicationPool | where {$_.Name -like "PowerPointAuto*"}
    #Create the PowerPoint Conversion Service
    $ppt = New-SPPowerPointConversionServiceApplication "PowerPoint Conversion Service" -ApplicationPool $pool
    #Create the Proxy for Service
    New-SPPowerPointConversionServiceApplicationProxy "PowerPoint Conversion Service Proxy" -ServiceApplication $ppt –AddToDefaultGroup
    

    Once complete your powershell window should look similar to mine below.

    clip_image010

    Also if you navigate in Central Administration back to your Service Applications, you will now see the PowerPoint Conversion Service Application and Proxy within the list.
    clip_image012

  4. For the PowerPoint Conversion Service to work, you will need create a new folder called PowerPointConversion on the server running the PowerPoint Conversion Service in the following location. C:\ProgramData\Microsoft\SharePoint. This folder directory is a hidden folder and you will need to show Hidden items in the ribbon of explorer to see it.
    clip_image014

    clip_image016
    clip_image018
  5. Once you have created this folder, view the security on this folder (Right click folder, click Properties, view the Security Tab) and give WSS_WPG Modify permissions. This will allow the Web Application pool service account to create folders and files within the newly created PowerPointConversion folder, which is required to convert a PowerPoint document.
  6. Lastly in Central Administration, under system Settings click on Manage Services on Server, and ensure that you have started PowerPoint Conversion Service.image

In Part 4 of these blog posts I will demo how to code against the PowerPoint Automation Services to convert PowerPoint Documents to PDF.

Setting up Word Automation Service for SharePoint 2013


  1. Setting up Word Automation Services for SharePoint 2013
  2. Setting up PowerPoint Automation Services for SharePoint 2013
  3. Converting a Library’s Word Documents to PDF
  4. Converting a Library’s PowerPoint documents to PDF
  5. Using ITextSharp to Merge PDF’s into one
  6. Using ITextSharp to create basic PDF page from item property

Word Automation Services for SharePoint 2013 is a SharePoint Service Application and it gives developers a way to perform server-side conversion of documents that are supported by Microsoft Word. This service was available in SharePoint 2010, however the newer version now allows on demand file conversions and converting streams to files. (Steams with Word Automation Service can only be used with on demand conversions, not Timer job-based requests.

This blog post will walk you through the steps of setting up your SharePoint 2013 on premise farm to use Word Automation Services. This feature is not available with SharePoint Online.

Before you start, ensure in your AD you have created a service account. Mine is called WordAutomation.

  1. Open Central Administration
  2. Under Application Management > Service Applications select Manage service applications
    image
  3. In the ribbon bar area, click New > Word Automation Services
    image
  4. On the Create New Word Automation Services Application dialog. Fill in:
    a. Name: WAS
    b. Create New Application pool: WASAppPool
    c. Security Account for Application Pool – Configurable: [Domain]\WordAutomation
        (Note: You might have to Register your new Managed account first, before it shows up in your Configurable Drop down list)
    d. Add to Default Proxy List: Tick
  5. Click Next
  6. On the next screen enter:
    a. Database Server: [SQL Server]
    b. Database Name: WASDatabase
  7. Click Finish
  8. Once created your Word Automation Services and Proxy will be displayed in the Service Applications list.

image

When you click WAS link you will be taken through to the configuration of the Word Automation Services. You will not need to change any of this to start using the Word Automation Services, however it is useful looking over these settings in case you need to change them in the future.

image

 

  • Lastly, in Central Administration, under System Settings, click on Services on Server, and ensure that you have Started the Word Automation Services.
    image

 

In Part 3 of these blog posts I will demo how to code against the Word Automation Services to convert Word Documents to PDF.