Cleaner HTML Markup with ASP.NET 4 Web Forms – Client IDs (VS 2010 and .NET 4.0 Series)

Scott Gu

This is the sixteenth in a series of blog posts I’m doing on the upcoming VS 2010 and .NET 4 release.

Today’s post is the first of a few blog posts I’ll be doing that talk about some of the important changes we’ve made to make Web Forms in ASP.NET 4 generate clean, standards-compliant, CSS-friendly markup. Today I’ll cover the work we are doing to provide better control over the “ID” attributes rendered by server controls to the client.

[In addition to blogging, I am also now using Twitter for quick updates and to share links. Follow me at: twitter.com/scottgu]
Clean, Standards-Based, CSS-Friendly Markup

One of the common complaints developers have often had with ASP.NET Web Forms is that when using server controls they don’t have the ability to easily generate clean, CSS-friendly output and markup. Some of the specific complaints with previous ASP.NET releases include:

* Auto-generated ID attributes within HTML make it hard to write JavaScript and style with CSS
* Use of tables instead of semantic markup for certain controls (in particular the asp:menu control) make styling ugly
* Some controls render inline style properties even if no style property on the control has been set
* ViewState can often be bigger than ideal

ASP.NET 4 provides better support for building standards-compliant pages out of the box. The built-in server controls with ASP.NET 4 now generate cleaner markup and support CSS styling – and help address all of the above issues.
Markup Compatibility When Upgrading Existing ASP.NET Web Forms Applications

A common question people often ask when hearing about the cleaner markup coming with ASP.NET 4 is “Great – but what about my existing applications? Will these changes/improvements break things when I upgrade?”

To help ensure that we don’t break assumptions around markup and styling with existing ASP.NET Web Forms applications, we’ve enabled a configuration flag – controlRenderingCompatbilityVersion – within web.config that let’s you decide if you want to use the new cleaner markup approach that is the default with new ASP.NET 4 applications, or for compatibility reasons render the same markup that previous versions of ASP.NET used:

image

When the controlRenderingCompatbilityVersion flag is set to “3.5” your application and server controls will by default render output using the same markup generation used with VS 2008 and .NET 3.5. When the controlRenderingCompatbilityVersion flag is set to “4.0” your application and server controls will strictly adhere to the XHTML 1.1 specification, have cleaner client IDs, render with semantic correctness in mind, and have extraneous inline styles removed.

This flag defaults to 4.0 for all new ASP.NET Web Forms applications built using ASP.NET 4. Any previous application that is upgraded using VS 2010 will have the controlRenderingCompatbilityVersion flag automatically set to 3.5 by the upgrade wizard to ensure backwards compatibility. You can then optionally change it (either at the application level, or scope it within the web.config file to be on a per page or directory level) if you move your pages to use CSS and take advantage of the new markup rendering.
Today’s Cleaner Markup Topic: Client IDs

The ability to have clean, predictable, ID attributes on rendered HTML elements is something developers have long asked for with Web Forms (ID values like “ctl00_ContentPlaceholder1_ListView1_ctrl0_Label1” are not very popular). Having control over the ID values rendered helps make it much easier to write client-side JavaScript against the output, makes it easier to style elements using CSS, and on large pages can help reduce the overall size of the markup generated.
New ClientIDMode Property on Controls

ASP.NET 4 supports a new ClientIDMode property on the Control base class. The ClientIDMode property indicates how controls should generate client ID values when they render. The ClientIDMode property supports four possible values:

* AutoID—Renders the output as in .NET 3.5 (auto-generated IDs which will still render prefixes like ctrl00 for compatibility)
* Predictable (Default)— Trims any “ctl00” ID string and if a list/container control concatenates child ids (example: id=”ParentControl_ChildControl”)
* Static—Hands over full ID naming control to the developer – whatever they set as the ID of the control is what is rendered (example: id=”JustMyId”)
* Inherit—Tells the control to defer to the naming behavior mode of the parent container control

The ClientIDMode property can be set directly on individual controls (or within container controls – in which case the controls within them will by default inherit the setting):

image

Or it can be specified at a page or usercontrol level (using the <%@ Page %> or <%@ Control %> directives) – in which case controls within the pages/usercontrols inherit the setting (and can optionally override it):

image

Or it can be set within the web.config file of an application – in which case pages within the application inherit the setting (and can optionally override it):
image

This gives you the flexibility to customize/override the naming behavior however you want.
Example: Using the ClientIDMode property to control the IDs of Non-List Controls

Let’s take a look at how we can use the new ClientIDMode property to control the rendering of “ID” elements within a page. To help illustrate this we can create a simple page called “SingleControlExample.aspx” that is based on a master-page called “Site.Master”, and which has a single control with an ID of “Message” that is contained with an container control called “MainContent”:

image

Within our code-behind we’ll then add some simple code like below to dynamically populate the Label’s Text property at runtime:

image

If we were running this application using ASP.NET 3.5 (or had our ASP.NET 4 application configured to run using 3.5 rendering or ClientIDMode=AutoID), then the generated markup sent down to the client would look like below:

image

This ID is unique (which is good) – but rather ugly because of the “ct100” prefix (which is bad).
Markup Rendering when using ASP.NET 4 and the ClientIDMode is set to “Predictable”

With ASP.NET 4, server controls by default now render their ID’s using ClientIDMode=”Predictable”. This helps ensure that ID values are still unique and don’t conflict on a page, but at the same time it makes the IDs less verbose and more predictable. This means that the generated markup of our control above will by default now look like below with ASP.NET 4:

image

Notice that the “ct100” prefix is gone. Because the “Message” control is embedded within a “MainContent” container control, by default it’s ID will be prefixed “MainContent_Message” to avoid potential collisions with other controls elsewhere within the page.
Markup Rendering when using ASP.NET 4 and the ClientIDMode is set to “Static”

Sometimes you don’t want your ID values to be nested hierarchically, though, and instead just want the ID rendered to be whatever value you set it as. To enable this you can now use ClientIDMode=static, in which case the ID rendered will be exactly the same as what you set it on the server-side on your control. This will cause the below markup to be rendered with ASP.NET 4:

image

This option now gives you the ability to completely control the client ID values sent down by controls.
Example: Using the ClientIDMode property to control the IDs of Data-Bound List Controls

Data-bound list/grid controls have historically been the hardest to use/style when it comes to working with Web Form’s automatically generated IDs. Let’s now take a look at a scenario where we’ll customize the ID’s rendered using a ListView control with ASP.NET 4.

The code snippet below is an example of a ListView control that displays the contents of a data-bound collection — in this case, airports:

image

We can then write code like below within our code-behind to dynamically databind a list of airports to the ListView above:

image

At runtime this will then by default generate a

    list of airports like below. Note that because the
      and
    • elements in the ListView’s template are not server controls, no IDs are rendered in our markup:

      image

      Adding Client ID’s to Each Row Item

      Now, let’s say that we wanted to add client-ID’s to the output so that we can programmatically access each

    • via JavaScript. We want these ID’s to be unique, predictable, and identifiable.

      A first approach would be to mark each

    • element within the template as being a server control (by giving it a runat=server attribute) and by giving each one an id of “airport”:

      image

      By default ASP.NET 4 will now render clean IDs like below (no ctl001-like ids are rendered):

      image

      Using the ClientIDRowSuffix Property

      Our template above now generates unique ID’s for each

    • element – but if we are going to access them programmatically on the client using JavaScript we might want to instead have the ID’s contain the airport code within them to make them easier to reference. The good news is that we can easily do this by taking advantage of the new ClientIDRowSuffix property on databound controls in ASP.NET 4 to better control the ID’s of our individual row elements.

      To do this, we’ll set the ClientIDRowSuffix property to “Code” on our ListView control. This tells the ListView to use the databound “Code” property from our Airport class when generating the ID:

      image

      And now instead of having row suffixes like “1”, “2”, and “3”, we’ll instead have the Airport.Code value embedded within the IDs (e.g: _CLE, _CAK, _PDX, etc):

      image

      You can use this ClientIDRowSuffix approach with other databound controls like the GridView as well. It is useful anytime you want to program row elements on the client – and use clean/identified IDs to easily reference them from JavaScript code.
      Summary

      ASP.NET 4 enables you to generate much cleaner HTML markup from server controls and from within your Web Forms applications.

      In today’s post I covered how you can now easily control the client ID values that are rendered by server controls. In upcoming posts I’ll cover some of the other markup improvements that are also coming with the ASP.NET 4 release.

      Hope this helps,

      Scott

