Tuesday, July 31, 2012

Creating a JavaScript Application Timeout

In my previous blog post, I showed how you can use jQuery dialog boxes to replace the JavaScript alert() and confirm() functions for a more advanced and skinnable interface.

In this blog post, I am going to dive a little deeper into the “timer” functionality and see how that can be combined with the dlgConfirm dialog box to create a pro-active application timer.

With today’s client-centric programming (JavaScript + Ajax = Web 2.0), your user may be using your application for quite some time before actually communicating back to the server.  If you are using any authentication or sessions, you may need to contact the server with a “keep-alive” message periodically while still keeping the application secure if the user steps away from the application for any length of time.

The technique I am going to demonstrate starts a count down timer on the client.  Each time a call is made back to the server, the timer is reset.  When the timer runs down to 0, a confirmation dialog appears asking the user if they want to continue.  If the user does not respond to the popup confirmation within a specified time period, the user is automatically logged out.

The Timer Code

function Timer(id, minLimit, callback) {
var thisTimer = this;
this.limit = 0
this.callback = callback;
this.minLimit = minLimit;
thisTimer.resetTimer();
thisTimer.id = "#" + id;
thisTimer.intv = setInterval(function () { thisTimer.intvFunc(); }, 1000);
}

Timer.prototype = {

intvFunc: function () {
$(this.id).text(this.formatTime(--this.timer));
if (this.timer === this.limit) {
clearInterval(this.intv);
this.callback.call(this);
}
},

formatNum: function (n) {
return n < 10 ? '0' + n : n;
},

formatTime: function (n, m, s) {
s = n % 60;
m = (n - s) / 60;
//return (m + (n > 0 ? 1 : 0)) + ' minute' + ((m > 0 || n == 0) ? 's' : '');
return [this.formatNum(m), ':', this.formatNum(s)].join('');
},

stopTimer: function () {
clearInterval(this.intv);
},

resetTimer: function () {
this.timer = this.minLimit * 60;
}

};


The Timer is basically a JavaScript object that attaches to an identifier (typically a <span> or <div>) to display a count down.  When the count down reaches 0, it fires a function call.

The following code:


Time for Fun in <span id='funTime'></span>.
<script type="text/javascript">
new Timer('funTime', 5, function () { alert('FUN TIME!!'); });
</script>



Produces a timer that looks like this:
Time for Fun in 03:03.

When the timer reaches 00:00, an alert box will popup that look like this:
image


The parameters for the Timer object are:
1) The id of the object (div or span) that will display the timer
2) The length of time for the Timer in minutes
3) The code that will fire if the Timer reaches 00:00


If you keep a reference to the Timer object, you can call other functions on the object like:
var myTimer = new Timer('funTime', 5, function () { alert('FUN TIME!!'); });
myTimer.stopTimer();  // Stop the count down
myTimer.resetTimer(); // Reset the count down to original


Creating a Auto-Logout Timer
Step 1: Create a place-holder for your Timer
Logout Warning in <span id="timeleft"></span>.<br />


Step 2: Create a function to Start the Global Timer


function startLogoutTimer() {
var timeLeft = 30;
window.logoutTimer = new Timer('timeleft', timeLeft, function () {
dlgConfirm(
'Auto-Logout',
'<h2 style="text-align: center;">You will be automatically logged off in ' + timeLeft + ' minute'
+ (timeLeft == 1 ? '' : 's') + '.<br /><br />Would you like to continue using the system?</h2>',
function () {
var delayTimer = $('#dlgConfirm').data('delayTimer');
delayTimer.stopTimer();

$.ajax({
url: '[Your Keep Alive URL]',
type: 'POST',
success: function (data) {
if (data.Errors) {
displayErrors(data.Errors);
}
else {
$('#dlgConfirm').dialog('close');
startLogoutTimer();
}
},
error: function (jqXhr, textStatus, errorThrown) {
alert("Error '" + jqXhr.status + "' (textStatus: '" + textStatus + "', errorThrown: '" + errorThrown + "')");
}
});
},
function () { window.location = '[Your Logout URL]'; }, 600, 1);
});
}


This code creates a new Timer and attaches it to the global window object as “.logoutTimer”.  It will display the Timer in the <span> we created in step 1.

