InfoPath - LoadDocumentAndPlayEventLog NullReferenceException

Error:

LoadDocumentAndPlayEventLog failed with unhandled exception System.NullReferenceException: Object reference not set to an instance of an object.     at Microsoft.Office.InfoPath.Server.Converter.DetectUnsupportedNamespaces.VerifyNamespace

This is a very uncommon bug.  Essentially, you are using custom code in InfoPath, and you are using code that doesn't have a namespace.  Say a helper function that you've included in your code.

When InfoPath Forms Services attempt to validate your form, it finds that your form template contains reference to code that doesn't have a namespace!

The fix is simple, create a namespace for the helper class, or move the helper class under the namespace of the form's namespace.

SharePoint - here is a REST service Project Item template

UPDATE:

There seems to be an issue when loading this template in VS.NET - sometimes there's an error that prevents the rest of CKSDEV from working correctly.  If you are unable to retract or deploy via CKSDEV after this template was included, please remove this template and restart VS.NET

 

CKSDEV provides a wonderful WCF template.  The main magic of this template is that it uses SharePoint 2010's MultipleBaseAddressBasicHttpBindingServiceHostFactory to create a service host and hook up all the configuration settings.  This is done by the hardwork Charlie Holland and described here http://www.chaholl.com/archive/2010/03/10/how-deploy-a-wcf-service-to-sharepoint-2010.aspx

I've been presenting a talk where you can start with the WCF template, then change the factory to MultipleBaseAddressWebServiceHostFactory and switch the service to a REST service.

 

Finally, I sat down and created a Project Item template for SharePoint.  With examples on how to create both a GET and a POST method in REST service.

 

Download:

 

Install:

  • You may need to unblock this file if you download it from IE
  • Copy the file to \Documents\Visual Studio 2010\Templates\ItemTemplates\Rest.zip
  • Close / Restart VS.NET

 

Use:

  1. Create a blank SharePoint Farm solution.  Services can't be deployed as Sandbox.
    image
    Figure: Create a SharePoint Farm solution. 
  2. Add new Project Item, the template does NOT appear under the SharePoint group currently.  So you'll need to search for it.  Will be fixed... "Soon".  Give it a nice name.
    image
    Figure: Search for the template and use it to create a REST service
  3. These files are created:
    image
    Figure: My RestService1

  4. The IRestService1.cs interface gives you the definition of your REST service methods.  I've provided a GET and a POST operation.
    image
    Figure: GET, and POST
  5. You should also add a CKS WCF Service
    image
  6. The solution should look like this:
    image
    Figure: OK, we have a REST service and a WCF SOAP service.

    Important.  CKSDEV's WCF Template hooks up token replacement for the *.svc file.  You need to add this, even if you don't use it, otherwise you'll need to modify your VS.NET project file manually.  You can remove this service after you've added it once. 

    I recommend you compare the difference between the CKSDEV WCF SOAP service and the REST Service.
  7. Deploy! 
    image
    Figure: I LOVE Deploy from VS.NET 2010
  8. Open a browser, go to:
    http://server/_vti_bin/SharePointProject1/RestService1.svc/GetItem/1

    You should get:
    image

  9. The returned content type is unrecognized by Internet Explorer, so it's prompting you to save it.
    Save this, and open it in notepad.
    image
    Figure: Haha!  JSON'ed HelloWorld Blob object returned from the server!

  10. In IE Developer Toolbar, you can watch the network traffic
    This particular request took 31ms and sent 463 bytes!
    image
    Figure: Extremely fast and efficient way to talk to SharePoint.

 

 

If you see this error:


Error

The type 'SharePointProject1.RestService1, $SharePoint.Project.AssemblyFullName$', provided as the Service attribute value in the ServiceHost directive could not be found.


This is because VS.NET does not perform Token Replacement for the *.svc file by default.  There are a few ways to fix this.

  1. The simplest way is to add a CKSDEV WCF Service Project Item.  You can remove it afterwards.
  2. Alternatively, you can edit your project file, you need to find this, and add svc;
    image

    Reload your project
    image

 

Minor issues need fix... one day:

