Sunday, September 8, 2013

Dynamic error handling in EPiServer 7 with static fallback

A lot of EPiServer editors want to be able to edit their 404 and 500 error pages in edit mode, and telling them that they have to poke around in static HTML files is usually not very popular.

But it’s important to remember that dynamic error pages cause some additional challenges that has to be considered. Examples are avoiding infinite loops, globalization and being able to add error pages for any HTTP status code that you’d like. The concept behind this solution is the same as the solution for EPiServer CMS 6, but it has been rewritten to MVC 4 and it now supports globalization.

I prefer to create a new pagetype for the error pages, in that way I can restrict where the pagetype can be used and who has access to creating and editing error pages. So let’s create our new pagetype called ErrorPage:

1: [ContentType(Order = 7000)]

2: [AvailablePageTypes(Availability = Availability.None)]

3: public class ErrorPage : SitePageData

4: {

5:   [Display(Order = 100)]

6:   [CultureSpecific]

7:   public virtual string HttpStatusCode { get; set; }


9:   [Display(Order = 1000, GroupName = SystemTabNames.Content)]

10:  [CultureSpecific]

11:  public virtual XhtmlString MainBody { get; set; }


13:   public override void SetDefaultValues(ContentType contentType)

14:   {

15:     base.SetDefaultValues(contentType);


17:     VisibleInMenu = false;

18:     DisableIndexing = true;

19:   }

20: }

There is nothing special with this page type. It has a string property called HttpStatusCode where the editor specifies which status code this error page should be used for, and a MainBody property where they can specify a friendly message. Now let’s create a controller for this pagetype:

1: [ContentOutputCache]

2: public class ErrorPageController : PageControllerBase<ErrorPage>

3: {

4:   public ActionResult Index(ErrorPage currentPage)

5:   {

6:     return View(PageViewModel.Create(currentPage));

7:   }

8: }

The only thing this controller does is to return a view, and that view can look like this:

1: @model PageViewModel<ErrorPage>


3: @{ Layout = "~/Views/Shared/Layouts/_Root.cshtml"; }


5: <h1>@Model.CurrentPage.PageName</h1>

6: @Html.DisplayFor(m => Model.CurrentPage.MainBody)

So far, so good! Right? Now comes the challenging part! The view we just created will never actually be seen by a visitor, the only time this view is displayed is in edit mode. Instead of the visitor seeing this view, an ErrorHandler will be in charge of finding the correct ErrorPage and displaying its content. Let’s take a look at our ErrorHandlerController:

1: public class ErrorHandlerController : Controller