If the timer reach 00:00, the function will be called that displays a dlgConfirm (see previous post for details on this object).  The dlgConfirm() method displays a message to the user asking if they want to stay logged in.  They have 1 minute to decide if they want to stay logged in.  If they do not answer the question – or if they select ‘No’ (they don’t want to stay logged in), they they are directed to the Logout URL for your application.  If they select ‘Yes’, they an ajax call is made to extend their login cookie from some period of time.  Also, if they select ‘Yes’, the global timer is restarted.

Then we start the whole process with a call to:
$(function () {
   startLogoutTimer();
}

The Delay Parameter for dlgConfirm()
The last parameter for dlgConfirm() is the delay parameter:
function dlgConfirm(title, msg, onYes, onNo, width, delay)

The relevant code in dlgConfirm for this parameter is:
var delayTimer = null;


var delay = $(this).data('delay');
if (delay > 0) {
    var html = $(this).html();
    html += "<br /><div  style='text-align: center;'>Time Left: <span id='delaytimer'></span></div><br />";

    $(this).html(html);
    var onNo = $(this).data('onNo');
    delayTimer = new Timer('delaytimer',
         delay,
         function () {
              $('#dlgConfirm').dialog('close'); onNo(); });
}


$(this).data('delayTimer', delayTimer);




What this code says is:
1) Look for the delay value in the “data”.
2) If its greater than 0, then do the following:
3) Get the current “message” in the dialog and append a break (<br />), then a
     centered <div>, then add the text and place-holder for:
     Time Left: 00:00
4) Put the new message (with the appended Timer placeholder) back into the dialog
5) Start a new Timer that will call the onNo function if the Timer expires

jQuery Replacements for JavaScript Alert & Confirm

When coding applications today, we are doing everything down on the browser to give the user a nice rich user experience.  Often, we need to alert the user to some situation or ask the use a question.
 
The quick and dirty methods for doing this are to use the built-in JavaScript functions: alert() and confirm().  The problem is, these functions do not give you enough control over their look and feel, and they render differently on different browsers.
 
Below, you can see the effect of the following JavaScript in the different browsers installed on my development machine:

alert('Here!');
confirm('Are you sure?');

Chrome

image image

IE9

image image

Firefox

image image

As you can see, each browser displays the code slightly different.  If your web application happens to have a different color scheme, you can see how this would further serve to confuse an end user.

The replacements I use for these features in my application centers around jQuery’s dialog plug-in.  The dialog plug-in is part of the jQuery UI library of extended input controls.  What this means as far as “look and feel” is that if you use the jQuery UI library, then all of your controls are “skinnable” and will have a consistent look throughout your application.
http://jqueryui.com/demos/dialog/

What I am going to show next is how I wrote extensions to the dialogs to make them work more like the JavaScript functions alert() and confirm().

Using the extensions, we will get a consistent look and feel along with several other nice enhancements:

Chrome:
image image

IE9:
image image

Firefox:
image image


First, on the page I put the following “<div>’s”:
<div id='dlgAlert'></div>
<div id='dlgConfirm'></div>


Alert Dialog (dlgAlert)

I then turn these divs into hidden dialogs.  Let’s examine the code one at a time starting with the alert (the simpler of the two):

$('#dlgAlert').dialog({
autoOpen: false,
modal: true,
close: function (event, ui) {
$(this).text('');
},
open: function (event, ui) {
$(this).dialog('option', 'position', 'center');
},
buttons: {
'Ok': function () {
$(this).dialog('close');

var onOK = $(this).data('onOK');
//alert('onOK: ' + (typeof onOK));
if (onOK != null) onOK();
}
}
});


autoOpen: false
Tells jQuery to turn the div into a dialog box, but do not open the dialog until we tell it to.

modal: true
Tells jQuery that when we open the dialog, we want the users focus to ONLY be on the dialog, so jQuery will place a translucent background over the screen, then display the dialog and the user will not be able to interact with anything on the screen except the dialog.


close: function()
When jQuery closes the dialog, the contents will be cleared.


open: function()
When jQuery opens the dialog, the dialog box will be positioned in the center of the screen.


buttons: ‘Ok’
Similar to the JavaScript alert() function, the dialog box will display an ‘Ok’ button to the user so that they can read the message and click the “ok” button.  The remainer of the Ok button code will be explained below.



