Monday 23 May 2011

Sending Emails using User/Sandboxed Solutions

If you've done any kind of work ​emailing using SharePoint you are probably aware of the SendEmail method available through the SharePoint object model as part of the Microsoft.SharePoint.Utilities namespace. It's very useful as it allows us to send emails with a minimum amount of effort and without having to know the SMTP settings and such like.

In user/sandboxed solutions, this method is not available so we're left in a bit of a limbo, not knowing how to achieve it as there's nothing else available in the object model that achieves the same thing. Let's say we have a custom "Contact Us" webpart which sends someone an email with the contact information: how can you achieve the same thing?

We could use the System.Net.Mail methods available to us, but is it reasonable to expect a content author to supply the webpart with the appropriate SMTP settings? Why is that an issue? Well, these settings can be accessed programmatically using the object model which would resolve this minor difficulty, but they are not available in a sandbox. They exist at Web Application level and are therefore off limits.

Fortunately, there is another way, and that is through using SharePoint Designer workflow. You can trigger a workflow to run programmatically and also supply information to the workflow and set the initiation values. Here's the code to start a workflow:

/// <summary>
/// Method to handle starting the appropriate Workflow as specified by the Site Collection Workflow Name property
/// </summary>
private void StartWorkflow()
{
SPWorkflowAssociation wfa = SPContext.Current.Web.WorkflowAssociations.GetAssociationByName(this.SiteCollectionWorkflowName, CultureInfo.InvariantCulture);
wfa.AssociationData = this.GetWorkflowInitiationData();
SPContext.Current.Site.WorkflowManager.StartWorkflow(null, wfa, this.GetWorkflowInitiationData(), SPWorkflowRunOptions.Asynchronous);
}


/// <summary>
/// Method to construct the Workflow Initiation/Association Data
/// </summary>
/// <returns>A string containing the XML schema and values necessary for the Association Data</returns>
private String GetWorkflowInitiationData()
{
String schema = "<dfs:myFields xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " +
"xmlns:dms=\"http://schemas.microsoft.com/office/2009/documentManagement/types\" " +
"xmlns:dfs=\"http://schemas.microsoft.com/office/infopath/2003/dataFormSolution\" " +
"xmlns:q=\"http://schemas.microsoft.com/office/infopath/2009/WSSList/queryFields\" " +
"xmlns:d=\"http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields\" " +
"xmlns:ma=\"http://schemas.microsoft.com/office/2009/metadata/properties/metaAttributes\" " +
"xmlns:pc=\"http://schemas.microsoft.com/office/infopath/2007/PartnerControls\" " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<dfs:queryFields /><dfs:dataFields><d:SharePointListItem_RW>{0}</d:SharePointListItem_RW>" +
"</dfs:dataFields></dfs:myFields>";


StringBuilder sb = new StringBuilder();
sb.AppendFormat("<d:EmailTo>{0}</d:EmailTo>", this.SendToEmailAddress);
sb.AppendFormat("<d:EmailSubject>{0}</d:EmailSubject>", this._subject.Text);
sb.AppendFormat("<d:EmailText>{0}</d:EmailText>", this._comments.Text);
sb.AppendFormat("<d:AutoResponseTo>{0}</d:AutoResponseTo>", this._email.Text);
sb.AppendFormat("<d:AutoResponseSubject>{0}</d:AutoResponseSubject>", this.AutoResponseSubject);
sb.AppendFormat("<d:AutoResponseText>{0}</d:AutoResponseText>", this.AutoResponseText);
return String.Format(schema, sb.ToString());
}

The StartWorkflow method is where the workflow starts and the GetWorkflowInitiationData is where the XML erquired to set the initiation properties are set up. From what I've seen, the XML stays pretty-much the same with properties being set using the "dataFields" namespace "d:". The property names are workflow initiation properties I set up in my workflow.  The SiteCollectionWorkflowName used in the StartWorkflow method is a property of the webpart which could be hidden away.

Monday 16 May 2011

Google Maps vs Bing Maps

One of the nice things about Google Maps is the ability to embed a static image of your map, rather than having to use a JavaScript API or resorting to downloading multiple JavaScript files to support the map's features. Simply using a URL to point to one of Google's services was enough when it was used as the SRC attribute of an IMG tag - simple. It was something that was missing, until fairly recently, from Bing Maps.

Fortunately, Bing Maps have caught up and now have a REST API available to support static imagery in the same way as Google Maps. The details can be found here: http://msdn.microsoft.com/en-us/library/ff701724.aspx.

Simply adding the correct URL to the SRC attribute of an IMG tag creates the correct image to embed thereby avoiding multiple JavaScript downloads and any use of a client-side API.

Publishing Images and Search Indexing

This may be something that you already know, but in SharePoint 2007 fields based on the Publishing Image​ field type caused problems when you wanted to use these kinds of field in the Search Results. 
The problem became apparent if you wanted to use something like a Roll Up Image on content pages where you wanted to display the image both using Content Query Web Parts (which don't have a problem with Publishing Image fields) and in Search Results. The problem is, Search doesn't index fields of this type. It just doesn't. So you can set up your metadata property which you can add a property mapping for the Publishing Rollup Image field but when you try to surface the data contained by the property in search it contains nothing.

Having experienced this problem in 2007 during a custom Search project, I was already aware of the limitation and then SharePoint 2010 came around and a similar requirement came about. I was approached to take a look into whether it was still a problem and had some expectation that perhaps it had been resolved. Alas, it has not.

So, buyer beware!!

How To Resolve

Fortunately, there is a simple work-around. Create a text field into which you automatically add the contents from your Publishing Image field as text by whatever means you prefer and then use that to set up your metadata property instead. Works like a charm.