Adding a dynamic watermark to a PDF file from a SharePoint Workflow

Posted at: 13:04 on 24 March 2010 by Muhimbi

workflow We have had a great response to our previous post about watermarking PDF files, however quite a few customers have told us that they want to use a dynamic watermark (e.g. a username, ip address etc) rather than a static one.

A quick introduction for those not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.

Update: Please note that as of version 3.5 the PDF Converter API and Server Platform also supports a native watermarking interface.

The solution presented below executes a workflow whenever a PDF file is added to a document library. It then applies security settings to prevent the watermark from being removed and then reads the watermark text from a SharePoint column. The workflow author can specify if the watermark is placed in the foreground or the background as well as the level of transparency. As the code is well documented it is easy to make further changes and customisations, e.g. the PDF file that needs to be watermarked may not be the file the workflow is acting on or the watermarked text may come from a workflow variable rather than a list column.

 
Create the workflow as follows:

  1. Download and install the Muhimbi Workflow Power Pack for SharePoint.
     
  2. Download and install the Muhimbi PDF Converter for SharePoint.
    Note that you need version 3.1.2.18 or newer, older versions do not allow watermarking.
     
  3. Download this article’s source code.
     
  4. We need to be able to access functionality in the Muhimbi.SharePoint.DocumentConverter.PDF and System.Drawing assemblies. Add these references to the relevant Web Application using the Workflow Power Pack Central Administration screens as described in the Administration Guide. Make sure to place each reference on a new line.
      
  5. Make sure you have the appropriate privileges to create workflows on a site collection.
     
  6. Create a new column named Watermark in the Shared Documents library. The content of this column will be used as the text for the watermark.
     
  7. Create a new workflow using SharePoint Designer.
     
  8. On the Workflow definition screen associate the workflow with the Shared Documents library, tick the box next to ‘Automatically start this workflow when a new item is created’ and proceed to the next screen. Please note that if you tick the ‘auto start on update’ box as well then the PDF file may be watermarked multiple times leading to unexpected results.
     
  9. We only want to act on files of type PDF. Although we could have put this validation in the code, in this example we use a workflow condition for it so add a Compare Any Data Source condition and:
     
    a.  Click on the first value followed by the display data binding (fx) button.
    b.  Select Current Item as the Source and select File Type in the Field. Click the OK button to continue.
    d.  Click on the second value and enter pdf. (Use lower case as the compare option is case sensitive).
     
  10. Click the Actions button and insert the Execute Custom Code action.
     
  11. Optionally click parameter 1 and enter a relative or absolute destination path. Leave the parameter empty to save the watermarked file on top of the existing PDF file. For details about how paths are handled, see this post and search for the words ‘this url’.
     
  12. Insert the C# based code embedded in step #3’s download (also listed below) by clicking this code.
     
    /*********************************************************************************************
                              Muhimbi PDF Converter - Dynamic Watermarking
     
                  Copyright 2010, Muhimbi Ltd - www.muhimbi.com - All rights reserved
     
     The following code shows a simple way of adding a dynamic watermark to existing PDF Files. 
     The watermark content is read from a SharePoint column and rendered on the center of the page.
     Error and permission checking as well as other minor features have been omitted for the sake 
     of brevity and clarity.
     
     Ideally PDF Conversion, applying security and watermarking is executed in the same step, see 
     http://www.muhimbi.com/blog/2010/01/configure-pdf-security-from-sharepoint.html
     
     This code requires Muhimbi’s PDF Converter and Workflow Power Pack to be installed.
    **********************************************************************************************/
     
    using System.Drawing;
    using System.IO;
    using Syncfusion.Pdf;
    using Syncfusion.Pdf.Parsing;
    using Syncfusion.Pdf.Graphics;
    using Muhimbi.SharePoint.DocumentConverter.PDF;
    using Syncfusion.Pdf.Security;
     
    SPFile spSourceDocument = MyWorkflow.Item.File;
     
    string destinationFileName = spSourceDocument.Name;
    string destinationFolderName = MyWorkflow.Parameter1 as string;
     
    // ** Read the text from the Column named 'Watermark', use a column name of your choice.
    string watermark = MyWorkflow.Item["Watermark"] as string;
    string pdfOwnerPassword = "some!Strong^Password";
     
    // ** z-order and transparency of the watermark
    bool watermarkInBackground = true;
    float watermarkTransparancy = 0.25f;
     
    // ** Method for creating a simple template, amend as needed
    WorkflowFunction<string, PdfTemplate> CreateWatermark = delegate(string[] parameters)
    {
        const float watermarkWidth = 250;
        const float watermarkHeight = 23;
        string watermarkText = parameters[0];
     
        if(watermarkText==null)
            watermarkText = "no watermark specified";
     
        // ** Create a new Template
        PdfTemplate template = new PdfTemplate(watermarkWidth, watermarkHeight);
        PdfGraphics tg = template.Graphics;
     
        // ** Rectangle
        PdfPen pen = new PdfPen(PdfBrushes.Black, 1);
        tg.DrawRectangle(pen, PdfBrushes.White, new RectangleF(0, 0, watermarkWidth, 
                                                               watermarkHeight));
     
        // ** Evaluation message text
        using (Font f = new Font("Arial", 10f, FontStyle.Regular))
        {
            PdfFont font = new PdfTrueTypeFont(f, false);
            PdfStringFormat stringFormat = new PdfStringFormat();
            stringFormat.Alignment = PdfTextAlignment.Center;
            stringFormat.LineAlignment = PdfVerticalAlignment.Middle;
            stringFormat.WordWrap = PdfWordWrapType.WordOnly;
            stringFormat.ClipPath = true;
            tg.DrawString(watermarkText, font, PdfBrushes.Black, new RectangleF(5, 0, 
                                    watermarkWidth - 10, watermarkHeight), stringFormat);
        }
     
        return template;
    };
     
    // ** Method for applying security settings, amend / uncomment as needed
    WorkflowMethod<PdfLoadedDocument> ApplySecurity = delegate(PdfLoadedDocument[] pdfDocuments)
    {
        PdfLoadedDocument pdfDocument = pdfDocuments[0];
        // ** Enable encryption of content
        pdfDocument.Security.KeySize = PdfEncryptionKeySize.Key128Bit;
        // ** Specify the password used for modifying permissions
        pdfDocument.Security.OwnerPassword = pdfOwnerPassword;
     
        // ** Apply the restrictions, if any. Note that by default nothing is 
        // ** allowed and we need to individually enable permissions
        pdfDocument.Security.Permissions = PdfPermissionsFlags.Default;
        // ** Allow Commenting / annotations
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.EditAnnotations);
        // ** Allow Accessibility
        //pdfDocument.Security.SetPermissions(PdfPermissionsFlags.AccessibilityCopyContent);
        // ** Allow Copy content via clipboard etc.
        //pdfDocument.Security.SetPermissions(PdfPermissionsFlags.CopyContent);
        // ** Allow Assemble / modification of document
        //pdfDocument.Security.SetPermissions(PdfPermissionsFlags.AssembleDocument);
        // ** Allow Form fields to be filled out
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.FillFields);
        // ** Allow high resolution printing
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.FullQualityPrint);
        // ** Allow any kind of printing
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.Print);
    };
     
    // ** Load the document from SharePoint
    PdfLoadedDocument sourceDocument = new PdfLoadedDocument(spSourceDocument.OpenBinary());
     
    // ** Restrict the rights
    ApplySecurity(sourceDocument);
     
    // ** Create the watermark Template
    PdfTemplate watermarkTemplate = CreateWatermark(watermark);
     
    // ** Iterate over all pages and apply the watermark
    foreach (PdfPageBase page in sourceDocument.Pages)
    {
        // ** Position the watermark at the centre of the page
        float wmWidth = watermarkTemplate.Width;
        float wmHeight = watermarkTemplate.Height;
        float wmLeft = ((page.Size.Width - wmWidth) / 2);
        float wmTop = ((page.Size.Height - wmHeight) / 2);
        PointF wmPosition = new PointF(wmLeft, wmTop);
     
        // ** Place watermark behind or in front of text?
        if (watermarkInBackground == true)
        {
            PdfTemplate pageTemplate = page.CreateTemplate();
            page.Layers.Clear();
            PdfGraphics g = page.Graphics;
            g.SetTransparency(watermarkTransparancy);
            g.DrawPdfTemplate(watermarkTemplate, wmPosition);
            g.SetTransparency(1f);
            g.DrawPdfTemplate(pageTemplate, PointF.Empty, page.Size);
        }
        else
        {
            PdfGraphics g = page.Graphics;
            g.SetTransparency(watermarkTransparancy);
            g.DrawPdfTemplate(watermarkTemplate, wmPosition);
        }
    }
     
    // ** Construct the path and file to write the watermarked PDF file to.
    if (string.IsNullOrEmpty(destinationFolderName) == true)
        destinationFolderName = spSourceDocument.ParentFolder.Url;
    SPFolder destinationFolder = Utility.GetSPFolder(destinationFolderName, MyWorkflow.Web);
    string destinationFilePath = string.Format("{0}/{1}", destinationFolder.Url,
                                               destinationFileName);
    SPWeb destinationWeb = destinationFolder.ParentWeb;
    SPFile spDestinationFile = destinationWeb.GetFile(destinationFilePath);
     
    // ** If a document library requires manual checkout and the file is not checked out, then 
    // ** check the file out before uploading.
    if (spDestinationFile.Exists && spDestinationFile.Item.ParentList.ForceCheckout &&
        spDestinationFile.CheckOutStatus == SPFile.SPCheckOutStatus.None)
    {
        spDestinationFile.CheckOut();
    }
     
    // ** Add the file to the site including the meta data
    using (MemoryStream watermarkedFile = new MemoryStream())
    {
        sourceDocument.Save(watermarkedFile);
        spDestinationFile = destinationWeb.Files.Add(destinationFilePath, watermarkedFile,
                                                     spSourceDocument.Item.Properties, true);
    }
     
    // ** Check the file back in if this script was responsible for checking it out.
    if (spDestinationFile.Item.ParentList.ForceCheckout == true)
    {
        spDestinationFile.CheckIn("Auto check-in after PDF watermarking.");
    }
          
  13. Click the Actions button, select Log to History List, click this message and enter File watermarked.
     
  14. Close the Workflow Designer.
     
  15. Add a new PDF file to your library to trigger the workflow and apply the watermark. Make sure the content for the Watermark field is specified when the file is added as otherwise a generic watermark text will be inserted.
      WaterMarkScript

 

