Friday, 17 February 2012

SPUser Class Example

Use the AllUsers property of the SPWeb class to return all the users of a site. This includes users granted permissions directly, users granted permissions through a group who have then visited the site, and users who have been referenced in a person field, such as being assigned a task. Calling AllUsers[name] will throw an exception if the user is not there.

Use the SiteUsers property of the SPWeb class to return all the users in the site collection.

Use the GetAllAuthenticatedUsers method of the SPUtility class to return all authenticated users of a site.

Use the GetUniqueUsers method of the SPAlertCollection class to return a list of users for a collection of alerts.

Otherwise, use the Users property of the SPGroup or SPWeb class to return the users in a group or site.

Use an indexer to return a single user from the collection. For example, if the collection is assigned to a variable named collUsers, use collUsers[index] in C#, or collUsers(index) in Visual Basic, where index is either the index number of the user in the collection or the user name of the user.

Every user has a unique member ID (ID property), has the permissions associated with that membership, and can be represented by an SPMember object. The following example assigns a user to an SPMember object, given a specified SharePoint Web site:





SPWeb oWebsite = SPContext.Current.Web;
SPMember oMember = oWebsite.AllUsers["Domain\\User_Alias"];





 

The following code example modifies the e-mail address, display name, and notes for a specified user object.






SPSite oSiteCollection = SPContext.Current.Site;
using (SPWeb oWebsite = oSiteCollection.AllWebs["Website_Name"])
{
SPUser oUser = oWebsite.AllUsers["User_Name"];

oUser.Email = " E-mail_Address";
oUser.Name = " Display_Name";
oUser.Notes = " User_Notes";

oUser.Update();
}




SPList Class Example

SPSite oSiteCollection = SPContext.Current.Site;
SPList oList = oSiteCollection.AllWebs["Site_Name"].Lists["List_Name"];

SPQuery oQuery = new SPQuery();
oQuery.Query = "<Where><Gt><FieldRef Name='ProjectedValue'/>" +
"<Value Type='Number'>500</Value></Gt></Where>";
SPListItemCollection collListItems = oList.GetItems(oQuery);

foreach (SPListItem oListItem in collListItems)
{
Label1.Text += "Item: " +
SPHttpUtility.HtmlEncode(oListItem["Title"].ToString()) +
"::" + "Value: " +
SPHttpUtility.HtmlEncode(oListItem["Investment"].ToString()) +
"::" + "Calculated: " +
SPHttpUtility.HtmlEncode(oListItem["ProjectedValue"].ToString()) +
"<BR>";
}
}

SharePoint 2010 Fields Validation

In SharePoint 2007 we have many issues with field validation when we add a new item to the list. So I solved it with JavaScript and I solved it with InfoPath Forms but it has never been easy.

In SharePoint 2010 we have great tools to perform field validation on the page without spending hours.

The first option is very intuitive. You can set the basic validation using the usual calculated fields functions during the creation of your field. You can use Excel functions just like I used it in the picture below to find out if some text appears in the field value or not. You can also build any other validation you want.



The User description field will show your users explanation about the content you want to see in the field. It will appear in red text under your field, like the example below.



Here are a few examples of field validation functions:

E-mail validation:

=AND((FIND(“@”,[E-mail filed])<>0),( FIND(“.”,[E-mail filed])<>0))

The AND function allows you to check more than one condition.

Today date validation:

=[Date field]>Today()

The second way to perform field validation is to edit the list forms using the InfoPath application.

This gives you really powerful abilities to edit your list forms. If you choose this method you’ll have much more options to design and validate your fields. For example: you can change the color of your field if it has not passed validation.

So, how do we do it?

Enter your list and click the “Customize Form” link. This link will open the InfoPath application (so make sure InfoPath is installed at your machine first). This button will design the “New”, “Edit” and “Design” forms but you will need to design it only once.



When the InfoPath form is opened you will see all the fields of your list at the left side of the window and you can add these fields to the form.

Let’s create some validation! Choose the field you want to validate and click the “Add Rule” button on the Ribbon.



Then click the “New” button and create a new Validation rule. You can also add a formatting rule to change the design of your fields that based on similar conditions. This is the “Conditional Formatting”, just a little redesigned.

Add a new validation rule. Click on “None” link under the “Condition” title to create new conditions.







Now it’s time to publish the form back to our SharePoint list. This is very easy to do. Just press the “File” option in the Ribbon and press the big “Quick Publish” button. That’s all. Now you have this validation in your SharePoint list forms.



Convert Office Documents (.docx, .pptx, .pub) into PDF Programmatically

Word

========

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Office.Interop.Word;

 

namespace DocConverter

{

class Program

{

static void Main(string[] args)

{

ApplicationClass wordApplication = new ApplicationClass();

 

Document wordDocument = null;

object paramSourceDocPath = @"D:\Fun\Test.docx";

object paramMissing = Type.Missing;

string paramExportFilePath = @"D:\Fun\Test.pdf";

WdExportFormat paramExportFormat = WdExportFormat.wdExportFormatPDF;

bool paramOpenAfterExport = false;

WdExportOptimizeFor paramExportOptimizeFor =

WdExportOptimizeFor.wdExportOptimizeForPrint;

WdExportRange paramExportRange = WdExportRange.wdExportAllDocument;

int paramStartPage = 0;

int paramEndPage = 0;

WdExportItem paramExportItem = WdExportItem.wdExportDocumentContent;

bool paramIncludeDocProps = true;

bool paramKeepIRM = true;

WdExportCreateBookmarks paramCreateBookmarks =

WdExportCreateBookmarks.wdExportCreateWordBookmarks;

bool paramDocStructureTags = true;

bool paramBitmapMissingFonts = true;

bool paramUseISO19005_1 = false;

try

{

// Open the source document.

wordDocument = wordApplication.Documents.Open(

ref paramSourceDocPath, ref paramMissing, ref paramMissing,

ref paramMissing, ref paramMissing, ref paramMissing,

ref paramMissing, ref paramMissing, ref paramMissing,

ref paramMissing, ref paramMissing, ref paramMissing,

ref paramMissing, ref paramMissing, ref paramMissing,

ref paramMissing);

 

// Export it in the specified format.

if (wordDocument != null)

wordDocument.ExportAsFixedFormat(paramExportFilePath,

paramExportFormat, paramOpenAfterExport,

paramExportOptimizeFor, paramExportRange, paramStartPage,

paramEndPage, paramExportItem, paramIncludeDocProps,

paramKeepIRM, paramCreateBookmarks, paramDocStructureTags,

paramBitmapMissingFonts, paramUseISO19005_1,

ref paramMissing);

}

catch (Exception ex)

{

// Respond to the error

}

finally

{

// Close and release the Document object.

if (wordDocument != null)

{

wordDocument.Close(ref paramMissing, ref paramMissing,

ref paramMissing);

wordDocument = null;

}

 

// Quit Word and release the ApplicationClass object.

if (wordApplication != null)

{

wordApplication.Quit(ref paramMissing, ref paramMissing,

ref paramMissing);

wordApplication = null;

}

 

GC.Collect();

GC.WaitForPendingFinalizers();

GC.Collect();

GC.WaitForPendingFinalizers();

}

 

}

}

}

 

PowerPoint

=============

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Office.Interop.PowerPoint;

namespace PowerPointConverter