Posted in ASP.NET | Leave a comment

How To Get Design-Time Support for DNN Controls

by Mitchel Sellers

As everyone that is a regular reader of this blog is already aware of I am a big fan of the WAP development model for all DotNetNuke development projects.  Now, one of the most common issues that individuals have reported to me is that “design-time” support is not there for common DotNetNuke user controls that are common to integrate with custom modules.  The controls I’m discussing are the DNN Label, Text Editor, and Url Controls.  Luckly though it is VERY simple to get full design time support for the controls and this article will show you how to accomplish this!

The Problem

Before I talk about the simple change that is needed I want to illustrate the problem that we are working to solve.  Specifically you will see the following when looking at the code view of your user control

.

The green line means that Visual Studio was not able to find the reference for the control listed.  If you switch to design view you will get an issue about not being able to render the control.

The Solution

The best part of all is that this is a very easy issue to solution for.  Right-click on your project and select “Properties”.  Switch to the “Web” portion of the settings and you should see the following section.

Simply copy the above listed settings.  The first option “Project Url” specifies the path to the module you are working on, in standard development environments you will just replace “yourmodule” with the name of the module.  The key piece of functionality here is with the “Override application root URL”.  This provides design time support to re-map the ~/ application root identifier.  This will allow Visual Studio to properly find the references.

After making this change, you can switch to design view on your project and should see full support for the control, for example the DNN Label Control:

Conclusion

I hope that this has helped you get design time support for common DNN user controls.  If you have any comments or feedback be sure to share them below.  If you need specific technical assistance please use the forums on this site.

Posted in DotNetNuke | Leave a comment

Developing a C# WAP Module in DNN

by Mitchel Sellers

Ever since OpenForce ’07 in Las Vegas last year I have found myself answering more questions regarding module development and being asked to provide training to more individuals.  I have decided that I should start creating more tutorials here that explain the module development process, therefore this article will be the first in a hopefully long series of DotNetNuke module development tutorials.  This article will walk you through the process of creating a new DotNetNuke module using the C# WAP Templates that are available from BiteTheBullet.co.uk.

Pre-Requisites

Prior to starting the items listed in the below tutorial you must have the following items installed and properly configured on your development machine.

  • Visual Studio 2005 SP 1 (NOT Express Edition)
  • DotNetNuke 4.x (Install OR source version)

To develop modules in DotNetNuke you may use the Install or Source version of DotNetNuke and it will NOT have any effect on your success in development of a custom module.  If you need assistance with installing DotNetNuke please see the tutorials available on this site.

Installing the templates

After you have verified that all pre-requisite installation items have been addressed you will need to install the module templates that are available from BiteTheBullet.co.uk.  You can download the templates using this link.

Simply double-click the .vsi file that is included in the zip package to start the installation.  The following screen will be displayed.

VS Installer

After clicking “Next” on that page you might see the following warning, simply click “Yes” to continue installing the templates.

Template Install Warning

The final screen will appear and you will then simply need to click “Finish” to complete the installation process.  Once this has completed successfully you are ready to move forward with the creation of the module.

Create the Project

Although a simple process the inital project creation is a common area that can quickly de-rail your attempt at successful DotNetNuke Module Development.  To create a new module project you will select New -> Project from the File menu within Visual Studio.

In the “New Project Window” select the “C# DNN Module Template”, you should see a screen similar to the below.

Project creation demo

In the name field you simply supply the name of the module that you are looking to create.  The location field is the most important field as this MUST be set to the /DesktopModules folder within your DotNetNuke installation and the checkbox for “Create folder for solution” must be unchecked!  Simply click “Ok” to create your module.

You now have the entire solution created, you will see a small welcome file that provides you with information regarding items that you need to do such as changing the namespaces of the project.

Building the Module

After you have created your project, you must build it before you can install it in your local DotNetNuke solution.  To do this simply select “Build Solution” from the Visual Studio build menu.

If you encounter multiple build errors, open the “References” folder and ensure that the “DotNetNuke.dll” reference was found.  If the reference does not exist successfully, remove it by right clicking on it and selecting “Remove”.  Then re-add it using the “Add Resource’ option, you will need to “Browse” to find the DotNetNuke.dll file which should be located in the bin directory of your DotNetNuke installation.  Once you have corrected the reference issue simply try the build process again!

Installing the Module

Installing the module is a very simple process.  You will first need to login to your DotNetNuke portal with host permissions.  Then navigate to the Module Definitions page which is available via the Host menu.  From this page, select “Import Module Definition” from the listing.  You will be taken to the following screen:

Import Module Definitions

From the drop down for Manifests select the YourModule.dnn file, where YourModule is the name you used when creating the module project.  Once you have selected your .dnn file click “Install Manifest”.  You should receive a long display that shows the installation progress and a success message in the end.

Typically this is the only step needed, however when using the “Import Manifest” method to install a module you must manually execute the SQL Scripts for your module.  This process is very simple.  Simply navigate to the SQL page available under the “Host” menu.  On this page you will be provided a “browse” button, use this button to browse to your folder location and select the “01.00.00.SqlDataProvider” file that was created with the project.  Click “Load” and the script should automatically load for you.  Check the “Run as Script” box, then simply click “Execute”.

You should see a “Success” message, if you have the success message the demo module is successfully installed and you can now add it to any page.

Packaging the Module

The fastest method to package your module is to use the “Create Module Package” option within the Module Definitions page.  To access this functionality, go to Module Definitions and select the “edit” link next to your module.  Once in the module, you can select “Create Module Package” from the action menu.

Depending on your version of DotNetNuke your exact options on this page might vary slightly.  The below is an example from DNN 4.5.2.  On this page you can select options for the auto creation of a manifest file (.dnn) as well as supports private assembly Sub Folder.  If you are on a newer version of DNN you will have more options which will allow you to easily create PA installs only.  For the specific examples of a C# Module using WAP we would check the box for “Create Manifest File” and NOT check the PA Sub folder option.  After clicking create it will create your needed zip file, the file will be placed in the /Install/Modules folder of your DNN installation.

Create Module Package

Where To Go From Here?

This article discusses the very basics of module development using the C# templates, but there are many things to be considered above and beyond this tutorial.  Below I will discuss some of these items.  If there are specific questions please let me know  I will look at creating follow-up articles in the future to cover some of these more advanced/detailed items.

Modify Namespaces

As mentioned earlier in the article the default module template places a starting namespace of YourCompany before all classes, pages, objects in your code.  This is something that in the long run you should change to represent a namespace that is unique to your business.  You can use the “Find/Replace in Files” option of Visual Studio to do this replacement, but I personally recommend a quick manual process to update this information to ensure that all changes are made successfully.

Modify SQL Script Prefixes

In addition to the default YourCompany prefix to all Namespaces all database tables and stored procedures created in the .SqlDataProvider file are pre-fixed with YourCompany_ before them.  For an actual module that you plan on implementing you should change this prefix to something specific to your organization AND project.  One item to note on this change is that you must not only change the code in the 01.00.00.SqlDataProvider and Uninstall.SqlDataProvider files but you must also change the hard coded prefix value that is stored in the SqlDataProvider.cs file in the Components folder.

Develop your Module