2: {

3:   private List<ErrorPage> _errorPages;

4:   private string _currentLanguage;


6:   private List<ErrorPage> ErrorPages

7:   {

8:     get

9:     {

10:       if (_errorPages == null || _errorPages.Count == 0)

11:       {

12:         if (string.IsNullOrWhiteSpace(_currentLanguage))

13:           _currentLanguage = ContentLanguage.PreferredCulture.Name;


15:         IContentTypeRepository contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();

16:         ContentType errorPage = contentTypeRepository.Load(typeof(ErrorPage));


18:         _errorPages = DataFactory.Instance.FindPagesWithCriteria(ContentReference.RootPage, new PropertyCriteriaCollection

19:         {

20:           new PropertyCriteria

21:           {

22:           Condition = CompareCondition.Equal,

23:           Name = "PageTypeID",

24:           Type = PropertyDataType.PageType,

25:           Value = errorPage.ID.ToString()

26:           }

27:         }, _currentLanguage).FilterForDisplay().Cast<ErrorPage>().ToList();

28:       }

29:       return _errorPages;

30:     }

31: }


33: public ActionResult Index()

34: {

35: // Get status code

36:     int statusCode = GetStatusCode();


38:     if (statusCode > 0)

39:     {

40:       Response.StatusCode = statusCode;


42:       // Check if page with this status code is defined in EPiServer

43:       ErrorPage errorPage = ErrorPages.Find(page => page.HttpStatusCode.Equals(statusCode.ToString()));

44:       if (errorPage == null)

45:       {

46:         string staticHtmlPage = GetStaticHtmlPage(statusCode);

47:         if (String.IsNullOrEmpty(staticHtmlPage))

48:         {

49:           throw new HttpException(

50:           String.Format("Could not get static html page for statuscode {0} in errorhandler", statusCode));

51:         }

52:         return new ExtendedContentResult

53:         {

54:           StatusCode = Response.StatusCode,

55:           Content = MvcHtmlString.Create(staticHtmlPage).ToString(),

56:           ContentEncoding = Encoding.UTF8,

57:           ContentType = "text/html",

58:           Headers = Response.Headers

59:         };

60:       }


62:       // Set RoutingConstants.NodeKey so EPi's extension method RequestContext.GetContentLink() will work

63:       ControllerContext.RouteData.DataTokens[RoutingConstants.NodeKey] = errorPage.ContentLink;

64:       return View("~/Views/ErrorHandling/ErrorHandler.cshtml", PageViewModel.Create(errorPage));

65:     }


67:     throw new HttpException("No status code sent to errorhandler");

68:   }


70:   /// <summary>

71:   /// Returns the static html file for the given status code as a string

72:   /// </summary>

73:   /// <param name="statusCode">HTTP Error code</param>

74:   /// <returns>The content of the static html file as a string</returns>

75:   private string GetStaticHtmlPage(int statusCode)

76:   {

77:     string filePath = String.Format("/StatusCodes/{0}.html", statusCode);

78:     string mapPath = Server.MapPath(filePath);

79:     if (System.IO.File.Exists(mapPath))

80:     {

81:       // Read the file as one string.

82:       StreamReader myFile = new StreamReader(mapPath);

83:       string myString = myFile.ReadToEnd();

84:       myFile.Close();

85:       return myString;

86:     }

87:     return String.Empty;

88:   }


90:   /// <summary>

91:   /// Get the HTTP error status code from the querystring and sets current language

92:   /// </summary>

93:   private int GetStatusCode()

94:   {

95:     int code = 0;

96:     string request = Server.UrlDecode(ControllerContext.HttpContext.Request.QueryString.ToString());

97:     if (!String.IsNullOrEmpty(request))

98:     {

99:        Regex regex = new Regex(@"(?:[0-9]{3}\;)");

100:       Match match = regex.Match(request);


102:       if (match.Success)

103:       {

104:         string[] requestQueryStrings = request.Split(';');

105:         if (requestQueryStrings.Length > 0)

106:         {

107:           int.TryParse(requestQueryStrings[0], out code);


109:           if (requestQueryStrings.Length > 1)

110:             SetCurrentLanguage(requestQueryStrings[1]);

111:           }

112:         }

113:       }


115:       return code;

116:     }


118:   /// <summary>

119:   /// Gets the current page language based on the url

120:   /// </summary>

121:   /// <param name="url">Url of current page</param>

122:   private void SetCurrentLanguage(string url)

123:   {

124:     string absoluteUrl = new Uri(url).AbsolutePath;

125:     if (string.IsNullOrWhiteSpace(absoluteUrl))

126:       return;


128:     string[] urlSegments = absoluteUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

129:     if (urlSegments.Length == 0)

130:       return;


132:     var regex2 = new Regex("([^\\s]+(?=\\.([a-zA-Z]+))\\.\\2)");

133:     Match match2 = regex2.Match(urlSegments[urlSegments.Length - 1]);

134:     if (match2.Success && !urlSegments[urlSegments.Length - 1].EndsWith(Settings.Instance.UrlRewriteExtension))

135:       return;


137:     _currentLanguage = GetLanguageFromDomainSegment(urlSegments[0]);

138:   }


140:   /// <summary>

141:   /// Returns the language to be used for the given domainSegment from EPiServerFramework.config

142:   /// </summary>

143:   /// <param name="domainSegment">Domain of the requested page</param>

144:   /// <returns></returns>

145:   private string GetLanguageFromDomainSegment(string domainSegment)

146:   {

147:     foreach (HostNameCollection siteHosts in EPiServerFrameworkSection.Instance.SiteHostMapping)

148:     {

149:       foreach (HostNameElement hostNameElement in siteHosts)

150:       {

151:         if (hostNameElement.Name.EndsWith(domainSegment, StringComparison.InvariantCultureIgnoreCase) &&

152:           hostNameElement.Name != "*")

153:           return hostNameElement.Language;

154:         }

155:       }

156:       return null;

157:     }

158: }