{

class Program

{

static void Main(string[] args)

{

Microsoft.Office.Interop.PowerPoint.Application ppApp = new Microsoft.Office.Interop.PowerPoint.Application();

Microsoft.Office.Interop.PowerPoint.Presentation presentation = ppApp.Presentations.Open(@"D:\Fun\Test.pptx", Microsoft.Office.Core.MsoTriState.msoTrue, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoFalse);

presentation.ExportAsFixedFormat(@"D:\Fun\Test.xps",

PpFixedFormatType.ppFixedFormatTypeXPS,

PpFixedFormatIntent.ppFixedFormatIntentPrint,

Microsoft.Office.Core.MsoTriState.msoFalse,

PpPrintHandoutOrder.ppPrintHandoutHorizontalFirst,

PpPrintOutputType.ppPrintOutputSlides,

Microsoft.Office.Core.MsoTriState.msoFalse,

null,

PpPrintRangeType.ppPrintAll,

"",

false,

false,

false,

true,

true,

System.Reflection.Missing.Value);

presentation.Close();

presentation = null;

ppApp = null;

GC.Collect();

 

}

}

}

 

Publisher
===============

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Office.Interop.Publisher;

using System.Reflection;

 

namespace PublisherConverter

{

class Program

{

static void Main(string[] args)

{

Microsoft.Office.Interop.Publisher.Application pbApp = new Microsoft.Office.Interop.Publisher.Application();

Microsoft.Office.Interop.Publisher.Document pbDoc = pbApp.Open(@"D:\Fun\Test4.pub", true, true, PbSaveOptions.pbDoNotSaveChanges);

pbDoc.ExportAsFixedFormat(PbFixedFormatType.pbFixedFormatTypePDF, @"D:\Fun\Test.pdf", PbFixedFormatIntent.pbIntentPrinting, true, 300, 450, 1200, 1800, 1, pbDoc.Pages.Count, 1, false, PbPrintStyle.pbPrintStyleDefault, false, false, false, System.Reflection.Missing.Value);

pbDoc.Close();

pbDoc = null;

pbApp = null;

GC.Collect();

}

}

}

Developing with SharePoint 2010 Word Automation Services

To build the application




  1. Start Microsoft Visual Studio 2010.

  2. On the File menu, point to New, and then click Project.

  3. In the New Project dialog box, in the Recent Template pane, expand Visual C#, and then click Windows.

  4. To the right side of the Recent Template pane, click Console Application.

  5. By default, Visual Studio creates a project that targets .NET Framework 4. However, you must target .NET Framework 3.5. From the list at the upper part of the File Open dialog box, select .NET Framework 3.5.

  6. In the Name box, type the name that you want to use for your project, such as FirstWordAutomationServicesApplication.

  7. In the Location box, type the location where you want to place the project.

    Figure 1. Creating a solution in the New Project dialog box

    Creating solution in the New Project box

  8. Click OK to create the solution.

  9. By default, Visual Studio 2010 creates projects that target x86 CPUs, but to build SharePoint Server applications, you must target any CPU.

  10. If you are building a Microsoft Visual C# application, in Solution Explorer window, right-click the project, and then click Properties.

  11. In the project properties window, click Build.

  12. Point to the Platform Target list, and select Any CPU.

    Figure 2. Target Any CPU when building a C# console application

    Changing target to any CPU

  13. If you are building a Microsoft Visual Basic .NET Framework application, in the project properties window, click Compile.

    Figure 3. Compile options for a Visual Basic application

    Compile options for Visual Basic applications

  14. Click Advanced Compile Options.

    Figure 4. Advanced Compiler Settings dialog box

    Advanced Compiler Settings dialog box

  15. Point to the Platform Target list, and then click Any CPU.

  16. To add a reference to the Microsoft.Office.Word.Server assembly, on the Project menu, click Add Reference to open the Add Reference dialog box.

  17. Select the .NET tab, and add the component named Microsoft Office 2010 component.

    Figure 5. Adding a reference to Microsoft Office 2010 component

    Add reference to Microsoft Office 2010 component

  18. Next, add a reference to the Microsoft.SharePoint assembly.

    Figure 6. Adding a reference to Microsoft SharePoint

    Adding reference to Microsoft SharePoint



The following examples provide the complete C# and Visual Basic listings for the simplest Word Automation Services application.






using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.Office.Word.Server.Conversions;

class Program
{
static void Main(string[] args)
{
string siteUrl = "http://localhost";
// If you manually installed Word automation services, then replace the name
// in the following line with the name that you assigned to the service when
// you installed it.
string wordAutomationServiceName = "Word Automation Services";
using (SPSite spSite = new SPSite(siteUrl))
{
ConversionJob job = new ConversionJob(wordAutomationServiceName);
job.UserToken = spSite.UserToken;
job.Settings.UpdateFields = true;
job.Settings.OutputFormat = SaveFormat.PDF;
job.AddFile(siteUrl + "/Shared%20Documents/Test.docx",
siteUrl + "/Shared%20Documents/Test.pdf");
job.Start();
}
}
}





To build and run the example




  1. Add a Word document named Test.docx to the Shared Documents folder in the SharePoint site.

  2. Build and run the example.

  3. After waiting one minute for the conversion process to run, navigate to the Shared Documents folder in the SharePoint site, and refresh the page. The document library now contains a new PDF document, Test.pdf.



Monitoring Conversion Status




In many scenarios, you want to monitor the status of conversions, to inform the user when the conversion process is complete, or to process the converted documents in additional ways. You can use the ConversionJobStatus class to query Word Automation Services about the status of a conversion job. You pass the name of the WordServiceApplicationProxy class as a string (by default, "Word Automation Services"), and the conversion job identifier, which you can get from the ConversionJob object. You can also pass a GUID that specifies a tenant partition. However, if the SharePoint Server farm is not configured for multiple tenants, you can pass null (Nothing in Visual Basic) as the argument for this parameter.

After you instantiate a ConversionJobStatus object, you can access several properties that indicate the status of the conversion job. The following are the three most interesting properties.

ConversionJobStatus Properties





















PropertyReturn Value
CountNumber of documents currently in the conversion job.
SucceededNumber of documents successfully converted.
FailedThe number of documents that failed conversion.

Whereas the first example specified a single document to convert, the following example converts all documents in a specified document library. You have the option of creating all converted documents in a different document library than the source library, but for simplicity, the following example specifies the same document library for both the input and output document libraries. In addition, the following example specifies that the conversion job should overwrite the output document if it already exists.






Console.WriteLine("Starting conversion job");
ConversionJob job = new ConversionJob(wordAutomationServiceName);
job.UserToken = spSite.UserToken;
job.Settings.UpdateFields = true;
job.Settings.OutputFormat = SaveFormat.PDF;
job.Settings.OutputSaveBehavior = SaveBehavior.AlwaysOverwrite;
SPList listToConvert = spSite.RootWeb.Lists["Shared Documents"];
job.AddLibrary(listToConvert, listToConvert);
job.Start();
Console.WriteLine("Conversion job started");
ConversionJobStatus status = new ConversionJobStatus(wordAutomationServiceName,
job.JobId, null);
Console.WriteLine("Number of documents in conversion job: {0}", status.Count);
while (true)
{
Thread.Sleep(5000);
status = new ConversionJobStatus(wordAutomationServiceName, job.JobId,
null);
if (status.Count == status.Succeeded + status.Failed)
{
Console.WriteLine("Completed, Successful: {0}, Failed: {1}",
status.Succeeded, status.Failed);
break;
}
Console.WriteLine("In progress, Successful: {0}, Failed: {1}",
status.Succeeded, status.Failed);
}





To run this example, add some WordprocessingML documents in the Shared Documents library. When you run this example, you see output similar to this code snippet,





Starting conversion job
Conversion job started
Number of documents in conversion job: 4
In progress, Successful: 0, Failed: 0
In progress, Successful: 0, Failed: 0
Completed, Successful: 4, Failed: 0






