MVC Browser Detection

Task:

For a recent project I had to look at a users browser to determine if the
browser meets our requirements. We have multiple third party sites that
we interface with and some of them do not work with certain browser
versions. One of the requirements of the project was to make it flexible
enough that a person on the tech support team would maintain the list once
we finished with it. Here is the solution that we came up with, some more
tweaks are needed but for now this is what we have deployed.

Browser Requirements:

We created an XML file that lives in the site directory on the live server.
It is able to be edited by the tech support team as needed.

  1. Supported – Allow normal login
  2. Warning – Not fully supported, post warning message, allow login
  3. Blocked – Access denied, post message, do not allow login
        <?xml version="1.0" encoding="utf-8" ?>
        <supportedbrowsers>
          <supported>
            <IE
              displayName ="Internet Explorer" MachineType="Windows" MinVersion ="9.0" MaxVersion ="9.0"
              link="http://windows.microsoft.com/en-US/internet-explorer/downloads/ie-8"
              />
            <Firefox
              displayName ="Mozilla Firefox" MachineType="Windows, Mac" MinVersion ="7.0" MaxVersion ="9.1"
              link="http://www.mozilla.com/en-US/firefox/all-older.html"
              />
            <Chrome
              displayName ="Google Chrome" MachineType="Windows, Mac" MinVersion ="10.0" MaxVersion ="16.0"
              link="https://www.google.com/chrome"
              />
            <Safari
              displayName ="Safari" MachineType="Mac" MinVersion ="5.0" MaxVersion ="6.0"
              link="http://www.apple.com/safari/download/"
              />
          </supported>
          <warning>
            <ie      MachineType="Windows" MinVersion ="8.0" MaxVersion ="8.0" />
            <firefox MachineType="Windows, Mac" MinVersion ="3.6" MaxVersion ="7.9" />
            <firefox MachineType="Windows, Mac" MinVersion ="9.2" MaxVersion ="1000" /> <!--For future versions-->
            <chrome  MachineType="Windows, Mac" MinVersion ="10.0" MaxVersion ="14.9" />
            <chrome  MachineType="Windows, Mac" MinVersion ="16.1" MaxVersion ="1000" /> <!--For future versions-->
            <safari  MachineType="Mac" MinVersion ="4.0" MaxVersion ="4.9" />
            <safari  MachineType="Mac" MinVersion ="6.1" MaxVersion ="1000" /> <!--For future versions-->
          </warning>
          <blocked>
            <ie      MachineType="Windows" MinVersion ="0" MaxVersion ="7.9" />
            <firefox MachineType="Windows, Mac" MinVersion ="0" MaxVersion ="3.5" />
            <chrome  MachineType="Windows, Mac" MinVersion ="0" MaxVersion ="9.9" />
            <safari  MachineType="Mac" MinVersion ="0" MaxVersion ="3.9" />
            <safari  MachineType="Windows" MinVersion ="0" MaxVersion ="100" />  
          </blocked>
        </supportedbrowsers>

SupportedBrowser Class:

This class contains all the logic for determining the browser requirements
based on the data in the XML file.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Xml.Linq;

namespace SSO_UI.Models
{
    public enum SupportStatus
    {
        Unknown = 0,
        Supported = 1,
        Warning = 2,
        Blocked = 3
    }

    public class SupportedBrowsers
    {
        readonly string _path = ConfigurationManager.AppSettings["SupportedBrowserPath"];

        [DefaultValue("")]
        public string BrowserName { get; set; }
        [DefaultValue("")]
        public string MachineType { get; set; }
        [DefaultValue(0)]
        public double Version { get; set; }
        [DefaultValue(false)]
        public bool IsValid { get; set; }

        /// <summary>
        /// Retrieve the message to display to the users based on the browser settings.
        /// </summary>
        public string GetMessage()
        {
            var msg = string.Empty;
            switch (SupportStatus)
            {
                case SupportStatus.Warning:
                    msg =
                        String.Format(
                            "Your web browser, {0} v{1} for {2}, is not fully supported.  Please consider downloading one of the supported browsers listed for full functionality within CampusConnect.",
                            BrowserName,
                            Version,
                            MachineType
                            );
                    break;
                case SupportStatus.Blocked:
                    msg =
                        String.Format(
                            "Your web browser, {0} v{1} for {2}, is not supported at all.  Please download one of the supported browsers listed to use CampusConnect.",
                            BrowserName,
                            Version,
                            MachineType
                            );
                    break;
            }
            return msg;
        }