Problem with Substituting a Dialog for alert()
Ok, so you may have noticed that in neither the “div”, not the dialog setup is there any “content”.  If you were to simply open this dialog box at this point with:
$(‘#dlgAlert’).dialog(‘open’);


You would get just an empty box with an ‘Ok’ button on it.  Additionally, we have another problem with the dialog box.  In JavaScript, if we write the following code:
alert(‘I am here!’);
$.ajax(…);


When the JavaScript hits the alert() function, all processing STOPS until the user clicks the ‘Ok’ button.  Only after the ‘Ok’ button is clicked will the $.ajax(…) call runs.  However, if we substitute our dialog box for the alert() call like this:
$(‘#dlgAlert’).dialog(‘open’);
$.ajax(…);


The dialog box would open, but code execution would NOT stop and the $.ajax(…) call would be made.  Clearly, this does not function like the JavaScript alert(), so we need to alter our thinking just a little.


dlgAlert() Function (replacement for JavaScript alert())
Here is the function that we call to make the dlgAlert dialog function close to the JavaScript alert() function:



function dlgAlert(title, msg, onOK) {
if (arguments.length < 3) onOK = null;
//alert('typeof onOK: ' + (typeof onOK));


$('#dlgAlert').data('onOK', onOK);

//alert('Added onOK');

$('#dlgAlert').html(msg)
.dialog('option', 'title', title)
.dialog('open');
}



If we call this function like this:
dlgAlert('Sample Title', 'Here!', function () { alert('You clicked Ok!'); });

We get a box that looks like this:
image

And when we click the ‘Ok’ button, we get the ugly JavaScript alert() box telling us that we clicked ‘Ok’:
image

So if we examine the code, we are passing the title (“Sample Title”), the message to be displayed (“Here!”) and the third parameter is a function to be called with the user clicks ‘Ok’ (function () { alert(‘You clicked Ok!’); }).  So in our original example, rather than placing the $.ajax(…) call AFTER the dialog display, instead, we would place it in the onOk parameter like this:
dlgAlert('Sample Title', 'Here!', function () { $.ajax(…) });

Now the $.ajax(…) call will happen after the user clicks ‘Ok’ just like in the JavaScript alert() example.

The way this works is that when we call the dlgAlert() function, we add the onOk parameter to the dlgAlert div object using this line of code:
$('#dlgAlert').data('onOK', onOK);


Then, when the dialog opens and the user clicks ‘Ok’, this code will run:
'Ok': function () {
    $(this).dialog('close');


    var onOK = $(this).data('onOK');
    //alert('onOK: ' + (typeof onOK));
    if (onOK != null) onOK();
}


Notice that the code picks up the ‘onOk’ function from the div:
var onOK = $(this).data('onOK');


Then, if the value is not null, the function is called:
if (onOK != null) onOK();


Message Can Be HTML
Another benefit to using these jQuery dialog boxes is that you can place any HTML in your “Message”.  For instance, the following dlgAlert() call:
dlgAlert('Sample Title',
'<div style="text-align: center">Center with <b>bold</b> text.</div>');


Produces:
image

A Little More Complicated:Confirm Dialog (dlgConfirm)
Ok…  so hopefully you got all that from the dlgAlert() example.  Let dive into the dlgConfirm dialog.  First, the initialization of the dialog:



$('#dlgConfirm').dialog({
autoOpen: false,
modal: true,
close: function (event, ui) {
$(this).text('');
},
open: function (event, ui) {
var width = $(this).data('width');
if (width > 0) $(this).dialog("option", "width", width);

var delayTimer = null;

var delay = $(this).data('delay');
if (delay > 0) {
var html = $(this).html();
html += "<br /><div style='text-align: center;'>Time Left: <span id='delaytimer'></span></div><br />";
$(this).html(html);
var onNo = $(this).data('onNo');
delayTimer = new Timer('delaytimer', delay, function () { $('#dlgConfirm').dialog('close'); onNo(); });
}

$(this).data('delayTimer', delayTimer);

$(this).dialog('option', 'position', 'center');
},
buttons: {
'Yes': function () {
$(this).dialog('close');

var onYes = $(this).data('onYes');
//alert('onOK: ' + (typeof onOK));
if (onYes != null) onYes();
},
'No': function () {
$(this).dialog('close');

var onNo = $(this).data('onNo');
//alert('onOK: ' + (typeof onOK));
if (onNo != null) onNo();
}
}
});



So, I’m not going through all of the code line by line as its very similar to the alert code.  The difference highlights are:
1) There is a width parameter  for expanding the dialog box
2) There is a delay parameter that will allow the dialog to display for a set period
3) There are 2 buttons: Yes and No (instead of Ok Cancel in the JavaScript version)