Identifying Documents That Failed to Convert




You may want to determine which documents failed conversion, perhaps to inform the user, or take remedial action such as removing the invalid document from the input document library. You can call the GetItems method, which returns a collection of ConversionItemInfo objects. When you call the GetItems method, you pass a parameter that specifies whether you want to retrieve a collection of failed conversions or successful conversions.

C#





Console.WriteLine("Starting conversion job");
ConversionJob job = new ConversionJob(wordAutomationServiceName);
job.UserToken = spSite.UserToken;
job.Settings.UpdateFields = true;
job.Settings.OutputFormat = SaveFormat.PDF;
job.Settings.OutputSaveBehavior = SaveBehavior.AlwaysOverwrite;
SPList listToConvert = spSite.RootWeb.Lists["Shared Documents"];
job.AddLibrary(listToConvert, listToConvert);
job.Start();
Console.WriteLine("Conversion job started");
ConversionJobStatus status = new ConversionJobStatus(wordAutomationServiceName,
job.JobId, null);
Console.WriteLine("Number of documents in conversion job: {0}", status.Count);
while (true)
{
Thread.Sleep(5000);
status = new ConversionJobStatus(wordAutomationServiceName, job.JobId, null);
if (status.Count == status.Succeeded + status.Failed)
{
Console.WriteLine("Completed, Successful: {0}, Failed: {1}",
status.Succeeded, status.Failed);
ReadOnlyCollection<ConversionItemInfo> failedItems =
status.GetItems(ItemTypes.Failed);
foreach (var failedItem in failedItems)
Console.WriteLine("Failed item: Name:{0}", failedItem.InputFile);
break;
}
Console.WriteLine("In progress, Successful: {0}, Failed: {1}", status.Succeeded,
status.Failed);
}





To run this example, create an invalid document and upload it to the document library. An easy way to create an invalid document is to rename the WordprocessingML document, appending .zip to the file name. Then delete the main document part (known as document.xml), which is in the Word folder of the package. Rename the document, removing the .zip extension so that it contains the normal .docx extension.

When you run this example, it produces output similar to the following.






Starting conversion job
Conversion job started
Number of documents in conversion job: 5
In progress, Successful: 0, Failed: 0
In progress, Successful: 0, Failed: 0
In progress, Successful: 4, Failed: 0
In progress, Successful: 4, Failed: 0
In progress, Successful: 4, Failed: 0
Completed, Successful: 4, Failed: 1
Failed item: Name:http://intranet.contoso.com/Shared%20Documents/IntentionallyInvalidDocument.docx





Another approach to monitoring a conversion process is to use event handlers on a SharePoint list to determine when a converted document is added to the output document library.


Deleting Source Files after Conversion









Console.WriteLine("Starting conversion job");
ConversionJob job = new ConversionJob(wordAutomationServiceName);
job.UserToken = spSite.UserToken;
job.Settings.UpdateFields = true;
job.Settings.OutputFormat = SaveFormat.PDF;
job.Settings.OutputSaveBehavior = SaveBehavior.AlwaysOverwrite;
SPFolder folderToConvert = spSite.RootWeb.GetFolder("Shared Documents");
job.AddFolder(folderToConvert, folderToConvert, false);
job.Start();
Console.WriteLine("Conversion job started");
ConversionJobStatus status = new ConversionJobStatus(wordAutomationServiceName,
job.JobId, null);
Console.WriteLine("Number of documents in conversion job: {0}", status.Count);
while (true)
{
Thread.Sleep(5000);
status = new ConversionJobStatus(wordAutomationServiceName, job.JobId, null);
if (status.Count == status.Succeeded + status.Failed)
{
Console.WriteLine("Completed, Successful: {0}, Failed: {1}",
status.Succeeded, status.Failed);
Console.WriteLine("Deleting only items that successfully converted");
ReadOnlyCollection<ConversionItemInfo> convertedItems =
status.GetItems(ItemTypes.Succeeded);
foreach (var convertedItem in convertedItems)
{
Console.WriteLine("Deleting item: Name:{0}", convertedItem.InputFile);
folderToConvert.Files.Delete(convertedItem.InputFile);
}
break;
}
Console.WriteLine("In progress, Successful: {0}, Failed: {1}",
status.Succeeded, status.Failed);
}











Console.WriteLine("Starting conversion job")
Dim job As ConversionJob = New ConversionJob(wordAutomationServiceName)
job.UserToken = spSite.UserToken
job.Settings.UpdateFields = True
job.Settings.OutputFormat = SaveFormat.PDF
job.Settings.OutputSaveBehavior = SaveBehavior.AlwaysOverwrite
Dim folderToConvert As SPFolder = spSite.RootWeb.GetFolder("Shared Documents")
job.AddFolder(folderToConvert, folderToConvert, False)
job.Start()
Console.WriteLine("Conversion job started")
Dim status As ConversionJobStatus = _
New ConversionJobStatus(wordAutomationServiceName, job.JobId, Nothing)
Console.WriteLine("Number of documents in conversion job: {0}", status.Count)
While True
Thread.Sleep(5000)
status = New ConversionJobStatus(wordAutomationServiceName, job.JobId, _
Nothing)
If status.Count = status.Succeeded + status.Failed Then
Console.WriteLine("Completed, Successful: {0}, Failed: {1}", _
status.Succeeded, status.Failed)
Console.WriteLine("Deleting only items that successfully converted")
Dim convertedItems As ReadOnlyCollection(Of ConversionItemInfo) = _
status.GetItems(ItemTypes.Succeeded)
For Each convertedItem In convertedItems
Console.WriteLine("Deleting item: Name:{0}", convertedItem.InputFile)
folderToConvert.Files.Delete(convertedItem.InputFile)
Next
Exit While
End If
Console.WriteLine("In progress, Successful: {0}, Failed: {1}",
status.Succeeded, status.Failed)
End While





 


Integrating with the Open XML SDK




The power of using Word Automation Services becomes clear when you use it in combination with the Welcome to the Open XML SDK 2.0 for Microsoft Office. You can programmatically modify a document in a document library by using the Welcome to the Open XML SDK 2.0 for Microsoft Office, and then use Word Automation Services to perform one of the difficult tasks by using the Open XML SDK. A common need is to programmatically generate a document, and then generate or update the table of contents of the document. Consider the following document, which contains a table of contents.

Figure 7. Document with a table of contents

Document with table of contentsLet’s assume you want to modify this document, adding content that should be included in the table of contents. This next example takes the following steps.

  1. Opens the site and retrieves the Test.docx document by using a Collaborative Application Markup Language (CAML) query.

  2. Opens the document by using the Open XML SDK 2.0, and adds a new paragraph styled as Heading 1 at the beginning of the document.

  3. Starts a conversion job, converting Test.docx to TestWithNewToc.docx. It waits for the conversion to complete, and reports whether it was converted successfully.




