mandag den 27. juli 2015

5 ting du bør gøre for at undgå at blive udmattet på arbejde

1. Planlæg din dag


Vælg tre ting ud du VIL nå i løbet af dagen. Vælg tre ting, der ikke er alt for nemme og ikke alt for svære at nå i løbet af dagen. De skal både være nemme at gå til men også være en smule udfordrende så det giver en god fornemmelse når du når opgaverne.

2. Start med den sværeste ting

fredag den 19. juni 2015

Der opstod en fejl i kommunikationen med Sitecore App Center-serveren

Problem

Når man logger ind i Sitecore App Center får man følgende fejl



Årsag

Jeg har undersøgt diverse Sitecore logs men der er ingen ting at se. Årsagen må derfor stå hen i det uvisse.

Løsning

Kør IISReset.exe i en kommandoprompt. ;-)


onsdag den 21. januar 2015

Er man virkelig en dygtig leder, hvis man har det helt fint med at fyre folk?

Toke Kruse har skrevet et rigtig godt blog-indlæg om, at dygtige ledere har det fint med at fyre folk. Se blog-indlægget her

Jeg er generelt enig synes dog blog-indlægget er lidt til den kyniske side.

De ledere, der har svært ved at fyre er, som jeg ser det, også dem, der har svært ved at formulere en konkret forventning til medarbejderne.

Hvis ikke medarbejderen ved, hvilke forventninger, der er og hvad der giver værdi for firmaet er det jo i sagens natur svært at indfri forventningen.

Så inden lederen kan være dygtig I Tokes terminologi som jeg ser det skal han
  1. Have formuleret en klart defineret målsætning/forventning
  2. Sikret en måde at måle om disse indfries
  3. Sikret, at alle forstår den målsætningen/forventningen
  4. Sikret rette mand til rette opgave
Hvis dette er opfyldt er jeg overbevist om, at man både kan få varmestue og højtydende medarbejdere. Og hvis man kan opnå det synes jeg man er en dygtig leder.

Opdatering: Teksten er også skrevet som kommentar på Tokes blog-indlæg og han har været så venlig at svar og som forventet er blog-indlægget også lidt tænkt som en provokation, da det jo er det der får flest op af sofaerne.

onsdag den 7. januar 2015

Jeg hader at planlægge min arbejdstid men hader også at vide, at det er nødvendigt....

Jeg har derfor de sidste par måneder tvunget mig selv til at starte med at notere på et papir, hvad dagen skal bruges på.  Det virker egentlig meget godt men.....

Det sætter en stor fed bremse i min iver og energi fra morgenstunden af og gør egentlig at jeg er lidt træt af det hele inden jeg er kommet rigtigt igang.
Lidt ligesom at starte med at læse sine mails om morgenen!

Jeg har derfor lavet følgende tiltag

Jeg planlægger nu morgendagens arbejde inden jeg går hjem. 
Samme øvelse som tidligere bare dagen inden

Fordele
  1. Bedre hukommelse af, hvilke udeståender, der skal følges op på.
  2. Man får tænkt de ting igennem man allerede er igang med og opdager måske noget man har overset.
  3. Hjernen får tid til at koble af inden fyraften da vi i mange tilfælde undgår at risikere at komme i tanke om noget efter fyraften da vi allerede har været det hele igennem i planlægningen.
Ulemper
  1. Ingen undskyldning for at lægge sig på sofaen fordi man er træt og udkørt.
  2. Svært at overbevise konen om at man er meget vigtig på arbejde fordi man ikke hele tiden kommer på ting man lige skal notere på et stykke papir.

torsdag den 4. december 2014

Dependency Injection in a Nutshell

Issue

How to explain in a nutshell how dependency injection makes our code more robust, simple and more easy to maintain.

Case

I have a class that returns a list of user objects loaded from an XML-file.

 public class OpusRepository : IOpusRespository  
 {  
   public virtual List<OpusUser> Users()  
   {  
     // Read file from static path  
     // Get users from file  
     // return list of users  
   }  
 }  

The code that uses the IOpusRepository gets an instance of the OpusRepository class by calling a custom class that creates an instance of the OpusRepository.
It looks like this

onsdag den 12. november 2014

How to move users from Sitecore 5.3 to Sitecore 7.2

Issue

Users from a Sitecore 5 solution is needed in a new Sitecore 7.2 solution.

Solution

Two webforms are made. One to serialize users from the Sitecore 5.3 site into xml and one to import users into the Sitecore 7.2 site from the xml.

Serialization of users from Sitecore 5.3

This code iterates all users in the solution and writes it to an xmlfile in the App_Data folder. Username, Email, Fullname and roles are serialized.


onsdag den 5. november 2014

Sitecore as a Nuget-package - Buildsetup Part 1


Issue

One of our main issues when developing Sitecore solutions is to keep our sites up to date with Sitecore files.

The solution to this issue has for years been to run a custom robocopy script that copies the files from the public development server.
This requires that the server is always reachable, up to date and not infected with misplaced files.