This code requires some explanation. When the Index() method of the ErrorHandlerController is called, the HTTP Status Code is retrieved from the request and if there exists an ErrorPage containing this status code, a Index view is returned for the ErrorHandler creating the model based on the ErrorPage. If there is no ErrorPage with the HTTP Status Code set, a static HTML file is used as fallback. This static HTML file should be located in the StatusCodes folder of your solution. So what does the ErrorPage Index view look like? It looks exactly like the View that was used for the ErrorPage that we saw previously:

1: @model PageViewModel<ErrorPage>


3: @{ Layout = "~/Views/Shared/Layouts/_Root.cshtml"; }


5: <h1>@Model.CurrentPage.PageName</h1>

6: @Html.DisplayFor(m => Model.CurrentPage.MainBody)

So what’s the difference between the ErrorPage Index view and the ErrorHandler Index view? As I’ve mentioned before, the ErrorPage Index view is only seen by the editor. The ErrorHandler Index view however, is the one that will be seen by the visitor. So how does this work? How is the ErrorHandler Index method invoked? It all comes down to the <httpErrors> settings under <system.webserver> in the web.config file:

1: <httpErrors errorMode="Custom" existingResponse="Replace">

2: <remove statusCode="404" />

3: <error statusCode="404" path="/Views/ErrorHandler" responseMode="ExecuteURL" />

4: </httpErrors>

Here we register a 404 status code, setting its path to the ErrorHandler View folder. In this way, the Index method of the ErrorHandlerController will be invoked for every 404 status code. So for every status code you want to catch, you have to add it to the web.config file as we’ve done above. Last, but not least, we need to add a new Route in Global.asax.cs:

1: protected override void RegisterRoutes(RouteCollection routes)

2: {

3:    // Route to handle error pages

4:    routes.MapRoute(

5:      "ErrorHandler",

6:      "Views/ErrorHandling/ErrorHandler",

7:      new { controller = "ErrorHandler", action = "Index" });


9:    base.RegisterRoutes(routes);

10: }

And that’s it! You now have dynamic error pages with a static fallback. AND, they support globalization, so you can have separate error pages for all your languages. As a last note, in the ErrorHandlerController, when retrieving a static error page a ExtendedContentResult is returned based on a solution by Andrew Rea. ExtendedContentResult looks like this:

1: public class ExtendedContentResult : ContentResult

2: {

3:   public int StatusCode { get; set; }

4:   public NameValueCollection Headers { get; set; }


6:   public override void ExecuteResult(ControllerContext context)

7:   {

8:     context.HttpContext.Response.StatusCode = StatusCode;

9:     foreach (string s in Headers.Keys)

10:     {

11:       context.HttpContext.Response.AddHeader(s, Headers[s]);

12:     }

13:     base.ExecuteResult(context);

14:   }

15: }


Monday, September 2, 2013

GitMinutes interview

Last week, my colleague Arve and I were interviewed by GitMinutes. The topic was migrating from Subversion to Git in a .NET Shop, with a lot of interesting discussions and great tips from the host, Thomas Ferris Nicolaisen. Tons of fun!

You can check it out here:

Monday, August 19, 2013

EPiServer 7 RequestError: Unable to load /EPiServer/shell/Stores/metadata/?...

Working in the new EPiServer 7 edit mode, I received the following javascript error:

RequestError: Unable to load /EPiServer/shell/Stores/metadata/?type=EPiServer.Core.ContentData&modelAccessor=%7B%22contentLink%22%3A%2212_24%22%7D&dojo.preventCache=1376896223044 status: 500 

This error prevented me from editing pages and even worst, creating new pages. Browsing to the url in the error message I saw that EPiServer was unable to locate my Editors.css file.

