Wednesday, February 29, 2012

Generate type safe classes for sharepoint - vol2

I created a T4 template file that will:
1 - Search for SharePoint content types.
2 - Generate type safe Names and ID's for them.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<# 
// GET fileNamespace -> http://lennybacon.com/CommentView,guid,6ba5f768-6325-4f09-8341-201122804f52.aspx
var hostServiceProvider = (IServiceProvider)Host;
var dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
var activeSolutionProjects = (Array)dte.ActiveSolutionProjects;
var dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0);
var defaultNamespace = dteProject.Properties.Item("DefaultNamespace").Value;
var templateDir = Path.GetDirectoryName(Host.TemplateFile);
var fullPath = dteProject.Properties.Item("FullPath").Value.ToString();
fullPath = fullPath.EndsWith("\\") ? fullPath.Substring(0, fullPath.Length-1) : fullPath;
var subNamespace = templateDir.Replace(fullPath, string.Empty).Replace("\\", ".");
var fileNamespace = string.Concat(defaultNamespace, subNamespace);

// GET All XML files -> http://weblogs.asp.net/lhunt/pages/CSharp-Coding-Standards-document.aspx
var searchPath = new DirectoryInfo(fullPath).Parent.FullName;
var folderList = new Stack<string>();
var allXmlFiles = new List<string>();
string[] currentFolders = null;
string[] currentFiles = null;
string thisFolder = null;
folderList.Push(searchPath);
while(folderList.Count > 0)
{
    thisFolder = folderList.Pop();
    currentFiles = Directory.GetFiles(thisFolder, "*.xml");
    foreach(string file in currentFiles) if (!file.Contains("\\Debug\\")) allXmlFiles.Add(file);      
    currentFolders = Directory.GetDirectories(thisFolder);
    if(currentFolders != null && currentFolders.Length > 0) foreach(string folder in currentFolders) folderList.Push(folder);    
}