Minor Problems:

  1. Project Item doesn't appear under SharePoint Group
    To properly create SharePoint Project Items, I need to define a proper .spdata file - this requires an additional project to declare a new SharePoint Project Item type. 
    This will also allow me to specify an ICON to use in the solution, instead of this silly red image looking icon.
  2. Not properly set up TokenReplacementFileExtension
    The project will also allow me to hook into the project item wizard and link up TokenReplacementFileExtension for svc.
  3. Need custom code...
    Custom code requires signed assemblies. Annoying.
  4. Or, preferably, I clean up the template a bit more and submit it to CKSDEV, which has most of these facilities already built in. 

InfoPath - check leap year using expression

 

This is a fun one.

  1. Use the undocumented msxsl:utc function, which checks / converts a date into a normalized representation.

    image

  2. So, what will happen if I test it with with a garbage date, say, the 29th of February of 2001, which isn't a leap year?

    image
  3. So just check if the result of the utc function returns empty string or not.

Expression

msxsl:utc(concat(my:year, "-02-29")) != ""

Pasting pictures from clipboard to SharePoint in browser, via Silverlight 5

Silverlight 5 was quietly released to the world to very little fanfare, considering the looming Windows 8 launch with WinRT next year, and the world (at least, Microsoft)'s shift to HTML5.

Still, there are a few gems in this version over Silverlight 4, in particular, you can now run trusted mode in browser, and trusted mode now has access to platform invoke.

That's right, repeat after me: Silverlight, in browser, unmanaged code.

And I just happened to have the perfect problem I've been wanting to solve forever.

 

Problem

One thing that has always peeved me when using the Rich HTML control in SharePoint is when it comes to imbedding images.  You can't easily add a picture to your Rich HTML, you need to open a different browser window, upload the picture, then find a link to that picture and insert it back in the HTML.

CTRL-V

Wouldn't it be nice if you could just paste a picture directly to SharePoint, like you could in Word, or Windows Live Writer.  The end user doesn't need to figure out where the picture will go.  SharePoint will do that.  Such a thing isn't possible with mere HTML since it doesn't support access to binary clipboard, but with Silverlight 5 we can now provide a solution.

 

 

Steps

  1. Configure in browser trusted mode
  2. Setting up Silverlight with native p/invoke calls to access the clipboard
  3. Using GDI to convert clipboard bitmap to a temporary PNG image file
  4. Upload PNG to SharePoint, using SharePoint client object model
  5. Insert HTML image reference in Silverlight Rich Text editor
  6. Update SharePoint page content from Silverlight Rich Text editor

 

 

1. Configure In Browser Trusted Mode

The easy step.  Head over to Silverlight project properties in VS.NET

image

Figure: Silverlight 5 specialty, elevated trust running in-browser.

2. Setting up Silverlight with native p/invoke and talk to the clipboard natively.

 

