InfoPath - packaging site columns and content types

WARNING

I do not recommend packaging and deploying content types.  Site columns are fine, but it is best to leave InfoPath content type to SharePoint - since it does quite a few other things during self provisioning and deployment.

WARNING - you have been warned.

 

A discussion popped up on the InfoPath forums and I took a good stab at it.

http://social.technet.microsoft.com/Forums/en-US/sharepoint2010customization/thread/d2982a7c-86e1-4868-864b-2fcea68274fd/

Jason Li 

Content Deployment between different environments (InfoPath, SharePoint Designer Workflow, Content Type) in SharePoint 2010
I have read many discussions on this (or similar topic) and still would like to post this again to see if I can get real answers:

  1. I have InfoPath forms.
  2. These forms are published as content types. 
  3. I have reusable SharePoint Designer workflows.  
  4. I have document libraries created to use these content types.
  5. I have security groups.

How do I move these things from DEV to TEST, UAT all the way into PROD. 

As to #3:  GUID is driving me crazy.

 

Content Type deployment across different farms

The only way I have ever got all the environments to obey my command regarding content types is to use Solution Packages.

  1. Create InfoPath form in DEV, when publishing, use Content Type, and Site Columns.
  2. Once the Content Type and Site Columns are created, package them into a WSP.  From now on, always deploy content type and site columns using WSP.  This forces All the GUIDS to be identical across all farms, which makes everything work nice.  (see below for steps and pictures).
  3. Reusable SharePoint designer workflows can be tied to these content type / site columns, so can document library.
  4. Deploy package to TEST, you can create document libraries with feature as well, or configure them manually.  I deploy InfoPath forms to TEST by publishing from InfoPath to the TEST environment, depending on whether you are doing:
    • Site content type: publish to the destination list - it should automatically pick up the content type since the GUID are the same.  Site columns will match too.
    • Central admin forms - deploy via central admin, this places the form template back in /FormServerTemplates/ on the root site, hook this back using Content Type / advanced / form template in each list.