dlgConfirm() Function (Replacement for JavaScript confirm())


function dlgConfirm(title, msg, onYes, onNo, width, delay) {
if (arguments.length < 3) onYes = null;
if (arguments.length < 4) onNo = null;
if (arguments.length < 5) width = 0;
if (arguments.length < 6) delay = 0;
//alert('dlgConfirm() - START');

$('#divLoading').hide();

$('#dlgConfirm').data('onYes', onYes);
$('#dlgConfirm').data('onNo', onNo);
$('#dlgConfirm').data('width', width);
$('#dlgConfirm').data('delay', delay);

//alert('Added onOK');

$('#dlgConfirm').html(msg)
.dialog('option', 'title', title)
.dialog('open');
}




The code for the dlgConfirm call takes more parameters. The first 2 are the same as the alert call: Title and Message. Obviously, for the message, you want to end the message in the form of a question that can be answered as a Yes or No.


The next 2 parameters: onYes and onNo are function calls that are called when the user presses either the Yes or No buttons respectively.  To “skip” these parameters, simply pass a null for those parameters.


The next parameter is the width.  By default, the dialog box options with a default width, but you might want to adjust that to better fit your message text.  to skip this parameter, pass a 0 as the width and it will be ignored.


The final parameter is the delay parameter.  To use this parameter, you will need an additional piece of code that I will place at the bottom of this article.  The delay feature is something that the JavaScript function does not have.  It allows the dialog to appear for a specified period of time and, if the user does not select Yes or No in that time period, the delay will time out and call the “onNo” function as if the user selected that button.  I will write another blog about how to use this feature to create an auto-logout for your application.


Calling dlgConfirm()
A sample dlgConfirm() call will look like this:



dlgConfirm(
'Sample Title',
'Do you want to continue?',
function() { alert('You clicked YES!'); },
function() { alert('You clicked NO!'); },
500,
0);




Which results in a dialog that looks like this:
image


Pressing Yes:
image

Pressing No:
image



Delay Parameter – Timer Code
To understand and user the “delay” parameter of the dlgConfirm() function, please read my next Blog post.  For completeness, I will include the Time code here:


Timer.prototype = {

intvFunc: function () {
$(this.id).text(this.formatTime(--this.timer));
if (this.timer === this.limit) {
clearInterval(this.intv);
this.callback.call(this);
}
},

formatNum: function (n) {
return n < 10 ? '0' + n : n;
},

formatTime: function (n, m, s) {
s = n % 60;
m = (n - s) / 60;
//return (m + (n > 0 ? 1 : 0)) + ' minute' + ((m > 0 || n == 0) ? 's' : '');
return [this.formatNum(m), ':', this.formatNum(s)].join('');
},

stopTimer: function () {
clearInterval(this.intv);
},

resetTimer: function () {
this.timer = this.minLimit * 30;
}

};

Friday, July 6, 2012

Blogs: Windows Live Writer & Blogger/BlogSpot

Ok…  So here is a blog about blogging.  I started this blog in March and I quickly learned that the thing I hated most about blogging was that my blogs will consist of articles that need both images and code snippets in them.

So right off the bat, I will have problems blogging with my IPad because I don’t have Visual Studio or my screen capture tool.  So I can use the IPad to start a blog or edit an existing blog, but I cannot use it to write a full blog.

The next thing I did not like about blogging was where to put the images.  When I write technical documentation for users or customers, I typically write in Word and possibly convert to PDF.  With those tools, I simply take a screen capture with Greenshot or SnagIt, mark up the image, copy to the clipboard and paste it into my document – quick and easy.  No saving to the disk, no uploading and inserting a link.  With blogging, my blog never “flowed” because I was always stopping to “deal” with images.  Additionally, copying and pasting code from Visual Studio never really displayed the way I wanted.