The location of the Editors.css file is configured in the uiEditorCssPaths attribute in episerver.config. Turns out I had moved the Editor.css file to a different folder and forgotten to update the uiEditorCssPaths attribute. So, update the Editors.css path, and the javascript error disappears!

Monday, August 5, 2013

Back to work

I've been on maternity leave for eight months, but today is my first day back at the office. Yes, in Norway you get up to a year paid parental leave, which is beyond great! However, staying at home for eight months might not be as fulfilling as first imagined, at least it wasn't for me. Don't get me wrong, I absolutely love spending time with my kid, but when he was asleep (and he sleeps a lot) I was bored out of my mind. So I quickly found the need to set myself some work-related goals. What better way to spend those quiet hours with a sleeping kid than to get certified and read all the books I've wanted to read but haven't had time to?
I wrote a blog post in January listing some of my goals for the first half of this year, and I'm glad to say I've achieved all the goals I set except for the bonus goal. In addition to these goals I've read quite a lot of books, I've been a technical reviewer on two books coming out soon and last but not least: I've become addicted to Coursera.
I started out taking one Coursera course in functional programming, which was an amazing course. I recommend anyone interested in programming to add yourself to the watchlist for future sessions of the course: This course resulted in an interview in one of the largest newspapers in Norway where I spoke about massive open online courses (MOOC). And that interview landed me a speaker spot at the first MOOC conference in Norway on September 10th, where I will be presenting my thoughts on MOOCs and the way they are heading. So to make the circle complete, I'm currently doing a Public Speaking course on Coursera in order to prepare for the presentation. Who knew you could take a public speaking course online and actually learn something?
Now that I'm back at work, I'm very glad to see my colleagues again and I can't wait to get started on some new projects! I'm in the process of setting myself some new goals, don't be surprised if I end up blogging about them when I'm done!

Tuesday, June 25, 2013

Win an e-copy of “Microsoft Dynamics CRM 2011 Applications (MB2-868) Certification Guide”

Packt Publishing has sponsored me with two e-copies of "Microsoft Dynamics CRM 2011 Applications (MB2-868) Certification Guide" which I’m going to give away. You can read my review of the book if you want to know what I thought about it.

To win one of the copies, leave a comment describing the worst CRM mistake you’ve done or seen someone else do. Also, remember to leave your e-mail address so I can get in touch with you. Now, humor me!

Thursday, May 23, 2013

Review of "Microsoft Dynamics CRM 2011 Applications (MB2-868) Certification Guide" by Danny Varghese

"Microsoft Dynamics CRM 2011 Applications (MB2-868) Certification Guide" is a helpful book in preparation for the MB2-868 certification. The author, Danny Varghese, gives a thorough overview of what Microsoft Dynamics CRM includes and he exemplifies the topics discussed by introducing the reader to business scenarios. The book covers the three main modules of Microsoft Dynamics CRM in great depth: Sales, Marketing and Customer Service. It also maps out the life-cycle of entities contained in these modules, the relationships between them, how you can get a clear view of your business by using the reporting and analysis features, and how this all plays together with Microsoft Outlook. In addition to the step-by-step guides  showing how you manage your entities, the reader can quiz himself in the "Test your knowledge" section at the end of each chapter. I will recommend this book to anyone taking the MB2-868 certification and to any professional working with Microsoft Dynamics CRM applications on a daily basis.

Wednesday, April 17, 2013

Review of “The art of unit testing” by Roy Osherove

I’ve wanted to read this book for ages, and I finally found the time to sit down and enjoy it to the fullest. I’m tempted to end the review here and now, simply by saying that this book is great and you have to read it. So if you trust me you don’t have to read on, just read the book!

Roy Osherove is able to explain the concepts of unit testing in the simplest way possible, starting with the basics and moving on to the more advanced topics like writing testable code before finishing with an overview of design and process. This makes the book relevant whether you’re new to unit testing or refreshing your knowledge. All good so far, but what makes the book really shine is his thorough focus on best practices, how to write clean and maintainable unit tests. In other words, how to succeed with unit testing in the long run. He also gives an overview of how to fail with unit testing, giving real-life examples from projects he’s been working on.