InfoPath forms always have data connections (since InfoPath can't do anything), you should convert all data connections to connection files hosted relatively on each DEV, TEST, etc. environment, this way you don't need to modify the form template, and depending which environment it's published to, it'll go and find the right sets of data.

 

Packaging InfoPath Content Type and Site Columns

 

Packaging site columns and content types are a lot easier now in SharePoint 2010 with VS.NET 2010. 

Grab CKS extension first.
image

  1. Create an empty SharePoint project on the DEV server.  I've always used Farm Solutions but there's probably no harm trying Sandbox Solutions.
  2. In the Server Explorer, navigate to your SharePoint DEV site.  Expand the nodes and find your Content Type under the root web.  Select Import Content Type to bring this definition into your SharePoint project.

    image
  3. This will import the content type under a feature with a default name Feature 1.  Rename the feature (VS.NET will fix all the related names).

    image
  4. Open the feature and fix the Title as well.  Notice that this feature is Web scoped.

    image
  5. Next, navigate in the Server Explorer to the site columns, and bring in the Site columns that was promoted from InfoPath.

    image
  6. This brings in two additional elements with pretty ugly GUID-names.  Need to tidy this up.

    image
  7. Open the Elements.xml file under the two site columns, and copy the <Field... /> definitions.
    Open the Elements.xml file under the InfoPath content type, and insert the site column definitions above the content type definition.

    (You'll see the content type has FieldRef to the exported columns).

    image
  8. Build the project into a package

    image
  9. Ta-da!

    image

SharePoint Saturday Melbourne update

I had a pleasure of flying down and presenting at the SharePoint Saturday Melbourne event.  Had a great time, met many new people and presented my talk on Custom Rest Services and jQuery AJAX, hope you guys enjoyed it.

Highlights for the presentation were the number of people that put up their hands saying they will try this in their environment - that was very encouraging - it's all fairly simple when you look at the result in small chunks.  Thank you guys.

 

The links to the PowerPoint presentation and the demo solution are here:

 

Because I ran out of time (sorry), the bits that I missed were:

  • The 3rd service factory in SharePoint 2010 is for the DataService, allowing you to connect any Entity Framework objects directly into a REST service end-point with almost no code.
  • The 3 service factories are not available in SharePoint 2007, this makes writing services VERY COMPLEX and I don't recommend doing it in 2007.  But you are not out of luck, JavaScript can call the 2007 SOAP services, as long as the correct SOAP envelop is formed before the POST call.  Luckily there are awesome people that has done most of this work: http://spservices.codeplex.com/
  • IMPORTANT: I did not explain what the jQuery.ajax's cache: false argument is.  This is very important.  When you call a REST service via a URL, if you do not very the URL/Query string, your browser will return you the same data that it has downloaded previously, and give you a cached copy.  This can be really bad since your service isn't actually called.  jQuery gets around this by appending a random number at the end of the query string, like this:

    image
    ?_=12312123

    This ensures the query string is unique and browser's cache mechanism won't interfere.  HTTP Response code 200 is success.  Rather than 304 which is browser cache.

SharePoint - Starting Site Workflows Manually

We probably all know that SharePoint 2010 allows you to create a new type of workflow: Site Workflow, these workflows are not bound to a list.  Site workflows are perfect for those one-off jobs that you'd trigger manually.

What's not obvious, to me initially, as well as to many other people, is how does a user actually start one off?

Here's the solution, in one picture, I bet half of you have never noticed this before even though it's always been there from the very beginning.

image

Hacking SP.UI.ModalDialog to download ReportServer PDF

We have a situation where we want to let our users click a report server link, but the report can take (depending on parameters) anywhere between 5 to 30 seconds to create and export as PDF.

Here's how you can "hack" some JavaScript to make the experience better.

 

Use SP.UI.ModalDialog

function do_modal(url, title, callback) {
    var options = {
        url: url,
        title: title,
        showClose: true
    }
    if (callback) {
        options.dialogReturnValueCallback = Function.createDelegate(null, callback);
    }
    return SP.UI.ModalDialog.showModalDialog(options);
}

 

This lets me call the Report Server URL via:

var d = do_modal(url, title);

I'm catching the dialog object, because I want to do a few more things.

 

Pesky IsDlg=1

Report Server is very perculiar with the Query paramaters passed in, and SP Modal Dialog likes to add a IsDlg=1 to the query string, this doesn't play well. 

image

"An attempt was made to set a report parameter 'IsDlg' that is not defined in this report.  (rsUnknownReportParameter)"

You can fix this two ways, first, if you have access to report server, you can just add a unused optional parameter IsDlg. 

If you don't have access to report server, you now need to hack the ModalDialog, which is what I did next.

 

Give me an IFrame, and I will give you a SRC

var f = d.get_frameElement();
f.src = url; // remove IsDlg=1

 

I ask for the IFrame object created by the dialog, and then brute force the SRC attribute to my url.  This undo the extra IsDlg appended by the ModalDialog.

 

Awesome, my report is now being downloaded, while the user waits with a nice friendly modal popup :-)

image

 

But wait, hmm, big problem...  screen is stuck

:-(

image

Because the PDF file is downloaded, the browser never receives the signal to stop and dispose the Modal Dialog.  Now the site is stuck.  Which brings us to my final trick.

 

Manually attach and watch the IFrame, and dispose the dialog when we're done

var t = function() {
    // ready state goes from "loading" to "interactive"
    if (f.readyState != 'loading') {
        d.close(SP.UI.DialogResult.cancel);
        return;       
    }
    setTimeout(t, 2000);       
};

setTimeout(t, 2000);

 

Let's define a function t, we'll be watching the readyState property of the IFrame object.  When it is no longer loading, we'll close the modal dialog.  Wait and recalculate every 2 seconds until this happens.

Here's the MSDN article on IFrame.readyState

http://msdn.microsoft.com/en-us/library/ms534359(v=VS.85).aspx

One of the more interesting thing is that when you are downloading a PDF, the readyState remains in the "interactive" state, and never reaches the "loaded" state.  This is the reason a loaded() event doesn't fire, and thus the default SharePoint ModalDialog doesn't know we're done and need to fix itself.

 

Everything in one go:

 

    var d = do_modal(url, title);
    var f = d.get_frameElement();
    f.src = url; // remove IsDlg=1
    var t = function() {
        // ready state goes from "loading" to "interactive"
        if (f.readyState != 'loading') {
            d.close(SP.UI.DialogResult.cancel);
            return;       
        }
        setTimeout(t, 2000);       
    };    
    setTimeout(t, 2000);

Not many lines of javascript, with an awesome result.