        public SupportStatus SupportStatus { get; set; }

        /// <summary>
        /// Constructor to seed the class with the relevant information
        /// </summary>
        public SupportedBrowsers(HttpBrowserCapabilitiesBase browser)
        {
            if (browser == null) return;

            BrowserName = browser.Browser;
            if (!GetMachineType()) return;
            if (!GetVersion(browser)) return;
            IsValid = GetSupportStatus();

        }

        /// <summary>
        /// Generate a list of supported browsers based on the machine type.
        /// </summary>
        /// <param name="machineType">
Windows, Mac</param>
/// <returns></returns>
        public List<string> GetList(string machineType)
        {
            const string li = "{0} {1} <a href='{2}' target='_blank'>Get {3}</a>";  
            // {0}: browserName
            // {1}: minVersion
            // {2}: link
            var list = new List<string>();

            var supported = XDocument.Load(_path).Descendants("supported").ToList();

            foreach (var el in supported.Descendants())
            {
                var elname = el.Name;
                var minVersionAtt = el.Attribute("MinVersion");
                var maxVersionAtt = el.Attribute("MaxVersion");
                var machineTypeAtt = el.Attribute("MachineType");
                var displayNameAtt = el.Attribute("displayName");
                var linkAtt = el.Attribute("link");

                if (minVersionAtt == null || maxVersionAtt == null || machineTypeAtt == null || displayNameAtt == null || linkAtt == null)
                    throw new Exception("Error in SupportedBrowsers.xml file.");

                try
                {
                    var version = minVersionAtt.Value == maxVersionAtt.Value
                                      ? "v" + maxVersionAtt.Value
                                      : "v" + minVersionAtt.Value + " - v" + maxVersionAtt.Value;
                    if (machineTypeAtt.Value.ToUpper().Contains(machineType.ToUpper()))
                        list.Add(string.Format(li,
                                               displayNameAtt.Value,
                                               version,
                                               linkAtt.Value,
                                               elname
                                               ));
                }
                catch (Exception)
                {
                    throw new Exception("Error in SupportedBrowsers.xml file.");

                }
            }

            return list;
        }

        /// <summary>
        /// Parse the machine type out of the UserAgent string.
        /// </summary>
        private bool GetMachineType()
        {
            var platform = HttpContext.Current.Request.UserAgent;
            if (platform == null)
                return false;
            if (platform.ToUpper().Contains("WINDOWS"))
            {
                MachineType = "Windows";
                return true;
            }
            if (platform.ToUpper().Contains("MACINTOSH"))
            {
                MachineType = "Mac";
                return true;
            }
            return false;
        }

        /// <summary>
        /// Get the browser version.  
        /// For IE we have temporarily hardcoded the version based on the Trident version in the UserAgent.
        /// ToDo: clean this up and make it work properly in the future.
        /// </summary>
        private bool GetVersion(HttpBrowserCapabilitiesBase browser)
        {
            double version;

            if (BrowserName == "IE")
            {
                var platform = HttpContext.Current.Request.UserAgent;
                if (platform != null && platform.Contains("Trident/5.0"))
                    Version = 9.0;
                else if (platform != null && platform.Contains("Trident/4.0"))
                    Version = 8.0;
                else if (double.TryParse(browser.Version, out version))
                    Version = version;
            } 
            else if (double.TryParse(browser.Version, out version))
                Version = version;
            else
                return false;
            return true;
        }

        /// <summary>
        /// Determine the level of support we have for this browser
        /// </summary>
        private bool GetSupportStatus()
        {
            var doc = XDocument.Load(_path);

            var supported = doc.Descendants("supported").Descendants(BrowserName).ToList();
            var warning = doc.Descendants("warning").Descendants(BrowserName).ToList();
            var blocked = doc.Descendants("blocked").Descendants(BrowserName).ToList();

            if (IsSupportedStatusInData(supported)) SupportStatus = SupportStatus.Supported;
            else if (IsSupportedStatusInData(warning)) SupportStatus = SupportStatus.Warning;
            else if (IsSupportedStatusInData(blocked)) SupportStatus = SupportStatus.Blocked;
            else
            {
                SupportStatus = SupportStatus.Blocked;
                return false;
            }

            return true;
        }