Naturally this is just a simple example. Feel free to play around with the code, change which parameters are passed into the workflow, modify where watermarked files are written to or use a different source for the watermark text. Leave a comment below if you are trying to do anything specific.

Update: If you get the message ‘no watermark specified’ then the workflow may have been triggered before you specified the contents of the Watermark field. For details see SharePoint Designer workflows trigger before properties are set.

.





Labels: , , , , ,

Porting a SharePoint 2007 WSPBuilder solution to SharePoint 2010 – Part 4

Posted at: 11:40 on 17 March 2010 by Muhimbi

In the final part of our series about porting a SharePoint 2007 based WSPBuilder project to SharePoint 2010 we discuss the changes made to our Installation scripts in order to make it work with both versions of SharePoint.

Please note that this article is based on our experiences with the beta version of SharePoint 2010. Some of the issues we have identified may have been resolved in the final release.  

 

The following posts are part of this series:

 

Installation Script

The installation scripts used for deploying the SharePoint part of our solutions are simple Windows CMD scripts. Our customers appear to prefer it this way as it allows them to see what is going on and make necessary amendments to deploy the software as part of a larger deployment script. Unfortunately we cannot use PowerShell as that technology is not available on all systems.

In order to make the same script work on SharePoint 2007 as well as 2010 environments we have had to make the following changes:

  1. Detect which version of SharePoint is installed.
  2. Adjust the STSADM path accordingly.
  3. Adjust the name of the WSP file accordingly as the WSP files for both environments are different.


  4.  