C#
Console.WriteLine("Querying for Test.docx");
SPList list = spSite.RootWeb.Lists["Shared Documents"];
SPQuery query = new SPQuery();
query.ViewFields = @"<FieldRef Name='FileLeafRef' />";
query.Query =
@"<Where>
<Eq>
<FieldRef Name='FileLeafRef' />
<Value Type='Text'>Test.docx</Value>
</Eq>
</Where>";
SPListItemCollection collection = list.GetItems(query);
if (collection.Count != 1)
{
Console.WriteLine("Test.docx not found");
Environment.Exit(0);
}
Console.WriteLine("Opening");
SPFile file = collection[0].File;
byte[] byteArray = file.OpenBinary();
using (MemoryStream memStr = new MemoryStream())
{
memStr.Write(byteArray, 0, byteArray.Length);
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Open(memStr, true))
{
Document document = wordDoc.MainDocumentPart.Document;
Paragraph firstParagraph = document.Body.Elements<Paragraph>()
.FirstOrDefault();
if (firstParagraph != null)
{
Paragraph newParagraph = new Paragraph(
new ParagraphProperties(
new ParagraphStyleId() { Val = "Heading1" }),
new Run(
new Text("About the Author")));
Paragraph aboutAuthorParagraph = new Paragraph(
new Run(
new Text("Eric White")));
firstParagraph.Parent.InsertBefore(newParagraph, firstParagraph);
firstParagraph.Parent.InsertBefore(aboutAuthorParagraph,
firstParagraph);
}
}
Console.WriteLine("Saving");
string linkFileName = file.Item["LinkFilename"] as string;
file.ParentFolder.Files.Add(linkFileName, memStr, true);
}
Console.WriteLine("Starting conversion job");
ConversionJob job = new ConversionJob(wordAutomationServiceName);
job.UserToken = spSite.UserToken;
job.Settings.UpdateFields = true;
job.Settings.OutputFormat = SaveFormat.Document;
job.AddFile(siteUrl + "/Shared%20Documents/Test.docx",
siteUrl + "/Shared%20Documents/TestWithNewToc.docx");
job.Start();
Console.WriteLine("After starting conversion job");
while (true)
{
Thread.Sleep(5000);
Console.WriteLine("Polling...");
ConversionJobStatus status = new ConversionJobStatus(
wordAutomationServiceName, job.JobId, null);
if (status.Count == status.Succeeded + status.Failed)
{
Console.WriteLine("Completed, Successful: {0}, Failed: {1}",
status.Succeeded, status.Failed);
break;
}
}




After running this example with a document similar to the one used earlier in this section, a new document is produced, as shown in Figure 8.

Figure 8. Document with updated table of contents

Document with updated table of contents

Conclusion




The Open XML SDK 2.0 is a powerful tool for building server-side document generation and document processing systems. However, there are aspects of document manipulation that are difficult, such a document conversions, and updating of fields, table of contents, and more. Word Automation Services fills this gap with a high-performance solution that can scale out to your requirements. Using the Open XML SDK 2.0 in combination with Word Automation Services enables many scenarios that are difficult when using only the Open XML SDK 2.0.

MS Word Office Automation - Filling Text Form Fields And Check Box Form Fields And Mail Merge

Using MailMerge:

(Insert - > Quick Parts -> Field -> Mail merge -> Merge field) First name: «firstName» Last name: «lastName»

Using Bookmarks:

( Insert -> BookMark) First name: (<- the bookmark is here, it’s not visible) Last name: And the code is following:

  1. Using bookmarks
        Open("D:/Doc1.doc");
        if (oDoc.Bookmarks.Exists("bkmFirstName"))
        {
            object oBookMark = "bkmFirstName";
            oDoc.Bookmarks.get_Item(ref oBookMark).Range.Text = textBox1.Text;
        }



    if (oDoc.Bookmarks.Exists("bkmLastName"))
    {
        object oBookMark = "bkmLastName";
        oDoc.Bookmarks.get_Item(ref oBookMark).Range.Text = textBox2.Text;
    }


    SaveAs("D:/Test/Doc2.doc"); Quit();
    MessageBox.Show("The file is successfully saved!");





  2. Using MailMerge
        Open("D:/Doc1.doc");
        foreach (Field myMergeField in oDoc.Fields)
        {
            //iTotalFields++;
            Range rngFieldCode = myMergeField.Code;
            String fieldText = rngFieldCode.Text;



        // GET only MAILMERGE fields
        if (fieldText.StartsWith(" MERGEFIELD"))
        {
            Int32 endMerge = fieldText.IndexOf("\\");
            Int32 fieldNameLength = fieldText.Length - endMerge;
            String fieldName = fieldText.Substring(11, endMerge - 11);


            fieldName = fieldName.Trim();
            if (fieldName == "firstName")
            {
                myMergeField.Select();
                oWordApplic.Selection.TypeText("This Text Replaces the Field in the Template");
            }
        }
    }
    SaveAs("D:/Test/Doc2.doc"); Quit();
    MessageBox.Show("The file is successfully saved!");






Other Method:
    ApplicationClass oWordApplic = new Microsoft.Office.Interop.Word.ApplicationClass();
    private Microsoft.Office.Interop.Word.Document oDoc = new Document();

    public void Open(string strFileName)
    {
        object fileName = strFileName;
        object readOnly = false;
        object isVisible = true;
        object missing = System.Reflection.Missing.Value;

        oDoc = oWordApplic.Documents.Open(ref fileName, ref missing, ref readOnly,
        ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref isVisible, ref missing, ref missing, ref missing, ref missing);

        oDoc.Activate();
    }

    public void SaveAs(string strFileName)
    {
        object missing = System.Reflection.Missing.Value;
        object fileName = strFileName;

        oDoc.SaveAs(ref fileName, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);
    }

    public void Quit()
    {
        object missing = System.Reflection.Missing.Value;
        oWordApplic.Application.Quit(ref missing, ref missing, ref missing);
    }

 

Introduction to Programming in InfoPath 2010

Imagine a scenario where the accounting department at Contoso, Inc. tracks corporate assets through an Asset Management System built on SharePoint. One module in the system allows employees to order office equipment such as laptops, conference phones, and ergonomic chairs through an InfoPath form. At first, the order form was built using only declarative logic. It could enforce required fields, surface validation messages, and submit the form to SharePoint without any code.

As the ordering process grew more complex, users started adding additional requirements to the system. The variety of inventory available to employees increased, so they wanted a way sort items by name and description real-time in the order form. Contoso also started shipping their office equipment out of three warehouses. This prevented the warehouse crew from fulfilling complete orders, and as such, the system needed to track shipping status and quantity of individual items in the order. To meet the new requirements, Contoso added the following features to the form:

  • A custom sort interface

  • Logic for managing complex data when the form is submitted

  • Logic to add items to a SharePoint list


Sort interfaces, sophisticated submit routines, and database (i.e. list) management are common requirements for forms. Fortunately, this functionality can be added to InfoPath forms with a few lines of code. Let me explain these features, the code required to build them, and the prerequisites for developing managed code in InfoPath in more detail.

Equipment Request Form


The employee orders module in the Asset Management system consists of three core components:

  1. A SharePoint form library, “Equipment Orders”, where users go to fill out the Equipment Order Request form shown below.

  2. A SharePoint list, “Equipment Inventory”, which stores the items available for users to order. This list contains fields specifying items’ names, descriptions, and quantities used to populate the Equipment Order Request form.

  3. A SharePoint list, “Equipment Shipping”, which stores a list of items ordered by users that have been scheduled for shipping. This list contains fields for the names and quantities of items being ordered as well as the name of the user who placed the order.


The Equipment Request Form enables users to sort through Contoso’s available inventory and submit a request to the warehouse for shipping.

Equipment Order Request Form

The order form is a repeating table, where each row in the table represents the name, description, and quantity of the item being ordered.

Equipment Order Request Form

Sorting data in the form


The Equipment Order Form has a Picture Button Control displaying an arrow next to each of the column labels.

Equipment Order Request Form

The buttons are used to sort the order items in ascending order by the respective column. When the user clicks the button, the values in the selected column are compared, and the rows of data are sorted based on the comparison result.