        /// <summary>
        /// Parses the actual XML Elements from the file and determines if it is supported.
        /// </summary>
        private bool IsSupportedStatusInData(IEnumerable<xelement> data)
        {
            foreach (var el in data)
            {
                var minVersionAtt = el.Attribute("MinVersion");
                var maxVersionAtt = el.Attribute("MaxVersion");
                var machineTypeAtt = el.Attribute("MachineType");

                if (minVersionAtt == null || maxVersionAtt == null || machineTypeAtt == null)
                    throw new Exception("Error in SupportedBrowsers.xml file.");

                try
                {
                    if (Version >= double.Parse(minVersionAtt.Value)
                        && Version <= double.Parse(maxVersionAtt.Value)
                        && machineTypeAtt.Value.ToUpper().Contains(MachineType.ToUpper()))
                        return true;
                }
                catch (Exception)
                {
                    throw new Exception("Error in SupportedBrowsers.xml file.");

                }
            }
            return false;
        }
    }
}

SupportedBrowser Partial View:

@{
    var windows = ViewBag.browser.GetList("Windows");
    var mac = ViewBag.browser.GetList("Mac");
}

@if (ViewBag.browser.SupportStatus == SupportStatus.Supported)
{
    return;
}

<div class="sysreq">
<h2>Supported Browsers</h2>
<h3>Windows</h3>
<ul class="bullets">
    @foreach (var li in windows)
    {
      <li> @Html.Raw(li.ToString())</li>
    }
</ul>
<h3>Mac</h3>
<ul class="bullets">
    @foreach (var li in mac)
    {
        <li> @Html.Raw(li.ToString())</li>
    }
</ul>
<p class="sysreq">More about <a href="http://students.msbcollege.edu/elearning/system-requirements/" target="_blank">System Requirements</a></p>
</div>

Login Page:

@model VMLogin

@{
    ViewBag.Title = "Log in ";
    Layout = "~/Views/Shared/_Login.cshtml";
}

<script language="javascript" type="text/javascript">
    window.onload = $(function () {
        $('#UserName').focus();
        var status = '@ViewBag.browser.SupportStatus';
        if (status == 'Blocked') //blocked
            $('#logindiv').addClass('hide');
    });
</script>

@Html.Image("loginimg", "login.jpg", "log in", 110, 25)
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    
    if (!String.IsNullOrEmpty(Model.ErrorMsg))
    { 
    <div class="error-state">
        <label class="error">@Model.ErrorMsg</label>
    </div>
    }
    if (ViewBag.browser.SupportStatus != SupportStatus.Supported)
    { 
        <div class="error-state">
            <label class="error">@ViewBag.browser.GetMessage()</label>
        </div>
    }
    <div id="logindiv">
        <fieldset>
            <div>
                @Html.LabelFor(model => model.UserName, new { @class = "textlabel" })
            </div>
            <div>
                @Html.TextBoxFor(model => model.UserName, new { @class = "textbox" })
                @Html.ValidationMessageFor(model => model.UserName)
            </div>
            <div>
                @Html.LabelFor(model => model.Password, new { @class = "textlabel" })
            </div>
            <div>
                @Html.PasswordFor(model => model.Password, new { @class = "textbox" })
                @Html.ValidationMessageFor(model => model.Password)
            </div>
        </fieldset>

        <input type="submit" value="Submit" class="button-1" name="navigation" />
    </div>
}

6 thoughts on “MVC Browser Detection

  1. Thanks this is what im looking for πŸ™‚
    SupportedBrowser file is it comes under model/utility? because in partial view I can see the html page…so where it comes …will it comes under model if its model then we cant have partial html page.
    Simply how can i use your files in my project.
    which file goes where?

    my proj structure controller,view,viewmodel and model.
    Could you help me out where your files will go in my project.
    Im newbie to MVC ..

    Like

  2. If I understand you right you are wondering how to display the partial views. What I didn't put in the post was an example of the controller method. I no longer have access to that codebase, but if I am reading the code right you need to set up the ViewBag.browser to have an instance of the SupportedBrowsers class passing in the browser information.

    Like

  3. I passed the supportedbrowsers instance in the viewbag.browser and works fine… I can able to get the messages but where to call this supportedbrowsers.cshtml file as I have not used it in any of the files.where and how to call it..

    sorry again im newbie πŸ™‚

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s