So I decided to write my next blog with Word so that it flowed.  My plan was to then see what the best way would be to copy and paste the word document into a blog post.  When I finish the post, I went to research posting Word to a blog and I found that starting with Word 2007 (my edition), it actually had a built in Blog posting tool!  How cool!  Problem was, I had trouble making it connect to my http://jdready.blogspot.com account.  So I created a WordPress.com account and I was able to make it connect there.  But still, there were formatting problems.

I continued my search for blog posting software when a product called “Windows Live Writer” continued to come up.  I already use Windows Live Messenger for IM and it turned out I already had Writer installed.  I was instantly able to connect to my original Blogger account – and, it magically uploaded and connected my images to my Blog!

Everything was going along swimingly!  I created several blogs and was really enjoying the experience… Until…  This morning, I tried to post a particularly long post, and when WLW connected to Blogger, I got an error (403 – Forbidden).  What is forbidden?  Why is it forbidden?  None of these questions were immediately answered, so I started Googling…

What I found was that it seemed to have something to do with images.  Up to this point, I was not paying any attention to how or why or where WLW was posting images – it was just magic that worked…  But not it was not working.  So I needed to learn more about this “magic”.  The posts indicated that WLW was saving images to another Google service called “Picasa”.  If you Google “Picasa”, you go to a Google photo sharing site and are prompted to download a program called Picasa.  This didn’t seem right, so I them Googled “picasa web album” and found what I was looking for!

Because I was already logged into Google, I was suddenly presented with 2 albums: JD Ready: Pocket Protector (0 photos) and Windows Live Writer (62 photos).
image

At first, I thought my 403 Errors were because I was trying to upload too many images (which is why I have a 5 part blog series instead of a 3 part series!).  But the truth is, when WLW starts communicating with Google (both Blogger and Picasa are Google products), it gets a security “token” if that token expires during a session (like me taking over an hour to write my 3rd blog post), Picasa’s service will return a 403.

All I had to do to clear the 403 was to shut down WLW and start it up again.  I presume it got a new security token and I was able to continue my blog posting!

Transforms for *.config Files, Part 5: SlowCheetah & Advanced

Can this be made any easier? Yes: SlowCheetah!
Ok… at this point you may be asking yourself if this process can be made any simpler. First, you have to be certain that you spell your transforms and Solution Configurations EXACTLY the same, otherwise they won’t get picked up properly. Additionally, you probably wouldn’t want to take the time to add the nice little <DependentUpon> tags to the transforms so they show up under the live config file.

Fortunately, you don’t have to. There is a Visual Studio Extension called SlowCheetah that will do this for you! SlowCheetah was originally developed for the same use we have described in these blog posts, but for modifying App.config files for console apps. See even App.config files do not have the transform capabilities of Web.config. SlowCheetah was developed by some Microsoft employees that wanted this transform capabilities. In fact, for console apps, SlowCheetah even hooks into your project script to run the transforms on application build, but only for Console apps – not web apps, so we still need our (Project).wpp.targets file.

Installing SlowCheetah
Tools >> Extension Manager >> Online Gallery >> Search for “Slow Cheetah”
Click “Download” and follow the instructions to install. When finished, you will need to restart Visual Studio to have the changes take effect.

Delete the log4net.Release.config and log4net.Debug.config files from your project. Now, RtClick on the log4net.config file and you should see a context menu like this:
image_thumb2_thumb
We now have a new options called “Add Transform”. Go ahead and select that option. You will get a notice that in order for your project to support transforms, the .csproj file will need to be modified and the project will be unloaded and reloaded to make that change happen. Go ahead and accept that message and you will end up with a project that looks like this:
image_thumb3_thumb

SlowCheetah went ahead an created the transform files we needed for all of our defined Solution Configurations and added them to our project and even added the <DependentUpon> tags to the files so they show up nicely under our log4net.config file.

The “REAL” Kicker: SlowCheetah’s Preview!
Ok… SlowCheetah is cool in that it will create our base transforms for any config file (including Web.config). But as I showed, it’s really nothing that you couldn’t do already. So what’s the real kicker to using SlowCheetah? Slow Cheetah will do a PREVIEW of your transform! You don’t even have to compile your code to see it!