Obviously this tutorial walked you through the default template.  Sadly at this time there are no “blank” module templates so a major part of building your own module will involve removing the template code that does not apply to your project.  The key to continuing the development is to ensure that if you add/remove control files that you remember to update the .dnn file!

Edit Assembly Name

By default the assembly name is simply ProjectName.dll.  This might expose you to a risk with your module and another module having a conflicting name.  I highly recommend modifying the the project properties to have a more unique assembly name.  I personally use ICG.Modules.ModuleName.dll for all of my modules, prefixing the ICG.Modules to identify my company (IowaComputerGurus) and the fact that it is a module.

Summary and Feedback

I hope that this article has served as a good overview of how to use the C# module templates to build a WAP module.  I know that this was covered in a high level manner to ensure that I could keep the length of the article short enough to keep everyone reading.  If you have questions/comments about this article please leave your feedback below, otherwise if you need technical assistance getting started please visit my forum!

Posted in DotNetNuke | Leave a comment

Building a Hello World Module

Orchard

Introduction

Orchard is built on top of ASP.NET MVC, which means that if you already know that framework you should feel right at home. If not, do not worry as we’ll explain everything we’ re doing.

MVC is a pattern where concerns are neatly separated: there is a model (M) for the data, a controller (C) that orchestrates the UI and determines how it operates on the model, and a view (V) whose only responsibility is to display what the controller hands it.

In the case of our Hello World module, we won’t have any data so the model will be of no concern to us. We will just have a controller and a view. All the rest will be some necessary plumbing to declare what we’re doing to Orchard. We will come back to these concepts and recapitulate once we’ve built our module.

Modules in Orchard are sets of extensions that can be packaged in order to be re-used on other Orchard sites. Modules are implemented as MVC Areas. Areas in MVC are sub-sites that contain a set of features that act in relative isolation from the other parts of the site. An Orchard module is simply an area with a manifest file. It may use Orchard APIs (but it doesn’t necessarily have to).

Generating the Module Structure

Before you can generate the file structure for your module, you need to download, install, and enable the Code Generation feature for Orchard. For more information, see Command-line Code Generation.

Once you have code generation enabled, open the Orchard command-line, and create the HelloWorld module with the following command:

codegen module HelloWorld

Modifying the Manifest

You should now have a new HelloWorld folder under the Modules folder of your Orchard web site. In this folder, you’ll find a module.txt file. Open it and customize it as follows:

name: HelloWorld
antiforgery: enabled
author: The Orchard Team
website: http://orchardproject.net
version: 0.5.0
orchardversion: 0.5.0
description: The Hello World module is greeting the world and not doing much more.
features:
    HelloWorld:
        Description: A very simple module.
        Category: Sample

This text file is describing your module to the system. The information contained in this file will be used for example in the features administration screen.

Note: be careful to use spaces and not tabs to indent this file

Adding the Route

Your module will have to handle the /HelloWorld relative URL under your Orchard web site. In order to declare what to do when that URL gets hit, create the following Routes.cs file in the HelloWorld folder:

using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Mvc.Routes;

namespace HelloWorld {
    public class Routes : IRouteProvider {
        public void GetRoutes(ICollection<RouteDescriptor> routes) {
            foreach (var routeDescriptor in GetRoutes())
                routes.Add(routeDescriptor);
        }

        public IEnumerable<RouteDescriptor> GetRoutes() {
            return new[] {
                new RouteDescriptor {
                    Priority = 5,
                    Route = new Route(
                        "HelloWorld",
                        new RouteValueDictionary {
                            {"area", "HelloWorld"},
                            {"controller", "Home"},
                            {"action", "Index"}
                        },
                        new RouteValueDictionary(),
                        new RouteValueDictionary {
                            {"area", "HelloWorld"}
                        },
                        new MvcRouteHandler())
                }
            };
        }
    }
}

A route is a description of the mapping between URLs and controller actions. This code maps the HelloWorld URL to the area HelloWorld with the Home controller and the Index action.

Creating the Controller

The new module also has a Controllers folder ready to be filled. Create the following HomeController.cs file in that folder:

using System.Web.Mvc;
using Orchard.Themes;

namespace HelloWorld.Controllers {
    [Themed]
    public class HomeController : Controller {
        public ActionResult Index() {
            return View("HelloWorld");
        }
    }
}

This is the controller that will handle the requests for the HelloWorld URL. The default action, index, is requesting that the HelloWorld view gets rendered.

Notice the Themed attribute on the controller class that will request that the view gets skinned with the currently active theme.

Creating the View

In the Views folder, create a folder named Home. In the Views\Home folder, create the following HelloWorld.cshtml file:

<h2>@T("Hello World!")</h2>

This file is specifying the core contents of our view. All the chrome around it will get added by the current theme’s default layout.

Notice that we used the T helper function that makes this view ready to be localized. This is not mandatory but it’s a nice touch.

Adding the new files to the project

We’re almost done. The only task remaining is to declare to the system the set of files in the module for dynamic compilation.

Open the HelloWorld.csproj file in a text editor and add the following lines after one of the </ItemGroup> tags:

<ItemGroup>
  <Compile Include="Routes.cs"/>
  <Compile Include="Controllers\HomeController.cs"/>
</ItemGroup>

Also add the following to the ItemGroup section that already has other Content tags:

<Content Include="Views\HelloWorld.cshtml" />

Activate the Module

Finally, you need to activate your new module. In the command line, type:

feature enable HelloWorld

You could also have done this from the “Features” screen in the site’s admin UI.

Use the Module

You may now add /HelloWorld to the URL of your Orchard site in your favorite web browser and obtain a nice Hello World message:

The UI for our completed moduleThe UI for our completed module

Conclusion

In this tutorial, we have built a very simple module that handles a route (/HelloWorld) through the home controller’s index action and serves a simple view that gets skinned by the current theme. We have done so with only free tools and in a way that differs very little from what you would do in a regular ASP.NET MVC area. We did get a few things for free by making this an Orchard module, such as activation/deactivation of the module, or theming of the view with no effort on our part.

Hopefully this will get you started with Orchard and prepare you to build more elaborate modules.

The code for this topic can be downloaded from here: HelloWorld.zip

Posted in Orchard | Leave a comment

Using the AJAX Control Toolkit in DotNetNuke

by Mitchel Sellers

I find that web development projects are requiring more and more AJAX functionality. The bells and whistles that come with AJAX are hard things for many people to live without. One of the more common toolsets to use when working with AJAX items in the .NET environment is the ASP.NET AJAX Control Toolkit. I have found that with VERY minimal effort you can fully implement the toolkit inside a DNN installation and it can even be done WITHOUT modification to the web.config file. This blog entry will provide information on how to incorporate the ASP.NET AJAX Control Toolkit into your project. I will simply discuss the DNN implementation, I am NOT going to actually discuss the usage of any specific control.

Get the Package

Once you have obtained the Control Toolkit Files you will want to copy the AjaxControlToolkit.dll file to the bin directory of your DotNetNuke installation being used for development. This process is similar to that of implementation in a standard ASP.NET application.

Reference and Usage

To actually use the controls and build your modules you must add two references to your module. System.Web.Extensions.dll and the newly added AjaxControlToolkit.dll file. The good news is that the System.Web.Extensions.dll is ALREADY part of a DNN installation for all versions 4.6.0 and later. THe AjaxControlToolkit.dll is the new file and is the only thing that will eventually be deployed to your production environment.

Once the references have been added to your project, you can add the controls to your module(s) using the examples provided in the “Demo” of the Control Toolkit. ALl in all, obtaining the files, adding references, and working with the controls is the exact same when working on DNN modules.

Deploying to DNN

Deploying to DNN is very simple, you must simply add the AjaxControlToolkit.dll file to your .dnn file as an individual listing in the Files section and include it in the package. As it is installed the .dll will be loaded and all controls are functional. Overall the proces to include this into your project is just the same as including any other DLL file.

Design Considerations

