Adding a License Agreement to an MVC 3 Application

Posted by Matt | Filed under , , ,

If you are writing a web application, often, you’ll need the user to accept a license or user agreement.  You can do this as part of the user registration process, however, what if your license agreement changes and you need the user to re-accept the agreement?  You could hardcode the check into your /Home/Index action, however, if the user bookmarks some other action or goes there directly, they can circumvent the new license agreement.

I wanted to create a way to force the user to accept the license agreement in a similar way to how the user is forced to authenticate themselves:  a page is displayed, regardless of where they try to go, and when they accept the agreement, they are only then redirected back to where they want to go in the first place.

In this article, I will show you how to add a click-through license agreement to your MVC3 web application.  It will store the user’s acceptance in the standard ASP.NET Profile system.  This data does not need to be stored in the profile system.  I just used it here as an example.  You can store it anywhere your business logic warrants with minor changes.

Add a Property to ASP.NET’s Profile

If you are not storing the user’s acceptance in the user profile, then you can skip this step.

Since we are using ASP.NET’s profile system to store the user’s acceptance, we need to add a property to the profile system.  We do this by adding the following into our application’s web.config file.

<system.web>
  <profile>
    <properties>
      <add name="AcceptedLicense" defaultValue="false"
           type="System.Boolean" />
    </properties>
  </profile>
<system.web>

 

If your web.config file already has the <profile> section, then just add the <properties> section inside your existing <profile> section.

Create the Attribute Class

We are going to be using an attribute to indicate which controllers and actions require acceptance of the license agreement. 

Create a new class and call it “RequiresLicenseAgreementAttribute”.  Override the OnActionExecuting method as follows:

public class RequiresLicenseAgreementAttribute : 
    ActionFilterAttribute
{
  public override void OnActionExecuting(
    ActionExecutingContext filterContext)
  {
    ProfileBase profile = ProfileBase.
      Create(filterContext.HttpContext.User.Identity.Name);
    bool hasAcceptedLicense = 
      (bool)profile.GetPropertyValue("AcceptedLicense");

    if (!hasAcceptedLicense)
    {
      filterContext.HttpContext.Response.StatusCode = 418;
      filterContext.HttpContext.
        ApplicationInstance.CompleteRequest(); 
      return;
    }

    base.OnActionExecuting(filterContext);
  }
}

This attribute performs the following:

  1. It checks our business logic to see if the user has accepted the license agreement.  In this sample, I am using the user’s profile.  However, this can be changes as per your own application’s business logic.
  2. If the user has not accepted the agreement, then we abandon the request and trigger a 418 status code.  418 is an unused code called “I’m a Teapot”.  It was originally created as a joke.  We will make use of it here since it is unlikely it will be used elsewhere.

Create the HttpModule to Intercept 418

Once we trigger the 418 code, we need to intercept it later on in the pipeline so that we can redirect the user.  For this, we need to create an HttpModule which will have a handler for the “end request” event.

Create a new class and call it “LicenseAgreementModule”.  In my sample, I put it in an HttpModules sub-namespace.

To this class’s Init() method, add the following:

context.EndRequest += new EventHandler(OnEndRequest);

Later on, when you’re running your app, you may get an exception about the following line requiring IIS integrated pipeline mode.  If so, then you can comment out this line since we are not going to need it.

//context.LogRequest += new EventHandler(OnLogRequest);

In our event handler, we will watch for status code 418.  If we see it, we will redirect the user to the license agreement action.

void OnEndRequest(object sender, EventArgs e)
{
  HttpContext context = ((HttpApplication)sender).Context;

  if (context.Response.StatusCode == 418)
  {
    string rawUrl = context.Request.RawUrl;
    if ((rawUrl.IndexOf("?ReturnUrl=", 
        StringComparison.Ordinal) == -1)
      && (rawUrl.IndexOf("&ReturnUrl=",
        StringComparison.Ordinal) == -1))
    {
      string strUrl = "/Account/LicenseAgreement";

      string str3 = strUrl + "?ReturnUrl=" + 
        HttpUtility.UrlEncode(rawUrl, 
          context.Request.ContentEncoding);

      context.Response.Redirect(str3, false);
    }
  }
}

