|
Code Endeavors, LLC
border_tl border_tr
DotNetNuke Blog Minimize

New Video Available Demonstrating How To Easily Get Silverlight Working With a DotNetNuke Module

I mentioned in my last blog that I planned on publishing a video that would give an overview of how the Silverlight module template works.  I have updated the download page to include a reference to it.

Please note the updating of a module through the install.aspx?mode=installresources link will not work for 5.01 due to a bug.  The original installation does work.  Updates can use the normal install wizard or can be patched by downloading the source and opening up Library\Services\Installer\Installer.vb file and adding the following line at around line #236.  Please note that this is not an official patch.

If kvp.Value.Package.IsValid Then
  InstallerInfo.RepairInstall = True  'quick fix to allow install packages to repair
 

New DotNetNuke Silverlight Module Template Published

I finally was able to get the multi-project template functionality working to enable the easy creation of a DotNetNuke Silverlight module.  The sample demonstrates how easy it is to have Silverlight interact with javascript, which in turn can talk directly to your server-side module code.  Unlike the other module templates, this one requires the creation two projects, one for your DNN module and one to host your Silverlight code.  Upon compilation, the Silverlight code gets zipped up into a file with a XAP extension.  This then gets packaged into the DNN module installation package and a source package. 

The diagram above shows the basic interactions with the server-side code and the client.  The DotNetNuke Framework box should be straight forward, as the previous AJAX templates work this way.  The server-side gray box simply denotes static files served up by IIS.  However, when the client loads them, you end up with managed C# or VB.NET code running on the client.  The interaction between our client-side module and the rest of the framework is quite cool.  When I was writing this code, I found myself saying, Wow!  It just works as I would expect!  I plan on creating a video explaining this soon. 

Since the Silverlight code can interact with javascript, it can easily call the dnn.xmlhttp methods to initiate a Control Method call to the server.  Those familiar with the edit-in-place capabilities in the Text/HTML module should know the Control Method calls work with the standard ASP.NET forms authentication scheme, which means we can easily verify the rights of a user.  The module generated from the template prove this point.

DNNSilverlightModule

Finally, I wanted to note how cool it is that Blend can open up a sln file directly and even compile it!  This means that you can open the same template solution, edit the XAML (UI) and compile directly in Blend.  Then simply refresh your dnn/install/install.aspx?mode=installresources link to re-install the module, and refresh your page with the module to instantly see your modifications.

Excited?  Go grab it now from CodePlex!


New Skin Template Released on CodePlex