Ok, but the problem we have right now is that our transforms are associated (read: <DependentUpon>) the transient Web.config and log4net.config. The “Preview” function of SlowCheetah compares the transform against is <DependentUpon> target. So what we need to do is move our transforms so that they hang from the *.BASE.config files.

SlowCheetah thinks any Web.*.config Pattern is a Transform
Another caveat to this process is that it appears that SlowCheetah thinks that ANY file with the pattern Web.*.config is a transform file. When I right click on Web.BASE.config, it gives me the option to “Preview Transform”:
image_thumb5_thumb

What I expected was the ability to “Add Transforms”. By renaming the file to Web.Main.BASE.config, I achieved what I was looking for:
image_thumb7_thumb

When finished, you should end up with log4net.BASE.config which is your main log4net configuration with 2 transforms: log4net.BASE.Debug.config and log4net.BASE.Release.config. and a log4net.config file which is the result file for the transform process. On the Web.config side, you should end up with a Web.Main.BASE.Config file which is your main Web configuration with 2 transforms: Web.Main.BASE.Debug.config and Web.Main.BASE.Release.config and a web.config file which is the result file for the transform process. You project should look similar to:
image_thumb8_thumb

Now, if you RtClick and of the transform files, you will get a content menu option to “Preview Transform” like this:
image_thumb10_thumb

Select the “Preview Transform” option and a window will display showing the original config file (Web.Main.BASE.config) and a temporary file with the transforms applied where you can then jump through the “differences” between the 2 files to confirm that your transforms worked!
image_thumb12_thumb

Final Step: Make the (Project).wpp.targets Use the New Names
So we almost have SlowCheetah working. Through this process, we have done a lot of naming changes. Our original log4net.config file is now really log4net.BASE.config. Our Web.config has morphed from Web.config to Web.BASE.config to Web.Main.BASE.config. Subsequently, out transforms have changed as well. In the end, after the transforms are complete, we still want the result of the transforms to become Web.config and log4net.config. So we modify the (Project).wpp.targets file to look like this:

<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />

<Target Name="TransformWebBASEConfig" BeforeTargets="BeforeBuild" >
<TransformXml Source="$(ProjectDir)Web.Main.BASE.config"
Transform="$(ProjectDir)Web.Main.BASE.$(Configuration).config"
Destination="$(ProjectDir)Web.config" />
</Target>

<Target Name="TransformLog4netBASEConfig" BeforeTargets="BeforeBuild" >
<TransformXml Source="$(ProjectDir)log4net.BASE.config"
Transform="$(ProjectDir)log4net.BASE.$(Configuration).config"
Destination="$(ProjectDir)log4net.config" />
</Target>
</Project>


Advanced Issue: TFS
I was going to make this series a 4 part-er, but I think I can wrap up the final issues here. How do these changes effect TFS? Our web.config and log4net.config files are the most important files when we publish, but are not important to save in Source Control (TFS). To accomplish this scenario, you should exclude the result config files from TFS:
Click the Web.config/log4net.config in Solution Explorer then:
File >> Source Control >> Exclude (file) from Source Control
image_thumb[14]_thumb

Now, if these files have ever been in Source Control, they will have their Read-Only flag set. If you do not remove this, MSBuild will fail when it tries to write the result of the transform to these files.

Advanced Issue: PUBLISHING
As far as “publishing” goes, You want the log4net.config and web.config files to be copied always to the output folder, but you want the *.BASE.config files to never get copied. Recall that the publish process is where MSBuild tries to do transformations. However, with this new format, there are no transformation files hanging from the root Web.config or log4net.config files, so no transforms are attempted and the files are simply copied to the output folder.

Transforms for ANY .config file: 5 Part Series
Part 1: Transforms on Build
This first part does an overview of the default behavior of transforms. It then shows how you can plug into the MSBuild process and apply your transforms at Build time rather than the default publish time.

Part 2: New Config Profiles
The second part of the series shows how to create a new solution configuration and the nuances of what happens behind the scenes (in case you want to delete a configuration). It also discusses having a local development configuration specific to an individual developer.

Part 3: Other Config Files
The third part discusses how to add a configuration for another element of the application (using log4net as an example). This post shows how this process differs from the handling of Web.config.