The resulting installation script is as follows:

@echo off
echo *******************************************************************************
echo Installing Muhimbi PDF Converter for SharePoint.       (c) 2010 www.muhimbi.com
echo *******************************************************************************
echo.
 
REM ** Detect which version of SharePoint is installed.
SET STSADM="%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\14\bin\STSADM.EXE"
SET SolutionWSP=Muhimbi.PDFConverter.SP2010.wsp
if exist %STSADM% goto endVersionDetection
    SET STSADM="%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\bin\STSADM.EXE"
    SET SolutionWSP=Muhimbi.PDFConverter.wsp
    if exist %STSADM% goto endVersionDetection
    echo - SharePoint does not appear to be installed on this server.
    goto endOfScript
:endVersionDetection
 
 
echo - Adding solution to Solution Store
%STSADM% -o addsolution -filename %SolutionWSP%
%STSADM% -o execadmsvcjobs
 
echo - Deploying solution
%STSADM% -o deploysolution -name %SolutionWSP% -immediate -allowgacdeployment -force
%STSADM% -o execadmsvcjobs
 
echo - Feature is self activating
 
echo - Deploying Resources
%STSADM% -o copyappbincontent
 
echo - If you are experiencing problems when accessing the solution then
echo   please read the Administration guide, particularly section 3.5.
 
echo - Installation finished.
 
:endOfScript
 
echo.
pause

 

Un-Installation Script

The changes required to the un-installation script are similar to the changes for the installation script. As the name of the Timer Service has changed between SharePoint 2007 and 2010 this name has been made dynamic as well.

The resulting script is as follows:

@echo off
echo *******************************************************************************
echo Un-installing Muhimbi PDF Converter for SharePoint.    (c) 2010 www.muhimbi.com
echo *******************************************************************************
echo.
 
REM ** Detect which version of SharePoint is installed.
set STSADM="%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\14\bin\STSADM.EXE"
set TimerServiceName="SharePoint 2010 Timer"
SET SolutionWSP=Muhimbi.PDFConverter.SP2010.wsp
if exist %STSADM% goto endVersionDetection
    SET SolutionWSP=Muhimbi.PDFConverter.wsp
    set STSADM="%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\bin\STSADM.EXE"
        set TimerServiceName="windows sharepoint services timer"
    if exist %STSADM% goto endVersionDetection
    echo - SharePoint does not appear to be installed on this server.
    goto endOfScript
:endVersionDetection
 
SET SolutionWSP="Muhimbi.PDFConverter.wsp"
 
echo - Retracting solution.
%STSADM% -o retractsolution -name %SolutionWSP% -immediate
%STSADM% -o execadmsvcjobs
 