Add Module to web.config

The next step is to add our HttpModule to the web application’s web.config file.

Add the following in the appropriate place:

<system.web>
  <httpModules>
    <add name="LicenseAgreement"
    type="LicenseAgreementSample.HttpModules.LicenseAgreementModule"/>
  </httpModules>
</system.web>

Create a LicenseAgreement Action

We need to create an action to display and handle the License Agreement acceptance.  We will put it in our AccountController.

[Authorize]
[HttpGet]
public ActionResult LicenseAgreement()
{
  return View();
}

[Authorize]
[HttpPost]
public ActionResult LicenseAgreement(string returnUrl)
{
  if (ModelState.IsValid)
  {
    ProfileBase profile = 
      ProfileBase.Create(User.Identity.Name);
    profile.SetPropertyValue("AcceptedLicense", true);
    profile.Save();

    if (Url.IsLocalUrl(returnUrl) && 
        returnUrl.Length > 1 && 
        returnUrl.StartsWith("/") && 
        !returnUrl.StartsWith("//") && 
        !returnUrl.StartsWith("/\\"))
    {
      return Redirect(returnUrl);
    }
    else
    {
      return RedirectToAction("Index", "Home");
    }
  }

  return View();
}

In the above, we have two handlers for our action:  one for the initial display of the agreement (first one), and one for the acceptance (second one).

The first one is pretty basic:  just show the view.

The second one does all the work:

  1. Save to our profile that the user accepted the license agreement
  2. If our URL has a return address stored in it, then redirect to that URL.
  3. If our model is not valid, then re-show our page.

Create the License Agreement View

Next, create the actual license agreement view that the user will see and accept.

Create a new view for your new action.  My sample license agreement is pretty bleak.  You will want to populate it with whatever agreement you require.  My view only contains a single button.

@{
  ViewBag.Title = "LicenseAgreement";
}

<h2>LicenseAgreement</h2>

@using (Html.BeginForm())
{
  <button type="submit">Accept</button>
}

Note that I am using the Razor engine above.

Apply our Attribute to our Controllers and Actions

Now that everything is in place, our final step is to apply our custom attribute to our controllers.  In this sample, I will force all actions in the HomeController to require acceptance of the license agreement.  Here, I can apply it to the controller itself.

[Authorize]
[RequiresLicenseAgreement]
public class HomeController : Controller

Note, that I have added the [Authorize] attribute as well.  Since we are storing the agreement in the user’s profile, a user needs to be logged in.

Once this is in place, when you run your application, you should see:

  1. The log in page, then
  2. The license agreement page, then
  3. The desired page that the user originally navigated to

Some Additional Notes

  • I originally thought that I would have to deal with attribute ordering to ensure the user logs in before they need to accept the agreement.  I have been told that authentication will always happen before any action filters.  So we do not need to worry about this.
  • If you want to add a “Do not accept” button to your license agreement view, you will need to do one of two things:
    1. Have that button go to a different action, or
    2. Add a check in your controller to see which button the user clicked

Final Word

As I write this, I am asking myself whether the HttpModule is really required.  Would it be possible to simply redirect right from the RequiresLicenseAgreementAttribute class instead of using the 418 status code?  Maybe.  I will have to try it one day.

References

I want to thank the guys in the MVC Forum.  They helped me out and gave me the info I needed to create this.

The following were used in the research to this solution:

Sample Application

Here is a link to the sample project using all of the above.

Mvc3LicenseAgreementSample.zip (1.98 MB)

Windows Azure Pricing

Posted by Matt | Filed under , , ,