The biggest complaint from users of the Ajax Control Toolkit with DNN is that you have multiple issues with CSS, styling, and positioning. It is very common for extenders to appear in dramatically incorrect locations. I have found that wrapping a module with a >div< tag and set its position to “relative” fixes about 99.9% of the design issues that come up with DotNetNuke and the controls.

Conclusion

Including the ASP.NET AJAX Control Tookit is a very easy process when working in DotNetNuke and doesn’t result in any major modifications to a DNN site, nor does it require any “out of the ordinary” procedures from an administrative point of view. This makes it really easy for individuals to quickly add nice user interface additions, to provide interactive sites.

As always, please feel free to share you comments/feedback below. If you have specific questions please use the forums.

Posted in DotNetNuke | Leave a comment

Building Razor Scripts for DotNetNuke

by Joe Brinkman

To highlight some of the capabilities of the Razor Host module, I decided to create a quick script to display user profiles.  This is something that many organizations need, so I thought it would be a great way to show off the power of Razor and show some integration with DotNetNuke.  I built a similar display using the Reports module in DotNetNuke Tips & Tricks #10.  Looking at the two solutions, it is easy to see the huge differences between the two approaches.

My original version of the contact listing, relied on a heavy dose of SQL and XSLT to create the listing.  It worked well, but if you look at the SQL and XSLT involved, you realize it is not for the feint of heart.  I think that the Razor version is much more approachable for the average power user with a little bit of scripting and HTML experience.  To begin with, lets look at what I am trying to build:

modern

To get started, I add the Razor Host module to the page.  The Razor Host is a module that will help us create our script, and render it to the page.  When we first add the module to the page, it will look pretty plain because we haven’t told it what module we want displayed.

NewRazor

Clicking the “Edit Script” link will open the script editor and allow us to choose and activate one of the pre-installed scripts, edit a script or even create a new script.

EditScript

In my case, I want to create a new script, so I click the “Add New Script File” link and enter TeamMembers as the name of my script.  I also choose to use C# for my script.

AddScript

This will create a simple script for me and place it in the RazorModule/RazorHost/Scripts directory.  The script filename is automatically prepended with an underscore and the appropriate extension for the type of script I am creating.  The underscore ensures that site visitors cannot craft a URL directly to the script file and is an important security consideration since the developer may not want just any user executing the script.

At this point you are ready to begin editing the script.  You have a wide choice of editors to choose from.  If you only need to make a simple tweak to an existing script, you can use the existing text box in DotNetNuke.  Alternatively, if you open the script in WebMatrix, you will have access to a more user friendly editing experience.  Unfortunately, I found the WebMatrix experience to be a little frustrating since it does not have built in Intellisense to tell you when your code won’t compile or to help you determine the available properties or methods on an object.  Microsoft invested a lot of man-years in creating the Intellisense experience in Visual Studio and it is a little bit of a let-down that they weren’t able to leverage that capability in WebMatrix.  Using WebMatrix, I often found that I would end up with mismatched braces or parenthesis, or that my VB background would takeover and I would use parenthesis when accessing an array element when I should have used square-brackets.  It is little things like this that make Visual Studio such a powerful editor.

Like most applications there are 3 main components that we must be concerned with when creating our Razor scripts: data, presentation and behavior.  Razor provides the ability to directly query the database using some straight forward syntax.  Of course, that is not recommended for most DotNetNuke applications, since the database is not guaranteed to be backwards compatible.  Also, if you looked at my earlier report module example, you will see that the SQL for working with user profile data is not exactly simple.  Because Razor has full code support, we can and should take advantage of the existing business layer in DotNetNuke.

Using the business layer I can get all of my user data with just a few lines of code:

1 @using DotNetNuke.Security.Roles
2
3 @{
4 var users = (new RoleController()).GetUsersByRoleName(Dnn.Portal.PortalId, "Coreteam");
5 }

Compared to the 100+ lines of SQL I had in my previous example, this is much easier.   In this example, I am just retrieving a list of users who are in a specific role.  This is perfect for my script. The real benefit is that DotNetNuke has a very rich API already for accessing all of the data for your website, so there is probably some way to get the data you need for your script without resorting to SQL.

With my user data in hand, I just use a simple foreach loop to output the same block of HTML over and over again for each user in my data set.  When developing my script, I found that the same script pattern was repeated over and over again.  I often wanted to output a chunk of HTML to display some profile property, but if the user had not filled out that profile element, I wanted to hide the element on the page.  Notice that in the picture above, that my listing shows a Twitter link, but the listing for Scott Willhite doesn’t have that link.  With Razor I can define methods inside a “function” block which are then available to the other script on my page.

01 @functions {
02 public static string GetProfileProperty(UserProfile profile, string propertyName)
03 {
04 ProfilePropertyDefinition objProperty = profile.GetProperty(propertyName);
05 if (objProperty == null || string.IsNullOrEmpty(objProperty.PropertyValue)) return string.Empty;
06
07 return objProperty.PropertyValue;
08 }
09
10 public static string GetFormattedProfileProperty(UserProfile profile, string propertyName, string tagformat)
11 {
12 string propertyval = GetProfileProperty(profile, propertyName);
13 if (propertyval != string.Empty)
14 {
15 return string.Format(tagformat, @propertyval);
16 }
17 return string.Empty;
18 }
19 }

Using these functions I can now display profile properties with a simple call:

1 @GetFormattedProfileProperty(user.Profile, "CompanyName", "<div class=\"company\">{0}</div>")

Putting it all together with some additional HTML we end up with the following script.