echo - Wait for the solution to be retracted across the farm (Check Central Admin).
pause
 
echo - Removing solution from Store.
%STSADM% -o deletesolution -name %SolutionWSP%
%STSADM% -o execadmsvcjobs
 
echo - Restarting timer service in order to release any GAC DLLs.
net stop %TimerServiceName%
net start %TimerServiceName%
 
echo - Un-installation finished.
 
:endOfScript
 
echo.
pause

 

 

That’s it. We hope you have enjoyed this series. Please leave feedback if you have any questions.

.

Labels: , , , ,

Porting a SharePoint 2007 WSPBuilder solution to SharePoint 2010 – Part 3

Posted at: 14:46 on 15 March 2010 by Muhimbi

SharePoint2010 In part of 3 of our series about porting a SharePoint 2007 based WSPBuilder project to SharePoint 2010 we discuss the changes made to our code in order to make everything work, and look good, in both versions of SharePoint

Please note that this article is based on our experiences with the beta version of SharePoint 2010. Some of the issues we have identified may have been resolved in the final release.  

 

The following posts are part of this series:

 

Fixing Central Administration links

We started of with the easy bit that required no coding, unless you consider writing XML files coding. As the configuration screen for our PDF Converter is located under External Service Connections in SharePoint 2007, we decided to add it to the same location in SharePoint 2010.

We added the Custom Action for SharePoint 2010 to the same elements file that holds the SharePoint 2007 definition. SharePoint will simply ignore locations it has no knowledge about, which is very convenient in this case.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 
  <!-- Settings screen in SP2007 Central Administration Application Management -->
  <CustomAction
    Id="Muhimbi.SharePoint.Administration.....ExternalService.DocumentConverterSettings"
    Title="Muhimbi Document Converter Settings"
    Location="Microsoft.SharePoint.Administration.ApplicationManagement"
    GroupId="ExternalService"
    Sequence="51"
  >
    <UrlAction Url="~site/_admin/Muhimbi.PDFConverter/WebAppDocumentConverterSettings.aspx"/>
  </CustomAction>
 
  <!-- Settings screen in SP2010 Central Administration Application Management -->
  <CustomAction
    Id="Muhimbi.....GeneralApplicationSettings.ExternalServiceConnections.DocumentConverterSettings"
    Title="Muhimbi Document Converter Settings"
    Description="Configure the Muhimbi Document Converter settings."
    Location="Microsoft.SharePoint.Administration.GeneralApplicationSettings"
    GroupId="ExternalServiceConnections"
    Sequence="51"
  >
    <UrlAction
        Url="/_admin/Muhimbi.PDFConverter/WebAppDocumentConverterSettings.aspx" />
  </CustomAction>

Updated elements.xml file. Note that some IDs have been truncated for readability 


Note that we had to change both the GroupID and Location for SharePoint 2010. At the time of writing a full list of Groups and Locations has not yet been published by Microsoft, but Arjen Bloemsma has created his own preliminary list. If the location you want to target is not found in Arjen’s list then you can follow the procedure set out below:

  1. Open Central Administration and navigate to the section you want to place your link in.
  2. Write down the name of an ASPX file that is already in that section.
  3. Search in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES for all files containing that file name.
  4. Open the file and copy the GroupID and Location.
     

Central-Admin-PDF-Settings
Muhimbi Document Converter Settings in External Service Connections 

 

Adding support for the Ribbon

With the links in Central Administration fixed we moved on to the SharePoint 2010 Ribbon. As we didn’t want to introduce too much change to our back end, we decided to only allow a single document to be selected for conversion at a time. Fortunately the SharePoint team had just provided an example on their blog about disabling a Ribbon button when more than 1 item is selected. Unfortunately their code didn’t work, but it gave us a good starting point.

The Ribbon is quite well documented. For details see:

 
Once we had the Ribbon changes up and running on SharePoint 2010 we noticed that SharePoint 2007 was no longer able to accept the WSP file. As a result we decided to create a shadow SPHive_2010 folder and modify our build scripts to create two separate WSP Files. For more details see Part 2 – Reconfiguring the Visual Studio Solution.
 