The sorting routine in this example is based on a complete solution provided by Hagen Green. Read through his post to learn how to provide a descending sort which also takes localization and data types into consideration.
private string GetValue(string xpath)

{

  // return the value of the specified node

  XPathNavigator myNav = this.MainDataSource.CreateNavigator().SelectSingleNode(xpath, NamespaceManager);

  if (myNav != null)

    return myNav.Value;

  else

    return "";

}

private void Swap(string xpath1, string xpath2)

{

  // swap two rows of the table

  XPathNavigator item1 = this.MainDataSource.CreateNavigator().SelectSingleNode(xpath1, NamespaceManager);

  XPathNavigator item2 = this.MainDataSource.CreateNavigator().SelectSingleNode(xpath2, NamespaceManager);

  if (item1 != null && item2 != null)

  {

    // Make a copy of item1

    // Move item2 to item1

    // Make the original item2 be item1 that we cloned earlier


    XPathNavigator item1Clone = item1.Clone();

    item1.ReplaceSelf(item2);

    item2.ReplaceSelf(item1Clone);

  }

}

private void SortOrder(string sortBy)

{

  string itemsToSort = "/my:myFields/my:Order/my:OrderItem";

  XPathNodeIterator items = this.MainDataSource.CreateNavigator().Select(itemsToSort, NamespaceManager);

  if (items != null)

  {

    int numItems = items.Count;

    // basic bubble sort implementation

    for (int i = 1; i < numItems; i++) // xpath is 1-based

    {

      for (int j = i + 1; j <= numItems; j++)

      {

        // swap (i,j) if necessary

        string iValue = GetValue(itemsToSort + "[" + i + "]" + sortBy);

        string jValue = GetValue(itemsToSort + "[" + j + "]" + sortBy);

        if (String.Compare(iValue, jValue, true) > 0)

          Swap(itemsToSort + "[" + i + "]", itemsToSort + "[" + j + "]");


      }

    }

  }

}

public void ItemNameSort_Clicked(object sender, ClickedEventArgs e)

{

  // Sort order by ItemName

  // Repeat this code for the other buttons

  string sortBy = "/my:ItemName";

  SortOrder(sortBy);

}

Managing complex data during submit and updating SharePoint lists using the SharePoint object model


The user is eventually going to finish selecting items and submit the order. Each item ordered through the form is handled independently because, for example, an item in the order may be delayed or shipped from a remote warehouse. So, we need submit logic which will break up the complex data (i.e. the repeating table of items being ordered) into individual rows, and add a shipping request to the Equipment Shipping list for each item-quantity pair. After an item is added to the Equipment Shipping list, a SharePoint workflow is used to track status and manage the Inventory Equipment list’s quantity values.

  1. The first thing you’ll need to do is use the Submit Options button on the Data tab in the ribbon to add a custom submit handler to your VSTA project.Submit Options

  2. Add a reference to Microsoft.SharePoint.dll to your VSTA project. This will allow you to develop code using the SharePoint object model. This DLL is installed in %CommonProgramFiles%\Microsoft Shared\Web Server Extensions\14\ISAPI with your licensed copy of Microsoft SharePoint Server.

  3. Add custom submit logic to create a SharePoint list item for each item in the order form. See the code below for an example, and notice the use of the ServerInfo class. The ServerInfo class is new to InfoPath 2010 and allows you to write portable code with relative references to SharePoint server URLs.




public void FormEvents_Submit(object sender, SubmitEventArgs e)

{

  // Loop through each item-quantity pair in the order form.

  // Submit pairs to the Equipment Shipping list.

  // Note: Workflow will handle updating item quantities and track shipping status.

  using (SPSite mySite = new SPSite(ServerInfo.SharePointSiteUrl.ToString()))

  {

    using (SPWeb myWeb = mySite.OpenWeb())

    {

      XPathNodeIterator orderItems;

      if (myWeb != null && myWeb.Lists["Equipment Shipping"] != null)

      {

        SPList shippingList = myWeb.Lists["Equipment Shipping"];

        myWeb.AllowUnsafeUpdates = true;

        orderItems = this.MainDataSource.CreateNavigator().Select("/my:myFields/my:Order/my:OrderItem", NamespaceManager);

        if (orderItems != null)

        {

          while (orderItems.MoveNext())

          {

            // Add rows from the form where user selected an item and specified a quantity.

            string itemName = orderItems.Current.SelectSingleNode("./my:ItemName", NamespaceManager).Value;

            string itemQuantity = orderItems.Current.SelectSingleNode("./my:ItemQuantity", NamespaceManager).Value;

            if (itemName != string.Empty && itemQuantity != string.Empty)

            {

              SPListItem shipItem = shippingList.AddItem();

              shipItem["Title"] = itemName;

              shipItem["Quantity"] = itemQuantity;

              shipItem.Update();

            }

          }

        }

      //cleanup

      //signal successful submit

      //return

      myWeb.AllowUnsafeUpdates = false;

      e.CancelableArgs.Cancel = false;

      return;

      }

    }

  }

}

Along with the features covered above, you’ll find that code is useful for implementing complex data validation logic and managing content from multiple data sources. Such requirements are especially common when your forms are part of an advanced application. You can learn more about validation and working with the InfoPath DOM in our MSDN XmlEvent.Validating documentation and our post on working with InfoPath data sources programmatically. You can also review the InfoPath and SharePoint object models on MSDN for a more granular view into programming with InfoPath 2010.

If you’d like to get started with programming in InfoPath, then please read on. The rest of this post introduces our system requirements, integrated development environment, and programmability user experience.

How to add code to an InfoPath form


To add code to an InfoPath form:

  1. Make sure you meet the minimum system requirements.

  2. Install Visual Studio Tools for Applications (VSTA).

  3. Choose a programming language.

  4. Add event handlers and code.


Minimum system requirements


The minimum system requirement to get started with InfoPath 2010 development is Microsoft .NET Framework 2.0, but we suggest you install Microsoft .NET Framework 3.5 SP1 if you’re developing for the SharePoint platform. You can install all versions of Microsoft .NET Framework from http://www.microsoft.com/downloads.

Installing Visual Studio Tools for Applications


Visual Studio Tools for Applications (VSTA) is an optional installation component available in Microsoft Office 2010 setup. To install VSTA:

  1. Launch Office 2010 setup from your Office 2010 installation media or from the Control Panel Programs and Features application.

  2. If you’re installing a new copy of Office 2010, click the Customize button in the installer. If you’ve already installed Office 2010, choose the Add or Remove Features radio button in the installer.

  3. Set the Visual Studio Tools for Applications option to Run from My Computer and continue through the setup wizard.


Office Setup

Choosing a programming language


InfoPath 2010 allows you to program in C# and Visual Basic .NET. If you want to program with Visual Basic, you do not need to do anything to select your programming language when designing InfoPath 2010 compatible forms. If you plan on programming with C#, or adding code to InfoPath 2007/2003 compatible forms, you can change the programming language by clicking the Language button in the Code group of the Developer tab.

Developer Tab

After you click the Language button, you can change your programming language by using the Form template code language drop down:

Form Options

Hint: You can change the default language for InfoPath 2010 compatible forms by using the Options menu in the Backstage.

  1. Click the File > Options tab

  2. Click the More Options button in the General category of the InfoPath Options dialog

  3. Change the Programming language dropdowns in the Programming Defaults section of the Design Options


Options

Adding event handlers


The Developer tab is the primary entry point for programming in InfoPath 2010. It’s designed to help you add event handlers compatible with the controls and mode of the form you are designing. For example, if you don’t have a control selected on your form view, then you’ll only be able to select the events that apply to the entire form. Notice that the Loading and View Switched event below are enabled, but the entire Control Events group is disabled.