001 @using DotNetNuke.Security.Roles
002 @using DotNetNuke.Entities.Users
003 @using DotNetNuke.Common.Utilities
004 @using DotNetNuke.Common
005 @using DotNetNuke.Entities.Profile
006 @using DotNetNuke.Entities.Portals
007
008 @{
009 var teamImageDir = Href(Dnn.Portal.HomeDirectory) + "teamImages";
010 var users = (new RoleController()).GetUsersByRoleName(Dnn.Portal.PortalId, "Coreteam");
011 }
012
013 <h2>Razor CoreTeam Contacts Example</h2>
014
015 @foreach (UserInfo user in users)
016 {
017 <div class="teammember">
018 <div class="teamheader clearfix">
019 <img class="profilephoto" style="width:120px;height:120px" src="@GetProfileUrl(user.Profile)" alt="Profile Photo for @user.DisplayName" />
020 <div class="organization">
021 <h2>
022 <a href="@Globals.UserProfileURL(user.UserID)" >@user.DisplayName</a>
023 @GetFormattedProfileProperty(user.Profile, "Country", "<span class=\"country\">( {0} )</span>")
024 </h2>
025 @GetFormattedProfileProperty(user.Profile, "CompanyName", "<div class=\"company\">{0}</div>")
026 @GetFormattedProfileProperty(user.Profile, "JobTitle", "<div class=\"title\">{0}</div>")
027 @GetFormattedProfileProperty(user.Profile, "Website", "<div class=\"website\"><span class=\"NormalBold\">Website: </span><a href=\"{0}\">{0}</a></div>")
028 </div>
029 <div class="social">
030 <div class="legend">Community Network</div>
031 <div class="community">
032 <div><span class="NormalBold">DotNetNuke Username: </span>@user.Username</div>
033 @{
034 var personalblog = GetProfileProperty(user.Profile, "PersonalBlog");
035 var dotnetnukeblog = GetProfileProperty(user.Profile, "DotNetNukeBlog");
036 }
037 @if (personalblog != string.Empty || dotnetnukeblog != string.Empty)
038 {
039 <div><span class="NormalBold">Blogs: </span>
040 @GetFormattedProfileProperty(user.Profile, "PersonalBlog", "<a href=\"{0}\" >Personal Blog</a>")@((personalblog != string.Empty && dotnetnukeblog != string.Empty) ? ", " : string.Empty )
041 @GetFormattedProfileProperty(user.Profile, "DotNetNukeBlog", "<a href=\"{0}\" >DotNetNuke Blog</a>")
042 </div>
043 }
044 </div>
045 <div class="socialmedia">
046 @GetFormattedProfileProperty(user.Profile, "FacebookUrl", "<a href=\"{0}\" ><img src=\"" + teamImageDir + "/fb.png\" /> Facebook</a>")
047 @GetFormattedProfileProperty(user.Profile, "LinkedInUrl", "<a href=\"{0}\" ><img src=\"" + teamImageDir + "/li.png\" /> LinkedIn</a>")
048 @GetFormattedProfileProperty(user.Profile, "TwitterUrl", "<a href=\"{0}\" ><img src=\"" + teamImageDir + "/tw.png\" /> Twitter</a>")
049 </div>
050 </div>
051
052 </div>
053 @GetFormattedProfileProperty(user.Profile, "Biography", "<div class=\"bio\"><h2>About Me</h2>{0}</div>")
054
055 </div>
056 }
057 @functions {
058 public static string GetProfileUrl(UserProfile profile)
059 {
060 string strPhotoURL = Globals.ApplicationPath + "/images/no_avatar.gif";
061 ProfilePropertyDefinition objProperty = profile.GetProperty("Photo");
062 if (objProperty != null &&
063 string.IsNullOrEmpty(objProperty.PropertyValue) == false &&
064 objProperty.Visibility == UserVisibilityMode.AllUsers)
065 {
066 DotNetNuke.Services.FileSystem.FileController objFiles = new DotNetNuke.Services.FileSystem.FileController();
067 DotNetNuke.Services.FileSystem.FileInfo objFile = objFiles.GetFileById(int.Parse(objProperty.PropertyValue), objProperty.PortalId);
068
069 // There is a bug in the Photo profile property that stores the host photo
070 // in the Current Portal and not in the host portal (-1). To overcome this
071 // bug we just look in the current Portal if we can't find a photo in the Profile Portal
072 if (objFile == null)
073 {
074 objFile = objFiles.GetFileById(int.Parse(objProperty.PropertyValue), objProperty.PortalId);
075 }
076
077 if (objFile != null)
078 {
079 PortalInfo objPortal = (new PortalController()).GetPortal(objFile.PortalId);
080 if (objPortal != null)
081 {
082 strPhotoURL = string.Format("{0}/{1}/{2}", Globals.ApplicationPath, objPortal.HomeDirectory, objFile.RelativePath);
083 }
084 }
085 }
086 return strPhotoURL;
087 }
088
089 public static string GetProfileProperty(UserProfile profile, string propertyName)
090 {
091 ProfilePropertyDefinition objProperty = profile.GetProperty(propertyName);
092 if (objProperty == null || string.IsNullOrEmpty(objProperty.PropertyValue)) return string.Empty;
093
094 return objProperty.PropertyValue;
095 }
096
097 public static string GetFormattedProfileProperty(UserProfile profile, string propertyName, string tagformat)
098 {
099 string propertyval = GetProfileProperty(profile, propertyName);
100 if (propertyval != string.Empty)
101 {
102 return string.Format(tagformat, @propertyval);
103 }
104
105 return string.Empty;
106 }
107
108 }

Once you break the script down, you will see that there is not a lot of code here.  Most of it is needed to get around a bug in DotNetNuke 5.6.0 for the PhotoURL profile property which does not seem to work correctly with virtual directories.  Below are the links for the full script, css and images for the contact list, and a link to the Razor Host Module beta.

DotNetNuke Razor Contact List

Razor Host Module 1.0 Beta

In my next razor post, I’ll show how I take this script, associated CSS and images and turn it into a full blown DotNetNuke module.

Posted in DotNetNuke | Leave a comment

Enabling Pretty Permalinks in WordPress

by Ruslan Yakushev

This walkthrough describes how to enable “Pretty Permalinks” for blog posts in the WordPress blog engine that is installed on IIS 7. Typically, without URL rewriting functionality on a Web server, WordPress users must use “Almost Pretty” URLs, for example, http://contoso.com/index.php/yyyy/mm/dd/post-name/. By using the URL Rewrite module, you can use “Pretty Permalinks,” for example, http://example.com/year/month/day/post-name/, for WordPress blogs that are hosted on IIS 7.

Prerequisites

This walkthrough requires the following prerequisites:

  1. IIS 7 with FastCGI and PHP installed. If you need to install PHP, follow the instructions in this article.
  2. WordPress installed. If you need to install WordPress, follow the instructions in this article or use the instructions from the official WordPress site.
  3. URL Rewrite installed.

Note that for the purposes of this walkthrough it is assumed that WordPress is installed in a Web site root directory. If WordPress is installed in a subdirectory, then the rewrite rules that are used in this walkthrough should be included in the Web.config file that is located within the same subdirectory where the WordPress files are.

Enabling Pretty Permalinks in WordPress

Use the following instructions to create pretty permalinks for your blog posts.

To enable pretty permalinks in Word Press:

  1. Log on to WordPress with Administrator user rights.
  2. In WordPress, click the Options tab.
  3. On the Options page, click the Permalinks subtab.
    This will take you to the page where you can customize how WordPress generates permalinks for blog posts.
  4. On the Permalinks page, select Custom, specify below and enter “/%year%/%monthnum%/%day%/%postname%/” in the Custom structure text box.
  5. Click Update Permalink Structure.

All the blog post links will have URLs that follow the format that you have specified, but if you click any one of those links the Web server will return a 404 – File Not Found error. This is because WordPress relies on a URL rewriting capability within the server to rewrite requests that have “pretty permalinks” to an Index.php file. In the next section, you will create a rule that will provide this capability.

Creating a Rewrite Rule

Open the Web.config file that is located in the same directory where the WordPress files are installed, and paste the following XML section into the system.webServer element:

<rewrite>
<rules>
<rule name=”Main Rule” stopProcessing=”true”>
<match url=”.*” />
<conditions logicalGrouping=”MatchAll”>
<add input=”{REQUEST_FILENAME}” matchType=”IsFile” negate=”true” />
<add input=”{REQUEST_FILENAME}” matchType=”IsDirectory” negate=”true” />
</conditions>
<action type=”Rewrite” url=”index.php” />
</rule>
</rules>
</rewrite>

This rule will try to match any requested URL. If the URL does not correspond to a file or a folder on the file system, it will rewrite the URL to the Index.php file. At that point, WordPress will determine which content to serve based on the REQUEST_URI server variable that contains the original URL before it was modified by this rule.

Testing the Rewrite Rule

After you save the rewrite rule to the Web.config file, open a Web browser and click any one of the permalinks in your WordPress blog. You should see the correct content returned by the Web server for each permalink.

Summary

In this walkthrough you learned how to use the URL Rewrite module to enable “pretty permalinks” in the WordPress blog engine. WordPress is just one example of the many popular PHP applications that can take advantage of the URL Rewrite module in IIS 7, a feature that enables user-friendly and search engine-friendly URLs.

Posted in WordPress | Leave a comment

Mapping Stored Procedure Results to a Custom Entity in Entity Framework

by Gil Fink

In the post I’m goingMapping Stored Procedure Results to a Custom Entity in Entity Framework
to explain how to map
results of a stored procedure
to a custom created entity
which we have created with the
Entity Framework designer.

Map Stored Procedures to Custom Entity

Sometimes we have stored procedures in our database which don’t
map to any table or view of our database. The problem with that is
that if we want to use those stored procedures and map them to
a custom entity that we have created we will get an error while
compiling the project. Entity Framework’s entities cannot be left
without a mapping to a table or a view. The workaround for such
situations is to use a dummy DefiningQuery element and to map the
entity to that element. Hopefully that in the next release of
Entity Framework we will get the ability to map stored procedures
to custom entities instead of using the hack I’m going to show.

How to do the hack?

In the following example I’ll use the following stored procedure:

CREATE PROCEDURE dbo.GetCourseIDAndCredits
AS
BEGIN
    SET NOCOUNT ON
    SELECT CourseID, Credits
    FROM Course
END

Pay attention that this stored procedure is simple and it’s only
to show how to perform the mapping to a custom entity. The
stored procedure returns the ID of a course with its credits.

Step 1
Import the stored procedure to the store model using Entity Framework
Wizard
.

Step 2
Create CourseCredits entity that matches the columns which the stored
procedure
returns. Make CourseID property as the entity primary key.
The following figure shows the result of step 2:
CourseCredits Designer Diagram

Step 3
Use the designer’s Function Import feature and on Add Function
Import
dialog, set the return type of the stored procedure to our new
CourseCredits entity (if you are not familiar with Function Import you can
read my previous post on this subject). The following figure shows the
dialog:
Add Function Import Dialog

Step 4
Create an entity type on the SSDL which will be the definition of
the entity type that we are going to map to the CourseCredits entity.
Open the model in Xml editor and define CourseCredits entity type like:

<EntityType Name="CourseCredits">
  <Key>
    <PropertyRef Name="CourseID" />
  </Key>
  <Property Name="CourseID" Type="int" Nullable="false"/>
  <Property Name="Credits" Type="int"/>
</EntityType>

Step 5
Since Entity Framework restrict us to map every entity to a table or view,
we need to use an hack and create a DefiningQuery on SSDL. Define the
CourseCredits entity set as follow in the SSDL:

<EntitySet Name="CourseCreditsSet" EntityType="SchoolModel.Store.CourseCredits">
  <DefiningQuery>
    SELECT cast(0 as int)  CourseID, cast(0 as int) Credits
    WHERE 1 = 2
  </DefiningQuery>
</EntitySet>

Pay attention that the DefiningQuery returns nothing. The where clause
will never happen. This is for the sake of not enabling using the custom
entity in other situations.

Step 6
Map the CourseCredits entity in the designer to the created dummy
DefiningQuery in the Mapping Details View:
Mapping Details View

Step 7
Test the solution. The following test code will print to the output
the mapped objects:

using (SchoolEntities context = new SchoolEntities())
{
    var courses = context.GetCourseIDAndCredits();
    foreach (var course in courses)
    {
        Console.WriteLine("{0} {1}", course.CourseID, course.Credits);
    }
    Console.ReadLine();
}

And the result of running the code:
Console Output

Summary

Lets sum up, I showed how to map stored procedure to a custom
created entity. In Entity Framework V1 this means that we need to
create a dummy DefiningQuery and to map the new entity to it in order
to enable that functionality. Hopefully that in V2 it will be resolved.

Posted in Entity Framework | Leave a comment

Web Application Form Design

by Luke Wroblewski

“Input elements should be organized in logical groups so that your brain can process the form layout in chunks of related fields.” HTML: the Definitive Guide

Quite rare is the Web application that doesn’t make extensive use of forms for data input and configuration. But not all Web applications use forms consistently. Variations in the alignment of input fields, their respective labels, calls to action, and their surrounding visual elements can support or impair different aspects of user behavior.

Form Layouts

When the time to complete a form needs to be minimized and the data being collected is mostly familiar to users (for instance, entering a name, address, and payment information in a check-out flow), a vertical alignment of labels and input fields is likely to work best. Each label and input field is grouped by vertical proximity and the consistent alignment of both input fields and labels reduces eye movement and processing time. Users only need to move in one direction: down.

vertical labels

In this layout, it’s advisable to use bold fonts for input field labels. This increases their visual weight and brings them to the foreground of the layout. When they are not bold, labels may compete with input fields for a user’s attention as they have almost equal visual weight.

When the data being collected by a form is unfamiliar or does not fall into easy to process groups (such as the various parts of an address), left-justifying input field labels makes scanning the information required by the form easier. Users can just scan the left column of labels up and down without being interrupted by input fields. However, the distance between the labels and input fields is often elongated by long labels, and as a result, completion times may suffer. Users have to “jump” from column to column in order to find the right association of input field and label before inputting data.

Left-Justified Horizontal Labels

An alternative layout, right aligns the input field labels so the association between input field and label is clear. However, the resulting left rag of the labels reduces the effectives of a quick scan to see what information the form requires. In the Western world, we read from left to right, so our eyes prefer a hard edge along the left side.

Right-Justified Horizontal Labels

Using Visual Elements

Due to the advantages of a “left-justified horizontal label” layout (easy scanning of input labels and reduced vertical screen space), it may be tempting to attempt to rectify its primary shortcoming: the separation of input fields and their respective labels.

One such approach features the addition of background colors and rules: the different background colors create a vertical unit of labels and a vertical unit of inputs; the horizontal rules form a relationship between each label and input field pair. Though this approach may seem desirable, it actually creates a few problems.

Through gestalt (our innate rules of visual perception), an additional 15 visual elements are added to the layout: the centerline, each background box, and each horizontal line. These elements begin to distract our eye and make it more difficult to focus on the most important elements in the layout: the labels and input fields. As Edward Tufte points out: “Information consists of differences that make a difference.” In other words, any visual element that is not helping your layout ends up hurting it. This can be seen when you try to scan the left column of labels. Your eye repeatedly pauses to consider each horizontal line and the box created by each combination of line and background color.

Backgrounds & Rules

Of course this doesn’t mean that background colors and rules should never be used within form layouts. When there is value in pointing out related groupings of information to users, a thin horizontal rule or light background color can visually unite related data. Both of these elements (rules and background colors) can be especially useful for drawing attention to the primary call to action of a form.

Seperating Related Content

Primary & Secondary Actions

The primary action associated with a form (most commonly “submit” or “save”) needs to carry a stronger visual weight (in the example above bright color, bold font, background color, etc.) than the other form elements and should vertically align with the input fields. This illuminates a path for users and guides them to completion of the form.

When a form has multiple actions such as “Continue” and “Go Back” it may be wise to reduce the visual weight of the secondary action. This minimizes the risk for potential errors and further directs users to completion.

Primary & Secondary Actions

Though these guidelines can help better position a form for your specific purpose, the combination of layout, visual elements, and content that’s right for you should still be verified through user testing or data analysis (completion rates, errors, etc.).

web form design

For more on Form Design…

Check out Luke’s book about Web form usability, visual design, and interaction design considerations: Web Form

Posted in Web Design | Leave a comment

Forms Authentication in ASP.NET 2.0

Overview

Forms authentication uses an authentication ticket that is created when a user logs on to a site, and then it tracks the user throughout the site. The forms authentication ticket is usually contained inside a cookie. However, ASP.NET version 2.0 supports cookieless forms authentication, which results in the ticket being passed in a query string.

If the user requests a page that requires authenticated access and that user has not previously logged on to the site, then the user is redirected to a configured logon page. The logon page prompts the user to supply credentials, typically a user name and password. These credentials are then passed to the server and validated against a user store, such as a SQL Server database. In ASP.NET 2.0, user-store access can be handled by a membership provider. After the user’s credentials are authenticated, the user is redirected to the originally requested page.

Forms authentication processing is handled by the FormsAuthenticationModule class, which is an HTTP module that participates in the regular ASP.NET page-processing cycle. This document explains how forms authentication works in ASP.NET 2.0.

IIS Authentication

ASP.NET authentication is a two-step process. First, Internet Information Services (IIS) authenticates the user and creates a Windows token to represent the user. IIS determines the authentication mode that it should use for a particular application by looking at IIS metabase settings. If IIS is configured to use anonymous authentication, a token for the IUSR_MACHINE account is generated and used to represent the anonymous user. IIS-then passes the token to ASP.NET.

Second, ASP.NET performs its own authentication. The authentication method used is specified by the mode attribute of the authentication element. The following authentication configuration specifies that ASP.NET uses the FormsAuthenticationModule class:

<authentication mode="Forms" />