// GET All Fields from XML files
var fields = new List<KeyValuePair<string, KeyValuePair<string, KeyValuePair<string, string>>>>();
foreach (string xmlFile in allXmlFiles)
{
 var doc = new XmlDocument();
    doc.Load(xmlFile);
    XmlElement root = doc.DocumentElement;
    foreach (XmlNode node in root.ChildNodes)
        if (node.Name == "ContentType" && node.Attributes != null)
  {
            var attributeName = node.Attributes["Name"];
            var attributeID = node.Attributes["ID"];
            var attributeDisplayName = node.Attributes["Description"];
   fields.Add(new KeyValuePair<string, KeyValuePair<string, KeyValuePair<string, string>>>((attributeName != null) ? attributeName.InnerText.Replace(" ", string.Empty).Replace(
                                            "-", string.Empty) : "",new KeyValuePair<string, KeyValuePair<string, string>>(xmlFile.Replace(searchPath + "\\",""), new KeyValuePair<string, string>((attributeID != null) ? attributeID.InnerText : "",(attributeDisplayName != null) ? attributeDisplayName.InnerText : ""))));
  }
}
#>// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ContentTypes.cs" company="Imtech ICT Integrated Solutions">
//   Copyright 2012 by Imtech ICT Integrated Solutions. All rights reserved. This material may not be duplicated for any profit-driven enterprise.
// </copyright>
// <summary>
//   Static ContentType Names And Ids
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace <#= fileNamespace #>
{
    using Microsoft.SharePoint;

    /// <summary>
    /// Static ContentTypeNames
    /// </summary>
    public static partial class ContentTypeNames
    {
<#
string previousFile = "";
bool firstTimeRun = true;
foreach (var field in fields.OrderBy(t => t.Value.Key).ThenBy(t => t.Key))
{
 if (previousFile != field.Value.Key)
 { if (!firstTimeRun)
  { #>

<# }
  if (firstTimeRun){firstTimeRun = false;} #>
        // <#= field.Value.Key #>
<#   previousFile = field.Value.Key;
 } #>
        public static readonly string <#= field.Key #> = "<#= field.Key #>";
<#}#>
    }

    /// <summary>
    /// Static ContentTypeIds
    /// </summary>
    public static partial class ContentTypeIds
    {
<#
previousFile = "";
firstTimeRun = true;
foreach (var field in fields.OrderBy(t => t.Value.Key).ThenBy(t => t.Key))
{
 if (previousFile != field.Value.Key)
 { if (!firstTimeRun)
  { #>

<# }
  if (firstTimeRun){firstTimeRun = false;} #>
        // <#= field.Value.Key #>
<#   previousFile = field.Value.Key;
 } #>
        public static readonly SPContentTypeId <#= field.Key #> = new SPContentTypeId("<#= field.Value.Value.Key #>");
<#  } #>
    }
}

Generate type safe classes for sharepoint - vol1

I created a T4 template file that will:
1 - Search for SharePoint fields.
2 - Generate type safe FieldNames and ID's for them.


<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<# // GET fileNamespace -> http://lennybacon.com/CommentView,guid,6ba5f768-6325-4f09-8341-201122804f52.aspx
var hostServiceProvider = (IServiceProvider)Host;
var dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
var activeSolutionProjects = (Array)dte.ActiveSolutionProjects;
var dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0);
var defaultNamespace = dteProject.Properties.Item("DefaultNamespace").Value;
var templateDir = Path.GetDirectoryName(Host.TemplateFile);
var fullPath = dteProject.Properties.Item("FullPath").Value.ToString();
fullPath = fullPath.EndsWith("\\") ? fullPath.Substring(0, fullPath.Length-1) : fullPath;
var subNamespace = templateDir.Replace(fullPath, string.Empty).Replace("\\", ".");
var fileNamespace = string.Concat(defaultNamespace, subNamespace);
// END GET fileNamespace

// GET All Fields from XML files -> http://weblogs.asp.net/lhunt/pages/CSharp-Coding-Standards-document.aspx
string searchPath = new DirectoryInfo(fullPath).Parent.FullName;
Stack<string> folderList = new Stack<string>();
List<string> allXmlFiles = new List<string>();
string[] currentFolders = null;
string[] currentFiles = null;
string thisFolder = null;
folderList.Push(searchPath);
while(folderList.Count > 0)
{
thisFolder = folderList.Pop();
currentFiles = Directory.GetFiles(thisFolder, "*.xml");
foreach(string file in currentFiles)
if (!file.Contains("\\Debug\\"))
allXmlFiles.Add(file);
currentFolders = Directory.GetDirectories(thisFolder);
if(currentFolders != null && currentFolders.Length > 0)
foreach(string folder in currentFolders)
folderList.Push(folder);
}
var fields = new List<KeyValuePair<string, KeyValuePair<string, KeyValuePair<string, string>>>>();
foreach (string xmlFile in allXmlFiles)
{
var doc = new XmlDocument();
doc.Load(xmlFile);
XmlElement root = doc.DocumentElement;
foreach (XmlNode node in root.ChildNodes)
if (node.Name == "Field" && node.Attributes != null)
{
var attributeName = node.Attributes["Name"];
var attributeID = node.Attributes["ID"];
var attributeDisplayName = node.Attributes["DisplayName"];
string fieldName = "";
string fieldId = "";
string fieldDisplayName = "";
if (attributeName != null) fieldName = attributeName.InnerText;
if (attributeID != null) fieldId = attributeID.InnerText;
if (attributeDisplayName != null) fieldDisplayName = attributeDisplayName.InnerText;
fields.Add(
new KeyValuePair<string, KeyValuePair<string, KeyValuePair<string, string>>>(
fieldName,
new KeyValuePair<string, KeyValuePair<string, string>>(
xmlFile.Replace(searchPath + "\\",""), new KeyValuePair<string, string>(fieldId, fieldDisplayName))));
}
}
// Get All SiteColumn Files
#>// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Fields.cs" company="Imtech ICT Integrated Solutions">
// Copyright 2012 by Imtech ICT Integrated Solutions. All rights reserved. This material may not be duplicated for any profit-driven enterprise.
// </copyright>
// <summary>
// Static Field Names And Ids
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace <#= fileNamespace #>
{
using System;

/// <summary>
/// Static FieldNames
/// </summary>
public static partial class FieldNames
{
<#
string previousFile = "";
foreach (var field in fields.OrderBy(t => t.Value.Key).ThenBy(t => t.Key))
{
if (previousFile != field.Value.Key)
{ #>

// <#= field.Value.Key #>
<# previousFile = field.Value.Key;
} #>
public static readonly string <#= field.Key #> = "<#= field.Key #>";
<#}#>
}

/// <summary>
/// Static FieldIds
/// </summary>
public static partial class FieldIds
{
<#
foreach (var field in fields.OrderBy(t => t.Value.Key).ThenBy(t => t.Key))
{
if (previousFile != field.Value.Key)
{ #>

// <#= field.Value.Key #>
<# previousFile = field.Value.Key;
} #>
public static readonly Guid <#= field.Key #> = new Guid("<#= field.Value.Value.Key #>");
<# } #>
}
}


This will create code like:



// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Fields.cs" company="Imtech ICT Integrated Solutions">
// Copyright 2012 by Imtech ICT Integrated Solutions. All rights reserved. This material may not be duplicated for any profit-driven enterprise.
// </copyright>
// <summary>
// Static Field Names And Ids
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ZP.Intranet.Helpers
{
using System;

/// <summary>
/// Static FieldNames
/// </summary>
public static partial class FieldNames
{

// Intranet\Content\SiteColumns\SiteColumns\Elements.xml
public static readonly string ColumnAuthor = "ColumnAuthor";
public static readonly string DocumentDescription = "DocumentDescription";
public static readonly string DocumentThema = "DocumentThema";
public static readonly string DocumentThemaTaxHTField0 = "DocumentThemaTaxHTField0";
public static readonly string FAQAntwoord = "FAQAntwoord";
public static readonly string GoogleMapsUrl = "GoogleMapsUrl";
public static readonly string isSticky = "isSticky";
public static readonly string JobTitle2 = "JobTitle2";
public static readonly string ListIndex = "ListIndex";
public static readonly string NewsExpirationDate = "NewsExpirationDate";
public static readonly string PublishingPageIntro = "PublishingPageIntro";
public static readonly string RouteContactDescription = "RouteContactDescription";
public static readonly string StickyExpirationDate = "StickyExpirationDate";
public static readonly string SummaryLinks1 = "SummaryLinks1";
public static readonly string WijzigingenTonen = "WijzigingenTonen";
public static readonly string Workingdays = "Workingdays";
public static readonly string ZMobiel = "ZMobiel";
public static readonly string ZPFax = "ZPFax";
public static readonly string ZPPicture = "ZPPicture";
public static readonly string ZPSecretariaat = "ZPSecretariaat";
public static readonly string ZPTelefoon = "ZPTelefoon";

// Kwaliteit\Content\SiteColumns\SiteColumns\Elements.xml
public static readonly string DocumentEigenaar = "DocumentEigenaar";
public static readonly string DocumentVersie = "DocumentVersie";
public static readonly string EvaluatieDatum = "EvaluatieDatum";
public static readonly string EvaluatieDatumVoorstel = "EvaluatieDatumVoorstel";
public static readonly string EvaluatieResultaat = "EvaluatieResultaat";
public static readonly string EvaluatieStatus = "EvaluatieStatus";
public static readonly string GewijzigdToelichting = "GewijzigdToelichting";
public static readonly string KwaliteitThema = "KwaliteitThema";
public static readonly string KwaliteitThemaTaxHTField0 = "KwaliteitThemaTaxHTField0";
public static readonly string Reactie = "Reactie";
public static readonly string ReactiePersoon = "ReactiePersoon";
public static readonly string RedenAfkeur = "RedenAfkeur";
public static readonly string RedenUitstel = "RedenUitstel";
public static readonly string Uitstel = "Uitstel";
}

/// <summary>
/// Static FieldIds
/// </summary>
public static partial class FieldIds
{

// Intranet\Content\SiteColumns\SiteColumns\Elements.xml
public static readonly Guid ColumnAuthor = new Guid("{8fed943f-9baf-44fe-b7c6-623abadabd42}");
public static readonly Guid DocumentDescription = new Guid("{1828f1f6-35fb-4795-92c9-477dcd54a921}");
public static readonly Guid DocumentThema = new Guid("{cd323e0e-665a-4709-81dc-5746d741e492}");
public static readonly Guid DocumentThemaTaxHTField0 = new Guid("{a3e9d854-9997-449a-97f3-8d7066bab16f}");
public static readonly Guid FAQAntwoord = new Guid("{bf170ac7-397c-4d58-97a3-6918d4c3b340}");
public static readonly Guid GoogleMapsUrl = new Guid("{863efa7c-a265-452e-97a2-52836ffbfa82}");
public static readonly Guid isSticky = new Guid("{88f5eeb8-ec0c-419b-9c30-c4c3295e5547}");
public static readonly Guid JobTitle2 = new Guid("{2ace342a-be43-4412-92c0-504cc73f2675}");
public static readonly Guid ListIndex = new Guid("{0cbd1771-2b6e-4f68-93ea-70f9bb2c16ca}");
public static readonly Guid NewsExpirationDate = new Guid("{0fe49e6f-e953-410b-99f5-44cfc71bd393}");
public static readonly Guid PublishingPageIntro = new Guid("{1b72fc77-3add-486c-869c-5c71c25a4c77}");
public static readonly Guid RouteContactDescription = new Guid("{4c9f4081-adbf-40c2-9cb7-ff464463b6d4}");
public static readonly Guid StickyExpirationDate = new Guid("{fbae95fc-0e10-47bc-8ef1-2cf61a417d12}");
public static readonly Guid SummaryLinks1 = new Guid("{3b4e0c4d-6dea-49c9-b80d-b536d301817a}");
public static readonly Guid WijzigingenTonen = new Guid("{1ec13dcd-adbd-485b-91ab-d703e7ce3ebc}");
public static readonly Guid Workingdays = new Guid("{65a02752-d6fe-4806-9813-f48f23e47cc8}");
public static readonly Guid ZMobiel = new Guid("{7e10bf64-6f59-4f01-b5e9-df6e63da3dc7}");
public static readonly Guid ZPFax = new Guid("{1dfd705e-77eb-4c06-9c81-7464b90df58e}");
public static readonly Guid ZPPicture = new Guid("{57bf7368-a9f4-4fc4-a746-b20ab49dc81c}");
public static readonly Guid ZPSecretariaat = new Guid("{8a797061-3daa-45a1-8d12-8f122ba5be57}");
public static readonly Guid ZPTelefoon = new Guid("{ca259e0c-055a-41e5-82f4-1705b4884c45}");

// Kwaliteit\Content\SiteColumns\SiteColumns\Elements.xml
public static readonly Guid DocumentEigenaar = new Guid("{4dd34746-7b40-4235-99a7-06cfec789291}");
public static readonly Guid DocumentVersie = new Guid("{e457c652-b3d7-4301-b485-e709256c2dd2}");
public static readonly Guid EvaluatieDatum = new Guid("{814e7328-1ede-42e6-8c36-49b852ca99ed}");
public static readonly Guid EvaluatieDatumVoorstel = new Guid("{c2c3cb23-dc2c-45e0-ac35-8ba706601f71}");
public static readonly Guid EvaluatieResultaat = new Guid("{84145504-5e19-4ffd-b023-d8b44562ae6a}");
public static readonly Guid EvaluatieStatus = new Guid("{9355964F-EA27-4CB4-ACD6-083E6E8165B1}");
public static readonly Guid GewijzigdToelichting = new Guid("{bd1743f0-6af7-4211-a340-50c0fa883f54}");
public static readonly Guid KwaliteitThema = new Guid("{2efce8ed-eb88-4afb-b3d7-ba01db53f0b4}");
public static readonly Guid KwaliteitThemaTaxHTField0 = new Guid("{c32bb43b-54f7-4e08-a169-cdec83885bbb}");
public static readonly Guid Reactie = new Guid("{9e89ed0d-bc55-4d3b-a908-568a93925327}");
public static readonly Guid ReactiePersoon = new Guid("{11189fed-e050-4317-8d67-0a5b90f48073}");
public static readonly Guid RedenAfkeur = new Guid("{21486E8F-A1E9-4104-AFD7-2871599C058B}");
public static readonly Guid RedenUitstel = new Guid("{4e331dd7-51ab-4a35-b996-7b8617bfa4c2}");
public static readonly Guid Uitstel = new Guid("{084d857a-546d-41bc-b95b-a535c375524e}");
}
}

Saturday, February 4, 2012

Manipulating query string, the type safe way!

Our goal for today: Safe some valuable time.

Everyone who is developing for the web will come across the use of query string parameters.

SNAGHTML1a40b7f2

Handling these little buggers has proven to be quite time consuming.
Say we have: ../Default.aspx?PageNumber=1
Methods like Page.Request.QueryString[] are far from intuitive:

The old way:

public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
int pageNumber;
const string qsParam = "PageNumber";
object qsObject = Page.Request.QueryString[qsParam];
if (qsObject != null)
{
int qsValue;
if (int.TryParse(qsObject.ToString(), out qsValue))
pageNumber = qsValue;
else
pageNumber = -1;
}
else
pageNumber = -1;
}
}

The better way:

public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
var queryString = new QueryStringParam();
int pageNumber = queryString.PageNumber ?? -1;
}
}

public class QueryStringParam : QueryStringParser
{
public int? PageNumber;
}

Source code of QueryStringParser:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Specialized;
using System.Web;
using System.Reflection;

namespace Helpers
{
public abstract class QueryStringParser
{
private NameValueCollection _queryString;

private FieldInfo[] _fields;
private IEnumerable<FieldInfo> Fields
{
get { return _fields ?? (_fields = GetType().GetFields()); }
}

protected QueryStringParser()
{
InitializeQueryString();
}

private void InitializeQueryString()
{
foreach (var fieldInfo in Fields) fieldInfo.SetValue(this, null);
_queryString = HttpUtility.ParseQueryString(HttpContext.Current.Request.Url.Query);
if (_queryString.Keys.Count > 0)
foreach (string key in _queryString.Keys)
{
var foundField = Fields.FirstOrDefault(cust => cust.Name == key);
if (foundField != null)
{
Object tempObject = null;
var value = _queryString[key];
var fieldType = foundField.FieldType;
try
{
if (fieldType == typeof(Guid?)) tempObject = new Guid(value);
if (fieldType == typeof(string)) tempObject = value;
if (fieldType == typeof(int?)) tempObject = int.Parse(value);
if (fieldType == typeof(bool?)) tempObject = bool.Parse(value);

// check for enum
var underlyingType = Nullable.GetUnderlyingType(fieldType);
if (underlyingType != null && underlyingType.IsEnum)
tempObject = Enum.Parse(underlyingType, value, true);

if (tempObject != null) foundField.SetValue(this, tempObject);
}
catch (Exception)
{
} // Can not convert querystring value into strongly typed parameter
}
}
}

public override string ToString()
{
foreach (var field in Fields)
{
var fieldValue = field.GetValue(this);
if (fieldValue != null)
_queryString[field.Name] = fieldValue.ToString();
else
_queryString.Remove(field.Name);
}
string returnValue = String.Format("?{0}", _queryString);
InitializeQueryString();
return returnValue;
}
}
public static class QueryStringParserExtensionMethods
{
public static bool HasValue(this string input)
{
return (!string.IsNullOrEmpty(input));
}
public static string Value(this string input)
{
return input;
}
}
}

Friday, October 7, 2011

Visual Studio Extension: JQuery Google CDN

I created a little item template for simply adding JQuery support to your SharePoint project.

More info to come...

Tuesday, December 28, 2010

APK Edit

Ever wanted to simply change an icon on your Android phone and found yourself lost in the endless lists of forum posts saying: you have to change heaven and earth to do this...
NOT ANYMORE!!!!

APK Edit is born!











With this little program you can simply change icons of your android application.
It even lets you change the applications name.
But wait mike..... there's more.....
It even lets you edit the text used in applications.

Sounds to good to be true...
Well... to be honest.... it is.
But I've come a long way from that initial question to the final answer.
My conclusion so far, it works for most of my applications.
It probably will work on most of yours as well.
Try it out now: APK Edit

Thursday, April 8, 2010

Passed exam 70-235

Victory Is Mine!!!
Today I successfully passed the exam 70-235 meaning that from now on I can call myself technical specialist in:
Developing Business Process and Integration Solutions by Using Microsoft BizTalk Server 2006

Next one will be exam: 70-241
Developing Business Process and Integration Solutions by Using Microsoft  BizTalk  Server  2006 R2

Friday, March 26, 2010

Let BizTalk automatically send a WCF Response message on delivery without the use of orchestrations.

Recently I faced the challenge of returning a delivery acknowledgement message to an WCF service when it shoots a new message in our BizTalk environment. All this needs to be done using “context based routing”, in other words, without the use of an orchestration. Main reason for this is the extra performance overhead you get using orchestrations.

To sum it all up, our goal for today is:

  • DON’T USE ORCHESTRATIONS!
  • Send a dummy message to the WCF service.
  • Wait for the message to be placed into the BizTalk messagebox.
  • Receive an acknowledgement that the message is indeed accepted by BizTalk.

After some “intelli searching” on the web I came across this article of Daniel Probert. In this article he shares his vision on the problem at hand. Being a total newby in BizTalk world his explanation was a bit mindboggling for me. After a couple of hours playing with the concept I manage to recreated his concept to the point I actually understood what was going on beneath the shiny surface.

It all has to do with a “magical” subscription that will be created on the fly by BizTalk when the receive-port receives a new message. But first things first!

To be able to send messages to BizTalk using a webservice we can use the “BizTalk WCF Service Publishing Wizard”. This Wizard will guide you thru the process of setting up your Webservice and “connecting” it to your BizTalk application, resulting in a newly created “receive port / location” in your BizTalk Application. This newly created port is by default a Two Way receive port, meaning it can talk both ways (Request/ Response). Exactly what I wished for… NOT!

The problem lies is the response handling, when BizTalk receives a new incoming message it will try to deliver it to its subscribers and.. that’s about it…. There’s no build-in response system! So what you will experience using the Web-service is a time-out (lack of response) on you WCF request.

Back to the “magical” subscription part… every time BizTalk accepts a message from a two way port it automatically creates a new subscription for its “response” port subscribing it to ALL messages containing the following promoted properties:

  • EpmRRCorrelationToken == {*****}
  • RouteDirectToTP == True

Sadly these properties can’t be promoted the usual way since they are some sort of special system properties. To accomplish the impossible we will have to create a custom pipeline that will do exactly that for us. And that’s exactly what I did, using a little code snippet from Daniel. clip_image002

After using this custom pipeline as your “Receive Pipeline” BizTalk will send back a copy of the incoming message as an acknowledgement to the initial WCF request.

Yeah! No more timeout’s, problem solved!
Installer: Windows Installer File (MSI)
Sourcecode: CodePlex
Documentation: Word Document