<CustomAction
Id="Muhimbi.SharePoint.DocumentConverter.PDF.Ribbon.Documents.Copies.Controls.PDFConversion.Action"
Location="CommandUI.Ribbon"
RegistrationType="ContentType"
RegistrationId="0x0101"
>
  <CommandUIExtension>
    <CommandUIDefinitions>
      <CommandUIDefinition
        Location="Ribbon.Documents.Copies.Controls._children">
        <Button Id="Muhimbi.SharePoint.DocumentConverter.PDF.Ribbon.Documents.Copies.Controls.
                    PDFConversion.Button"
                Command="Muhimbi.SharePoint.DocumentConverter.PDF.Ribbon.Documents.Copies.Controls.
                         PDFConversion.Button.Command"
                Image16by16="/_layouts/images/Muhimbi.PDFConverter/pdf16.gif"
                Image32by32="/_layouts/images/Muhimbi.PDFConverter/pdf32.gif"
                LabelText="$Resources:ecb_title;"
                Sequence="11"
                TemplateAlias="o1" />
      </CommandUIDefinition>
    </CommandUIDefinitions>
    <CommandUIHandlers>
      <CommandUIHandler
        Command="Muhimbi.SharePoint.DocumentConverter.PDF.Ribbon.Documents.Copies.Controls.
                 PDFConversion.Button.Command"
        CommandAction="javascript:window.location='{SiteUrl}/_layouts/Muhimbi.PDFConverter/
                       Convert.aspx?ListId={ListId}&amp;ItemId={ItemId}&amp;Source=' + 
                       window.location"
        EnabledScript="javascript:function singleEnable()
        {
          var items = SP.ListOperation.Selection.getSelectedItems();
          var ci = CountDictionary(items);
          return (ci == 1);
        }
        singleEnable();" />
    </CommandUIHandlers>
  </CommandUIExtension>
</CustomAction>

Custom Action for the PDF Conversion button and script to only allow a single selection


Note that we have wrapped some lines in the code listed above to make things more readable. When copying this code, please make sure everything between double quotes, with the exception of the EnabledScript attribute, is placed on a single line.
 

PDF-Converter-Doclib
Look at our new button in the Ribbon, isn’t it shiny?

 

Fixing visual problems

As you may remember from Part 1 – Introduction / Problems installing the existing 2007 version on SP2010, the formatting of certain page elements don’t look quite right in SharePoint 2010. The main problems were related to the vertical spacing of check boxes and additional vertical space being added between elements.

After some investigation we decided that the easiest way to solve these rending problems was to add a little bit of conditional code that checks the version number of SharePoint and depending on the version output some additional CSS styles.

<!-- Additional SP2010 Styles. Disabled by default -->
<style id="sp2010AdditionalStyles" runat="server" Disabled="true">
.FixSP2010CheckBox {position:relative; top:2px}
#ctl00_PlaceHolderMain_convertersSection .ms-authoringcontrols IMG { display:none}
.FixSP2010Button {position:relative; top:-2px}
</style>
 
... Irrelevant code removed
 
<asp:Button id="btnTestServer" Text="Test" OnClick="btnTestServer_OnClick" 
class="ms-ButtonHeightWidth FixSP2010Button" style="width:80px" runat="server"/>
 
... Irrelevant code removed
 
<wssawc:InputFormCheckBox ID="chkAllowWordProcessing" CssClass="FixSP2010CheckBox"
               LabelText="Word Processing (e.g. MS-Word, RTF, TXT)" runat="server"/>
<wssawc:InputFormCheckBox ID="chkAllowSpreadsheets" runat="server" CssClass="FixSP2010CheckBox"
               LabelText="Spreadsheets (e.g. Excel, CSV)"/>
 
... etc

 Changes to the ASPX Application page (Relevant fragments only)

/// <summary>
/// Resolve the differences between SharePoint 2007 and 2010
/// </summary>
private void FixSharePointDifferences()
{
    int spVersion = SPFarm.Local.BuildVersion.Major;
    if (spVersion == 12)
    {
        // ** SharePoint 2007
        sp2010AdditionalStyles.Disabled = true;
    }
    else
    {
        // ** SharePoint 2010
        sp2010AdditionalStyles.Disabled = false;
    }
}

Changes to the Code Behind file. Invoke this method from the page’s OnLoad

 
With these changes in place the screens suddenly look a lot better in SharePoint 2010. As the style changes are not active in SharePoint 2007 everything continues to work fine in that environment as well.

SP2010-After-Fixing
SharePoint 2010 look and feel before and after fixing visual problems

 

Miscellaneous changes

In addition to some minor code changes, the addition of the DynamicMasterPageFile attribute is worth mentioning as well. By replacing the MasterPageFile attribute - in Application Pages hosted inside a site collection - with DynamicMasterPageFile, the Quick Access menu is magically added to the left of the page. The absence of this menu in previous SharePoint versions has always been a pet peeve to me.

Note that this new attribute is incompatible with SharePoint 2007. Therefore a copy of the ASPX file with the MasterPageFile attribute replaced with DynamicMasterPageFile will need to be placed in the shadow SPHive_2010 folder. We tried setting this value manually (via reflection) in the page’s PreInit event, but that resulted in the SharePoint 2010 page being rendered in the SharePoint 2007 look and feel.
 

<%@ Page Language="C#" DynamicMasterPageFile="~masterurl/default.master" 
Inherits="Muhimbi.SharePoint.DocumentConverter.PDF.Convert" %>

 

 

Continue to Part 4 – Updating deployment scripts.

.






Labels: , , , , ,

Porting a SharePoint 2007 WSPBuilder solution to SharePoint 2010 – Part 2

Posted at: 15:11 on 12 March 2010 by Muhimbi

SharePoint2010