Note Because forms authentication does not rely on IIS authentication, you should configure anonymous access for your application in IIS if you intend to use forms authentication in your ASP.NET application.

ASP.NET Forms Authentication

ASP.NET forms authentication occurs after IIS authentication is completed. You can configure forms authentication with the forms element.

Forms Authentication Configuration

The default attribute values for forms authentication are shown in the following configuration-file fragment.

<system.web>
  <authentication mode="Forms">
    <forms loginUrl="Login.aspx"
           protection="All"
           timeout="30"
           name=".ASPXAUTH"
           path="/"
           requireSSL="false"
           slidingExpiration="true"
           defaultUrl="default.aspx"
           cookieless="UseDeviceProfile"
           enableCrossAppRedirects="false" />
  </authentication>
</system.web>

The default attribute values are described below:

  • loginUrl points to your application’s custom logon page. You should place the logon page in a folder that requires Secure Sockets Layer (SSL). This helps ensure the integrity of the credentials when they are passed from the browser to the Web server.
  • protection is set to All to specify privacy and integrity for the forms authentication ticket. This causes the authentication ticket to be encrypted using the algorithm specified on the machineKey element, and to be signed using the hashing algorithm that is also specified on the machineKey element.
  • timeout is used to specify a limited lifetime for the forms authentication session. The default value is 30 minutes. If a persistent forms authentication cookie is issued, the timeout attribute is also used to set the lifetime of the persistent cookie.
  • name and path are set to the values defined in the application’s configuration file.
  • requireSSL is set to false. This configuration means that authentication cookies can be transmitted over channels that are not SSL-encrypted. If you are concerned about session hijacking, you should consider setting requireSSL to true.
  • slidingExpiration is set to true to enforce a sliding session lifetime. This means that the session timeout is periodically reset as long as a user stays active on the site.
  • defaultUrl is set to the Default.aspx page for the application.
  • cookieless is set to UseDeviceProfile to specify that the application use cookies for all browsers that support cookies. If a browser that does not support cookies accesses the site, then forms authentication packages the authentication ticket on the URL.
  • enableCrossAppRedirects is set to false to indicate that forms authentication does not support automatic processing of tickets that are passed between applications on the query string or as part of a form POST.

Authorization Configuration

In IIS, anonymous access is enabled for all applications that use forms authentication. The UrlAuthorizationModule class is used to help ensure that only authenticated users can access a page.

You can configure UrlAuthorizationModule by using the authorization element as shown in the following example.

<system.web>
  <authorization>
    <deny users="?" />
  </authorization>
</system.web>

With this setting, all unauthenticated users are denied access to any page in your application. If an unauthenticated user tries to access a page, the forms authentication module redirects the user to the logon page specified by the loginUrl attribute of the forms element.

Forms Authentication Control Flow

Figure 1 shows the sequence of events that occur during forms authentication.

Ff647070.formsauth(en-us,PandP.10).gif

Figure 1. Forms authentication control flow

  1. The user requests the Default.aspx file from your application’s virtual directory. IIS allows the request because anonymous access is enabled in the IIS metabase. ASP.NET confirms that the authorization element includes a <deny users=”?” /> tag.
  2. The server looks for an authentication cookie. If it fails to find the authentication cookie, the user is redirected to the configured logon page (Login.aspx), as specified by the LoginUrl attribute of the forms element. The user supplies and submits credentials through this form. Information about the originating page is placed in the query string using RETURNURL as the key. The server HTTP reply is as follows:
    302 Found Location:
    
    http://localhost/FormsAuthTest/login.aspx?RETURNURL=%2fFormAuthTest%2fDefault.aspx
    
  3. The browser requests the Login.aspx page and includes the RETURNURL parameter in the query string.
  4. The server returns the logon page and the 200 OK HTTP status code.
  5. The user enters credentials on the logon page and posts the page, including the RETURNURL parameter from the query string, back to the server.
  6. The server validates user credentials against a store, such as a SQL Server database or an Active Directory user store. Code in the logon page creates a cookie that contains a forms authentication ticket that is set for the session.In ASP.NET 2.0, the validation of user credentials can be performed by the membership system. The Membership class provides the ValidateUser method for this purpose as shown here:
    if (Membership.ValidateUser(userName.Text, password.Text))
    {
        if (Request.QueryString["ReturnUrl"] != null)
        {
            FormsAuthentication.RedirectFromLoginPage(userName.Text, false);
        }
        else
        {
            FormsAuthentication.SetAuthCookie(userName.Text, false);
        }
    }
    else
    {
        Response.Write("Invalid UserID and Password");
    }
    

    Note When using the Login Web server control, it automatically performs the following steps for you. The preceding code is provided for context.

  7. For the authenticated user, the server redirects the browser to the original URL that was specified in the query string by the RETURNURL parameter. The server HTTP reply is as follows:
    302 Found Location:
    
    http://localhost/TestSample/default.aspx
    
  8. Following the redirection, the browser requests the Default.aspx page again. This request includes the forms authentication cookie.
  9. The FormsAuthenticationModule class detects the forms authentication cookie and authenticates the user. After successful authentication, the FormsAuthenticationModule class populates the current User property, which is exposed by the HttpContext object, with information about the authenticated user.
  10. Since the server has verified the authentication cookie, it grants access and returns the Default.aspx page.

FormsAuthenticationModule

ASP.NET 2.0 defines a set of HTTP modules in the machine-level Web.config file. These include a number of authentication modules as shown here:

<httpModules>
  ...
  <add name="WindowsAuthentication"
       type="System.Web.Security.WindowsAuthenticationModule" />
  <add name="FormsAuthentication"
       type="System.Web.Security.FormsAuthenticationModule" />
  <add name="PassportAuthentication"
       type="System.Web.Security.PassportAuthenticationModule" />
  ...
</httpModules>

Only one authentication module is used for each request. The authentication module that is used depends on which authentication mode has been specified by the authentication element, usually in the Web.config file in the application’s virtual directory.

The FormsAuthenticationModule class is activated when the following element is in the Web.config file.

<authentication mode="Forms" />

The FormsAuthenticationModule class constructs a GenericPrincipal object and stores it in the HTTP context. The GenericPrincipal object holds a reference to a FormsIdentity instance that represents the currently authenticated user. You should allow forms authentication to manage these tasks for you. If your applications have specific requirements, such as setting the User property to a custom class that implements the IPrincipal interface, your application should handle the PostAuthenticate event. The PostAuthenticate event occurs after the FormsAuthenticationModule has verified the forms authentication cookie and created the GenericPrincipal and FormsIdentity objects. Within this code, you can construct a custom IPrincipal object that wraps the FormsIdentity object, and then store it in the HttpContext. User property.

Note If you do this, you will also need to set the IPrincipal reference on the Thread.CurrentPrincipal property to ensure that the HttpContext object and the thread point to the same authentication information.

Forms Authentication Cookies

The FormsAuthentication class creates the authentication cookie automatically when the FormsAuthentication.SetAuthCookie or FormsAuthentication.RedirectFromLoginPage methods are called.

The following properties are included in a typical forms authentication cookie:

  • Name. This property specifies the name of the cookie.
  • Value. This property specifies value of the cookie.

In a typical forms authentication cookie, the value contains a string representation of the encrypted and signed FormsAuthenticationTicket object. The cookie contains the following properties:

  • Expires. This property specifies the expiration date and time for the cookie. Forms authentication only sets this value if your code indicates that a persistent forms-authentication cookie should be issued.
  • Domain. This property specifies the domain with which the cookie is associated. The default value is null.
    • HasKeys. This property indicates whether the cookie has subkeys.
  • HttpOnly. This property specifies whether the cookie can be accessed by client script. In ASP.NET 2.0, this value is always set to true. Internet Explorer 6 Service Pack 1 supports this cookie attribute, which prevents client-side script from accessing the cookie from the document.cookie property. If an attempt is made to access the cookie from client-side script, an empty string is returned. The cookie is still sent to the server whenever the user browses to a Web site in the current domain.

    Note Web browsers that do not support the HttpOnly cookie attribute either ignore the cookie or ignore the attribute, which means that the session is still subject to cross-site scripting attacks.

  • Path. This property specifies the virtual path for the cookie. The default value is “/”, indicating root directory.
  • Secure. This property specifies whether the cookie should only be transmitted over an HTTPS connection. The Secure property should be set to true so that the cookie is protected by SSL encryption.
  • Version. This property specifies the version number of the cookie.