Developer Tab

But, as soon as I select a text box on the form, the Control Events group lights up.

Developer Tab

Notice that the Sign, Context Changed, and Changing events are disabled in both screenshots of the Developer tab. That’s because I’m working with a browser compatible form, and those events are only available for InfoPath Filler forms.

Note: You’ll find a table of all events, and their compatibility, towards the end of this section.

Certain control and form programming events can be accessed through buttons on other tabs in the ribbon. If you add a Picture Button control on the form view, highlight the button, and then click on the Properties tab then you’ll find the Custom Code button enabled. Clicking the Custom Code button in the ribbon will add an OnClick event for the Picture Button control.

Custom Code Button

In the Equipment Order Request form, we added a Submit event handler to add items to a SharePoint list. To do this, navigate to the Data tab, and click the Submit Options button in the Submit Form group.

Submit Options Button

This will launch the Submit Options dialog where you can check “Allow users to submit this form”, “Perform custom action using Code”, and then click the Edit Code button.

Submit Options

The Fields task pane is another main entry point to add event handlers. In the next screenshot, I access the Validating and Changed events for ItemDescription by right clicking the field in the Fields task pane and scrolling through its context menu.

Fields Taskpane

The following tables provide a list of all events and compatibility in InfoPath 2010. Note that InfoPath forms trigger three types of events: Form Events, Data Events, and Button Events. Aside from the button event, InfoPath event handling is different from other web programming paradigms (e.g. WinForm and HTML forms); events are fired when the data changes, not when control state changes. As such, you should consider the optimal configuration for your forms’ post-back settings to provide the best performance while still ensuring that events get fired when necessary. See our performance post on MSDN to learn more about general performance and event handler post-backs.

Tables

After you’ve designed your form and authored the source code, the final step is to publish the form. Your InfoPath form with code can be published to SharePoint and to client machines, but you need to make a security decision before you publish: configure the form as domain trust or full trust.

 

Domain trust forms can be published to SharePoint as Sandboxed Solutions directly from the InfoPath 2010 Designer. With Sandboxed Solutions, SharePoint Server farm administrators can restrict the resources available to the code and developers cannot access resources subject to operating system security. This establishes a safe environment where Site Collection administrators can publish code to SharePoint without the overhead of administrator approval!

Note: Publishing full trust forms to a client-side environment requires that the form is signed with a code-signing certificate or installed through a custom MSI built in Visual Studio. Publishing a full trust form to SharePoint requires a farm administrator to activate the solution through the SharePoint Central Administration portal.

Best Practice: You should always use the lowest level of trust possible when publishing forms.

Summary


Most forms you design with InfoPath 2010 are not going to require code, but when they do, just install Visual Studio Tools for Applications and you’re ready to start programming. To add code, select the language of your choice, and use entry points in the Ribbon and Fields task pane to automatically insert event handlers. Finally, decide whether or not your form requires full-trust, and publish it to SharePoint or a client environment accordingly. If you’re interested in learning more about the InfoPath and SharePoint programmability, please visit http://www.msdn.com and keep checking for updates here on the InfoPath blog.

Document Creation and Conversion with the OpenXML SDK and SharePoint 2010 Word Automation Services

the solution to write the documents to a document library in SharePoint and then use Word Automation Services to automatically convert the docx files to PDF format.

Setup