In part of 2 of our series about porting a SharePoint 2007 based WSPBuilder project to SharePoint 2010 we discuss the changes made to our Visual Studio 2008 based project to support both versions of SharePoint

Please note that this article is based on our experiences with the beta version of SharePoint 2010. Some of the issues we have identified may have been resolved in the final release.  

 

The following posts are part of this series:

WSP Builder

On January 13 a new ‘2010’ compatible version of WSPBuilder was released. I seriously doubt this new version is required in order to build SharePoint 2010 compatible solutions, but we upgraded to it nevertheless.

Note that if you want to use WSPBuilder to build a hybrid SharePoint 2007 / 2010 solution or a solution that just targets SharePoint 2007 then you must perform the build on a machine that runs SharePoint 2007. The latest version of WSPBuilder checks which version of SharePoint is installed on the build machine and adds SharePoint 2010 specific elements to the generated WSP file, causing deployments to fail in SharePoint 2007 environments.

Unfortunately it does not seem to be possible to modify this behaviour using a command line switch.

 

Visual Studio Project structure

VS2008-ProjectsDepending on the complexity of your project, and on the need to add any SharePoint 2010 specific functionality, you may not need to make any changes to your project structure.

However, in our case we decided to make the following changes:

  1. We renamed the 12 folder to SPHive. From a functional perspective there is no need to make this change, however it provides our developers with a more consistent experience as, depending on the platform they are targeting, the files may go to either the 12 or 14 hive.
     
  2. A separate shadow SPHive_2010 folder was created to store SharePoint 2010 specific copies of files that will break compatibility when deployed to SharePoint 2007. For example element files that contain Custom Actions that target the SharePoint 2010 specific Ribbon will prevent a WSP file from deploying to SharePoint 2007. Application pages that contain the DynamicMasterPageFile attribute will break compatibility as well.
     

When a build is carried out the two SPHive folders are merged and 2 separate WSP files are created. For details see the next section.

 

Post Build event

Although the WSPBuilder Extensions for Visual Studio are great, our projects don’t use them. Instead our main project’s Post Build event invokes either WSPBuilder manually to generate WSP files or carries out a deployment using XCOPY.

As part of this exercise we have made the following changes:

  1. Auto detect the installed version of SharePoint.
  2. Determine the location of the SharePoint root directory (12 or 14 Hive).
  3. Added support for generating both SharePoint 2007 and 2010 WSP files when doing a release build.
  4. Added the ability to carry out an XCOPY deployment of a merged version of the SPHive and SPHive_2010 folders..
     

A simplified copy of our PDF Converter’s post build event is included below. As all our development servers run the 64 bit version of Win2K8 or Win2K8R2 this script may not work on Win2K3 or 32 bit installations. Some long lines, especially those invoking WSPBuilder, have been wrapped and reformatted for readability. When copying this script please make sure that multi-line commands are all placed on a single line.

set useWSPBuilder=false
 
set gacutil="$(SolutionDir)..\..\SharedBinaries\GACUtil\gacutil.exe"
set wspbuilder="$(SolutionDir)..\..\SharedBinaries\WSPBuilder\wspbuilder.exe"
set SPHive_2010=$(ProjectDir)SPHive_2010
 
@echo ** Detect which version of SharePoint is installed.
set CommonProgramsFolder=%CommonProgramW6432%
set SharePointRoot=%CommonProgramsFolder%\Microsoft Shared\Web Server Extensions\14
set IsSP2010=true
if exist "%SharePointRoot%\bin\STSADM.EXE" goto endVersionDetection
        set SharePointRoot=%CommonProgramsFolder%\Microsoft Shared\Web Server Extensions\12
        set IsSP2010=false
        if exist "%SharePointRoot%\bin\STSADM.EXE" goto endVersionDetection
        echo ** SharePoint does not appear to be installed on this server.
        goto end
:endVersionDetection
 
echo Detected SharePoint Root: "%SharePointRoot%"
 
if $(ConfigurationName)==Debug goto debugMode
        @echo ** Not running in debug mode so enabling WSPBuilder
        set useWSPBuilder=true
:debugMode
 