internal class Native
{
    [DllImport("user32.dll", EntryPoint = "CloseClipboard", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CloseClipboard();

    [DllImport("user32.dll", EntryPoint = "GetClipboardData", SetLastError = true)]
    public static extern IntPtr GetClipboardData(ClipboardFormat uFormat);

    [DllImport("user32.dll", EntryPoint = "IsClipboardFormatAvailable", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool IsClipboardFormatAvailable(ClipboardFormat format);

    [DllImport("user32.dll", EntryPoint = "OpenClipboard", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool OpenClipboard([In] IntPtr hWndNewOwner);

}

 

In my paste function:

private void Paste()
{
    if (!Application.Current.HasElevatedPermissions)
    {
        MessageBox.Show("No Elevated Permissions - can't do p/invoke :'-(");
        return;
    }
    IntPtr p = IntPtr.Zero;

    bool opened = Native.OpenClipboard(p);

    if (!opened) 
    {
        return; //unhappy
    }

    try {

        if (Native.IsClipboardFormatAvailable(ClipboardFormat.CF_BITMAP))
        {
            IntPtr p4 = Native.GetClipboardData(ClipboardFormat.CF_BITMAP);

            // GASP.  We have a pointer to our bitmap!
        }
    }
    finally 
    {
        Native.CloseClipboard();
    }

}

 

3. Using GDI to convert clipboard bitmap to a temporary PNG image file

It's awesome we have a pointer, but what do we do with it?  This next part eluded me for months, I had to stop work, and go on the Internet to ask for help.  3 months later, at the end of 2011 a reply came through.  Use GDI+ to convert the pointer to a file!  Genius!  Bravo!

Note that in the GDI+ GdipSaveImageToFile call, I use the PNG Encoder - so the bitmap is saved in PNG format in my temporary file.

Oh, right, more native p/invoke, different DLL this time.

internal class Native
{

   ... <snip earlier clipboard p/invoke>
    [DllImport("gdiplus.dll", CharSet = CharSet.Unicode)]
    public static extern int GdipCreateBitmapFromHBITMAP(IntPtr hbitmap, IntPtr hpalette, out IntPtr bitmap);
    [DllImport("gdiplus.dll", CharSet = CharSet.Unicode)]
    public static extern int GdipSaveImageToFile(IntPtr image, string filename, ref Guid classId, IntPtr encoderParams);
    [DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    public static extern long GdiplusStartup(out IntPtr token, ref GdiplusStartupInput gdiplusStartupInput, out IntPtr gdiplusStartupOutput);
    [DllImport("gdiplus.dll")]
    public static extern void GdiplusShutdown(IntPtr token);
}

 

IntPtr gdipToken = IntPtr.Zero; ;
string fileName = string.Empty;

try
{

    IntPtr gdiplusStartupOutput;
    GdiplusStartupInput input = new GdiplusStartupInput(1);
    long num0 = Native.GdiplusStartup(out gdipToken, ref input, out gdiplusStartupOutput);

    IntPtr zero = IntPtr.Zero;
    IntPtr palette = IntPtr.Zero;

    int num = Native.GdipCreateBitmapFromHBITMAP(p4, palette, out zero);
    if (num != 0)
    {
        return;
    }

    // JPG Encoder {557CF401-1A04-11D3-9A73-0000F81EF32E}
    // PNG Encoder {557CF406-1A04-11D3-9A73-0000F81EF32E}
    Guid classId = Guid.Parse("{557CF406-1A04-11D3-9A73-0000F81EF32E}");

    fileName = System.IO.Path.GetTempFileName();

    int img = Native.GdipSaveImageToFile(zero, fileName, ref classId, palette);
    if (img != 0)
    {
        return;
    }
}
finally
{
    Native.GdiplusShutdown(gdipToken);
}

 

4. Upload PNG to SharePoint, using SharePoint client object model

 

using(FileStream fs = File.OpenRead(fileName))
{
    SP.ClientContext ctx = SP.ClientContext.Current;

    SP.Web web = ctx.Web;
    SP.List library = web.Lists.GetByTitle("Images");

    byte[] content = new byte[fs.Length];
    var newFile = new SP.FileCreationInformation();
    int dummy = fs.Read(content, 0, (int)fs.Length);
    newFile.Content = content;
    newFile.Url = string.Format("paste_{0}.png", DateTime.Now.Ticks);
    var uploadFile = library.RootFolder.Files.Add(newFile);
    ctx.Load(uploadFile);
    ctx.ExecuteQueryAsync(
        delegate {
            this.Dispatcher.BeginInvoke(() =>
            {

// update our rich text editor in step 5!
            });
        },
        delegate {  // our code don't fail!       
        });

}

 

5. Insert HTML image reference in Silverlight HTML Text editor

I'm using the wonderful free VectorLight.NET Liquid HTML Editor control.  Need free registration.  Supports converting between Rich XAML and HTML formats.  Here I'm inserting a <Xaml><Image /></Xaml>

 

ctx.ExecuteQueryAsync(
    delegate {
        this.Dispatcher.BeginInvoke(() =>
        {
            this.listBox1.Items.Add(uploadFile.ServerRelativeUrl);

            InlineUIContainer container = new InlineUIContainer();
            Uri server = new Uri(ctx.Url);
            string path = string.Format("{0}://{1}{2}", server.Scheme, server.Host, uploadFile.ServerRelativeUrl);
            this.richTextBox1.Insert(string.Format("<Xaml><Image Source=\"{0}\" /></Xaml>", path));
           
        });
    },
    delegate {
   
    });

5.1 Pictures - just to prove it works

image

Figure: Pasting picture into HTML Editor within Silverlight.

image

Figure: My SharePoint image library, filled with pasted images :-)

image

Figure: Dumping Editor's HTML to MessageBox - you can see the <img> HTML is inserted properly.

 

6. Update SharePoint page content from Silverlight Rich Text editor

This part is the most ugly bit of the code.  Heavily nested since I keep using anonymous delegates, and it's pretty late so I'm not going to clean it up tonight.

The Save button click.


private void buttonSave_Click(object sender, RoutedEventArgs e)
{
    var ctx = SP.ClientContext.Current;
    var library = ctx.Web.Lists.GetByTitle("Site Pages");
    var items = library.GetItems(SP.CamlQuery.CreateAllItemsQuery());

    var filepath = this.autoCompleteBox1.Text;  // I store a list of pages in the dropdown...
    ctx.Load(items);

    ctx.ExecuteQueryAsync(
        delegate
        {
            // switch back to UI thread
            this.Dispatcher.BeginInvoke(() =>
            {
                SP.ListItem page = null;
                foreach (var item in items)
                {
                    // super ugly code - should filter the files in the CamlQuery above - but too tired to write Caml
                    if (item["FileLeafRef"].ToString() == filepath)
                    {
                        page = item;
                    }
                }
                page["WikiField"] = this.richTextBox1.HTML;
                page.Update();  // update SPListItem, then ExecuteQuery to push the update back through ClientService.svc
                ctx.ExecuteQueryAsync(
                    delegate
                    {
                        // switch back to UI thread

                        this.Dispatcher.BeginInvoke(() =>
                        {
                            // refresh browser
                            HtmlPage.Document.Submit();
                        });
                    },
                    delegate { });
            });
        },
        delegate
        {
        });
}

 

image

Figure: The Silverlight webpart pushing HTML back into a Wikipage

 

 

There are some notes on security, which I leave right at the end, but this is important.

Trusted mode / In Browser

  1. When running under http://localhost/ SL5 skips checking this (easy for debug)
  2. For normal operation, requires Silverlight XAP file to be signed with a code trust certificate.  You can generate one yourself, just make sure you add it to the right store.
    image
    Figure: Yes... Trusted Root Certification Authorities.  Yep sounds about right!
  3. And requires a registry key to be present for Silverlight
    image
    FIgure: OMG #1, Registry, really!?
  4. You will need to deploy this to your uses via a group policy, or a click once application if your user has permissions to write to their own registry. 

This bit I think is the part that makes the solution safe, but also very difficult to deploy.  But if you want the nice editing experience with paste functionality, here you go!

 

 

Downloads

  • XAP file (Contact me for the XAP file - it needs a bit of cleaning up, and I need to test the certificate)
  • SPClip cert

 

And here we go, first big post of the year.  Have a great 2012 everyone!

SharePoint - disguise your long running AJAX calls

I have to confess I haven't had so much laugh in SharePoint for a long time.

OK, here's the problem:

  1. I'm calling a custom REST service that I've developed - the REST service checks a bunch of database records, as well as creating a new site and activate a number of features on that site. 
  2. Basically, it takes a while to run.  May be around 15 seconds.

 

image
Figure: Once you click this link it gets busy on the server.

image
Figure: Once it's clicked, I disable the link

Put up a dialog to tell the user to wait

The first thing we should do is put up a dialog to tell the user hey something's happening.

Waldek Mastykarz has an awesome article on how to do most of this, so I won't type out his code.  http://blog.mastykarz.nl/sharepoint-2010-ui-tip-non-obtrusive-progress-messages/

image 
Figure: Here's my blocking dialog.  No close box.  It spins for about 15 seconds and then disappears when the AJAX call receives a success response.

 

But waiting for 15 seconds really gets boring.

You realize that you must use better messages, and update it as you wait. 

image

image

image

 

Here's the javascript code.

 

    var msgs = [
        "Calculating web paths",
        "Negotiating with site collection",
        "Creating empty site template",
        "Activating Features",
        "Synchronizing template",
        "Setting up form libraries",
        "Copying pages",
        "Configuring webparts",
        "Chasing chickens"];   
   
    var p = function(){
        if (waitDialog) {
            var msg = msgs[Math.floor(Math.random()*msgs.length)];
            waitDialog.get_html().getElementsByTagName('TD')[1].innerHTML = msg;
            setTimeout(p, 1000);
        }
    };
    setTimeout(p,1000);

Create an array of status messages - these (aside from the chicken) are really what the REST service is doing.  I also create a function p, which choses a random message and updates the waitDialog.  Repeat every second.  When the AJAX call completes, it destroys the waitDialog, and set it to null.  This stops the setTimeout loop.

 

Some sort of magic happened

Suddenly, because things are updating on screen, the process doesn't seem long at all.  You click it, a few messages flash past, before you know it the site's created and ready to go.

So there you have it, the trick really is just a clever disguise. 

You show users random messages and distract them from the fact that they have to wait for 15 seconds.