The book was published in 2009, so the tools and testing frameworks used are a bit outdated. However, as the book is not written to teach you how to use a specific tool or framework, this doesn’t matter. What’s important is understanding why and when the tools are helpful and in what situations they are applicable. If you really want the tools and testing frameworks to be updated, you’ll be glad to know the 2nd edition is on its way.

The tone of the book is very informal, and it’s one of those books you can read just for fun. You don’t need to stack up on coffee beforehand, the book itself will keep you awake. What surprised me the most while reading it was the chapter on “Integrating unit testing into the organization”, which gives you advice on where to start if your company is not yet accustomed to unit testing, and how to get the managers onboard. It also contains all the answers to those difficult questions you will be asked if you decide to be the one to push for change in your company. I wish more books had a chapter like this! Being convinced by reading the book is a piece of cake, but being able to convince the rest of the organization can be quite a lot of work, and this chapter lightens the workload.

I could go on and on about how great this book is, but I’ll keep it short. I know for sure that this is not the only time I’ll read the book, I will probably re-read it over and over again to remind myself of Roy’s best practices. And I’ll definitely keep it close whenever I’m writing unit tests so that I can look up all his helpful tips when I get stuck. So go buy this book and enjoy! Or even better: wait until June when the 2nd edition is to be released!

Thursday, March 21, 2013

The dangers of forking and dongles

Three days ago, Adria Richards published a blog post about an incident that happened at the PyCon conference last week. Two guys made a sexual joke during one of the lightning talks and Adria responded by taking a photograph of them and posting it on Twitter. In a separate tweet she asked the organizers to address the issue, which they did. You can read her blog post here:

Two days ago, one of the guys making the joke posted an apology and an explanation to Hacker News. It turns out he was fired from his job as a consequence:

There are several questions here that need to be addressed: Is making sexual jokes at a conference acceptable? If someone is breaking a code of conduct, is it acceptable to post their photograph to Twitter? What will this do to the developer community?

Is making sexual jokes at a conference acceptable?

No, it’s not acceptable. Frankly, I wouldn’t care if I heard someone make a sexual joke, but some people might find this offensive and therefore it’s a good idea to save your sexual jokes for a more private audience. As Adria states in her blog post, the PyCon conference had a pretty extensive Code of Conduct: “All communication should be appropriate for a professional audience including people of many different backgrounds. Sexual language and imagery is not appropriate for any conference venue, including talks.”

If someone is breaking a code of conduct, is it acceptable to post their photograph to Twitter?

Absolutely not, and I find this to be a much bigger offence than making a sexual joke. Public shaming is never an acceptable solution, and as we’ve seen in this case it can have terrible consequences. As Adria points to the Code of Conduct regarding the jokes made, I’d like to point out that the Code of Conduct also states that “Harassment includes […] harassing photography or recording”.

What will this do to the developer community?

This is the question that worries me the most. There are a lot of people working very hard to make more women interested in technology, and they have identified the need for more women in the business. There are, however, an alarming number of skeptics out there, and by skeptics I mean men who prefer the business to be male dominated and who don’t actually want more women to take an interest in technology.

My fear is that Adria’s reaction to the jokes at PyCon will cause the number of skeptics to increase. One of the jokes she perceived as being sexual was in fact not sexual at all. Should a male developer have to censor himself in fear of a female developer overhearing and perceiving what is being said as sexual? If that is the direction we’re going in, I’m not sure I want more women to enter the business myself. No one, neither men nor women, should have to censor themselves out of fear for being publicly shamed.

I sincerely hope Aria will apologize for posting the photography, and delete the blog post and tweets from the conference so that the man will not be affected by this when he goes looking for a new job.

Monday, March 18, 2013

Being a technical reviewer

Do you love reading books and would like to contribute to the collection of great books out there, without actually writing one yourself? You should check out technical reviewing. In this blog post I’ll give a short overview of what technical reviewing is all about and how you can become a technical reviewer.