@REM Do we want to build using XCopy or WSPBuilder?
if %useWSPBuilder%==false goto useXCopy
    @echo ** Build mode: WSP Builder
 
    @echo ** Remove files from the WSPBuilder GAC Directory
    del /F /Q "$(ProjectDir)GAC\*.*"
 
    @echo ** Move dependent DLLs to GAC directory to allow WSPBuilder to package them up
    move /y "$(TargetDir)Muhimbi.SharePoint.Common.dll" "$(ProjectDir)GAC"
    move /y "$(TargetDir)Muhimbi.SharePoint.Diagnostics.dll" "$(ProjectDir)GAC"
    move /y "$(TargetDir)Muhimbi.Licensing.Base.dll" "$(ProjectDir)GAC"
    move /y "$(TargetDir)Muhimbi.Licensing.Validator.dll" "$(ProjectDir)GAC"
    move /y "$(TargetDir)Muhimbi.Licensing.SharePoint.dll" "$(ProjectDir)GAC"
 
    @echo ** Building WSP for SharePoint 2007
    %wspbuilder% -BuildDDF True -BuildWSP true -ResetWebServer False -Outputpath 
        "$(ProjectDir)Solution" -12Path "$(ProjectDir)SPHive" -GACPath "$(ProjectDir)GAC" 
        -ProjectPath "$(ProjectDir)" -Cleanup True -Excludefiletypes "cmd,cs,scc" 
        -WSPName Muhimbi.PDFConverter.wsp -BuildSafeControls False
 
    @echo ** Building SPHive for SharePoint 2010
    set SPHive_Temp=$(ProjectDir)_SPHive_Temp
    rm -f -r "%SPHive_Temp%"
    md "%SPHive_Temp%"
    xcopy /E /Y "$(ProjectDir)SPHive" "%SPHive_Temp%"
    xcopy /E /Y "%SPHive_2010%" "%SPHive_Temp%"
 
    %wspbuilder% -BuildDDF True -BuildWSP true -ResetWebServer False -Outputpath 
        "$(ProjectDir)Solution" -12Path "%SPHive_Temp%" -GACPath "$(ProjectDir)GAC" 
        -ProjectPath "$(ProjectDir)" -Cleanup True -Excludefiletypes "cmd,cs,scc" 
        -WSPName Muhimbi.PDFConverter.SP2010.wsp -BuildSafeControls False
 
    @echo ** Cleaning up
    rm -f -r "%SPHive_Temp%"
    del /F /Q "$(ProjectDir)Solution\makecab.ddf"
    goto end
 
:useXCopy
    @echo ** Build mode: XCOPY
    xcopy /E /Y "$(ProjectDir)SPHive" "%SharePointRoot%"
 
    if %IsSP2010%==false goto skipSP2010Copy
        @echo Copying SharePoint 2010 specific files
        xcopy /E /Y "%SPHive_2010%" "%SharePointRoot%"
    :skipSP2010Copy
 
    @echo ** Installing GAC assemblies
    %gacutil% /if "$(TargetDir)Muhimbi.SharePoint.Common.dll"
    %gacutil% /if "$(TargetDir)Muhimbi.SharePoint.Diagnostics.dll"
    %gacutil% /if "$(TargetDir)Muhimbi.Licensing.Base.dll"
    %gacutil% /if "$(TargetDir)Muhimbi.Licensing.Validator.dll"
    %gacutil% /if "$(TargetDir)Muhimbi.Licensing.SharePoint.dll"
 
    @echo ** Installing $(OutDir)$(TargetFileName) into the GAC...
    %gacutil% /if "$(TargetPath)"
 
    echo ** Recycling App Pools
    cscript //NoLogo "$(SolutionDir)\RecycleAppPools.vbs"
 
:end
 
@echo ** rebuilding sitemaps and translations
"%SharePointRoot%\bin\STSADM.EXE" -o copyappbincontent

 

The vbscript file that we use to recycle the application pools at the end of the build process was not working on our SharePoint 2010 development machine for some reason. We are not sure if this is related to the fact that we migrated to Win2K8R2 (from Win2K8) as part of the SharePoint 2010 deployment process or if it is because the script was always broken. Basically it fell over if one of the Application Pools was not started.

The new script is now as follows:

Set oWebAdmin = GetObject("winmgmts:root\WebAdministration")
Set oAppPools = oWebAdmin.InstancesOf("ApplicationPool")
For Each oAppPool In oAppPools
    WScript.Echo "Recycling application pool: " & oAppPool.Name
    '** Only recycle pools that are currently started
    if oAppPool.GetState = 1 then
        oAppPool.Recycle    
    end if
Next

 

 

Continue to Part 3 – Programmatic / visual changes.

.






Labels: , , , , ,

Porting a SharePoint 2007 WSPBuilder solution to SharePoint 2010 – Part 1

Posted at: 17:01 on 11 March 2010 by Muhimbi

SharePoint2010 When we decided to make our popular PDF Converter for SharePoint compatible with SharePoint 2010, we had no idea what we were in for. Will it be a nightmare, will it just work, will we need to throw everything away?…. we simply didn’t know. Fortunately SharePoint 2010 is much like SharePoint 2007 and as a result we released the SharePoint 2010 compatible version earlier today.

As there is very little available information or guidance on this topic, we would like to share our experience with migrating SharePoint 2007 based WSPBuilder projects to SharePoint 2010 while maintaining a single code base that supports both environments.

This post, the first one in the series, describes what happened when we tried to install a SharePoint 2007 based solution on a machine running SharePoint 2010.

The following posts are part of this series:

Please note that this article is based on our experiences with the beta version of SharePoint 2010. Some of the issues we have identified may have been resolved in the final release.

     

Our goal