TemplateSkinMenu1I am pleased to announce the availability of a new DotNetNuke skin template on CodePlex.  I decided to add two additional templates (DotNetNuke Skin (VB) and DotNetNuke Skin (C#)).  Like the AJAX Module templates, these templates will provide all the MSBuild goodness to automatically create a source and installation package using the information collected in the wizard upon project creation.  The default skin that is created demonstrates some of the new functionality now possible with the DNNMenu v2.x like

  • Rounded glossy corners
  • Animation (requires updated navigation provider)
  • Substitution of items for images
  • Control over nearly every aspect of the menu via CSS Selectors

To get started simply

  • Download the msi setup package and install it
  • Start Visual Studio, Choose New Project... and Select DotNetNuke Skin

NewProjectDotNetNukeSkin

  • Fill out the wizard

NewProjectWizard 

  • Choose Load project normally when the Security Warning shows

NewProjectWarning

  • Read documentation for pointers

NewProjectDoc

  • Compile the skin (You are not actually compiling a dll, this is simply running the custom MSBuild script to package your skin)
  • Install the skin like any other through DotNetNuke.  Note:  the ability to install via install.aspx?mode=installresources is not possible at this time due to a bug in DNN5.

For those of you who wish to simply learn from an already created skin, simply download the Sample installation package directly.

The sample skin has two kinds: index and animated.  This demonstrates both flavors of skin development (ascx and html using the new object tag syntax).  The animated uses ascx due to the lack of support for custom attributes in the html token replacement engine in dnn.  (I am still trying to get this added- please don't direct your annoyances at me).  It also has a packaged container which demonstrates a few more css tweaks.

TemplateSkinMenu2

Finally, I want to encourage people to download the latest version of the WebControls on CodePlex.  It is not required for this skin/template, but there was one minor fix I added to address the animation triggering multiple times.


Getting Continuous Integration Working With CodePlex: Part III

Last post in this series we learned some basics of MSBuild, including the use of 3rd party tasks.  We saw that by simply importing some other targets file we can extend the functionality available to our scripting language.  The last two pieces to this puzzle happen to be solved by use of 3rd party plugins available on CodePlex:  Deployment and Automating our Build.

Deployment

Every build we wish to publish can easily be published to our project page via the CodePlex MSBuild tasks.  The first step is to import the CodePlex targets file into our script.

<Import Project="$(RootDir)\lib\MSBuildCodePlexTasks\CodePlex.WebServices.Client.Targets" Condition="'$(DeployToCodePlex)' == '1'" />

With our custom tasks imported, creating a new release is as simple as defining a new target and setting a bunch of properties.

<Target Name="DeployToCodePlex" Condition="$(DeployToCodePlex) == '1' and $(ConfigurationName)=='Release'" >
    <CreateRelease
        ProjectName="$(ProjectName)"
        ReleaseName="v$(Major).$(Minor).$(Build).$(Revision) - Nightly"
        IsDefaultRelease="$(IsDefaultRelease)"
        ShowToPublic="$(ShowToPublic)"
        ShowOnHomePage="$(ShowOnHomePage)"
        Description="$(Description)"
        ReleaseDate="$(ReleaseDate)"
        ReleaseStatus="$(ReleaseStatus)"
        Username="$(User)"
        Password="$(Password)" />

Most of the properties mentioned here are pretty self-evident.  What may not be is where they are defined.  The fact that we are publishing our source code to the central server and it is this code that we are using to publish code, it probably is not a good idea to put our username and password directly into this script, or any script that gets uploaded to our source control.  Instead, what I chose to do was create a file outside my folder structure that gets imported when it exists. 

<Import Project="$(RootDir)\..\..\DNNWCNonDist.Targets" Condition="Exists('$(RootDir)\..\..\DNNWCNonDist.Targets')" />

This will allow your users who download your code to still run most of your build tasks, but will not give them rights to create a public release!  The basics of this file are as follows.

xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
    <DeployDir>$(RootDir)\deployDeployDir>
    <DeployToCodePlex>1DeployToCodePlex>
    <DeployToWebcontrolsSite>0DeployToWebcontrolsSite>  
    <DotNetNukeDir>D:\dev\DotNetNuke\Core_Development\DotNetNuke_Cambrian\WebsiteDotNetNukeDir>
    <WebSiteDir>$(RootDir)\websiteWebSiteDir>

    
    <IsDefaultRelease>falseIsDefaultRelease>
    <ShowToPublic>falseShowToPublic>
    <ShowOnHomePage>falseShowOnHomePage>
    <Description>Nightly BuildDescription>
    <ReleaseStatus>PlannedReleaseStatus>
    <User>******User>
    <Password>******Password>
PropertyGroup>
Project>

With our release created it is now time to use another task to upload our files.  First we need to define what files to use by creating an ItemGroup.

<ItemGroup>
    <ReleaseFile Include="$(DeployDir)\WebControls.install.v$(Major).$(Minor).$(Build).$(Revision).zip">
        <FileType>RuntimeBinaryFileType>
    ReleaseFile>
    <ReleaseFile Include="$(DeployDir)\WebControls.source.v$(Major).$(Minor).$(Build).$(Revision).zip">
        <FileType>SourceCodeFileType>
    ReleaseFile>
ItemGroup>

We then use the UploadFiles task to cause our files to be uploaded.

<UploadFiles
        ProjectName="$(ProjectName)"
        ReleaseName="v$(Major).$(Minor).$(Build).$(Revision) - Nightly"
        ReleaseFiles="@(ReleaseFile)"
        Username="$(User)"
        Password="$(Password)" />

Triggering an Automated Build

In order to completely automate our process, we need a piece of software to monitor our source code at some defined interval and pull down any changes, then simply initiate our build process.  Since all of our deployment logic is encapsulated in our build script this is probably the easiest portion to configure.  I chose to use CruiseControl.NET running locally to handle my builds mainly due to the fact that CodePlex offers a sourcecontrol block for us found here.  If your new to CruiseControl.NET I suggest digging through their documentation.  Once you have cruise installed simply follow the instructions on the CodePlex page:  Copy 3 dlls into the CruiseControl.NET\Server folder and make sure you have the J# redistributable installed.  Yes, I agree, requiring J# for a .NET plugable architecture like Cruise is a bit odd.  To configure your project simply find the CruiseControl.NET Start menu entry and choose CruiseControl.NET Config to edit the xml file used in setting up your project.

<cruisecontrol>
    <project>
        <name>DotNetNuke_WebControlsname>
        <triggers>
            <intervalTrigger seconds="3000" buildCondition="IfModificationExists"/>
        triggers>
        <workingDirectory>E:\dev\DotNetNuke\CodePlex\DotNetNuke.WebControlsworkingDirectory>
        <sourcecontrol type="codeplex">
            <project>dnnwebcontrolsproject>
            <projectPath>/trunkprojectPath>
        sourcecontrol>
        <tasks>
            <msbuild>
                <executable>C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exeexecutable>
                <workingDirectory>E:\dev\DotNetNuke\CodePlex\DotNetNuke.WebControls\workingDirectory>
                <projectFile>DotNetNuke.WebControls.slnprojectFile>
                <buildArgs>/noconsolelogger /p:Configuration=Release /v:diagbuildArgs>
                <targets>Buildtargets>
                <timeout>3000timeout>
                <logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dlllogger>
            msbuild>
        tasks>
    project>
cruisecontrol>

With cruise you simply define an interval trigger to pull your sourcecode directory.  I chose to only perform a build if a modification has taken place, which I believe is the default.  The interaction with CodePlex is all handled by their plugin, simply specify the project name and path within the source control tree to pull and then launch a msbuild task passing in your solution file.  You will notice that there is no secure information here.  Instead it will be assumed that the DNNWCNonDist.Targets file, mentioned earlier, will be relatively located to the working directory.  Without this file our builds will still work, they just won't be published. 

Closing Thoughts

Obviously I could have spent a lot of time going into each of the details on how to customize your own build environment.  I purposefully chose to address this series at a higher level, to try and give you a roadmap of where to start and what are the pieces you need to consider.  Hopefully you found it helpful.


JSON and DateTime Kind

I've been working on a project for a client recently and was bitten by a bug recently.  It has to do with using the Microsoft AJAX Extensions, specifically, the JavascriptSerializer.  Our use of JSON is simply as an easy way to serialize our objects, whether it be to disk or across the wire.  What was happening is objects that contained dates would get their time shifted when they reached the other side.  The explanation is that the serialization format of JSON has no way of representing what .NET uses its Kind property for.  And since there is no Kind available on deserialization it assumes UTC. 

For those who don't know or don't memorize, here is a quick bit of trivia.  Assume central time.

  ToLocal ToUniversalTime
DateTime.Kind = Local Unchanged +6 hours
DateTime.Kind = Utc - 6 hours Unchanged
DateTime.Kind = Unspecified ? ?

The answer is...

 

 

 

 

A DateTime of Unspecified Kind that has ToLocal applied to it will be -6.  Similarly a DateTime of Unspecified Kind that has ToUniversalTime applied will be +6.  The logic is that .NET does not know the kind.  If you call ToLocal or ToUniversalTime you must want a conversion.  As to why the JSON deserialization process chooses to make it Utc I don't know.  It would seem to make more sense to simply use Unspecified as the json has not specified a kind.  There are several blog posts covering this subject (here and here for example) but the solutions offered seemed to come up short of what I desired.  I want to serialize any object and not have to inform the other developers that DateTime properties need this extra logic in their setters. 

Fortunately, the JavascriptSerializer allows you to add your own Converters with ease.  Information on how to use it can be found on Microsoft's site here.  However, when I first looked at it I was a bit confused by the SupportedTypes property, as I have never seen code quite like it.

        public override IEnumerable SupportedTypes
        {
            //Define the ListItemCollection as a supported type.
            get { return new ReadOnlyCollection(new List(new Type[] { typeof(ListItemCollection) })); }
        }

Can it really be necessary to create a new ReadOnlyCollection, passing it a new generic List, passing it a Type array, which specifies a Type?  Yeah, this is a bit more complicated than it needs to be!  I will post my entire console sample for you to play with yourself.  Hopefully it saves someone the trouble I went through.

 

using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;

namespace jsonconverters
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified);
            
            Console.WriteLine(now.ToString());
            var title = new Title() { Name = "Iron Man", ReleaseDate = now, CreateDate = now };

            var serializer = new JavaScriptSerializer();
            
            //comment this line out to see results without our custom converter
            serializer.RegisterConverters(new JavaScriptConverter[] { new DateTimeConverter() });

            var json = serializer.Serialize(title);
            Console.WriteLine(json);
            var newTitle = serializer.Deserialize(json);
            Console.WriteLine(newTitle.Name);
            Console.WriteLine(newTitle.ReleaseDate);
            Console.WriteLine(newTitle.ReleaseDate == now);
            Console.WriteLine(newTitle.CreateDate);
            Console.WriteLine(newTitle.CreateDate == now);
            Console.WriteLine(newTitle.LastUpdate.HasValue);
            Console.ReadLine();

        }
    }

    <span class="kwrd">public</span> <span class="kwrd">class</span> Title
    {
        <span class="kwrd">public</span> <span class="kwrd">string</span> Name { get; set; }
        <span class="kwrd">public</span> DateTime ReleaseDate { get; set; }
        <span class="kwrd">public</span> DateTime? CreateDate { get; set; }
        <span class="kwrd">public</span> DateTime? LastUpdate { get; set; }
    }

    <span class="kwrd">public</span> <span class="kwrd">class</span> DateTimeConverter : JavaScriptConverter
    {
        <span class="kwrd">private</span> <span class="kwrd">string</span> _dateKey = <span class="str">"d"</span>;
        <span class="kwrd">private</span> <span class="kwrd">string</span> _kindKey = <span class="str">"k"</span>;
        <span class="kwrd">public</span> <span class="kwrd">override</span> IEnumerable<Type> SupportedTypes
        {
            get { <span class="kwrd">return</span> <span class="kwrd">new</span> List<Type>() { <span class="kwrd">typeof</span>(DateTime), <span class="kwrd">typeof</span>(DateTime?) }; }
        }

        <span class="kwrd">public</span> <span class="kwrd">override</span> IDictionary<<span class="kwrd">string</span>, <span class="kwrd">object</span>> Serialize(<span class="kwrd">object</span> obj, JavaScriptSerializer serializer)
        {
            Dictionary<<span class="kwrd">string</span>, <span class="kwrd">object</span>> result = <span class="kwrd">new</span> Dictionary<<span class="kwrd">string</span>, <span class="kwrd">object</span>>();
            <span class="kwrd">if</span> (obj.GetType() == <span class="kwrd">typeof</span>(DateTime))
            {
                DateTime d = (DateTime)obj;
                result[_dateKey] = d.Ticks;
                <span class="kwrd">if</span> (d.Kind != DateTimeKind.Unspecified)
                    result[_kindKey] = d.Kind;
                <span class="kwrd">return</span> result;

            }
            <span class="kwrd">else</span> <span class="kwrd">if</span> (obj.GetType() == <span class="kwrd">typeof</span>(DateTime?))
            {
                DateTime? theDate = (DateTime?)obj;
                <span class="kwrd">if</span> (theDate.HasValue)
                {
                    result[_dateKey] = theDate.Value.Ticks;
                    <span class="kwrd">if</span> (theDate.Value.Kind != DateTimeKind.Unspecified)
                        result[_kindKey] = theDate.Value.Kind;
                }
            }
            <span class="kwrd">return</span> result;
        }

        <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">object</span> Deserialize(IDictionary<<span class="kwrd">string</span>, <span class="kwrd">object</span>> dictionary, Type type, JavaScriptSerializer serializer)
        {
            <span class="kwrd">if</span> (dictionary.ContainsKey(_dateKey))
            {
                var d = <span class="kwrd">new</span> DateTime(<span class="kwrd">long</span>.Parse(dictionary[_dateKey].ToString()), DateTimeKind.Unspecified);
                <span class="kwrd">if</span> (dictionary.ContainsKey(_kindKey))
                    d = DateTime.SpecifyKind(d, (DateTimeKind)dictionary[_kindKey]);
                <span class="kwrd">return</span> d;
            }
            <span class="kwrd">return</span> <span class="kwrd">null</span>;
        }
    }
}</pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style><br><strong><a style="color:#800000; text-decoration:none" href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2159/Getting-Continuous-Integration-Working-With-CodePlex-Part-II.aspx" target="_main">Getting Continuous Integration Working With CodePlex: Part II</a></strong><p>Last post I went through the basics of what you do to set up your project on CodePlex along with how to hook into the source control.  This blog will explain what I did for my build script.  Specifically, how I handled versioning and packaging.</p> <p>Most developers when confronted with a mundane task that is continually repeated will be compelled to automate it.  We are programmers after all, we enjoy making the computer making our lives easier.  One of these tasks is the creation of a software package to hand off to someone.  Over the years there has been numerous ways to do this, from bat files issuing dos commands, to Apache Ant, and more recently MSBuild.  Most open source shops seem to gravitate towards Ant or its .NET couterpart <a href="http://en.wikipedia.org/wiki/NAnt">Nant</a> due to its open source roots.  Seeing that I am one who spends most of my development life in Visual Studio 2008, the obvious choice to me is MSBuild.  I say this because this is what Visual Studio uses under the covers as your vbproj/csproj files are actually MSBuild scripts.  Quite a people never know this because it is not obvious how to open them up to edit.  </p> <p><strong>MSBuild Basics</strong></p> <p>Of course you could open up your project file with some other text editor other than Visual Studio, but what fun would that be, as you would be doing without any Intellisense (yes, I am spoiled by this feature).  So in order to open your project script up in visual studio, you must first have a solution defined, then you simply right-click on your project in your solution and choose Unload Project.  With your project now grayed out, right-click again and choose Edit.  You will now see the MSBuild script.  Seeing that this process of unloading our project is a bit annoying, I choose to not edit this file very much.  Instead I suggest simply specifying in the file that it should import another file in our project.  To see another example of this, search the file for either one of these references</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">Import</span> <span class="attr">Project</span><span class="kwrd">="$(MSBuildBinPath)\Microsoft.VisualBasic.targets"</span> <span class="kwrd">/></span>