Part 4: Making the transforms look nice
This fourth part is just a quickie to show how you can manually make your other configuration files sit under their respective parent files.

Part 5: SlowCheetah & Advanced
This fifth and final post is where I try to bring together all of the elements from the previous posts together and introduce a Visual Studio extension that allows much of the previous posts to be accomplished easily and automatically.

Transforms for *.config Files, Part 4: Making the transforms look nice

Nicely Display the log4net.*.config Files
So I have created the Debug and Release log4net config files. So we have BASE which is transformed by either Debug or Release (depending on which is selected) and ultimately creates log4net.config which is the configuration file that our application will use. However, as you can see below, all of these files are at the root level of the project where Web.config has the transformations located as sub-files under Web.config.
image

We can make this happen for log4net as well. As we did in Part 1 of this blog, Unload the project, then edit the .csproj file. If you search for Web.Release.config, you should find some lines that look similar to (notice the <DependentUpon> tag):

<Content Include="Web.config" />
<Content Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</Content>


If you search for log4net.Release.config and log4net.Debug.config and add this tag to their “content” tags like this (changing Web.config to log4net.config):


<Content Include="log4net.config">
<SubType>Designer</SubType>
</Content>
<Content Include="log4net.BASE.config">
<SubType>Designer</SubType>
</Content>
<Content Include="log4net.Release.config">
<DependentUpon>log4net.config</DependentUpon>
<SubType>Designer</SubType>
</Content>
<Content Include="log4net.Debug.config">
<DependentUpon>log4net.config</DependentUpon>
<SubType>Designer</SubType>
</Content>


If we then Save, close and re-load the project, we have a nicely formatted config with the transforms sitting nicely beneath the log4net.config (just like Web.config):
image


In part 5 of this series, I will show you a Visual Studio Extension called SlowCheetah that makes the process of creating and managing configuration file for any configuration as easy as it is for Web.config.  It even adds the ability to Preview your transforms.


Transforms for ANY .config file: 5 Part Series
Part 1: Transforms on Build
This first part does an overview of the default behavior of transforms. It then shows how you can plug into the MSBuild process and apply your transforms at Build time rather than the default publish time.

Part 2: New Config Profiles
The second part of the series shows how to create a new solution configuration and the nuances of what happens behind the scenes (in case you want to delete a configuration). It also discusses having a local development configuration specific to an individual developer.

Part 3: Other Config Files
The third part discusses how to add a configuration for another element of the application (using log4net as an example). This post shows how this process differs from the handling of Web.config.

Part 4: Making the transforms look nice
This fourth part is just a quickie to show how you can manually make your other configuration files sit under their respective parent files.

Part 5: SlowCheetah & Advanced
This fifth and final post is where I try to bring together all of the elements from the previous posts together and introduce a Visual Studio extension that allows much of the previous posts to be accomplished easily and automatically.

Thursday, July 5, 2012

Transforms for *.config Files, Part 3: Other Config Files


In Part 1 of this series, we ran through an overview of transforms and how we can leverage then at build time rather than the default publish/deploy time.  In Part 2, we went over how you can add your own transform to Web.config in addition to the Web.Debug.config and Web.Release.config.  In Part 3 of this series, we are going to see how we can leverage transforms for other configuration files – not just Web.config.

What Other Config Files? Maybe Log4Net.config?
When you are working on a web project, you cannot help but use Web.config as it has essential elements needed for every web-based application.  One thing that we do at my work is set up logging for every application.  We use log4net as our logger of choice and we choose to keep the details of the log configuration in a separate file from Web.config.  In the Web.config:
<add key="log4Net.ConfigurationFile" value="log4net.config" />
Which tells log4net to get its configuration from “log4net.config”.