When we started the planning phase for the SharePoint 2010 migration, we decided that ideally we would end up with a single code base that can be used to build a single WSP file that is compatible with both SharePoint 2007 and 2010. To make the product easy to maintain we only want a single Visual Studio Solution and as little code duplication as possible. At the same time we wanted to leverage SharePoint 2010’s new facilities such as the Ribbon without breaking compatibility.

Although we had to give up our single WSP file dream, more on that later, we managed to achieve our other goals.

 

Installing a SharePoint 2007 based solution on SharePoint 2010

When we embarked on our migration project we briefly thought: what if it just works? That would have been great, but unfortunately we stumbled at the first hurdle, the installer.

The installer for the SharePoint WFE part of the solution is a simple cmd script that invokes stsadm directly. In SharePoint 2010 stsadm lives in a different directory from its SharePoint 2007 counterpart so our dream ended rather abruptly.

Once we had fixed the installer we stumbled onto a rather annoying problem that kept us busy for the better part of 2 days. Whenever we tried to deploy a WSP file (any WSP file it turned out) the process failed without any error messages. However, the following message was logged to the Windows Application Event log: Requested registry access is not allowed.

After consulting many sources, it turned out that if the first attempt to install SharePoint 2010 fails (ours did) then the privileges on the HKLM\SOFTWARE\Microsoft \Shared Tools\Web Server Extensions\14.0\Secure\FarmAdmin registry key are not configured during subsequent successful installations. After giving the Everyone group full control on this key it was finally possible to deploy WSP files. (I realise that giving Everyone access is not best practice, but it solved the problem on our development server).

For details about the changes we had to make to our deployment scripts see part 4 of this series.

 

Where are my menu options?

Once the wsp file was installed and deployed, the Convert to PDF option became visible in the ECB (file context menu), woohooo! Unfortunately, the link to our Central Administration based configuration screen was missing. Because the Central Administration screens have been restructured, the Locations recognised by SharePoint 2007 have no direct mapping to their SharePoint 2010 equivalents. It is a shame that SharePoint is not automatically remapping those Locations that have a direct equivalent in SharePoint 2010. After all, the External Service Connections Location is available in both versions.
 

SP2007-Central-AdminLink to the PDF Converter Settings screen in SharePoint 2007


For details about the changes we had to make in order to make the menus work, see part 3 of this series.

 

Why does it all look wrong?

The main problems we encountered during this entire exercise were related to the visual fidelity of our Application screens. We take great pride in the appearance of our solutions so we were disappointed to see that, even though we have gone through great lengths to only use those user interface elements and controls that ship with SharePoint, things didn’t look quite right.

The main visual problems we encountered are as follows:

  1. The vertical alignment of checkboxes is completely off.
  2. There is extra vertical spacing between the various user interface elements.
  3. Some elements, particularly buttons, have a different default width and therefore wrap over multiple lines.

 
The following screenshots provide a visual comparison between the SharePoint 2007 and 2010 versions before any changes were made.

SP2010-Before-Fixing  Original SharePoint 2007 interface and the SharePoint 2010 one before making any changes

 

This concludes the overview of identified problems.

Continue to Part 2 – Reconfiguring the Visual Studio Solution.

.






Labels: , , , , ,

Using the Muhimbi PDF Converter with SharePoint 2010

Posted at: 10:09 on by Muhimbi

SharePoint2010When we released the Office 2010 compatible version of the Muhimbi PDF Converter for SharePoint we were already planning, and worrying about, a SharePoint 2010 compatible version. Worrying indeed, call it fear of the unknown or fear of change, but we were anticipating a lot of work.

Fortunately we quickly found out that SharePoint 2010 is, surprise surprise, much like SharePoint 2007. If it wasn’t for an annoying SharePoint 2010 deployment bug that took us 2 days to solve, we could have completed all investigations and required changes in 2 or 3 days.

To cut a rather long story short, a beta version of the PDF Converter for SharePoint 2010 is now available. Please contact us if you want to participate in the beta program. Note that the download available on our website is the SharePoint 2007 version, which will not work with SharePoint 2010.

Although the beta is fully functional and integrates nicely with new SharePoint 2010 features such as the Ribbon, the documentation has not yet been updated. Fortunately, much - including the installation process - is the same. The only difference is the location of some of the administrative screens as highlighted in the screenshots below.

Over the next week we will release a number of blog posts describing our experience with porting a SharePoint 2007 WSPBuilder application to SharePoint 2010 and making the same code base work with both versions. If you are interested in this topic then make sure you subscribe to our RSS feed or follow us on Twitter.

 

Central-Admin-PDF-Settings Access the PDF Converter Configuration screen via General Application Settings

 

PDF-Converter-Doclib  The PDF Converter integrates with the new Ribbon bar

 

SP2010-License-Manager  After installing the License Manager, it can be opened from the System Settings screen

 

SharePoint-Designer The new version is compatible with SharePoint Designer 2010 workflows

.





Labels: , , , , ,

Subscribe to News feed