Solution

I have made a script that takes a Sitecore zip installation file as input and creates a nuget-package from it.

This article will show how this is done.

Following steps are taken when creating the nuget-package
  1. Initializing variables
  2. Unpacking zip-file
  3. Moving data-folder to App_Data/Sitecore
  4. Creating force.txt files to force empty directories to be included in nuget-package
  5. Copy developer license file to App_Data/Sitecore/License
  6. web.config changes - Correcting paths to dataFolder and license file 
  7. Creating nuspec file - Syncronising version of Sitecore with the nuget-package
  8. Creating nuget package

tirsdag den 21. oktober 2014

Parameter "AssemblyFiles" has invalid value

Issue

When  referencing an assembly the compiler fails with this error

 error MSB3248: Parameter "AssemblyFiles" has invalid value "C:\CruiseControl\...\myAssembly.dll". Culture is not supported. [C:\CruiseControl\Projects\TemplateSite\WebApp\WebApp.csproj]  
 error MSB3248: Parameter name: name [C:\CruiseControl\..\WebApp.csproj]  
 myAssembly is an invalid culture identifier. [C:\CruiseControl\..\WebApp.csproj]  

Cause

This is caused by an invalid value in the AssemblyCulture attribute in the Assembly.cs

 [assembly: AssemblyCulture("myInvalidValue")]  

Solution

Set the AssemblyCulture to a valid value e.g. en-US or da-DK or just set it to default


 [assembly: AssemblyCulture("")]  

torsdag den 16. oktober 2014

How to prevent NAnt task returning code 3 to fail full buildprocess

Issue

The issue is that my buildscript fails because a task returns a code different from 0 even though the executable succeeds.

The buildscript contains a task that copies my website to a development server.
I use RoboCopy.exe to do this.

The task looks like this

  <target name="upload" depends="release"> 
   <exec 
    program="${robocopy}" 
    commandline=".\Website ${devroot}\www ${robocopyUploadArgs}" 
           /> 
  </target> 

However RoboCopy has a set of codes it can return depending on how many files were copied. See http://ss64.com/nt/robocopy-exit.html for more info.
Here it tells that a return value below 9 is a success.

Solution

By specifying to parameters; failonerror="false" and resultproperty="return.code" it is possible to tell NAnt that we dont want to fail on error but want the exit-code to be put in a property named return.code. Following a simple <if> task enables us to test and fail if the return code is larger than 8.
The script now looks like this (remember to add property named return.code)

torsdag den 18. september 2014

Sublayout caching disabled when sublayout is hidden by serverside code

Issue

I have a leftmenu that can be configured on the page-template to be hidden using a Sitecore field. When the editor sets a checkmark in the HideLeftMenu field the placeholder containing the sc:Sublayout control is hidden. This seems to result in that caching of the Sublayout is ignored.
The code looks like this

Markup

 <asp:PlaceHolder runat="server" ID="phLeftColumn">  
   <div class="col-sm-4 col-md-3">  
     <asp:ContentPlaceHolder runat="server" ID="cphPageContentLeft">  
       <dom:Sublayout runat="server" ID="submenu" Path="/layouts/LeftMenu.ascx" Cacheable="True" VaryByData="True" />  
     </asp:ContentPlaceHolder>  
   </div>  
 </asp:PlaceHolder>  

Code behind

 phLeftColumn.Visible = Sitecore.Context.Item["HideLeftMenu"] != "1";   

lørdag den 23. august 2014

Automatic Multisite Handling By Traversing Sitecore Tree

Add automatic multisite handling to your Sitecore site by traversing the Sitecore Tree to find the available sites. You can decide to use the site name as subdomain or you can specify a hostName in a Sitecore field on the site-item.


 

 

 

 

lørdag den 18. december 2010

MVC 3

I have just installed VS 2010 and MVC3.
I like the idea that abstraction layers now is part of the programming interface and that it is now possible to test your website using unit testing. Hurra for that... But.. and I think there is a big but.

I have just created a new web application using the MVC 3 template.
This template creates a small site with login/logout/register functionality out of the box. Very nice.

But what I dont fancy at all is the fact that there is a lot of late bound code (Code that is resolved at Runtime).

Examples on late bound code:
1. ViewBag.Title (Set in Views and used in _layout)
2. @Html.Partial("_LogOnPartial") used in _layout
3. @Html.ActionLink("Register", "Register") (I know this is not likely to change name but what if it does and you forget to change it just one place?)

All these issues results in runtime errors but could in the "good" old days have been caught by the compiler by doing following..

1. Using Page.Title or a extension method on Page
2. Using @Register and tag
3. This is pretty much the same but would not resolve in a runtime error but a "not found" page. I dont know which one is better

Am I the only one with this concern?

lørdag den 11. december 2010

Asymmetric Encryption on Client and Server

In order to use SSL you need at dedicated server. This is not cost effective so the solution is to encrypt the stuff yourself.