About a year ago, I received an e-mail from Packt Publishing asking if I would like to be a technical reviewer for one of their upcoming books, “FusionCharts Beginner’s Guide: The Official Guide for FusionCharts Suite”. They had found me through a blog post I’d written about FusionCharts, and needless to say I was delighted to be asked and I immediately said yes. Skipping ahead 10 months, I started feeling a bit restless and wanted something to do. I found a couple of publishers looking for technical reviewers, and sent them an e-mail showing my interest. Some of them have continuously sent me e-mails with their upcoming books in need of reviewers, but unfortunately none of the books were in my field of expertise. Until a couple of weeks ago, The Pragmatic Bookshelf was looking for reviewers for “The healthy programmer”, coming out in June. Again, I immediately said yes, and I submitted my technical review yesterday.

What does it mean to be a technical reviewer?

The job of a technical reviewer is to read the book, comment on the content and answer questions. For example you might get a list of questions like these:

  • Who is the audience of the book?
  • Have the author left out any important topics?
  • Is the order of the content logical?
  • Are the code examples correct?
  • What could the author do to make the book more interesting?
  • If the book consistent?
  • Have the author explained the concepts clearly enough?

The questions in the list above all concern the book as a whole, but the questions can also be asked per chapter. For example: Which topic do you think should follow this chapter?

As you might have noticed, none of these questions address spellchecking or formatting, the only thing the technical reviewer should focus on is the quality of the content. A technical review is often done before the book is copyedited, which means you should expect quite a lot of spelling mistakes and some strange formatting. Luckily for you, you can ignore them all.

How does the process of reviewing work?

The two technical reviews I’ve done have been quite different from one another. For the first book, I received two chapters per week, which I had to review and submit before receiving two new chapters. So I had no idea what the topic of the next chapters were and in some cases the chapters were given in the incorrect order, for example I reviewed chapter 8 before I reviewed chapter 5. This raised some challenging questions like Which topic do you think should follow this chapter? In addition to this, the book was full of code examples which had to be tried out.

For the second book, I received the whole book at once, and I was given a week to review it. This book did not contain any code, so the questions asked were focusing more on the structure of the book, the amount of details given etc.

As you can see, these were two very different review processes. One of the things they had in common was tight deadlines. Don’t expect to be given a lot of time to complete the review, remember that this is a book the author and the publisher wants to publish as soon as possible.

How can I become a technical reviewer?

A lot of publishers are looking for technical reviewers, check out their websites to find their contact information. Here is a list of the publishers I’ve been in contact with:

Remember, you will not get paid for doing a technical review! You will however, most likely be mentioned in the acknowledgements sections of the book and receive a copy of it when it is published. Most importantly, you get to contribute to a great book!

Thursday, March 14, 2013

Fifteen minutes of fame

Last week a journalist and a photographer from Norway's largest newspaper, Aftenposten, came to visit me. They were interested in the how I’ve been keeping myself updated while I’m on maternity leave. The article was published in todays newspaper, and as it was not published online I’ve simply included an image of the article.

Update: The article was published online the next day:

Friday, March 8, 2013

Do not take affirmative action towards women in technology

I’ve never seen myself as a major feminist. I support a lot of feminist views, but I don’t feel the need to participate in demonstrations for women's rights. In fact, one of my main “feminist” concerns is the lack of female developers, and I think that says a lot about how lucky I think we, the women, are in Norway.

A lot of people are working very hard in order to attract more women to technology related jobs, and I applaud these people. Your work is very important, unfortunately this blog post is not about you. This blog post is about thoughts and ideas that rise for the same purpose, but might give the opposite result. In other words, this blog post is a plead against well-meant measures which in the end might make women step back from technology.

Do not take affirmative action

(Before I begin my plead, I would like to point out that this is strictly my point of view, other women in the business might not agree with me.)

There was a discussion recently on the lack of female speakers at conferences, and one of the suggestions was to take affirmative action to increase the numbers. For example, if there are 100 timeslots available at the conference, 25 of these should be reserved women to ensure that 25% of the speakers are female. Would this result in the number of female speakers at the conference increasing? Absolutely! But what would probably happen over time? If women were accepted as speakers just because of their sex, we risk the quality of the conference. I’m not saying that women are inferior to men as public speakers or as technology specialists, I believe the same thing would happen if we were to say that 25% of all speakers should be redheads. Or 25% of all speakers should be shorter than 175 cm.