As a development platform for web applications, Windows Azure is very enticing.  It has front-end and back-end processing, on-demand scalability, robust file storage, simple object table storage, fast content delivery, and access to SQL databases.  However, for small or unproven applications, it’s actually cost prohibitive.

I want to concentrate on the “compute” cost of Windows Azure.  The standard compute rate is $0.12 per hour.  What does “compute” mean?  It does not mean CPU computing time.  It is available time.  This means a web application sitting idle is still costing you $0.12 per hour.  At this rate, it will cost $86.40 for a 30-day month, just to have the application “up”.

Many business applications not only have a web front-end, but they also have one or more worker back-end processes.  For Windows Azure, each worker back-end process also costs you “compute” time.  So, two processes (one for front-end, one for back-end) will cost $0.12 per hour x 2 processes x 24 hours x 30 days = $172.80 per month.

For a small developer starting a new application, that kind of price is ridiculous.  Especially considering that you can get a VPS server for under $50 per month.  Granted, you don’t get the redundancy and immediate scalability.  But is that really worth an extra $120 a month?

If we look at some of the Windows Azure “packages”, none of the non-introductory packages include enough “compute” time to cover an entire month.  A 31-day month requires 744 “compute” hours per small instance.  What is the point of offering 250 hours of compute time a month?  To avoid extra costs, I can only run my application 8 hours a day.

Let’s also examine this from the point of view of a new application.  No developer I know hopes that their new application won’t gain traction.  I think it is every developers best wishes that their application will get popular.  They hope that one day they’ll have to worry about scalability.  During the design phase, this presents quite a dilemma:  do you develop the application against Windows Azure now because you hope that the application will be popular and you like the development environment, or do you develop the application normally knowing that later on you will have to convert it?  For small developers, it’s a no-brainer:  you develop normally because the costs are too high otherwise.

For this reason, Microsoft needs to reduce the cost barrier-to-entry.  They need to make Windows Azure more cost-friendly for new and unproven applications.  Even if this means a cut in service somehow. For example, they could have a slower CPU, or a lower peak bandwidth.  They could have a shared processes option.  There are many possibilities.

An alternative is some kind of Windows Azure-lite: allow people to run Windows Azure projects outside of the main Windows Azure cloud in a simulated environment.  They already have it in the development environment, why not allow people to install a fake cloud on a VPS server?  This way, developers can create the application using the Azure framework and if the application proves to need the full resources available in a cloud environment, it’s as easy as uploading the existing project unchanged to the real Azure cloud.

By keeping small developers away from the production environment, Microsoft is going to drive developers to Amazon’s or Google’s cloud solutions and they’re going to kill the Azure platform for all but the largest of enterprises.

Additional Resources

Alternative Cloud Platforms

Mixing Table Per Type and Table Per Hierarchy Inheritance

Posted by Matt | Filed under , , , , , ,

When working in Silverlight RIA applications, there are some common scenarios that are useful:

  • Table Per Type inheritance – Useful when you have one table that “extends” another table with additional data.  Your main table would be a common “type” and you’d have one (or more) tables which add more data.  More information
  • Table Per Hierarchy inheritance – Useful when you have one table that has many “types” inside it with some data useful depending on the “type”.  Usually, one column in your table would denote the “type” of data, and from there, your application would read additional columns.  These additional columns often would be unused if a row’s “type” was not of a particular value.  More Information
  • Table Per Concrete Type inheritance – Useful when you want to have a “history” table in addition to the “current” data.  More Information

All 3 of these are useful in their own times.  But what if you needed to mix types?

Here is a particular database scenario which I believe to be quite common:

image


In this setup, we have a database representing an online store.  One table lists all the products.  Some products are shirts, other products are shoes.  Two additional tables exist to provide additional data depending on the product type (similar to Table Per Type).  However, we have a discriminator (ProductType) to determine the product type (similar to Table Per Hierarchy).  In our case, if ProductType == 1, then there’s a Shirt record corresponding to the Product record.