Add a properly formatted log4net.config file to the project (RtClick Project >> Add >> New Item >> C# >> Data >> XML).  Copy and paste this new file and rename the copy to log4net.BASE.config.  You should now have 2 files in your project that look like this:
image

Create the Transformation Files!
What we want to do now is generate the transformation files for our Solution Configurations (like we have for our Web.Config).  If you recall, for the Web.config files, we simply RtClick’ed on the Web.config file and we had a context menu item called “Add Config Transforms (it’s greyed out at this point because we have all the transforms for all of our solution configurations):
image 

So to create the transforms, we should just do the same thing, right?  Well, when we RtClick the log4net.config file, our context menu does not have the “Add Config Transforms” menu item!:
image

It seems that Microsoft assumed that transforms were only good for Web.config!  There is a Visual Studio Add-on that will allow you to do this easily, but before we introduce this product, let’s first do it the “Hard Way”…

Adding Config Transforms Manually (The “Hard Way”)
Let’s say in our log4net.config file we are going to log to a database and in our BASE config file we have the connection string pointing to our Dev SQL server.  You might see a config “connectionString” line that looks like this (lots of other keys have been removed):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
. . .
<appender name="AdoNetAppender_SqlServer" type="log4net.Appender.AdoNetAppender">
. . .
<connectionString name="Log4NetConString" value= "Data Source=otis-web02\otisdevsql;Initial Catalog=WebAppsLog;Persist Security Info=True;User ID=Logger;Password=L0gg3r02;MultipleActiveResultSets=True;" />
. . .
</appender>
. . .
</log4net>
</configuration>




We now want to create a log4net.Release.config that will transform this line to  point to the Production SQL Server.  We will add a new XML file to our project and name the file log4net.Release.config and place the following content in the file:


<?xml version="1.0" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<log4net>
<appender name="AdoNetAppender_SqlServer" type="log4net.Appender.AdoNetAppender">
<connectionString name="Log4NetConString" value= "Data Source=ReleaseSQLServer;Initial Catalog=WebAppsLog;Persist Security Info=True;User ID=LogUser;Password=xyz123;MultipleActiveResultSets=True;" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
</appender>
</log4net>
</configuration>


Remember that transforms are, by default, only associated with Web.config files.  So even when you Publish your project, MSBuild will ignore your log4net.Release.config file and will not automatically apply the transform.  You must TELL MSBuild to do this.  From Part 1 of this series, we can open the (Project).wpp.targets file and add the step for MSBuild to transform the log4net.BASE.config file using the log4net.


<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />

<Target Name="TransformWebBASEConfig" BeforeTargets="BeforeBuild" >
<TransformXml Source="$(ProjectDir)Web.BASE.config"
Transform="$(ProjectDir)Web.$(Configuration).config"
Destination="$(ProjectDir)Web.config" />
</Target>

<Target Name="TransformLog4netBASEConfig" BeforeTargets="BeforeBuild" >
<TransformXml Source="$(ProjectDir)log4net.BASE.config"
Transform="$(ProjectDir)log4net.$(Configuration).config"
Destination="$(ProjectDir)log4net.config" />
</Target>
</Project>


Now, if we Build the project (using the Release configuration), we get the desired log4net.config line with the Connection string set to ReleaseSQLServer:


<connectionString name="Log4NetConString" value="Data Source=ReleaseSQLServer;Initial Catalog=WebAppsLog;Persist Security Info=True;User ID=LogUser;Password=xyz123;MultipleActiveResultSets=True;" />


Objective Complete: Transforming a non-Web.config Config File
Ok, so we accomplished the main objective of this post which is to make a config file other than Web.config do a transform when we build the project.  You can follow the same technique to build your additional transforms (Debug, JDRLocal, etc.).  For the remainder of this post, let’s dig just a little bit deeper.


In Part 4 of this series, I’ll just show you how to format your non-Web.config configuration files so that they appear in the Solution Explorer under the parent configuration file.


Transforms for ANY .config file: 5 Part Series
Part 1: Transforms on Build
This first part does an overview of the default behavior of transforms. It then shows how you can plug into the MSBuild process and apply your transforms at Build time rather than the default publish time.

Part 2: New Config Profiles
The second part of the series shows how to create a new solution configuration and the nuances of what happens behind the scenes (in case you want to delete a configuration). It also discusses having a local development configuration specific to an individual developer.

Part 3: Other Config Files
The third part discusses how to add a configuration for another element of the application (using log4net as an example). This post shows how this process differs from the handling of Web.config.

Part 4: Making the transforms look nice
This fourth part is just a quickie to show how you can manually make your other configuration files sit under their respective parent files.

Part 5: SlowCheetah & Advanced
This fifth and final post is where I try to bring together all of the elements from the previous posts together and introduce a Visual Studio extension that allows much of the previous posts to be accomplished easily and automatically.