The action is well-meant, but the result might be catastrophic. If I was interested in submitting a talk to a conference, and I found out that 25% of the timeslots were reserved for women, I would probably not submit my talk after all. The wish of all women in the business is to be accepted because we deserve it as individuals, not because we deserve it as a group. I would never accept a job if I got it “just because I’m female”. I would never speak at a conference if my talk was accepted “just because I’m female”. I want to earn my place through blood, sweat and tears, in the same way a man does.

Happy international women's day!


Friday, March 1, 2013

Review of “Microsoft Dynamics CRM 2011 Customization & Configuration (MB2-866) Certification Guide” by Neil Benson

“Microsoft Dynamics CRM 2011 Customization & Configuration (MB2-866) Certification Guide” is a book meant for preparation to the MB2-866 exam. While the book covers the skills measured in the exam in great detail, it is not one of those books you can simply read and then expect to pass. As the title portrays, the book covers configuration and customization of a CRM system which means that in order to fully understand what is going on, you need to try it out for yourself.

If you don’t have access to a CRM installation or you don’t have System Administrator privileges, the preface of the book will give you the necessary information in order to obtain this. As large parts of the book require you to try out the customizations and configurations yourself, it’s not your typical “read on a bus” book. A good example is the “How to create a user” section which outlines step by step where you need to click and what information is required in order to create a user. Because of these sections, I spent most parts of my time with the book next to a computer so I could click wherever the author wanted me to click.

After finishing the book I went through the list of skills measured by Microsoft to see if I thought all them were covered in the book. In my opinion the book covers these very well, but I found the order of the chapters in the book to be a bit confusing while reading it. A lot of the concepts that were explained at the end of the book were referred to in the earlier chapters, causing trouble for those who are not familiar with the concepts. This means that the book shouldn’t necessarily be read from front to back, you might have to jump between chapters if a topic you are not familiar with is mentioned but not explained. That being said, flipping through the book is not as easy as it should be. There’s something about the size of the headings that makes it difficult to see if a section is the child or the sibling of the previous section, making the table of contents the only logical way to navigate through the book.

The one thing I absolutely love about this book is that each chapter contains a “Test your knowledge” section with multiple choice questions similar to the questions you can expect to find on the exam. The answers to these questions can be found in Appendix B, but unfortunately the number of answers for a chapter does not always match the number of questions. This means that you might have to dig up the correct answer yourself in some cases. In addition to the “Test your knowledge” sections, the book ends with a “Sample Certification Exam Questions” chapter containing 75 sample questions with answers and explanations to these answers. Luckily, all the answers for this chapter are not only present, but they are also explained in case you chose the wrong answer.

There are some annoyances while reading this book, spelling mistakes for example. First edition books often contain spelling mistakes and we simply have to tolerate that, but this book contains too many of them. An example is a complete diagram explaining the components of a form, where ‘form’ is spelled ‘from’ a large number of times. This can create some confusion as parts on the form are marked "from header” and “from body”. Another annoyance is the author supplying us with incredibly bad mnemonics in order to help us remember the concepts. I appreciate all help I can get in order to remember, but in this case the mnemonics were not helpful at all (at least not to me). In my opinion, a mnemonic is something you have to be able to relate to, and therefore should create for yourself.

Ok, I’m done complaining now. All in all, this book is great when it comes to explaining the concepts of customizing and configuring Microsoft Dynamics CRM 2011 and I would recommend the book to anyone taking the MB2-866 exam. However, I do not think that only reading this book is enough to make you pass, at least not for me. You will need to have tried out the customizations and configurations in real-life scenarios in order to fully get the hang of it.

Monday, February 4, 2013

My review of “Introducing HTML5” by Bruce Lawson and Remy Sharp