For lack of a better name, I’m calling this Table Per Type Hierarchy inheritance.

By default, entity framework would generate the following:

image

 

This would work, however, there are 3 side-effects to this:

  1. Product has a navigation member called “Shirt”.  This means “Product has a Shirt”.  Likewise, “Shirt has a Product” since Shirt has a navigation member called “Product”.  In reality, “Shirt is a Product”.   This is “unnatural”.
  2. If I wanted to query all the Products which are Shirts, then I’d have to query “where ProductType == 1”.
  3. There are no safeguards to ensure that “if ProductType == 1 then a Shirt record exists”.

Instead, we want to setup a conditional hierarchy.  At first, you may think that we can just use Table Per Hierarchy to create a hierarchy between Shirts and Products and Shoes and Products, then add a conditional for Shirt “where ProductType = 1”.  However, this is not the case.  The entity framework user interface does not allow you to select a member from Product as a condition for Shirt.  Maybe this is a bug.  Maybe you can do this by editing the Xml.  To get this to work, you need to add a proxy entity between Product and Shirt.

Step 1.  Start with your default entity framework setup.

image[8]

 


Step 2.  Remove the 2 associations that were generated.  Click each and delete.

image

 


Step 3.  Remove the ProductType member from Product.

Step 4.  Make Product abstract.

image

Step 5.  This step is exactly as is done for normal Table Per Hierarchy inheritance.  Create a new entity and call it “ShirtProduct”.  Derive this new entity from Product.  Map the new entity to the Products table.  In the mapping, add a condition such that ProductType = 1.  But don’t move any properties from Product to ShirtProduct.

Step 6.  This step is just like normal Table Per Type inheritance.  Derive Shirt from ShirtProduct. 

image

Remove ProductId from Shirt since we already have it in Product.  Once we’ve done that, our entity model should look like this:

image

 

 

Step 7.  Repeat steps 5 and 6 for Shoe, but set it’s condition to “ProductType = 2”.  Our model now looks like:

image

The blue rectangle represents the portion using Table Per Hierarchy inheritance.  The red rectangle represents the portion using Table Per Type inheritance.

If we had any products which did not require extra data in secondary tables, we could just use Table Per Hierarchy inheritance.  For example, if we had a third product type called “Pants”, then we could create a new entity called “Pants” (not “PantsProduct”), derive it from Product and set it’s condition to be ProductType = 3.  In this case, the proxy class is the working class.

To access the classes, we do so just like Table Per Type.  The example code shows ‘get’ functions for a DomainContext in a Silverlight RIA appliication:

public IQueryable<Shirt> GetShirts()
{
	return this.ObjectContext.Products.OfType<Shirt>();
}

public IQueryable<Shoe> GetShoes()
{
	return this.ObjectContext.Products.OfType<Shoe>();
}

When I generated my DomainContext, I chose to retrieve and update the ProductTable.  So my DomainContext already had GetProducts() and InsertProduct().  This means:

  1. I can use GetProducts() to get a list of all products, and
  2. I can insert a product of any type by calling InsertProduct()

Actually, for a Silverlight RIA application, you may choose to not call InsertProduct() directly.  Instead, you can use the following code:

Shirt shirt = new Shirt();
shirt.ProductId = Guid.NewGuid();
shirt.Title = "Red Polo";
shirt.Price = new Decimal(11.99);
shirt.Colour = "Red";
shirt.Style = "Polo";

ProductDomainContext context = 
   (ProductDomainContext)domainDataSource1.DomainContext;
context.Products.Add(shirt);
context.SubmitChanges();

 

Notice 2 things:

  1. I am using the normal entity framework model to insert a Shirt into the list of Products.
  2. I am not setting Product.ProductType anywhere.

The entity framework takes care of inserting the correct data into the Shirts table, the correct data into the Products table, and setting ProductType = 1 properly.

Magical!