To follow along with this walkthrough without changes, setup your SharePoint instance (at http://localhost) as follows:

  • Create a document library called "Created Docs" in the root site

  • Create a document library called "Converted Docs" in the root site


If you use a remote server, and/or a different site or library names, you'll need to adjust some of the URI and path strings in the code below to make it work.

Writing to a SharePoint Library


Carrying on from last time, wire up the click event of the CreateOneSharePointDocumentButton:


Click event handler



  1. private void CreateOneDocumentOnSharePointButton_Click(object sender, EventArgs e)

  2. {

  3.     gen.CreateOneDocumentOnSharePoint();

  4. }





Generate a stub for the CreateOneDocumentOnSharePoint() method in the DocGenerator class using the Ctrl+. technique made possible by the Visual Studio 2010 Productivity Power Tools.

Switch to and add a using statement to the DocGenerator class to give you access to the SharePoint Client Libraries - giving it an alias will help disambiguate the File class later:




  1. using SPC = Microsoft.SharePoint.Client;





Using the SharePoint Client Libraries, it's very easy to write documents to a document library, and there's no need to write a document to a local drive. This means we'll use a different overload of the OpenXML SDK's WordprocessingDocument.Create() method that writes, not to a file, but to a MemoryStream.


CreateOneDocumentOnSharePoint



  1. internal void CreateOneDocumentOnSharePoint()

  2. {

  3.   SPC.ClientContext clientContext = new SPC.ClientContext("http://localhost");

  4.   string fileUrl = "/Created Docs/MyVeryVeryCoolDoc.docx";


  5.   sw.Reset();

  6.   sw.Start();


  7.   using (MemoryStream ms = gen.CreatePackage())

  8.   {

  9.     ms.Seek(0, SeekOrigin.Begin);

  10.     SPC.File.SaveBinaryDirect(clientContext, fileUrl, ms, true);

  11.   }





In this code, you create a new SharePoint.Client.ClientContext that gives access to the site (in this case at http://localhost, but if you've got things set up differently, change it here).

Create an overload of the CreatePackage() method in the DocumentCreator class that creates and populates a MemoryStream:


CreatPackage Overload



  1. internal MemoryStream CreatePackage()

  2. {


  3.   MemoryStream ms = new MemoryStream();

  4.   using (WordprocessingDocument package =

  5.     WordprocessingDocument.Create

  6.     (ms, WordprocessingDocumentType.Document))

  7.   {

  8.     CreateParts(package);

  9.   }


  10.   return ms;


  11. }





Move the pointer to the start of the MemoryStream and call the File.SaveBinaryDirect() method passing in the ClientContext, a string indicating where the file should be written, the stream and a boolean that tells SharePoint whether or not to overwrite an existing file with the same name.

Running the app and clicking the One document in SharePoint button shows that it's very fast - in my case 102ms

Writing 1 document to SharePoint took 0.1 seconds

Writing lots of documents is fast too - add an event handler to the CreateOneSharePointDocumentButton:


Click Event Handler



  1. private void CreateManyDocumentsOnSharePointButton_Click(object sender, EventArgs e)

  2. {

  3.   gen.CreateManyDocumentsOnSharePointInParallel((int)NumberOfDocumentsToCreate.Value);


  4. }





And add a CreateManyDocumentsOnSharePointInParallel() method that uses a Parallel.For() loop to call CreatePackage() and File.SaveBinaryDirect() for as many files as you create:


Create lots of SharePoint Docs



  1. internal void CreateManyDocumentsOnSharePointInParallel(int NumberOfDocs)

  2. {

  3.   SPC.ClientContext clientContext = new SPC.ClientContext("http://localhost");

  4.   string fileUrl = "/Created Docs/MyEvenCoolerDoc{0:D5}.docx";


  5.   sw.Reset();

  6.   sw.Start();


  7.   Parallel.For(0, NumberOfDocs, i =>

  8.   {


  9.     using (MemoryStream ms = gen.CreatePackage())

  10.     {

  11.       ms.Seek(0, SeekOrigin.Begin);

  12.       SPC.File.SaveBinaryDirect(clientContext, string.Format(fileUrl, i), ms, true);

  13.     }


  14.   });


  15.   sw.Stop();


  16.   System.Windows.Forms.MessageBox.Show(string.Format(

  17.       "Wrote {3} documents to SharePoint ({1}{2}) in {0} ms (using parallel processing)",

  18.       sw.ElapsedMilliseconds,

  19.       clientContext.Url,

  20.       fileUrl,

  21.       NumberOfDocs));


  22. }





This is also pretty fast - in my case 40ms per document.

Writing 100 documents to SharePoint (in parallel) took 4 seconds

Navigating to the document library shows all those documents sitting just where you'd expect to see them:

Cool documents created en-masse

Converting Word Documents to a Fixed Format (PDF or XPS)


PDFUp until now, we've not had to use Word (or any other Office client) as all we've been doing is generating documents, not rendering them. Just like you can create an HTML document without requiring a browser, it's perfectly valid to create a Word document (or any other OpenXML format document) without using Word.

However, to view the document, or to create a fixed version of it like PDF or XPS, it's necessary to render it. Up until the release of SharePoint 2010, the highest fidelity way to do this was to open the document in Word. Of course, doing that on the server was fraught with difficulty. Word is not designed to be a server-side tool - it throws (sometimes modal) dialogs, it spends a lot of resources on updating the screen and it's not optimised for multi-processor, large memory scenarios. When there is a user interacting with Word though, the bottleneck is rarely the computer.

The SharePoint team addressed this problem with the Word Automation Services feature in SharePoint 2010 (standard edition and higher). Word Automation Services is the client code from Word with the UI bits stripped out and optimised to run as a server process. All of the rendering engine is available for SharePoint to use without any of the issues (both technical and from a licensing point of view) of using Word on a server. There's lots of great info on Word Automation Services on MSDN and elsewhere. Here's the list of resources I provided in the first post in this series:

Word Automation Services (WAS) document conversion jobs run as as an asynchronous server-side job that can either be scheduled automatically (for example, when a document is placed in a folder) or programmatically. Either way, the job won't start immediately, just the next time the WAS scheduler runs. The frequency of the scheduler running is set in Central Administration - see the links above for details on how to set it up. I set it to the minimum interval - one minute.

Interacting programmatically with the service is pretty straightforward, but there are two gotchas:

  1. the .NET libraries are 3.5 only, so the project you create must be a .NET 3.5 project, and

  2. the calls will fail (with cryptic exceptions) if it's not a 64-bit call, so you must target either x64 or Any processor type, not x86.


Create a new console application and make sure that the target framework is 3.5.

Create a new console application targetting Framework 3.5

Open the Visual Studio Configuration Manager dialog by dropping down the Solution Configurations drop-down on the Visual Studio Standard toolbar (or choosing Configuration Manager from the Build menu):

Choose Configuration Manager

Next, add a Solution Platform:

image

to target Any CPU (or x64)

image

Now you're ready to start building.

Converting a single document to PDF


Add references to the Microsoft.SharePoint and Microsoft.Office.Word.Server assemblies.

Add using statements for those assemblies:




  1. using Microsoft.SharePoint;

  2. using Microsoft.Office.Word.Server.Conversions;





Add a couple of static string properties to the class that you can adjust to suit the way you've got your SharePoint setup configured:




  1. // If you manually installed Word Automation Services, then replace the name

  2. // in the following line with the name that you assigned to the service when

  3. // you installed it.

  4. static string cWordServicesName = "Word Automation Services";

  5. static string siteUrl = "http://localhost";





Now you can initiate the conversion of a single document:


Convert a single document



  1. private static void SingleConv()

  2. {

  3.   using (SPSite spSite = new SPSite(siteUrl))

  4.   {

  5.     ConversionJob job = new ConversionJob(cWordServicesName);

  6.     job.UserToken = spSite.UserToken;

  7.     job.Settings.UpdateFields = true;

  8.     job.Settings.OutputFormat = SaveFormat.PDF;

  9.     job.AddFile(siteUrl + "/Created%20Docs/MyAwesomeDoc.docx",

  10.         siteUrl + "/Converted%20Docs/MyAwesomeDoc.pdf");

  11.     job.Start();

  12.     Console.WriteLine("Job ID: {0} started", job.JobId);

  13.     Console.WriteLine("Press the any key ...");

  14.     Console.ReadKey();

  15.   }

  16. }





There are a few things to note here.

Firstly, you get a reference to the Site using the SharePoint libraries, not the SharePoint Client libraries that we used to write the Word docs to the list in the first place.

Next, you need to pass a user token to the new ConversionJob, and you get that from the SPSite user token.

Third, you specify the output format using the SaveFormat enumeration.

Finally, remember the service is performed asynchronously and so although you get a Job ID back, you don't get any more information about the job status (more on that when we do bulk conversions)

Converting documents to PDF en-masse


Converting whole libraries at once is also very easy. The ConversionJob class has an AddLibrary() method that takes as parameters a source and destination SPList object.


Converting whole libraries



  1. private static void BulkConv()

  2. {

  3.   using (SPSite spSite = new SPSite(siteUrl))

  4.   {

  5.     Console.WriteLine("Starting conversion job");

  6.     ConversionJob job = new ConversionJob(cWordServicesName);

  7.     job.UserToken = spSite.UserToken;

  8.     job.Settings.UpdateFields = true;

  9.     job.Settings.OutputFormat = SaveFormat.PDF;

  10.     job.Settings.OutputSaveBehavior = SaveBehavior.AlwaysOverwrite;

  11.     SPList listToConvert = spSite.RootWeb.Lists["Created Docs"];

  12.     SPList listToPopulate = spSite.RootWeb.Lists["Converted Docs"];

  13.     job.AddLibrary(listToConvert, listToPopulate);

  14.     job.Start();

  15.     Console.WriteLine("Bulk conversion job {0} started", job.JobId);

  16.     ConversionJobStatus status = new ConversionJobStatus(cWordServicesName,

  17.         job.JobId, null);

  18.     Console.WriteLine("Number of documents in conversion job: {0}", status.Count);

  19.     while (true)

  20.     {

  21.       System.Threading.Thread.Sleep(5000);


  22.       status.Refresh();

  23.       if (status.Count == status.Succeeded + status.Failed)

  24.       {

  25.         Console.WriteLine("{2} Completed, Successful: {0}, Failed: {1}",

  26.             status.Succeeded, status.Failed, DateTime.Now);

  27.         break;

  28.       }

  29.       Console.WriteLine("{2} In progress, Successful: {0}, Failed: {1}",

  30.           status.Succeeded, status.Failed, DateTime.Now);

  31.     }


  32.     Console.ReadKey();

  33.   }

  34. }





Checking the status of the job is straightforward (as long as you have the JobId - a GUID uniquely identifying this conversion job). The ConversionJobStatus object holds information about the conversion job including how many documents are to be converted, how many have been converted successfully and how many have failed. Calling the Refresh() method gets the most up-to-date status and you can use that to poll for completion. Remember that jobs only start every <n> minutes, where n is a setting in SharePoint Central Administration

Converting documents is an asynchronous process

The result is a SharePoint list full of PDF files, created without ever needing to open Word.

A library full of converted PDFs

A converted PDF in Adobe Reader

Conclusion


The combination of the OpenXML SDK and Word Automation Services makes server-side document creation simple, scalable and efficient. This is definitely a tool worth adding to your arsenal.

Sharepoint - Membership.CreateUser Method

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Security" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">

public void CreateUser_OnClick(object sender, EventArgs args)
{
MembershipCreateStatus result;

try
{
// Create new user.

if (Membership.RequiresQuestionAndAnswer)
{
MembershipUser newUser = Membership.CreateUser(
UsernameTextbox.Text,
PasswordTextbox.Text,
EmailTextbox.Text,
PasswordQuestionTextbox.Text,
PasswordAnswerTextbox.Text,
false,
out result);
}
else
{
MembershipUser newUser = Membership.CreateUser(
UsernameTextbox.Text,
PasswordTextbox.Text,
EmailTextbox.Text);
}

Response.Redirect("login.aspx");
}
catch (MembershipCreateUserException e)
{
Msg.Text = GetErrorMessage(e.StatusCode);
}
catch (HttpException e)
{
Msg.Text = e.Message;
}
}

public string GetErrorMessage(MembershipCreateStatus status)
{
switch (status)
{
case MembershipCreateStatus.DuplicateUserName:
return "Username already exists. Please enter a different user name.";

case MembershipCreateStatus.DuplicateEmail:
return "A username for that e-mail address already exists. Please enter a different e-mail address.";

case MembershipCreateStatus.InvalidPassword:
return "The password provided is invalid. Please enter a valid password value.";

case MembershipCreateStatus.InvalidEmail:
return "The e-mail address provided is invalid. Please check the value and try again.";

case MembershipCreateStatus.InvalidAnswer:
return "The password retrieval answer provided is invalid. Please check the value and try again.";

case MembershipCreateStatus.InvalidQuestion:
return "The password retrieval question provided is invalid. Please check the value and try again.";

case MembershipCreateStatus.InvalidUserName:
return "The user name provided is invalid. Please check the value and try again.";

case MembershipCreateStatus.ProviderError:
return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

case MembershipCreateStatus.UserRejected:
return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

default:
return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
}
}

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Create User</title>
</head>
<body>

<form id="form1" runat="server">
<h3>Create New User</h3>

<asp:Label id="Msg" ForeColor="maroon" runat="server" /><br />

<table cellpadding="3" border="0">
<tr>
<td>Username:</td>
<td><asp:Textbox id="UsernameTextbox" runat="server" /></td>
<td><asp:RequiredFieldValidator id="UsernameRequiredValidator" runat="server"
ControlToValidate="UserNameTextbox" ForeColor="red"
Display="Static" ErrorMessage="Required" /></td>
</tr>
<tr>
<td>Password:</td>
<td><asp:Textbox id="PasswordTextbox" runat="server" TextMode="Password" /></td>
<td><asp:RequiredFieldValidator id="PasswordRequiredValidator" runat="server"
ControlToValidate="PasswordTextbox" ForeColor="red"
Display="Static" ErrorMessage="Required" /></td>
</tr>
<tr>
<td>Confirm Password:</td>
<td><asp:Textbox id="PasswordConfirmTextbox" runat="server" TextMode="Password" /></td>
<td><asp:RequiredFieldValidator id="PasswordConfirmRequiredValidator" runat="server"
ControlToValidate="PasswordConfirmTextbox" ForeColor="red"
Display="Static" ErrorMessage="Required" />
<asp:CompareValidator id="PasswordConfirmCompareValidator" runat="server"
ControlToValidate="PasswordConfirmTextbox" ForeColor="red"
Display="Static" ControlToCompare="PasswordTextBox"
ErrorMessage="Confirm password must match password." />
</td>
</tr>
<tr>
<td>Email Address:</td>
<td><asp:Textbox id="EmailTextbox" runat="server" /></td>
<td><asp:RequiredFieldValidator id="EmailRequiredValidator" runat="server"
ControlToValidate="EmailTextbox" ForeColor="red"
Display="Static" ErrorMessage="Required" /></td>
</tr>

<% if (Membership.RequiresQuestionAndAnswer) { %>

<tr>
<td>Password Question:</td>
<td><asp:Textbox id="PasswordQuestionTextbox" runat="server" /></td>
<td><asp:RequiredFieldValidator id="PasswordQuestionRequiredValidator" runat="server"
ControlToValidate="PasswordQuestionTextbox" ForeColor="red"
Display="Static" ErrorMessage="Required" /></td>
</tr>
<tr>
<td>Password Answer:</td>
<td><asp:Textbox id="PasswordAnswerTextbox" runat="server" /></td>
<td><asp:RequiredFieldValidator id="PasswordAnswerRequiredValidator" runat="server"
ControlToValidate="PasswordAnswerTextbox" ForeColor="red"
Display="Static" ErrorMessage="Required" /></td>
</tr>

<% } %>

<tr>
<td></td>
<td><asp:Button id="CreateUserButton" Text="Create User" OnClick="CreateUser_OnClick" runat="server" /></td>
</tr>
</table>
</form>

</body>
</html>

Sharepoint - Return Items from a List

The following example returns items from the Calendar list in the current Web site if the event occurs after a specified date. To improve performance, the example uses the GetItems(SPQuery) method to reduce the scope of the query to a limited set of items. The example uses a constructor to instantiate an SPQuery object, and then assigns to the Query property of the query object a string in Collaborative Application Markup Language (CAML) that specifies the inner XML for the query (in other words, the <Where> element). After the Query property is set, the query object is passed to the GetItems method to return and display items.





string listTitle = TextBox1.Text;
SPWeb mySite = SPContext.Current.Web;
SPList myList = mySite.Lists[listTitle];

SPQuery myQuery = new SPQuery();

myQuery.Query = "<Where><Geq><FieldRef Name = \"EventDate\"/>" +
"<Value Type = \"DateTime\">2010-06-01</Value></Geq></Where>";

SPListItemCollection myItems = myList.GetItems(myQuery);

for (int i = 0; i < myItems.Count; i++)
{
SPListItem item = myItems[i];

Label1.Text += SPEncode.HtmlEncode(item["Title"].ToString()) + " : " +
SPEncode.HtmlEncode(item["Start Time"].ToString()) + " : " +
SPEncode.HtmlEncode(item["End Time"].ToString()) + "<BR>";
}





The previous example assumes the existence of a text box that can be used to type the name of a list, and a label to display items that are returned. Indexers are used both to return the list that is typed by the user and to enumerate the item collection. To work with individual items, indexers are used to specify the names of columns from which to retrieve values. In this case, the specified field names pertain to a standard SharePoint Foundation Calendar list.

The following example returns only Title column values in cases where the Stock column value surpasses 100.






SPWeb mySite = SPContext.Current.Web;
SPList list = mySite.Lists["Books"];

SPQuery query = new SPQuery();
query.Query = "<Where><Gt><FieldRef Name='Stock'/><Value Type='Number'>100</Value></Gt></Where>";

SPListItemCollection myItems = list.GetItems(query);

foreach (SPListItem item in myItems)
{
Response.Write(SPEncode.HtmlEncode(item["Title"].ToString()) + "<BR>");
}





The example assumes the existence of a Books list that has a Stock column that contains number values.

 

Cross-List Queries










You can perform cross-list queries to query more efficiently for data across multiple Web sites. The following example uses the SPSiteDataQuery class to define a query, and then uses the GetSiteData method to return items from a standard SharePoint Foundation Tasks list (specified by ServerTemplate = "107") where the Status column equals "Completed". For a list of the SharePoint Foundation server templates, see SPListTemplateType.




SPWeb webSite = SPContext.Current.Web;
SPSiteDataQuery query = new SPSiteDataQuery();

query.Lists = "<Lists ServerTemplate=\"107\" />";
query.Query =
"<Where><Eq><FieldRef Name=\"Status\"/>" +
"<Value Type=\"Text\">Completed</Value></Eq></Where>";

System.Data.DataTable items = webSite.GetSiteData(query);

foreach (System.Data.DataRow item in items)
{
Response.Write(SPEncode.HtmlEncode(item["Title"].ToString()) + "<BR>");
}