In preparation for the 70-480 “Programming in HTML5 with JavaScript and CSS3” exam, I’ve spent parts of the last week reading the first edition of “Introducing HTML5” by Bruce Lawson and Remy Sharp. The book has left me wishing for more and wanting to dive head first into the world of HTML5, which means that the authors have been good ambassadors for the topics in question.

That being said, you have to suffer through some bad jokes and unrealistic examples before the book realizes its true potential. The structure and mood of the book varies depending on the author of the chapter, giving an impression of the authors not necessarily deciding on a writing style before beginning the book. For example, whereas one topic includes a real-life case study the readers can relate to, another topic is exemplified through markup containing the “weaponry of fairies”. Luckily however, as the more advanced topics are covered, the examples improve drastically and reflect realistic usages which will be of great use for the reader.

The authors are clear on the scope of the book, which makes it very easy for the reader to fully understand what HTML5 is all about. Instead of keeping quiet about the topics not covered in the book, we get a clear overview of what they are and why they are not in the scope. They have included a couple of topics which are not part of the HTML5 specification but are so closely related or useful that the reader will benefit from learning about them. One of these topics are the Geolocation API. The authors are also able to create a great balance between what the future may provide (as HTML5 is still in its early stages) and the steps needed to take in order to ensure backwards compatibility. An example of this is the WAI-ARIA section of the book, where the readers are guided through the means of ensuring top accessibility both now and in the future.

A lot of developers are guilty when it comes to reading about a topic, but not trying it out themselves (or vice versa), which is why I always cheer when I reach the end of a chapter and find a “try it yourself” section containing exercises. Sadly, this book does not have that. Neither does it have a “What now?” section for the readers interested in more after finishing the book. Not having this doesn’t affect the quality of the book itself, but further engaging the reader would only be positive.

The main reason for me enjoying this book so much is the simplicity with which the Javascript APIs are explained. The authors focus on what is needed to use the core features of the APIs, making it easy for the reader to grasp the main concepts. In some cases the authors leave it at that, while in others they go on to explain the more advanced features, showing us the exiting possibilities of the APIs.

As mentioned earlier I read the first edition, but if you’re looking for an easy-to-read HTML5 book containing a lot of great Javascript and a couple of bad jokes, I would recommend picking up the second edition of this book. Enjoy!

Friday, January 25, 2013

My plans and goals for the next six months

As I’m on maternity leave with a newborn I’m not allowed to bring anywhere for some time, I have a lot of free time on my hands. I could spend all that time doing chores, but that would probably make me a monster filled with an immense hatred towards vacuum cleaners, so instead I’ve decided to spend my spare time becoming a better developer.
I’ve thought a lot about what I want to achieve and finally I’ve made a plan and set myself some goals:

1) Pass exam 70-480: “Programming in HTML5 with JavaScript and CSS3”

DSC01617I’ve scheduled this exam for February 7th, and during the last couple of weeks I’ve been reading some books in preparation for it:
“HTML5 for web designers” by Jeremy Keith
"CSS3 for web designers" by Dan Cederholm
"Introducing HTML5" by Bruce Lawson and Remy Sharp

2) Write a review of "Microsoft Dynamics CRM 2011 Customization & Configuration (MB2-866) Certification Guide"

DSC01618I’ve been asked to write a book review of the newly published certification guide for the MB2-866 exam. As the book is meant as study material for the exam, I feel I need to have a go at the exam after reading the book in order to state my opinion on the quality of the book and whether it covers the syllabus completely. The review will be published here in the beginning of March.

3) Write more blog posts

If you’re wondering why I’m bothering to publish my plans and goals, it’s partly because writing more blog posts is a goal in itself. The other reason for publishing this is to make sure I actually follow through with my plans. It always harder to admit defeat if you have to do it in public.

4) (Bonus goal) Pass exam 70-486: “Developing ASP.NET 4.5 MVC Web Applications”

When my leave is over, I know I will be working more with MVC than I have until now and I can’t wait! Therefore, I have a hope of passing this certification before I get back to the office. However, I’ve set this as a bonus goal as I realize three certifications in six months might be a bit too much with a toddler.
Wish me luck!