Included is a sample Silverlight RIA application that demonstrates this.  It includes a list of all products, a list of shirts, and a list of shoes.  It also inserts a new shirt into the database.

TPTHSample.zip (4.58 MB)

I’d like to thank Lingzhi Sun over on the MSDN Forums for help in this matter.

Ugly Yellow Tooltips in your MFC Application Using the Windows 7 Theme

Posted by Matt | Filed under , , , ,

I found another bug in MFC in Visual Studio 2010, but it’s an easy one to fix.

If you create a new MFC Ribbon project and use the Windows 7 colour scheme, then you’ll notice that the tooltips are ugly and yellow.

image

If you want to get the attractive tooltips back, you need to add a little bit of code.  Pat Brenner gave a solution when I reported the problem on the MSDN Forums.  This solution will work if you are allowing theme switching in your application.  If you are not, then you need to add similar code, elsewhere.  If this is the case for you, add the following code to CMainFrame::OnCreate():

CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = FALSE;
ttParams.m_bDrawSeparator = FALSE;
ttParams.m_clrFillGradient = afxGlobalData.clrBarFace;
ttParams.m_clrFill = RGB(255, 255, 255);
ttParams.m_clrBorder = afxGlobalData.clrBarShadow;
ttParams.m_clrText = afxGlobalData.clrBarText;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
  RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);

If you do this, you’ll get the attractive tooltips you’re looking for.

image

Correcting a Windows 7 Ribbon project using Visual Studio 2010

Posted by Matt | Filed under , , ,

So you’ve created a new MFC Ribbon application that has the Windows 7 ribbon look to it.  Unfortunately, when you run the application, it looks like this:

Screen_Bad

Notice the big ugly button in the corner, and the quick access toolbar is not drawn correctly (down arrow menu button).

This happens when you do not select the “Enable visual style switching” option.

image

The correction is to find this line of code in MainFrm.cpp:

CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(
	CMFCVisualManagerWindows7));

and add the following line immediately after it:

m_wndRibbonBar.SetWindows7Look(TRUE);

Once you’ve done that, your window will look correct.

Screen_Good

I’ve reported the issue on Microsoft Connect.  A link to the report can be found here.

Tip: Install the MFC Source Code

Posted by Matt | Filed under ,

I cannot express how many times I’ve needed to step through the MFC source code in order to debug a problem I was having.  Often, the problem ends up being my own issue, but by stepping through the MFC source code, I was able to get a better understanding of what exactly the issue is.

So today’s tip:  make sure you install the MFC source code when you’re installing Visual Studio.  It’s an optional component, so best to do a custom install to ensure it’s selected.

Visual Studio 2010 Launches Today

Posted by Matt | Filed under , ,

Microsoft is releasing the newest version of it’s development environment today.  This new version is slated to include many new features, not only for managed code, but also for unmanaged C++ code.

The launch is comprised of a 2-day event.  The first day has keynotes and talks about Visual Studio 2010.  Day 2 is the official launch for Silverlight 4.

Channel 9 has it’s own launch center set up to cover the 2-day event.

Microsoft has announced the availability (to download or purchase) Visual Studio 2010.

Compatible with Windows 7 Application Testing

Posted by Matt | Filed under , ,

Compatible with Windows 7 Back with Vista, Microsoft had 2 logo designations:  Works with Vista and Certified for Vista.  The former was a free self-test, the latter was an expensive 3rd party test.  For Windows 7, Microsoft has consolidated the two designations into a single logo:  Compatible with Windows 7.  This is done as a self-test using the latest toolkit.

However, not all companies have the time and/or resources to do the testing themselves.  Sometimes it’s more economical to farm the manual work off to another party to do the testing.  Let’s face it, would you rather spend your time adding new features and improvements, or would you rather be spending time testing your application for Windows 7 compatibility?