This can be done but is not an easy job due to the fact that you need to find code that can encrypt and decrypt both on client and server.

You need this server class

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Web;  
 using System.Security.Cryptography;  
 using System.IO;  
 public class Cryptography  
 {  
   public static RSACryptoServiceProvider rsa;  
   public static void AssignParameter()  
   {  
     const int PROVIDER_RSA_FULL = 1;  
     const string CONTAINER_NAME = "SpiderContainer";  
     CspParameters cspParams;  
     cspParams = new CspParameters(PROVIDER_RSA_FULL);  
     cspParams.KeyContainerName = CONTAINER_NAME;  
     cspParams.Flags = CspProviderFlags.UseMachineKeyStore;  
     cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";  
     rsa = new RSACryptoServiceProvider(cspParams);  
   }  
   public static string EncryptData(string data2Encrypt)  
   {  
     AssignParameter();  
     StreamReader reader = new StreamReader(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\publickey.xml"));  
     string publicOnlyKeyXML = reader.ReadToEnd();  
     rsa.FromXmlString(publicOnlyKeyXML);  
     reader.Close();  
     //read plaintext, encrypt it to ciphertext  
     byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);  
     byte[] cipherbytes = rsa.Encrypt(plainbytes, false);  
     return Convert.ToBase64String(cipherbytes);  
   }  
   public static void AssignNewKey()  
   {  
     AssignParameter();  
     //provide public and private RSA params  
     StreamWriter writer = new StreamWriter(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\privatekey.xml"));  
     string publicPrivateKeyXML = rsa.ToXmlString(true);  
     writer.Write(publicPrivateKeyXML);  
     writer.Close();  
     //provide public only RSA params  
     writer = new StreamWriter(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\publickey.xml"));  
     string publicOnlyKeyXML = rsa.ToXmlString(false);  
     writer.Write(publicOnlyKeyXML);  
     writer.Close();  
   }  
   public static string DecryptData(string data2Decrypt)  
   {  
     if (data2Decrypt == null)  
       return null;  
     AssignParameter();  
     byte[] getpassword = Convert.FromBase64String(data2Decrypt);  
     StreamReader reader = new StreamReader(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\privatekey.xml"));  
     string publicPrivateKeyXML = reader.ReadToEnd();  
     rsa.FromXmlString(publicPrivateKeyXML);  
     reader.Close();  
     //read ciphertext, decrypt it to plaintext  
     byte[] plain = rsa.Decrypt(getpassword, false);  
     return System.Text.Encoding.UTF8.GetString(plain).Replace(UniqueGuidForEncryption.ToString(), "");  
   }  
 }  

And you need this set of javascript files and a reference to them

http://www.e-keys.dk/js/RSA/jsbn.js
http://www.e-keys.dk/js/RSA/prng4.js
http://www.e-keys.dk/js/RSA/rng.js
http://www.e-keys.dk/js/RSA/rsa.js
http://www.e-keys.dk/js/RSA/base64.js

Add this code to the OnLoad

 if( !System.IO.File.Exists(Server.MapPath("App_Data/publickey.xml")) || Request.QueryString["newkey"] != null )  
   Cryptography.AssignNewKey();  


This will create the public and the private key xml file that is used to encrypt/decrypt the data before it is sent in the postback.

Add this script to you page

 function do_encrypt(val) {  
   if (val.length == 0)  
     return val;  
   var rsa = new RSAKey();  
   rsa.setPublic('', '');  
   var res = rsa.encrypt(val);  
   return hex2b64(res);  
 }  
 function hashAndEncrypt(val) {  
   var rsa = new RSAKey();  
   rsa.setPublic('', '');  
   var res = rsa.encrypt(val);  
   return hex2b64(res);  
 }  

and add this two methods to your code behind

 protected String RSAModulus  
 {  
   get  
   {  
     XmlDocument doc = new XmlDocument();  
     doc.Load(Server.MapPath("App_Data/publickey.xml"));  
     return BitConverter.ToString(Convert.FromBase64String(doc.SelectSingleNode("RSAKeyValue/Modulus").InnerText)).Replace("-", "");  
   }  
 }  
 protected String RSAExponent  
 {  
   get  
   {  
     XmlDocument doc = new XmlDocument();  
     doc.Load(Server.MapPath("App_Data/publickey.xml"));  
   return BitConverter.ToString(Convert.FromBase64String(doc.SelectSingleNode("RSAKeyValue/Exponent").InnerText)).Replace("-", "");  
   }  
 }  

This is needed because the client side encryption needs hexa-decimal keys and the server side uses Base64Strings.

You need to call DataBind() as the last thing in OnLoad.

The last thing you need to do is encrypt the content of the textboxes. This is done in the click event of the button like this

 document.getElementById("textbox-id").value = do_encrypt(document.getElementById("textbox-id").value)  


You need to set the value for one and each of the textboxes that you need encrypted.

On the server you just call Cryptography.DecryptData(text) to decrypt the content of the textbox.

This should be it.

mandag den 14. december 2009

Cookies in ASP.NET

I always find myself struggling with Cookies in asp.net.

I always fail to add the cookies the right way or to remove them.
I always tend to open old projects to see how I coped then.
But most of the project are filled with different tries to get the cookie stuff to work so it doesnt help much.

There are three issues that we are dealing with

1. Adding a cookie that never expires
2. Adding a cookie that expires with a day or so
3. Removing a cookie

1 + 2. The first mistake I make is trying to add a cookie to the Request.Cookie collection which obviously doesnt work due to the fact it is a collection comming from the server.

All cookie should be added to the Response.Cookie collection.
For adding expiration you just set the Expire property on the HttpCollection before adding the cookie. For a cookie that never expires you set Expire = DateTime.MaxValue;

3. Removing a cookie is not done "just" by removing the cookie from the request cookie collection. You also need to add a new cookie with the same name as the one you want to remove and setting Expire to yesterday to Response.Cookie collection . This forces the cookie to expire the next time the client requests the page.

 HttpCookie c = new HttpCookie(CookieName, "false");  
 c.Expires = DateTime.Now.AddDays(-1);  
 Response.Cookies.Add(c);  
 Request.Cookies.Remove(CookieName);   

Very simple but a hell of a job if you forget just one of them.

onsdag den 9. december 2009

Changing control names on postback

I recently spent a whole day figuring out why my UserControl continuesly displayed inline errormessages instead of the validation summary I did tell it to use.

After af lot of investigation I found out that the controls in my UserControl did have changing control names upon postback due to inserted into sitecore placeholder.

I have failed to reproduce this scenario in a controlled matter but did find a fix to the problem.

Find the control that changes name upon postback and use this implementation of the sitecore placeholder.


 public class SCPlaceholder : Sitecore.Web.UI.WebControls.Placeholder  
   {  
     private List UsedKeys = System.Web.HttpContext.Current.Items["pckeys"] as List;  
     public SCPlaceholder()  
     {  
       if (UsedKeys == null)  
       {  
         UsedKeys = new List();  
         System.Web.HttpContext.Current.Items["pckeys"] = UsedKeys;  
       }  
     }  
     protected override void AddedControl(System.Web.UI.Control control, int index)  
     {  
       Sublayout s = control as Sublayout;  
       if (s != null && s.Controls.Count == 1)  
       {  
         string name = this.Key + "_" + s.Controls[0].GetType().Name;  
         if (UsedKeys.Contains(name))  
         {  
           for (int i = 0; i < 5; i++)  
           {  
             if (!UsedKeys.Contains(name + "_" + i))  
               name += "_" + i;  
           }  
         }  
         s.ID = name + "_sub";  
         s.Controls[0].ID = name;  
         UsedKeys.Add(name);  
       }  
       base.AddedControl(control, index);  
     }  
   }   

Experiences on how to reproduce this in a safe and clean environment is appreciated.

mandag den 2. november 2009

Early vs. late binding

In the good old COM days the early vs. late binding issue was an issue that all developers should deal with. This article describes the pros and cons of the early vs. late binding issue.

This days I havnt been dealing with this issue very much all though the System.Reflection namespace has been used a lot it is not excatly the same. The reflection namespace allows us to examine assemblies for methods and properties but is done to make a system very generic and should be designed not to fail if a specific method or property is not found.

The tendency in more and more generic CMS systems and internal modules raise this issue again.
Lets have a look at some code sample

 public class MyItems  
 {  
   public Dictionary items = new Dictionary();  
   public Dictionary Items  
   {  
     get  
     {  
       return items;  
     }  
   }  
 }  
 public class MyClass  
 {  
   public void OnLoad()  
   {  
     MyItems items = new MyItems();  
     String s = Items["Item1"];  
   }  
 }  


Two classes have been made. One that contains an Items property that returns a generic dictionary with all available items and one that uses late binding when accessing the Items property. It is late binding because we dont know if the item exists and the compiler cant warn us if we make a spelling error.
Depending on how the Items property are created the application might fail if a none existing item is accessed. Other applikations might just return an empty string but this might as well result in undesired result.

A far better way, though not solving but makeing handling of these issues more simple, is to create extension methods like this.

This results in a new static class and a change in the MyClass


 public static class MyExtensionMethods  
 {  
   public static String Item1(this MyItems item)  
   {  
     if (!item.Items.ContainsKey("Item1"))  
       throw new ApplicationException("Item1 does not exists");  
     return item.Items["Item1"];  
   }  
 }  
 public class MyClass  
 {  
   public void OnLoad()  
   {  
     MyItems items = new MyItems();  
     String s = items.Item1();  
   }  
 }  


This still uses late binding but it is now done in a more controlled manner and all classes that uses MyItems and has a using MyExtensionMethods; now can use the Item1() extension.
If the Item1 for some reason should change name or some other handling should be done if the item does not exists or is empty it only needs to be done place. And if you need to change the Item1() extension name the compiler now helps you find all references.

All though this sample code does not remove the late binding it sure removes much of the complexity of it by putting it together in one place.
Of course you can wrap the MyItems class in a wrapper class that exposes Item1 as a property which will give the same result as the extension method but requires more code.
And why not use this very neat feature in the .NET 3.5 framework called ExtensionMethods?

torsdag den 15. oktober 2009

How to Binary serialize a MailMessage for use with MessageQueue

The MailMessage object is in itself not serializeable.
The MessageQueue needs a serializeable object in order to work so as is the MailMessage is no target for the MessageQueue.

This has bothered me for a long time but finally the right project arrived with the need of Queuing MailMessages.

Almost all examples on the internet describes how to XML serialize the MailMessage but most of the times only the simple types are serialized; like To, From, Subject and Body.
What about AlternateViews and Attachments? This has been left for your imagination.

I decided to start my own SerializeableMailMessage class that should be Binary seriablizeable. Its only hard work and 500 lines of code is the result of this effort.

There is no tricky stuff in it. It is only hard work figuring out how to serialize each object in the MailMessage object.

You use it by creating a new SerializeableMailMessage() feeding it with a MailMessage object. GetMailMessage() on the SeriablizeableMailMessage object gives you the MailMessage object back.

 using System;  
 using System.Collections.Generic;  
 using System.Text;  
 using System.Text.RegularExpressions;  
 using System.Net;  
 using System.Net.Mail;  
 using System.IO;  
 using System.Net.Mime;  
 using System.Collections.Specialized;  
 namespace Netmester.Mail.Serialization  
 {  
      [Serializable]  
      internal class SerializeableLinkedResource  
      {  
           String ContentId;  
           Uri ContentLink;  
           Stream ContentStream;  
           SerializeableContentType ContentType;  
           TransferEncoding TransferEncoding;  
           internal static SerializeableLinkedResource GetSerializeableLinkedResource(LinkedResource lr)  
           {  
                if (lr == null)  
                     return null;  
                SerializeableLinkedResource slr = new SerializeableLinkedResource();  
                slr.ContentId = lr.ContentId;  
                slr.ContentLink = lr.ContentLink;  
                if (lr.ContentStream != null)  
                {  
                     byte[] bytes = new byte[lr.ContentStream.Length];  
         lr.ContentStream.Position = 0;  
                     lr.ContentStream.Read(bytes, 0, bytes.Length);  
                     slr.ContentStream = new MemoryStream(bytes);  
                }  
                slr.ContentType = SerializeableContentType.GetSerializeableContentType(lr.ContentType);  
                slr.TransferEncoding = lr.TransferEncoding;  
                return slr;  
           }  
           internal LinkedResource GetLinkedResource()  
           {  
                LinkedResource slr = new LinkedResource(ContentStream);  
                slr.ContentId = ContentId;  
                slr.ContentLink = ContentLink;  
                slr.ContentType = ContentType.GetContentType();  
                slr.TransferEncoding = TransferEncoding;  
                return slr;  
           }  
      }  
      [Serializable]  
      internal class SerializeableAlternateView  
      {  
           Uri BaseUri;  
           String ContentId;  
           Stream ContentStream;  
           SerializeableContentType ContentType;  
           List<SerializeableLinkedResource> LinkedResources = new List<SerializeableLinkedResource>();  
           TransferEncoding TransferEncoding;  
           internal static SerializeableAlternateView GetSerializeableAlternateView(AlternateView av)  
           {  
                if (av == null)  
                     return null;  
                SerializeableAlternateView sav = new SerializeableAlternateView();  
                sav.BaseUri = av.BaseUri;  
                sav.ContentId = av.ContentId;  
                if (av.ContentStream != null)  
                {  
                     byte[] bytes = new byte[av.ContentStream.Length];  
         // Reset read position  
         av.ContentStream.Position = 0;  
         av.ContentStream.Read(bytes, 0, bytes.Length);  
         sav.ContentStream = new MemoryStream(bytes);  
                }  
                sav.ContentType = SerializeableContentType.GetSerializeableContentType(av.ContentType);  
                foreach (LinkedResource lr in av.LinkedResources)  
                     sav.LinkedResources.Add(SerializeableLinkedResource.GetSerializeableLinkedResource(lr));  
                sav.TransferEncoding = av.TransferEncoding;  
                return sav;  
           }  
           internal AlternateView GetAlternateView()  
           {  
                AlternateView sav = new AlternateView(ContentStream);  
                sav.BaseUri = BaseUri;  
                sav.ContentId = ContentId;  
                sav.ContentType = ContentType.GetContentType();  
                foreach (SerializeableLinkedResource lr in LinkedResources)  
                     sav.LinkedResources.Add(lr.GetLinkedResource());  
                sav.TransferEncoding = TransferEncoding;  
                return sav;  
           }  
      }  
      [Serializable]  
      internal class SerializeableMailAddress  
      {  
           String User;  
           String Host;  
           String Address;  
           String DisplayName;  
           internal static SerializeableMailAddress GetSerializeableMailAddress(MailAddress ma)  
           {  
                if (ma == null)  
                     return null;  
                SerializeableMailAddress sma = new SerializeableMailAddress();  
                sma.User = ma.User;  
                sma.Host = ma.Host;  
                sma.Address = ma.Address;  
                sma.DisplayName = ma.DisplayName;  
                return sma;  
           }  
           internal MailAddress GetMailAddress()  
           {  
                return new MailAddress(Address, DisplayName);  
           }  
      }  
      [Serializable]  
      internal class SerializeableContentDisposition  
      {  
           DateTime CreationDate;  
           String DispositionType;  
           String FileName;  
           Boolean Inline;  
           DateTime ModificationDate;  
           SerializeableCollection Parameters;  
           DateTime ReadDate;  
           long Size;  
           internal static SerializeableContentDisposition GetSerializeableContentDisposition(System.Net.Mime.ContentDisposition cd)  
           {  
                if (cd == null)  
                     return null;  
                SerializeableContentDisposition scd = new SerializeableContentDisposition();  
                scd.CreationDate = cd.CreationDate;  
                scd.DispositionType = cd.DispositionType;  
                scd.FileName = cd.FileName;  
                scd.Inline = cd.Inline;  
                scd.ModificationDate = cd.ModificationDate;  
                scd.Parameters = SerializeableCollection.GetSerializeableCollection(cd.Parameters);  
                scd.ReadDate = cd.ReadDate;  
                scd.Size = cd.Size;  
                return scd;  
           }  
           internal void SetContentDisposition(ContentDisposition scd)  
           {  
                scd.CreationDate = CreationDate;  
                scd.DispositionType = DispositionType;  
                scd.FileName = FileName;  
                scd.Inline = Inline;  
                scd.ModificationDate = ModificationDate;  
                Parameters.SetColletion(scd.Parameters);  
                scd.ReadDate = ReadDate;  
                scd.Size = Size;  
           }  
      }  
      [Serializable]  
      internal class SerializeableContentType  
      {  
           String Boundary;  
           String CharSet;  
           String MediaType;  
           String Name;  
           SerializeableCollection Parameters;  
           internal static SerializeableContentType GetSerializeableContentType(System.Net.Mime.ContentType ct)  
           {  
                if (ct == null)  
                     return null;  
                SerializeableContentType sct = new SerializeableContentType();  
                sct.Boundary = ct.Boundary;  
                sct.CharSet = ct.CharSet;  
                sct.MediaType = ct.MediaType;  
                sct.Name = ct.Name;  
                sct.Parameters = SerializeableCollection.GetSerializeableCollection(ct.Parameters);  
                return sct;  
           }  
           internal ContentType GetContentType()  
           {  
                ContentType sct = new ContentType();  
                sct.Boundary = Boundary;  
                sct.CharSet = CharSet;  
                sct.MediaType = MediaType;  
                sct.Name = Name;  
                Parameters.SetColletion(sct.Parameters);  
                return sct;  
           }  
      }  
      [Serializable]  
      internal class SerializeableAttachment  
      {  
           String ContentId;  
           SerializeableContentDisposition ContentDisposition;  
           SerializeableContentType ContentType;  
           Stream ContentStream;  
           System.Net.Mime.TransferEncoding TransferEncoding;  
           String Name;  
           Encoding NameEncoding;  
           internal static SerializeableAttachment GetSerializeableAttachment(Attachment att)  
           {  
                if (att == null)  
                     return null;  
                SerializeableAttachment saa = new SerializeableAttachment();  
                saa.ContentId = att.ContentId;  
                saa.ContentDisposition = SerializeableContentDisposition.GetSerializeableContentDisposition(att.ContentDisposition);  
                if (att.ContentStream != null)  
                {  
                     byte[] bytes = new byte[att.ContentStream.Length];  
                  att.ContentStream.Position = 0;   
         att.ContentStream.Read(bytes, 0, bytes.Length);  
                     saa.ContentStream = new MemoryStream(bytes);  
                }  
                saa.ContentType = SerializeableContentType.GetSerializeableContentType(att.ContentType);  
                saa.Name = att.Name;  
                saa.TransferEncoding = att.TransferEncoding;  
                saa.NameEncoding = att.NameEncoding;  
                return saa;  
           }  
           internal Attachment GetAttachment()  
           {  
                Attachment saa = new Attachment(ContentStream, Name);  
                saa.ContentId = ContentId;  
                this.ContentDisposition.SetContentDisposition(saa.ContentDisposition);  
                saa.ContentType = ContentType.GetContentType();  
                saa.Name = Name;  
                saa.TransferEncoding = TransferEncoding;  
                saa.NameEncoding = NameEncoding;  
                return saa;  
           }  
      }  
      [Serializable]  
      internal class SerializeableCollection  
      {  
           Dictionary<String, String> Collection = new Dictionary<string, string>();  
           internal SerializeableCollection()  
           {  
           }  
           internal static SerializeableCollection GetSerializeableCollection(NameValueCollection col)  
           {  
                if (col == null)  
                     return null;  
                SerializeableCollection scol = new SerializeableCollection();  
                foreach (String key in col.Keys)  
                     scol.Collection.Add(key, col[key]);  
                return scol;  
           }  
           internal static SerializeableCollection GetSerializeableCollection(StringDictionary col)  
           {  
                if (col == null)  
                     return null;  
                SerializeableCollection scol = new SerializeableCollection();  
                foreach (String key in col.Keys)  
                     scol.Collection.Add(key, col[key]);  
                return scol;  
           }  
           internal void SetColletion(NameValueCollection scol)  
           {  
                foreach (String key in Collection.Keys)  
                {  
                     scol.Add(key, this.Collection[key]);  
                }  
           }  
           internal void SetColletion(StringDictionary scol)  
           {  
                foreach (String key in Collection.Keys)  
                {  
                     if (scol.ContainsKey(key))  
                          scol[key] = Collection[key];  
                     else  
                          scol.Add(key, this.Collection[key]);  
                }  
           }  
      }  
      /// <summary>  
      /// Serializeable mailmessage object  
      /// </summary>  
      [Serializable]  
      public class SerializeableMailMessage  
      {  
           Boolean IsBodyHtml { get; set; }  
           String Body { get; set; }  
           SerializeableMailAddress From { get; set; }  
           List<SerializeableMailAddress> To = new List<SerializeableMailAddress>();  
           List<SerializeableMailAddress> CC = new List<SerializeableMailAddress>();  
           List<SerializeableMailAddress> Bcc = new List<SerializeableMailAddress>();  
           SerializeableMailAddress ReplyTo { get; set; }  
           SerializeableMailAddress Sender { get; set; }  
           String Subject { get; set; }  
           List<SerializeableAttachment> Attachments = new List<SerializeableAttachment>();  
           Encoding BodyEncoding;  
           Encoding SubjectEncoding;  
           DeliveryNotificationOptions DeliveryNotificationOptions;  
           SerializeableCollection Headers;  
           MailPriority Priority;  
           List<SerializeableAlternateView> AlternateViews = new List<SerializeableAlternateView>();  
           /// <summary>  
           /// Creates a new serializeable mailmessage based on a MailMessage object  
           /// </summary>  
           /// <param name="mm"></param>  
           public SerializeableMailMessage(MailMessage mm)  
           {  
                IsBodyHtml = mm.IsBodyHtml;  
                Body = mm.Body;  
                Subject = mm.Subject;  
                From = SerializeableMailAddress.GetSerializeableMailAddress(mm.From);  
                To = new List<SerializeableMailAddress>();  
                foreach (MailAddress ma in mm.To)  
                {  
                     To.Add(SerializeableMailAddress.GetSerializeableMailAddress(ma));  
                }  
                CC = new List<SerializeableMailAddress>();  
                foreach (MailAddress ma in mm.CC)  
                {  
                     CC.Add(SerializeableMailAddress.GetSerializeableMailAddress(ma));  
                }  
                Bcc = new List<SerializeableMailAddress>();  
                foreach (MailAddress ma in mm.Bcc)  
                {  
                     Bcc.Add(SerializeableMailAddress.GetSerializeableMailAddress(ma));  
                }  
                Attachments = new List<SerializeableAttachment>();  
                foreach (Attachment att in mm.Attachments)  
                {  
                     Attachments.Add(SerializeableAttachment.GetSerializeableAttachment(att));  
                }  
                BodyEncoding = mm.BodyEncoding;  
                DeliveryNotificationOptions = mm.DeliveryNotificationOptions;  
                Headers = SerializeableCollection.GetSerializeableCollection(mm.Headers);  
                Priority = mm.Priority;  
                ReplyTo = SerializeableMailAddress.GetSerializeableMailAddress(mm.ReplyTo);  
                Sender = SerializeableMailAddress.GetSerializeableMailAddress(mm.Sender);  
                SubjectEncoding = mm.SubjectEncoding;  
                foreach (AlternateView av in mm.AlternateViews)  
                     AlternateViews.Add(SerializeableAlternateView.GetSerializeableAlternateView(av));  
           }  
           /// <summary>  
           /// Returns the MailMessge object from the serializeable object  
           /// </summary>  
           /// <returns></returns>  
           public MailMessage GetMailMessage()  
           {  
                MailMessage mm = new MailMessage();  
                mm.IsBodyHtml = IsBodyHtml;  
                mm.Body = Body;  
                mm.Subject = Subject;  
                if( From != null )  
                     mm.From = From.GetMailAddress();  
                foreach (SerializeableMailAddress ma in To)  
                {  
                     mm.To.Add(ma.GetMailAddress());  
                }  
                foreach (SerializeableMailAddress ma in CC)  
                {  
                     mm.CC.Add(ma.GetMailAddress());  
                }  
                foreach (SerializeableMailAddress ma in Bcc)  
                {  
                     mm.Bcc.Add(ma.GetMailAddress());  
                }  
                foreach (SerializeableAttachment att in Attachments)  
                {  
                     mm.Attachments.Add(att.GetAttachment());  
                }  
                mm.BodyEncoding = BodyEncoding;  
                mm.DeliveryNotificationOptions = DeliveryNotificationOptions;  
                Headers.SetColletion(mm.Headers);  
                mm.Priority = Priority;  
                if (ReplyTo != null)  
                     mm.ReplyTo = ReplyTo.GetMailAddress();  
                if (Sender != null)  
                     mm.Sender = Sender.GetMailAddress();  
                mm.SubjectEncoding = SubjectEncoding;  
                foreach (SerializeableAlternateView av in AlternateViews)  
                     mm.AlternateViews.Add(av.GetAlternateView());  
                return mm;  
           }  
      }  
 }  

onsdag den 14. oktober 2009

Third party webservices

Every time I have to make integration with third party webservices I always try to detach the application from the webservice due to the constant errors/problems with webservices.

I am still looking for an easy way to make a test page that easily test and validates a given webservice and method.

I always spent a lot of time assuring that it isnt some bug I have introduced in my application that has broken the webservice integration.

Once in a while it is some bug introduced by myself but in 8 out of 10 times it is data that the webservice collect that are invalid or the webservice itself that fails.

I will write my experiences with this issue here and try to create an easy way of testing a webservice.

But please share if you have any ideas.

Me and sitecore

This blog exists because I have a love/hate relationship with the CMS created by Sitecore which is a big part of my job as a web developer in Netmester A/S in Denmark.

I am always challeging Sitecore and Sitecore unfornunately often breaks to all the weird stuff Im doing to it every day.

Ill create blogs about all the weird stuff I meet and struggles to solve every day.

And this is the first in many.

Im at the moment developing a solution in Sitecore 6.1.0 which is currently the latest Sitecore release (not recommended version).

One of the new things in Sitecore 6 is that all security (rights, roles and other membership related stuff) are implemented using standard asp.net security rather than using Sitecores own security.
This should in itself be an approvement but sitecore has for one thing decided to implement the Sitecore domain functionality as part of the username which I personally think is pretty stupid.

Im using a third party member system that supplies users to the website (username, email, name etc).

In order to be able to log out this user, after login, using FormsAuthentication.SignOut() the user NEEDS to be added to the asp.net
Membership and therefore is visible in Sitecore security editor. With more than 5000 users this makes the security editor a bit crowded.
In order to put the users in a customer specific domain I need to prefix the user with the domain name followed by a backslash (\)
This makes it hard to map the sitecore user with the third pary member system or at least makes it more complicated because I now need to remove
the domain name from the sitecore user or append the domain name to the third party username.



I can live with this but the latest issue I have found is not easy to solve.
When redirecting from a page in my website (to login page due to user not logged in) sitecore throws an error looking like this

 System.InvalidOperationException: user at Sitecore.Caching.UserProfile.UserProfileCache.GetRecord(String userName, String propertyName)  
 at Sitecore.Security.UserProfile.GetPropertyValueCore(String propertyName)  
 at Sitecore.Security.UserProfile.get_SerializedData()  
 at Sitecore.Security.UserProfile.get_CustomProperties()  
 at Sitecore.Security.UserProfile.SerializeCustomProperties()  
 at Sitecore.Security.UserProfile.Save()  
 at System.Web.Profile.ProfileModule.OnLeave(Object source, EventArgs eventArgs)  
 at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()  
 at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)  

This exception is thrown in another thread than the request is executing in because no Session is available when I pick up the exception in Global.asax.

No user is logged in when the exception is thrown and it makes sense that the GetRecord() failes due to username is empty.

I have added a profile provider and a profile property that makes it possible to get a DB instance of the user logged in. This might be the trigger of the exception but I havnt been able to solve the bug.

This is the profile section of the web.config


 <profile defaultProvider="sql" enabled="true" inherits="Sitecore.Security.UserProfile, Sitecore.Kernel">  
  <providers>  
   <clear/>  
   <add name="sql" type="System.Web.Profile.SqlProfileProvider" connectionStringName="core" applicationName="sitecore"/>  
   <add name="switcher" type="Sitecore.Security.SwitchingProfileProvider, Sitecore.Kernel" applicationName="sitecore" mappings="switchingProviders/profile"/>  
   <add name="CommunityProfileProvider" applicationName="Community" type="Netmester.Profile.GenericProfileProvider" connectionStringName="Community"/>  
  </providers>  
  <properties>  
   <clear/>  
   <add type="System.String" name="SC_UserData"/>  
   <add name="CommunityProfile" provider="CommunityProfileProvider" type="Netmester.Framework.Community.DomainObjects.User"/>  
  </properties>  
 </profile>