<span class="kwrd"><</span><span class="html">Import</span> <span class="attr">Project</span><span class="kwrd">="$(MSBuildBinPath)\Microsoft.CSharp.targets"</span> <span class="kwrd">/></span> 

</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p>A couple concepts you need to know is that variables within MSBuild are defined with a $(VariableName) token.  There are also <a href="http://msdn.microsoft.com/en-us/library/ms164309(VS.80).aspx">predefined variables/properties</a> like MSBuildBinPath that are given to us.  So the first thing that we are going to do is add a line right under this Import that we found.</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">Import</span> <span class="attr">Project</span><span class="kwrd">="$(MSBuildProjectDirectory)\build.targets"</span> <span class="attr">Condition</span><span class="kwrd">="Exists('$(MSBuildProjectDirectory)\build.targets)"</span> <span class="kwrd">/></span></pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p>You will notice we used another attribute of the Import element called Condition.  This allows us to open the project even when our additional build script has not been defined.  We are done with this script, as I think it unwise to tamper too much with it, as Visual Studio may stomp on us at some point.  So now all you need to do is right-click on the project and choose Reload Project.</p>
<p>It is now time to create our build.targets file.  Simply do a file add new item and choose an XML Item with the file name of build.targets.  The shell of our script needs to contain a MSBuild root element.  </p><pre class="csharpcode"><span class="kwrd"><?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span> ?<span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Project</span> <span class="attr">xmlns</span><span class="kwrd">="http://schemas.microsoft.com/developer/msbuild/2003"</span><span class="kwrd">></span>

<span class="kwrd"></</span><span class="html">Project</span><span class="kwrd">></span>
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p> </p>
<p><strong>MSBuild Properties and Targets</strong></p>
<p>Earlier I showed you how to use pre-existing properties.  It is now time to learn how to define our own.  This is done via a Property Group.</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">PropertyGroup</span><span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">MyVariable</span><span class="kwrd">></span>DotNetNuke Rocks<span class="kwrd"></</span><span class="html">MyVariable</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">PropertyGroup</span><span class="kwrd">></span>
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p>Targets in MSBuild can be thought of much in the same way as methods to a programmer.  One cool thing about MSBuild is that you can even "override" targets.  Probably the two most common targets overridden are BeforeBuild and AfterBuild.  To do this, add the following to your script.</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">="BeforeBuild"</span><span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">Message</span> <span class="attr">Text</span><span class="kwrd">="$(MyVariable) and so does MSBuild"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">Target</span><span class="kwrd">></span>
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p>Before you compile and get disappointed, you need to know about another shortcoming in Visual Studio.  It does not automatically grab your changes.  You need to unload and reload your project to have your changes take effect.  Also note that if you have a syntax error you will not be able to reload your project.  In this case you can either try and update your mistake in notepad, or simply rename your file as our Import earlier will gracefully not error out when our file does not exist.</p>
<p>Now if you do a build you should see a message in your Build Output window.</p>
<p><strong>Task Items and Calling Targets</strong></p>
<p>Two more basic things to cover before we tackle our versioning and packaging.  Another common task for our script is to obtain a list of files to act upon.  In MSBuild this is done through the creation of Task Items.</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">CreateItem</span> <span class="attr">Include</span><span class="kwrd">="$(TargetDir)*.dll;$(MSBuildProjectDirectory)*.ascx;"</span><span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Include"</span> <span class="attr">ItemName</span><span class="kwrd">="InstallZipFiles"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">CreateItem</span><span class="kwrd">></span>
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p>The only difference in accessing this "variable" is that we use the @ sign:  @(InstallZipFiles).</p>
<p>Finally, it will help you to organize your script into your own targets.  In order to do this you need to know how to define and call a target.</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">="BeforeBuild"</span><span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">CallTarget</span> <span class="attr">Targets</span><span class="kwrd">="SetVersionInfo"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">Target</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">="AfterBuild"</span><span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">CallTarget</span> <span class="attr">Targets</span><span class="kwrd">="ZipFiles"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">Target</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">="SetVersionInfo"</span><span class="kwrd">></span>
    <span class="rem"><!-- Versioning logic here --></span>
<span class="kwrd"></</span><span class="html">Target</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">="ZipFiles"</span><span class="kwrd">></span>
    <span class="rem"><!-- Packaging logic here --></span>
<span class="kwrd"></</span><span class="html">Target</span><span class="kwrd">></span>
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p><strong></strong> </p>
<p><strong>Using Third Party Tasks</strong></p>
<p>One of the unfortunate things about MSBuild is that there is not a lot of Tasks that come with it.  Simple things like Copying files it can do, but zipping, versioning, and updating files are missing.  Luckily there are many community efforts out there that fill in these gaps.  The one that I choose to start with is the <a href="http://msbuildtasks.tigris.org/">MSBuild Community Tasks</a>.  These tasks cover (or at least get you started) most of what you wish to automate.  Once installed, you can simply import the tasks into your script from their global location.  Think of this like adding a reference in your .NET project. </p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">Import</span> <span class="attr">Project</span><span class="kwrd">="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"</span> 
<span class="attr">Condition</span><span class="kwrd">="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')"</span> <span class="kwrd">/></span></pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p>However, what I find most helpful is to create a folder one level up from my project folder that contains all my MSBuild tasks and import the targets file from there.   I can then easily add new tasks and import them all from a central location that I control and may be specific to my project.</p>
<p>Reading through the community task help file you will have all the information you need to version your project.  Here is the script my <a href="http://www.codeplex.com/codeendeavortemplate">CodeEndeavor Templates</a> currently use, though I am considering making my own version soon.</p><pre class="csharpcode"><span class="rem"><!-- Obtain Version information from version.txt --></span>
<span class="kwrd"><</span><span class="html">Version</span> <span class="attr">BuildType</span><span class="kwrd">="Automatic"</span> <span class="attr">RevisionType</span><span class="kwrd">="None"</span> <span class="attr">VersionFile</span><span class="kwrd">="version.txt"</span> <span class="attr">StartDate</span><span class="kwrd">="12/1/2008"</span><span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Major"</span> <span class="attr">PropertyName</span><span class="kwrd">="Major"</span> <span class="kwrd">/></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Minor"</span> <span class="attr">PropertyName</span><span class="kwrd">="Minor"</span> <span class="kwrd">/></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Build"</span> <span class="attr">PropertyName</span><span class="kwrd">="Build"</span> <span class="kwrd">/></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Revision"</span> <span class="attr">PropertyName</span><span class="kwrd">="Revision"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">Version</span><span class="kwrd">></span>
<span class="rem"><!-- DNN requires single digits to be prefixed with a zero --></span>
<span class="kwrd"><</span><span class="html">CreateProperty</span> <span class="attr">Value</span><span class="kwrd">="0$(Major)"</span> <span class="attr">Condition</span><span class="kwrd">="$(Major) <= 9"</span> <span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Value"</span> <span class="attr">PropertyName</span><span class="kwrd">="Major"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">CreateProperty</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">CreateProperty</span> <span class="attr">Value</span><span class="kwrd">="0$(Minor)"</span> <span class="attr">Condition</span><span class="kwrd">="$(Minor) <= 9"</span> <span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Value"</span> <span class="attr">PropertyName</span><span class="kwrd">="Minor"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">CreateProperty</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">CreateProperty</span> <span class="attr">Value</span><span class="kwrd">="0$(Build)"</span> <span class="attr">Condition</span><span class="kwrd">="$(Build) <= 9"</span> <span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Value"</span> <span class="attr">PropertyName</span><span class="kwrd">="Build"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">CreateProperty</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">CreateProperty</span> <span class="attr">Value</span><span class="kwrd">="0$(Revision)"</span> <span class="attr">Condition</span><span class="kwrd">="$(Revision) <= 9"</span> <span class="kwrd">></span>
    <span class="kwrd"><</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">="Value"</span> <span class="attr">PropertyName</span><span class="kwrd">="Revision"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">CreateProperty</span><span class="kwrd">></span>

<span class="rem"><!-- Write new version to assemblyinfo.cs --></span>
<span class="kwrd"><</span><span class="html">FileUpdate</span> <span class="attr">Files</span><span class="kwrd">="@(AssemblyInfoFiles)"</span> <span class="attr">Encoding</span><span class="kwrd">="ASCII"</span> <span class="attr">Regex</span><span class="kwrd">="AssemblyVersion\(".*"\)\>"</span>
  <span class="attr">ReplacementText</span><span class="kwrd">="AssemblyVersion("$(Major).$(Minor).$(Build).$(Revision)")>"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">FileUpdate</span> <span class="attr">Files</span><span class="kwrd">="@(dnnFile)"</span> <span class="attr">Regex</span><span class="kwrd">="<version>.*</version>"</span> 
  <span class="attr">ReplacementText</span><span class="kwrd">="<version>$(Major).$(Minor).$(Build)</version>"</span> <span class="kwrd">/></span>
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p> </p>
<p>The final thing we need to do is automatically create a zip package for our project.  We will use the Task Items we covered earlier for this task.</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">Zip</span> <span class="attr">Files</span><span class="kwrd">="@(InstallZipFiles)"</span> <span class="attr">WorkingDirectory</span><span class="kwrd">="$(MSBuildProjectDirectory)\"</span> 
    <span class="attr">ZipFileName</span><span class="kwrd">="$(DeployDir)\$(YourVariableForProject).install.v$(Major).$(Minor).$(Build).$(Revision).zip"</span> <span class="kwrd">/></span>
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>

<p>To see the full scripts for the concepts discussed in this entry, grab the source from either the <a href="http://www.codeplex.com/dnnclientapi">DotNetNuke ClientAPI</a> or <a href="http://www.codeplex.com/dnnwebcontrols">DotNetNuke WebControls</a>.</p><br><strong><a style="color:#800000; text-decoration:none" href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2158/Getting-Continuous-Integration-Working-With-CodePlex-Part-I.aspx" target="_main">Getting Continuous Integration Working With CodePlex: Part I</a></strong><p>I figured it may help some people to share my experience with getting my two projects (<a href="http://www.codeplex.com/dnnclientapi">DotNetNuke ClientAPI</a> and <a href="http://www.codeplex.com/dnnwebcontrols">DotNetNuke WebControls</a>) up on <a href="http://www.codeplex.com">CodePlex</a> and allowing them to be do automatic nightly builds.  The one thing I want to make clear before I start is that this is just one of many ways to do it.  </p> <p>In order to successfully get nightly builds to work you need to put quite a few pieces of a puzzle together.</p> <ul> <li><strong>Setting Up Project On CodePlex</strong>  <li><strong>Source Control</strong>  <li><strong>Build Script</strong>  <li><strong>Versioning / Labeling</strong>  <li><strong>Packaging</strong>  <li><strong>Deployment</strong>  <li><strong>Triggering Build on Build Server</strong></li></ul> <p> </p> <p>This blog will focus on the first two</p> <p><strong>Setting Up Project On CodePlex</strong></p> <p>This part is fairly well documented.  <a href="http://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Start%20a%20Project&referringTitle=Home">This page</a> is a good place to start.  The restrictions on what type of projects can be hosted are found on that page.</p> <p><strong>Source Control</strong></p> <p>This one is both simple and hard at the same time.  Its simple due to the fact that CodePlex offers the goodness of TFS to host your source code for you.  Its difficulty lies in which client you choose to both push and pull your source code to the server.</p> <p>When you click on any CodePlex project's Source Code tab you will be presented with 4 options:</p> <ul> <li><strong>Visual Studio Team Explorer</strong>:  The Visual Studio Team Explorer client provides access to Team Foundation Server from within Visual Studio. For information on installing the client and connecting to the Team Foundation Server please read the <a href="http://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Obtaining%20the%20Team%20Explorer%20Client">Visual Studio Team Explorer wiki page</a>. You will need the information on the right to add the Team Foundation Server in your Visual Studio Team Explorer.  <li><strong>Teamprise Explorer</strong>:  Teamprise Explorer is a cross-platform Team Foundation Server client written by the folks at <a href="http://www.teamprise.com">Teamprise</a>. Teamprise is offering a complimentary license for anyone wanting to use Teamprise to connect to CodePlex. For information on obtaining the client and connecting to the Team Foundation Server please read the <a href="http://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Obtaining%20the%20Teamprise%20Client">Teamprise Explorer wiki page</a>. You will need the information on the right to connect to the Team Foundation Server in Teamprise Explorer.  <li><strong>CodePlex Client</strong>: The CodePlex Client provides access to the Team Foundation Server using an edit-merge-commit style of source control access. For more information on the client please visit the <a href="http://www.codeplex.com/CodePlexClient">CodePlex Client project</a>. You will need the information on the right the first time you connect to CodePlex with the client.  <li><strong>Subversion</strong>:  There are many Subversion clients available that can be used with CodePlex. TortoiseSVN is a popular Subversion client that works as an extension in Windows Explorer. For more information please see <a href="http://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Using%20TortoiseSVN%20with%20CodePlex&referringTitle=Source%20control%20clientshttp://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Using%20TortoiseSVN%20with%20CodePlex&referringTitle=Source%20control%20clients">Using TortoiseSVN with CodePlex</a>.</li></ul> <p>My original thought was to use what Microsoft provided.  So I started with the Visual Studio Team Explorer.  The problem was when I clicked on the link I was presented with this warning.</p> <p><b><em>!! Warning !! - If you use the Team Explorer client then it will add source control binding information into your project files. When users download your source code and try to open it in Visual Studio they will get error messages because of the source control bindings. You can use any of the other available </em><a href="http://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Source%20control%20clients&referringTitle=Obtaining%20the%20Team%20Explorer%20Client"><em>source control clients</em></a><em> as an alternative.</em></b></p> <p>Having your users open up your project and the first thing they are presented with is an error dialog.  Not cool.  This one was ruled out.</p> <p>Looking at teamprise and realizing it was a 3rd party product that required licence fees caused me to shy away from this option.  Yes, I know, they state they offer a free licence to CodePlex users.   Something about how this offer is only advertised on a blog post from early 2007 mentioning beer turned me off... go figure.</p> <p>Next I looked at the CodePlex Client.  First thing I notice here is another "warning".</p> <p><em><strong>The CodePlex Client is not currently being maintained. The focus of the CodePlex team now is on the </strong></em><a href="http://www.codeplex.com/SvnBridge"><em><strong>SvnBridge</strong></em></a><em><strong> project (based on </strong></em><a href="http://www.codeplex.com/CodePlex/WorkItem/View.aspx?WorkItemId=7082"><em><strong>overwhelming user feedback</strong></em></a><em><strong>).</strong></em></p> <p>Hmm.  What is this SVNBridge.  Having worked on an Agile project for over a year, I am somewhat familiar with SubVersion, so I know that is what SVN stood for.  Its project page stated</p> <p><em><strong>SvnBridge allows you to use TortoiseSVN and other Subversion clients with Team Foundation Server. It converts the calls made by your Subversion client to the API supported by TFS.</strong></em>  <p>So it looks like you need to run this bridge locally for TortoiseSVN to talk to TFS.  Ok...  I can manage that... but wait!</p> <p><b><em>Note: You don't need to download SvnBridge to use Subversion clients with CodePlex. Every CodePlex project automatically has a Subversion URL (e.g. </em><a href="https://svnbridge.svn.codeplex.com/svn"><em>https://svnbridge.svn.codeplex.com/svn</em></a><em>).</em></b>  <p>Uh, ok.  So apparently I don't need this SvnBridge to use TortoiseSVN.  So we are now left with Subversion.  Specifically TortoiseSVN.</p> <p>If your reading this far you may already want to give up...  I don't blame you.  You chose to be a developer on the Microsoft platform because using their toolsets are generally easy.  This seems far from easy.  And if you are not familiar with setting up a SVN project you may be thinking that you want to throw in the towel.  But don't just yet.  Let me say that once you set up your project in SVN, using it is as easy as using Windows Explorer.</p> <p>So what you need to do is generally follow the steps outlined on the <a href="http://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Using%20TortoiseSVN%20with%20CodePlex&referringTitle=Source%20control%20clientshttp://www.codeplex.com/CodePlex/Wiki/View.aspx?title=Using%20TortoiseSVN%20with%20CodePlex&referringTitle=Source%20control%20clients">CodePlex SVN Help page</a>. </p> <li>If you do not already have TortoiseSVN installed, you can download it here: <a href="http://tortoisesvn.net/downloads">http://tortoisesvn.net/downloads</a>  <li>Go to the Source Control tab of <em>your</em> CodePlex project and make a note of the "Subversion URL"  <li>Open Windows Explorer  <li>Create a <strong><u><em>new</em></u></strong> folder where you want to be your working directory for your source code.  <em>If that directory already has code in it, you should not use it.  Rename it or pick a different name.  The folder needs to be empty.  Yes, I know this doesn't make sense to me either.  </em>  <li>Right click on the new folder and select "SVN Checkout...".  <em>Yes.  TortoiseSVN integrates directly into Windows Explorer.  It adds context menu items and eventually you will see that it even paints pretty pictures on your folders letting you know that you have up-to-date or pending file changes</em>.  <em>If you are like me you may be thinking why would I ever choose Checkout now?  We don't have and files in the folder and we haven't hooked it up to any source control. Why does Checkout equate to Create Project?  Just do it!</em>  <li>In the "URL of repository" field enter the Subversion URL. Example: https://svnbridge.svn.codeplex.com/svn  <li>Click OK on the SVN Checkout screen.  <em>You will then notice that the once empty folder now contains a new folder called .svn.  This is how subversion works, there is no local database, it uses the file system to store its meta-data.  Don't delete these folders!</em>  <li>The next step is to copy your source code into the folder.  <li>Finally, right click on the folder and choose SVN Commit.  <em>You will then be presented with a somewhat familiar dialog, allowing you to associate comments to your change set.  You can also choose which files you wish to include in source control.  <strong>IMPORTANT NOTE:  </strong>You may notice that you can exclude certain files by right-clicking it, you can even choose to exclude with wild-cards (obj*).  Don't do this yet, it won't work (most of the time) and you will just get frustrated.  First you need to check in your files, then once your done, the next time you check-in you can do you exclusions so those files will no longer show up in the list.  I think this has to do with the meta-data (.svn folders) not being present in the sub-folders, though am not sure.</em>  <li>Thats it.  Now simply modify the files at your leisure and check-in the changes with explorer.  You can check to see that the files uploaded successfully by going to the Source Control tab of your project page.  <p> </p> <p>The next entry in this series will dive into a little detail of how I chose to version and package my code with my build script.</p></li><br><strong><a style="color:#800000; text-decoration:none" href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2101/New-DotNetNuke-AJAX-Module-Template-Now-Available-on-CodePlex.aspx" target="_main">New DotNetNuke AJAX Module Template Now Available on CodePlex</a></strong><p>Those of you who have been following my previous blog entries on <a href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2066/Utilizing-the-Microsoft-AJAX-Framework-and-ClientAPI-to-Develop-Rich-Modules-Part-I.aspx">Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules</a> will be happy to learn that I have just posted the module templates on <a href="http://www.codeplex.com/codeendeavortemplate">CodePlex</a>. Unlike the previous version, this one utilizes the <a href="http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.templatewizard.iwizard(VS.80).aspx">IWizard Template wizard framework</a> offered by Visual Studio.NET for greater control over how the templates get configured, including the new DotNetNuke 5 manifest.  </p> <p><img src="http://i3.codeplex.com/Project/Download/FileDownload.aspx?ProjectName=codeendeavortemplate&DownloadId=51043"> </p> <p>Even though it is now at least twice as easy to get up and running with the templates, I still offer an updated video on the installation and module creation process.  Please visit the <a href="http://www.codeplex.com/codeendeavortemplate">CodePlex</a> page for more details.</p> <p>Finally, if you like this offering, please <a href="http://www.dotnetnuke.com/Products/Development/Forge/tabid/824/Default.aspx?page=1&filter=1&text=endeavors">vote for it</a>.</p><br><strong><a style="color:#800000; text-decoration:none" href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2074/Utilizing-the-Microsoft-AJAX-Framework-and-ClientAPI-to-Develop-Rich-Modules-Part-III.aspx" target="_main">Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules: Part III</a></strong><p>As mentioned in <a href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2067/Utilizing-the-Microsoft-AJAX-Framework-and-ClientAPI-to-Develop-Rich-Modules-Part-II.aspx">Part II</a> of this series, this entry will focus on another advantage of writing rich client side objects representing our modules on the client side.  One very common use case that comes up again and again is how can one module talk to another on the same page.  For a long time DotNetNuke had this capability from the server side through something called Inter-Module Communication (IMC).  With the adoption of AJAX it is common to hear confusion as to how can IMC work.  The question usually is asked like this.  I have Module1 talking to Module2 just fine until I "enabled AJAX" by wrapping it in an UpdatePanel.  Now Module2 no longer updates when Module1 posts back.  Or another variation is I make a ClientAPI callback that tries to either update the rendering of the module or tries to do IMC and it doesn't work.  Why not?</p>  <p>To understand why neither of these scenarios works the developer needs to have a basic understanding of what is happening behind the scenes.  An AJAX call into some server-side code should never be about sending a request from the client and have the entire page refresh.  If this is the requirement, then just use a normal postback!  Instead, AJAX calls are about requesting some piece of data (data here could be anything including HTML) that in turn affects the client-side state in some way, usually resulting in some UI update.  One of the primary motivations of using AJAX is to make a more responsive and efficient web experience.  So it makes sense that the AJAX you are adding should be written in such a way to minimize the strain on the server along with the bandwidth of the network.  </p>  <p>The idea behind the UpdatePanel is to allow only a portion of the page to be posted to the server, have less processing on the server happen than a normal postback, and only send down the updated portions of the page.  This is better than the postback, but not as good as it could be.  For the scenario where a user needs to do some sort of IMC, the UpdatePanel gets rather tricky real quick due to the need for triggers.</p>  <p>The approach I am pushing is to simply have the module become aware of the other modules it may wish to communicate with on the client.  Once aware, the module can simply interact with the other module's client-side object model, invoking methods and properties as needed.  I mentioned in <a href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2066/Utilizing-the-Microsoft-AJAX-Framework-and-ClientAPI-to-Develop-Rich-Modules-Part-I.aspx">Part I</a> of this series that there was an important difference between the initialize and load events.  The load event will fire after <strong><em>all</em></strong> client-side objects have been initialized.  This offers an ideal place for our modules to become aware of each other.</p>  <pre class="csharpcode"><font size="1">    _onLoad: <span class="kwrd">function</span>(src, arg)
    {
        <span class="rem">//page is completely loaded, you can now access any element or component</span>
        <span class="kwrd">var</span> components = arg.get_components();
        <span class="rem">//sample of how to do client-side IMC - look for other components we are interested in</span>
        <span class="rem">//in this case, we will look for other instances of the same module that are not ourselves</span>
        <span class="rem">//but you can communicate with any components</span>
        <span class="kwrd">for</span> (<span class="kwrd">var</span> i=0; i<components.length; i++)
        {
            <span class="kwrd">if</span> (Object.getTypeName(components[i]) == Object.getTypeName(<span class="kwrd">this</span>) && components[i].get_id() != <span class="kwrd">this</span>.get_id())
                <span class="kwrd">this</span>.add_propertyChanged(components[i]._delegates.componentPropChangedDelegate);
        }
    },</font></pre>
<style type="text/css">

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>An additional bonus to this event is the arg parameter contains a list of components for us via the get_components property.  We simply need to enumerate the list of components looking for an object of the same type that is not ourself and add an event handler to it.  Obviously, this logic can be tweaked to look for any type of component.  The <a href="http://www.asp.net/ajax/documentation/live/ClientReference/Sys/ComponentClass/ComponentPropertyChangedEvent.aspx">propertyChanged event</a> is another feature that the Sys.Component offers.  Whenever we wish to notify other objects of some property changing we simply call <a href="http://www.asp.net/ajax/documentation/live/ClientReference/Sys/ComponentClass/ComponentRaisePropertyChangedMethod.aspx">raisePropertyChanged</a>.  In our module's case we will raise the SayHello property changed event when we click a button</p>

<pre class="csharpcode"><font size="1">        <span class="kwrd">this</span>.raisePropertyChanged(<span class="str">'SayHello'</span>);</font></pre>
<style type="text/css">

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>This event will get trapped by other instances and update the UI accordingly</p>

<pre class="csharpcode"><font size="1">    _onPropChanged: <span class="kwrd">function</span>(src, args)
    {
         <span class="kwrd">this</span>.showMessage(String.format(<span class="str">'You {0} to {1} but not to me?'</span>, args.get_propertyName(), src.get_name()));
    },</font></pre>
<style type="text/css">

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>Notice how we get both the source object that raised the event, along with the propertyName that raised the event.  Obviously this allows for a single event handler to service many different property changed events.  Additionally, it allows for easy interaction with the calling object.  This is effectively all you should need to get your modules talking.  Obviously, each module you talk to could do whatever logic you'd like, including making AJAX calls and updating UI.  </p>

<p>This concludes the mini-series of blog entries on Microsoft AJAX and the ClientAPI.  Hopefully it gives the reader enough information to go and create a new breed of module, one that fits better into the AJAX era.</p>

<p> </p>

<p>For those interested in my upcoming plans I wanted to mention that I will soon be updating the free module templates <a href="http://www.codeplex.com/codeendeavortemplate">found on codeplex</a> which will generate nearly all that has been covered in this series.  Until then, I figured I'd let anyone who is interested play with the package the template creates:  <a href="http://www.dotnetnuke.com/LinkClick.aspx?fileticket=xcDZbpGhDo0%3d&tabid=875&mid=2664">Install</a> or <a href="http://www.dotnetnuke.com/LinkClick.aspx?fileticket=7RTCaTtubj0%3d&tabid=875&mid=2664&forcedownload=true">Source</a>.  Note:  This module requires DNN5.  I haven't gotten around to updating the manifest yet, that will be done by the time I release the template.  Enjoy!</p><br><strong><a style="color:#800000; text-decoration:none" href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2067/Utilizing-the-Microsoft-AJAX-Framework-and-ClientAPI-to-Develop-Rich-Modules-Part-II.aspx" target="_main">Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules: Part II</a></strong><p>In part one of this series I mentioned that the Microsoft AJAX Framework along with the ClientAPI are different than a lot of other javascript frameworks since they allow for an end-to-end integration directly with .NET.  This entry will discuss how the this integration makes life easier for a DotNetNuke module developer by allowing communication to and from the server to be simple.   </p>  <p>The Microsoft AJAX Framework allows for server-side code to add a reference to the js file, initialize the client-side object, and pass in property values by having any control implement the <a href="http://www.asp.net/AJAX/Documentation/Live/tutorials/IScriptControlTutorial1.aspx">IScriptControl interface</a>.  For a DotNetNuke module this means a usercontrol typically inheriting from PortalModuleBase.  Since we plan on having both our Edit control and View controls implement the interface we will create a base class to encapsulate the common logic.  Perhaps this base class will make it into the core some day, but until then the <a href="http://www.codeplex.com/codeendeavortemplate">CodeEndeavors templates</a> will include it.</p>  <pre class="csharpcode"><font size="1">    <span class="kwrd">Public</span> <span class="kwrd">Class</span> AjaxPortalModuleBase
        <span class="kwrd">Inherits</span> Entities.Modules.PortalModuleBase : <span class="kwrd">Implements</span> IScriptControl

        <span class="kwrd">Public</span> <span class="kwrd">Event</span> AddScriptComponentDescriptors(<span class="kwrd">ByVal</span> Descriptor <span class="kwrd">As</span> ScriptComponentDescriptor)
        <span class="kwrd">Public</span> <span class="kwrd">Event</span> AddScriptReferences(<span class="kwrd">ByVal</span> References <span class="kwrd">As</span> List(Of ScriptReference))
        <span class="kwrd">Public</span> <span class="kwrd">Event</span> AddLocalizedMessages(<span class="kwrd">ByVal</span> Messages <span class="kwrd">As</span> Dictionary(Of <span class="kwrd">String</span>, <span class="kwrd">String</span>))

<span class="preproc">#Region</span> <span class="str">"Event Handlers"</span>
        <span class="rem">'Enable Control Callbacks for this module</span>
        <span class="rem">'We are passing in the second argument to ensure our callbacks reach this instance of our object</span>
        <span class="kwrd">Private</span> <span class="kwrd">Sub</span> Page_Init(<span class="kwrd">ByVal</span> sender <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> System.EventArgs) <span class="kwrd">Handles</span> <span class="kwrd">Me</span>.Init
            ClientAPI.RegisterControlMethods(<span class="kwrd">Me</span>, <span class="kwrd">Me</span>.ClientID)
        <span class="kwrd">End</span> <span class="kwrd">Sub</span>

        <span class="kwrd">Private</span> <span class="kwrd">Sub</span> Page_PreRender(<span class="kwrd">ByVal</span> sender <span class="kwrd">As</span> <span class="kwrd">Object</span>, <span class="kwrd">ByVal</span> e <span class="kwrd">As</span> System.EventArgs) <span class="kwrd">Handles</span> <span class="kwrd">Me</span>.PreRender
            <span class="rem">'Tell DotNetNuke we need to use the ScriptManager</span>
            DotNetNuke.Framework.AJAX.RegisterScriptManager()

            <span class="rem">'Register our client-side Script Control</span>
            ScriptManager.GetCurrent(<span class="kwrd">Me</span>.Page).RegisterScriptControl(<span class="kwrd">Me</span>)
            ScriptManager.GetCurrent(<span class="kwrd">Me</span>.Page).RegisterScriptDescriptors(<span class="kwrd">Me</span>)
        <span class="kwrd">End</span> <span class="kwrd">Sub</span>

<span class="preproc">#End Region</span></font></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>As you can see we have a class that inherits from PortalModuleBase and implements the IScriptControl interface.  We also are defining some important events that our usercontrols will trap and provide the necessary information (more on this in a bit).  In <a href="http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2066/Utilizing-the-Microsoft-AJAX-Framework-and-ClientAPI-to-Develop-Rich-Modules-Part-I.aspx">Part I</a> we looked at how the client-side can invoke a server-side method using the ClientAPI's Control Methods.  Here we are getting our first look at how the methods are hooked up.  The earliest possible event needs to register that the module supports Control Methods.  Just prior to rendering we need to inform the DotNetNuke Framework that we will require the ScriptManager to be present.  This code is really not necessary anymore as DNN5 now requires MSAJAX to be present and thus the ScriptManager.   Finally, we need to tell the script manager that our control will supply ScriptControl goodness.</p>

<p>It is not time to review our base class's implementation of the IScriptControl interface.</p>

<pre class="csharpcode"><font size="1"><span class="preproc">#Region</span> <span class="str">"IScriptControl"</span>
        <span class="kwrd">Private</span> <span class="kwrd">Function</span> GetScriptReferences() <span class="kwrd">As</span> IEnumerable(Of ScriptReference) <span class="kwrd">Implements</span> IScriptControl.GetScriptReferences
            <span class="kwrd">Dim</span> refs <span class="kwrd">As</span> List(Of ScriptReference) = <span class="kwrd">New</span> List(Of ScriptReference)
            <span class="kwrd">RaiseEvent</span> AddScriptReferences(refs)
            <span class="kwrd">Return</span> refs
        <span class="kwrd">End</span> <span class="kwrd">Function</span>

        <span class="kwrd">Private</span> <span class="kwrd">Function</span> GetScriptDescriptors() <span class="kwrd">As</span> IEnumerable(Of ScriptDescriptor) <span class="kwrd">Implements</span> IScriptControl.GetScriptDescriptors
            <span class="kwrd">Dim</span> descs <span class="kwrd">As</span> List(Of ScriptDescriptor) = <span class="kwrd">New</span> List(Of ScriptDescriptor)
            <span class="kwrd">Dim</span> desc <span class="kwrd">As</span> ScriptComponentDescriptor = <span class="kwrd">New</span> ScriptComponentDescriptor(<span class="str">"ClientNamespaceHere"</span>)
            <span class="kwrd">Dim</span> jss <span class="kwrd">As</span> JavaScriptSerializer = <span class="kwrd">New</span> JavaScriptSerializer()
            <span class="kwrd">Dim</span> msgs <span class="kwrd">As</span> Dictionary(Of <span class="kwrd">String</span>, <span class="kwrd">String</span>) = <span class="kwrd">New</span> Dictionary(Of <span class="kwrd">String</span>, <span class="kwrd">String</span>)

            desc.AddScriptProperty(<span class="str">"id"</span>, <span class="kwrd">String</span>.Format(<span class="str">"'{0}'"</span>, <span class="kwrd">Me</span>.ClientID))

            <span class="kwrd">RaiseEvent</span> AddLocalizedMessages(msgs)
            desc.AddScriptProperty(<span class="str">"msgs"</span>, <span class="kwrd">String</span>.Format(<span class="str">"'{0}'"</span>, jss.Serialize(msgs))) 

            <span class="kwrd">RaiseEvent</span> AddScriptComponentDescriptors(desc)

            descs.Add(desc)
            <span class="kwrd">Return</span> descs
        <span class="kwrd">End</span> <span class="kwrd">Function</span>

<span class="preproc">#End Region</span></font></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style><style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>The GetScriptReferences method simply creates a generic list of ScriptReferences and delegates the responsibility of filling the list to the control that inherits from it by passing it as an argument for the AddScriptReferences event.  This is how our module's external javascript references will get added to our rendered page.  We will cover the consumption of this event when we review the module's code.</p>

<p>The GetScriptDescriptors method has a bit more meat to it.  The purpose of this method is twofold:  to provide the MSAJAX Framework the information necessary to create our client-side object and to initialize its properties with values on the server.  The only piece of information the framework needs to create our object is the client-side object name, which gets assigned by our control inheriting from our base class via the Descriptor's Type property.   Properties are initialized by calling the AddScriptProperty method passing in the non-prefixed property names.  If you refer back to Part I you will remember that we prefixed our properties with get_ and set_.  If you have reviewed the code you may be asking why the javascript did not have a set_id method.  This method is unnecessary since the Sys.Component method already defines it, and in fact the component requires it.  Since it needs to be unique we will use an ID that is guaranteed by the ASP.NET runtime to be unique the ClientID.  </p>

<p>There are two events left to discuss in our base class.  The AddLocalizedMessages simply passes a Dictionary of string/string to our module for additional messages to be sent down.  It then uses the JSON serializer included in the MSAJAX Framework to serialize the dictionary into a single property called msgs.  The AddScriptComponentDescriptors event allows for custom properties to be added.</p>

<p>With the common base class defined, lets now turn our attention to the DotNetNuke module inheriting from it.  </p>

<pre class="csharpcode"><font size="1">    <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
    <span class="rem">'- The ControlMethodClass attribute identifies the class as supporting Control Methods</span>
    <span class="rem">'- The Friendly name parameter is usually named after server-side class namespace, but could be shortened if you like</span>
    <span class="rem">'- If you change this make sure you also change its client calling code found in EditDNNAjaxModule1.ascx.js</span>
    <span class="rem">'-   dnn.xmlhttp.callControlMethod('YourCompanyHere.Modules.DNNAjaxModule1.EditDNNAjaxModule1.' + this._ns, </span>
    <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
    <ControlMethodClass(<span class="str">"YourCompanyHere.Modules.DNNAjaxModule1.EditDNNAjaxModule1"</span>)> _
    Partial <span class="kwrd">Class</span> EditDNNAjaxModule1
        <span class="kwrd">Inherits</span> AjaxPortalModuleBase</font></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>The first thing that should stick out is we are decorating our class with the ControlMethodClass attribute which as the comment states identifies the class as supporting control methods.  Obviously we are inheriting from the base class we just covered.</p>

<pre class="csharpcode"><font size="1"><span class="preproc">#Region</span> <span class="str">"AjaxPortalModuleBase Events"</span>
        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <span class="rem">'- This is where your client-side javascript that uses the MS AJAX framework needs to be registered</span>
        <span class="rem">'- Adding the reference here ensures that the MS AJAX script is run before our script which uses things like Type.registerNamespace run</span>
        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <span class="kwrd">Protected</span> <span class="kwrd">Sub</span> AjaxPortalModuleBase_AddScriptReferences(<span class="kwrd">ByVal</span> References <span class="kwrd">As</span> List(Of ScriptReference)) <span class="kwrd">Handles</span> <span class="kwrd">MyBase</span>.AddScriptReferences
            References.Add(<span class="kwrd">New</span> System.Web.UI.ScriptReference(<span class="kwrd">Me</span>.ModulePath & <span class="str">"EditDNNAjaxModule1.ascx.js"</span>))
        <span class="kwrd">End</span> <span class="kwrd">Sub</span>

        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <span class="rem">'- Add any localized text needed on the client</span>
        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <span class="kwrd">Protected</span> <span class="kwrd">Sub</span> AjaxPortalModuleBase_AddLocalizedMessages(<span class="kwrd">ByVal</span> Messages <span class="kwrd">As</span> Dictionary(Of <span class="kwrd">String</span>, <span class="kwrd">String</span>)) <span class="kwrd">Handles</span> <span class="kwrd">MyBase</span>.AddLocalizedMessages
            Messages(<span class="str">"SettingsSaved"</span>) = Localization.GetString(<span class="str">"SettingsSaved.Client"</span>, LocalResourceFile)
        <span class="kwrd">End</span> <span class="kwrd">Sub</span>

        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <span class="rem">'- This is where you add your custom properties that are to be sent down to the client-side object</span>
        <span class="rem">'- By default we are passing the naming container ID (ns) and localized messages (msgs) </span>
        <span class="rem">'- If you add your own properties make sure you update your client code with those properties found in EditDNNAjaxModule1.ascx.js</span>
        <span class="rem">'-      get_ImagePath: function() {return this._imagePath;},</span>
        <span class="rem">'-      set_ImagePath: function(value) {this._imagePath = value;},</span>
        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <span class="kwrd">Protected</span> <span class="kwrd">Sub</span> AjaxPortalModuleBase_AddScriptComponentDescriptors(<span class="kwrd">ByVal</span> Descriptor <span class="kwrd">As</span> ScriptComponentDescriptor) <span class="kwrd">Handles</span> <span class="kwrd">MyBase</span>.AddScriptComponentDescriptors
            <span class="rem">'IMPORTANT!  Enter Client Namespace + ObjectName as Type</span>
            Descriptor.Type = <span class="str">"YourCompanyHere.EditDNNAjaxModule1"</span>

            <span class="rem">'---------------------------------------------------------------------------------------------------------</span>
            <span class="rem">'Add custom properties here</span>
            <span class="rem">'   Descriptor.AddScriptProperty("ImagePath", String.Format("'{0}'", Me.ModulePath & "images/"))</span>

            <span class="kwrd">If</span> <span class="kwrd">Not</span> Settings <span class="kwrd">Is</span> <span class="kwrd">Nothing</span> <span class="kwrd">Then</span>
                <span class="rem">'This sample uses the Module Settings collection as a custom property serialized to JSON</span>
                <span class="kwrd">Dim</span> jss <span class="kwrd">As</span> JavaScriptSerializer = <span class="kwrd">New</span> JavaScriptSerializer()
                Descriptor.AddScriptProperty(<span class="str">"settings"</span>, <span class="kwrd">String</span>.Format(<span class="str">"'{0}'"</span>, jss.Serialize(Settings)))
            <span class="kwrd">End</span> <span class="kwrd">If</span>
            <span class="rem">'---------------------------------------------------------------------------------------------------------</span>
        <span class="kwrd">End</span> <span class="kwrd">Sub</span>
<span class="preproc">#End Region</span></font></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style><style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>The first event, AddScriptReferences, is rather straight forward.  We simply create a new ScriptReference passing in the javascript file location.  The AddLocalizedMessages event handler simply adds our localized message to the dictionary.  However, I do believe it worth spending a little time discussing this approach.  The Microsoft AJAX Framework <a href="http://www.asp.net/AJAX/Documentation/Live/tutorials/LocalizeResourcesInScript.aspx">defines one method</a> for localization of messages in javascript by creating multiple scripts, one for each locale.  I find this method a bit of a nuisance.  The method I am suggesting is to simply have a property called msgs on the client that gets a serialized dictionary of name value pairs.  Then in script use the dictionary to obtain each message.</p>

<pre class="csharpcode"><font size="1">getMessage: <span class="kwrd">function</span>(key)
{
    <span class="kwrd">return</span> <span class="kwrd">this</span>._msgs[key];
},</font></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>The final event is the AddScriptComponentDescriptors.  As mentioned earlier, we need to set the Type to the exact object defined in our javascript.  The next portion of code is where we can add any custom properties that we need on the client.  In this case we are serializing the entire Settings hashtable to json to read on the client.  If you remember we had client-side code in the setter that deserialized this value.</p>

<pre class="csharpcode"><font size="1">set_settings: <span class="kwrd">function</span>(value) {<span class="kwrd">this</span>._settings = Sys.Serialization.JavaScriptSerializer.deserialize(value);},</font></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>

<p>The final portion of code for our EditModule is to write our ControlMethod that is exposed to the client.</p>

<pre class="csharpcode"><font size="1"><span class="preproc">#Region</span> <span class="str">"Control Methods"</span>
        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <span class="rem">'- Exposing methods to the client code is as simple as adding the ControlMethod attribute</span>
        <span class="rem">'- IMPORTANT!   ALWAYS SECURE YOUR CONTROL METHODS IF INTERACTING WITH SENSITIVE DATA</span>
        <span class="rem">'-------------------------------------------------------------------------------------------------------------</span>
        <ControlMethod()> _
        <span class="kwrd">Public</span> <span class="kwrd">Function</span> UpdateSettings(<span class="kwrd">ByVal</span> Settings <span class="kwrd">As</span> Dictionary(Of <span class="kwrd">String</span>, <span class="kwrd">Object</span>)) <span class="kwrd">As</span> <span class="kwrd">Boolean</span>
            <span class="kwrd">If</span> <span class="kwrd">Me</span>.EditMode <span class="kwrd">Then</span> <span class="rem">'Making sure only those users with Edit Mode rights can call this method</span>
                <span class="kwrd">Dim</span> modController <span class="kwrd">As</span> DotNetNuke.Entities.Modules.ModuleController = <span class="kwrd">New</span> DotNetNuke.Entities.Modules.ModuleController()
                <span class="kwrd">For</span> <span class="kwrd">Each</span> key <span class="kwrd">As</span> <span class="kwrd">String</span> <span class="kwrd">In</span> Settings.Keys
                    modController.UpdateModuleSetting(<span class="kwrd">Me</span>.ModuleId, key, Settings(key))
                <span class="kwrd">Next</span>
            <span class="kwrd">End</span> <span class="kwrd">If</span>
            <span class="kwrd">Return</span> <span class="kwrd">True</span>
        <span class="kwrd">End</span> <span class="kwrd">Function</span>
<span class="preproc">#End Region</span></font></pre>

<p>Unlike earlier versions of client-callbacks exposed in the ClientAPI, this one can accept any number of parameters and a variety of argument types. In this case we will accept the updated settings object we sent down during the initial rendering of the page.  The method needs to be decorated with the ControlMethod attribute and if it exposes or updates sensitive information it needs to be secured.  You may be wondering why did the ClientAPI invent such a thing as ControlMethods when the Microsoft AJAX Framework already has the ability to make AJAX calls into either a webservice or a PageMethod.  The answer is simple.  Neither WebServices nor PageMethods allow you to call into a specific instance of a module since a PageMethod must be defined as static/Shared and a webservice obviously has no context of a module as it is a different class entirely.</p>

<p>That covers the basics of writing our server-side module that supports the creation of a client-side object.  Before I sign off I feel it necessary to contrast this form of AJAX development with the kind that the UpdatePanel offers.  The major difference is in what is sent between the client / server exchange.  For ControlMethods only the serialized settings object plus minimal identifiers (ClientID) is posted.  For UpdatePanel the entire form is posted.  On the server side, only the first few events in the ASP.NET event lifecycle are fired whereas the UpdatePanel needs nearly all events to fire.  Finally the response text of the ControlMethod contains a simple boolean, whereas the UpdatePanel contains a large portion of HTML including ViewState.  This is not to say that I do not think that the UpdatePanel has its place in the AJAX space.  I am only saying that its place should be used more with retro-fitting existing applications to AJAX, not new modules being developed from scratch.  The next entry in this series will showcase yet another advantage this approach has over the UpdatePanel:  Client-side Inter-Module Communication.</p>

<p>Hopefully, this entry sheds some light on why the Microsoft AJAX Framework has some additional features than other client-side only frameworks.  If you still wish to learn more of the differences, especially relating to the client-side features compared to JQuery, which recently got integrated into DNN5, I suggest listening to <a href="http://www.hanselminutes.com/default.aspx?showID=146">this podcast by Scott Hanselman</a>.</p></span>
<!-- End_Module_382 -->
</div></td>

  </tr>
</table></div>
<table width="100%" borer="0" cellpadding="0" cellspacing="0">
  <tr>
    <td align="right" class="RedTitleBottomAction"><a id="dnn_ctr382_dnnACTIONBUTTON6_ico3" href="javascript:__doPostBack('dnn$ctr382$dnnACTIONBUTTON6$ico3','')"><img title="Print" src="/images/action_print.gif" alt="Print" style="border-width:0px;" /></a><a id="dnn_ctr382_dnnACTIONBUTTON6_lnk3" title="Print" class="CommandButton" href="javascript:__doPostBack('dnn$ctr382$dnnACTIONBUTTON6$lnk3','')">Print</a>  </td>
  </tr>
</table>

</td>

                    
                  </tr>
              </table></td>
                <td class="ceProfile">
                    <span>Jon Henning</span><br />
                    <a href="https://mvp.support.microsoft.com/profile=6F33D95A-9330-449D-A946-96BCEF8AC324"><img src="/Portals/_default/Skins/IC-SkinRed/images/mvp.gif" border="0" /></a><br />
                    <br />
                    <img src="/Portals/_default/Skins/IC-SkinRed/images/dnntrustee.gif" border="0" /><br />
                    <a href="http://www.dotnetnuke.com/LinkClick.aspx?link=851&tabid=824&mid=2189"><img src="/Portals/_default/Skins/IC-SkinRed/images/dnnclientapi.gif" border="0" /></a><br />
                    <a href="http://www.dotnetnuke.com/Products/Development/Projects/CoreWebControls/tabid/873/Default.aspx"><img src="/Portals/_default/Skins/IC-SkinRed/images/dnnwebcontrols.gif" border="0" /></a><br />
                </td>
            </tr>
            <tr>
              <td><table width="100%" cellpadding="0" cellspacing="0" border="0">
                  <tr>
                    <td><img src="/Portals/_default/Skins/IC-SkinRed/images/border_bl.gif" alt="border_bl" /></td>
                    <td class="BorderBottom"></td>
                    <td><img src="/Portals/_default/Skins/IC-SkinRed/images/border_br.gif" alt="border_br" /></td>
                  </tr>
              </table></td>
            </tr>
        </table></td>
      </tr>
      <tr>
          <td><table width="100%" cellpadding="0" cellspacing="0" border="0">
              <tr>
                <td><img src="/Portals/_default/Skins/IC-SkinRed/images/ibl.png" alt="border_bl" style="behavior: url(/Portals/_default/Skins/IC-SkinRed/images/pngbehavior.htc);" width="50" height="100" /></td>
                <td id="dnn_BottomPane" class="ceIconNav"><a name="371"></a><span id="dnn_ctr371_ContentPane" class="DNNAlignleft"><!-- Start_Module_371 --><div id="dnn_ctr371_ModuleContent">
	<div id="dnn_ctr371_Navigate_Panel1" class="iconBar">

	</div>

<!-- End_Module_371 -->
</div></span>
</td>

                <td><img src="/Portals/_default/Skins/IC-SkinRed/images/ibr.png" alt="border_br" style="behavior: url(/Portals/_default/Skins/IC-SkinRed/images/pngbehavior.htc);" width="50" height="100" /></td>
              </tr>
          </table></td>
      </tr>
      <tr>
        <td class="BottomPane"><table width="100%" border="0" cellspacing="0" cellpadding="0" class="BottomPane">
            <tr>
              <td width="50%"><span id="dnn_dnnCOPYRIGHT_lblCopyright" class="Copyright">Copyright 2009 by Code Endeavors</span>
</td>
              <td>
                <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=jon%40codeendeavors%2ecom&item_name=Code%20Endeavors Donation&no_shipping=1&cn=Suggestions&tax=0¤cy_code=USD&lc=US&bn=PP%2dDonationsBF&charset=UTF%2d8" target="_blank">
                    <img src="/Portals/_default/Skins/IC-SkinRed/images/paypal_button.gif" border="0" alt="Please consider supporting the on-going free offerings to the community.  Donate Today." />
                </a>
              </td>
              <td width="50%" align="right"> <a id="dnn_dnnTERMS_hypTerms" class="Footer" href="http://codeendeavors.33.websecurestores.com/Terms.aspx">Terms Of Use</a>   <a id="dnn_dnnPRIVACY_hypPrivacy" class="Footer" href="http://codeendeavors.33.websecurestores.com/Privacy.aspx">Privacy Statement</a> </td>
            </tr>
        </table></td>
      </tr>
    </table></td>
  </tr>
</table>


        <input name="ScrollTop" type="hidden" id="ScrollTop" />
        <input name="__dnnVariable" type="hidden" id="__dnnVariable" />
    

<script type="text/javascript">
//<![CDATA[
Sys.Application.initialize();
//]]>
</script>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJMjgzMDU3ODk3D2QWBmYPFgIeBFRleHQFPjwhRE9DVFlQRSBIVE1MIFBVQkxJQyAiLS8vVzNDLy9EVEQgSFRNTCA0LjAgVHJhbnNpdGlvbmFsLy9FTiI+ZAIBD2QWDAIBDxYCHgdWaXNpYmxlaGQCAg8WBB4HY29udGVudGQfAWhkAgMPFgIfAgUPLERvdE5ldE51a2UsRE5OZAIEDxYCHwIFIENvcHlyaWdodCAyMDA5IGJ5IENvZGUgRW5kZWF2b3JzZAIFDxYCHwIFC0RvdE5ldE51a2UgZAIGDxYCHwIFE0NvZGUgRW5kZWF2b3JzLCBMTENkAgIPZBYCAgEPZBYCAgQPZBYCZg9kFg5mD2QWAmYPDxYGHghDc3NDbGFzcwUFTG9naW4fAAUGTE9HIElOHgRfIVNCAgJkZAIBD2QWAmYPDxYGHwMFBUxvZ2luHwAFCFJFR0lTVEVSHwQCAmRkAgMPFgIeBWNsYXNzBRRUb3BQYW5lIEROTkVtcHR5UGFuZWQCBQ8WAh8FBRVMZWZ0UGFuZSBETk5FbXB0eVBhbmVkAgYPZBYCAgEPZBYQZg8PFgIfAWhkZAIBD2QWAgICDxYCHwFoZAICD2QWAmYPDxYCHgdUb29sVGlwBQhNaW5pbWl6ZWQWAmYPDxYGHghJbWFnZVVybAUPL2ltYWdlcy9taW4uZ2lmHg1BbHRlcm5hdGVUZXh0BQhNaW5pbWl6ZR8GBQhNaW5pbWl6ZWRkAgQPDxYCHwFoZGQCBQ8PFgIfAWhkZAIGDw8WAh8BaGRkAgcPDxYCHwFoZGQCCA8PFgIfAWhkZAIHDxYCHwUFFlJpZ2h0UGFuZSBETk5FbXB0eVBhbmVkAggPZBYCAgEPZBYCZg9kFgICAQ9kFgICAQ9kFgICAg8WDh4JU3RhcnRTaXplAkgeCUhvdmVyU2l6ZQJQHgdFbmFibGVkaB4KQ3NzSWNvbkJhcgUMY2VJY29uTmF2QmFyHg5Dc3NJY29uRGVmYXVsdGUeC0RlZmF1bHRJY29uBRwvaW1hZ2VzLzcyL2NvZGVlbmRlYXZvcnMucG5nHhNQTkdCZWhhdmlvckxvY2F0aW9uBTQvRGVza3RvcE1vZHVsZXMvQ29kZUVuZGVhdm9yc0ljb25OYXYvcG5nYmVoYXZpb3IuaHRjZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUUZG5uJGRubk5BViRjdGxkbm5OQVZxa3pXXoqHlx99lWYU2SJuoMCJ+Q==" /></form>
</body>
</html>