To meet this need, I’ve started LogoPlus.  LogoPlus is a service where businesses can farm out the manual labour of testing their applications against the toolkit.  They should still make sure they meet the logo requirements.  However, the work of setting up the Windows 7 Ultimate x64 operating systems, testing and re-testing, etc. can be done by LogoPlus.

If you’re in charge of getting your product Compatible with Windows 7 tested, I encourage your to check out the service.  I think you’ll find the the prices are competitive and economical.

Multiple Projects and Include Paths

Posted by Matt | Filed under ,

One thing that I am struggling with in working in new development projects is how to deal with multiple vcproj projects and the include paths that go with them.  What is the best schema for dealing with include folders?  Currently, we copy files that we know are needed in other projects to a common include folder. 

For example, if we have projects A.vcproj and B.vcproj, and B.vcproj requires A.h from A.vcproj, then in A.vcproj we will have a custom build step for A.h to copy it to the common include folder.  B.vcproj will then add the common include folder to it’s include path.  This method avoids having to know which project and which directory contains the header file.  It works well for the 60 projects involved in our main solution file.

There is a caveat to this system:  sometimes it’s not possible to keep a strict dependency tree for projects.  For example, occasionally, there may be 2 projects which create a circular dependency based on include requirements.  We work very hard to avoid this for DLLs.  However, for LIBs, we are less strict.  As such, we cannot rely on one project’s header files being copied before it’s needed by another project.  Our solution was to create a pre-process build step that would parse the project files and perform the copy for all projects before the first one was built.

It’s not elegant, but it works.  And sometimes that’s what matters.

What I’m wondering now is how other companies deal with similar issues.  How do you deal with including files from other projects?  Do you use a common include folder?  Do you hard code the paths to the included files in your #include statement?  Or do you have another system that’s working well for you?

Allowing CView Classes to Handle Tab Navigation

Posted by Matt | Filed under , , ,

Working on a recent project, I wanted to give my CView-derived view class Tab navigation (like CFormView has).  By default, if you add CEdit and other controls to your CView-derived view, pressing Tab will do nothing.  Also, adding CStatic with & accelerators are ignored.

Doing some Googling, I found the following solution.  Add the following function to your CView-derived class:

BOOL CMyView::PreTranslateMessage(MSG* pMsg)
{
  if(IsDialogMessage(pMsg))
    return TRUE;
  else
    return CWnd::PreTranslateMessage(pMsg);
}

At first, it was working great.  However, I noticed that my overall application accelerators were not working.

Looking deeper into CFormView, I found the solution. The above code is not enough.  The following is a more complete solution.

BOOL CMyView::PreTranslateMessage(MSG* pMsg)
{
  ASSERT(pMsg != NULL);
  ASSERT_VALID(this);
  ASSERT(m_hWnd != NULL);

  // allow tooltip messages to be filtered
  if (CView::PreTranslateMessage(pMsg))
    return TRUE;

  // don't translate dialog messages when in Shift+F1 help mode
  CFrameWnd* pFrameWnd = GetTopLevelFrame();
  if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)
    return FALSE;

  // since 'IsDialogMessage' will eat frame window accelerators,
  //   we call all frame windows' PreTranslateMessage first
  pFrameWnd = GetParentFrame();   // start with first parent frame
  while (pFrameWnd != NULL)
  {
    // allow owner & frames to translate before IsDialogMessage does
    if (pFrameWnd->PreTranslateMessage(pMsg))
      return TRUE;

    // try parent frames until there are no parent frames
    pFrameWnd = pFrameWnd->GetParentFrame();
  }

  // don't call IsDialogMessage if form is empty
  if (::GetWindow(m_hWnd, GW_CHILD) == NULL)
    return FALSE;

  // filter both messages to dialog and from children
  return PreTranslateInput(pMsg);
}

I will admit, this code came directly from CFormView in the MFC source for Visual Studio 2008 (give credit where credit is due).

After including this code in my class, tab navigation is working as well as overall application accelerators.