Creating the Forms Authentication Cookie

The forms authentication cookie is created by the FormsAuthentication class as follows. Once the user is validated, the FormsAuthentication class internally creates a FormsAuthenticationTicket object by specifying the cookie name; the version of the cookie; the directory path; the issue date of the cookie; the expiration date of the cookie; whether the cookie should be persisted; and, optionally, user-defined data.

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
        "userName",
        DateTime.Now,
        DateTime.Now.AddMinutes(30), // value of time out property
        false, // Value of IsPersistent property
        String.Empty,
        FormsAuthentication.FormsCookiePath);

Next, forms authentication uses the Encrypt method for encrypting and signing the forms authentication ticket, if the protection attribute of the forms element is set to All or Encryption.

string encryptedTicket = FormsAuthentication.Encrypt(ticket);

The following text shows the process used when the protection attribute is set to All:

  • Create a serialized forms authentication ticket. A byte array representation of the ticket is created.
  • Sign the forms authentication ticket. The message authentication code (MAC) value for the byte array is computed by using the algorithm and key specified by the validation and validationKey attributes of the machineKey element. By default, the SHA1 algorithm is used.
  • Encrypt forms authentication ticket. The second byte array that has been created is encrypted by using the Encrypt method of the FormsAuthentication class. The Encrypt method internally uses the algorithm and key specified by the decryption and decryptionKey attributes on the machineKey element. ASP.NET version 1.1 uses the 3DES algorithm by default. ASP.NET version 2.0 uses the Rinjdael (AES) algorithm by default.
  • Create HTTP cookie or query string as appropriate. The encrypted authentication ticket is then added to an HttpCookie object or query string if forms authentication is configured for cookieless authentication. The cookie object is created using the following code:
    HttpCookie authCookie = new HttpCookie(
                                FormsAuthentication.FormsCookieName,
                                encryptedTicket);
    
  • Set forms authentication cookie as secure. If the forms authentication ticket is configured to use SSL, the HttpCookie. Secure property is set to true. This instructs browsers to only send the cookie over HTTPS connections.
    authCookie.Secure = true;
    
  • Set the HttpOnly bit. In ASP.NET 2.0, this bit is always set.
  • Set appropriate cookie attributes. If needed, set the path, domain and expires attributes of the cookie.
  • Add the cookie to the cookie collection. The authentication cookie is added to the cookie collection to be returned to the client browser.
    Response.Cookies.Add(authCookie);
    

Each time a subsequent request is received after authentication, the FormsAuthenticationModule class retrieves the authentication ticket from the authentication cookie, decrypts it, computes the hash value, and compares the MAC value to help ensure that the cookie has not been tampered with. Finally, the expiration time contained inside of the forms authentication ticket is verified.

Note ASP.NET does not depend on the expiration date of the cookie because this date could be easily forged.

Role Authorization

In ASP.NET 2.0, role authorization has been simplified. You no longer need to retrieve role information when the user is authenticated or add role details to the authentication cookie. The .NET Framework 2.0 includes a role management API that enables you to create and delete roles, and add users to and remove users from roles. The role management API stores its data in an underlying data store that it accesses through an appropriate role provider for that data store. The following role providers are included with the .NET Framework 2.0 and can be used with forms authentication:

  • SQL Server. This is the default provider and it stores role information in a SQL Server database.
  • Authorization Manager (AzMan). This provider uses an AzMan policy store in an XML file, in Active Directory, or in Active Directory Application Mode (ADAM) as its role store. It is typically used in an intranet or extranet scenario where Windows authentication and Active Directory are used for authentication.

For more information about using the role management API, see How To: Use Role Manager in ASP.NET 2.0.

Cookieless Forms Authentication

ASP.NET 2.0 supports cookieless forms authentication. This feature is controlled by the cookieless attribute of the forms element. This attribute can be set to one of the following four values:

  • UseCookies. This value forces the FormsAuthenticationModule class to use cookies for transmitting the authentication ticket.
  • UseUri. This value directs the FormsAuthenticationModule class to rewrite the URL for transmitting the authentication ticket.
  • UseDeviceProfile. This value directs the FormsAuthenticationModule class to look at the browser capabilities. If the browser supports cookies, then cookies are used; otherwise, the URL is rewritten.
  • AutoDetect. This value directs the FormsAuthenticationModule class to detect whether the browser supports cookies through a dynamic detection mechanism. If the detection logic indicates that cookies are not supported, then the URL is rewritten.

If your application is configured to use cookieless forms authentication and the FormsAuthentication.RedirectFromLoginPage method is being used, then the FormsAuthenticationModule class automatically sets the forms authentication ticket in the URL. The following code example shows what a typical URL looks like after it has been rewritten:

http://localhost/CookielessFormsAuthTest/(F(-k9DcsrIY4CAW81Rbju8KRnJ5o_gOQe0I1E_jNJLYm74izyOJK8GWdfoebgePJTEws0Pci7fHgTOUFTJe9jvgA2))/Test.aspx

The section of the URL that is in parentheses contains the data that the cookie would usually contain. This data is removed by ASP.NET during request processing. This step is performed by the ASP.NET ISAPI filter and not in an HttpModule class. If you read the Request.Path property from an .aspx page, you won’t see any of the extra information in the URL. If you redirect the request, the URL will be rewritten automatically.

Note It is not possible to secure authentication tickets contained in URLs. When security is paramount, you should use cookies to store authentication tickets.

Membership and Login Controls

ASP.NET 2.0 introduces a membership feature and set of login Web server controls that simplify the implementation of applications that use forms authentication.

Membership provides credential storage and management for application users. It also provides a membership API that simplifies the task of validating user credentials when used with forms authentication. The membership feature is built on top of a provider model. This model allows implementing and configuring different providers pointing to different user stores. ASP.NET 2.0 includes the following membership providers:

  • Active Directory membership provider. This provider uses either an Active Directory or Active Directory Application Mode (ADAM) user store.
  • SQL Server membership provider. This provider uses a SQL Server user store.

You can also add support for custom user stores. For example, you can add support for other Lightweight Directory Access Protocol (LDAP) directories or other existing corporate identity stores. To do so, create a custom provider that inherits from the MembershipProvider abstract base class.

ASP.NET login controls automatically use membership and forms authentication and encapsulate the logic required to prompt users for credentials, validate users, recover or replace passwords, and so on. In effect, the ASP.NET login controls provide a layer of abstraction over forms authentication and membership, and they replace most, or all of, the work you would normally have to do to use forms authentication.

For more information about using the membership feature and login controls, see How To: Use Membership in ASP.NET 2.0.

Web Farm Scenarios

In a Web farm, you cannot guarantee which server will handle successive requests. If a user is authenticated on one server and the next request goes to another server, the authentication ticket will fail the validation and require the user to re-authenticate.

The validationKey and decryptionKey attributes in the machineKey element are used for hashing and encryption of the forms authentication ticket. The default value for these attributes is AutoGenerate.IsolateApps. The keys are auto-generated for each application, and they are different on each server. Therefore, authentication tickets that are encrypted on one computer cannot be decrypted and verified on another computer in a Web farm, or in another application on the same Web server.

To address this issue, the validationKey and decryptionKey values must be identical on all computers in the Web farm. For more information about configuring the machineKey element, see How To: Configure MachineKey in ASP.NET 2.0.

Additional Resources



Posted in ASP.NET | Leave a comment