Wednesday, May 30, 2012

HTML Helper for Action image in MVC (that is, img tag with anchor tag within)

This article aims to explain how can we create and use HTML helper method for IMG html tag, to display an image, and to call a controller action while user clicks over it.

Walkthrough:

Step 1: Create new folder in your MVC structure.



Step 2: Add a new class file "MyHelper.cs" in that folder, and create a "static" class within:
namespace MVC.Infrastructure
{
    public static class MyHelpers
.....

Step 3: Import System, and System.Web.Mvc namespaces in that class.
using System;
using System.Web.Mvc;

Step 4: Write following helper method in MyHelper.cs class.

public static MvcHtmlString ActionImage(this HtmlHelper html, string imagePath, string alt, string cssClass,
            string action, string controllerName,object routeValues)
        {
            var currentUrl = new UrlHelper(html.ViewContext.RequestContext);
            var imgTagBuilder = new TagBuilder("img"); // build the <img> tag
            imgTagBuilder.MergeAttribute("src", currentUrl.Content(imagePath));
            imgTagBuilder.MergeAttribute("alt", alt);
            imgTagBuilder.MergeAttribute("class", cssClass);
            string imgHtml = imgTagBuilder.ToString(TagRenderMode.SelfClosing);
            var anchorTagBuilder = new TagBuilder("a"); // build the <a> tag
            anchorTagBuilder.MergeAttribute("href", currentUrl.Action(action, controllerName, routeValues));
            anchorTagBuilder.InnerHtml = imgHtml; // include the <img> tag inside
            string anchorHtml = anchorTagBuilder.ToString(TagRenderMode.Normal);
            return MvcHtmlString.Create(anchorHtml);
        }

Step 5: Open your razor/ aspx view. Import namespace of above class in your view. (This example deals with razor syntax, but it will be easy for you to convert into aspx, if you are using aspx engine).

@using MVC.Infrastructure

Step 6: Use the created HTML helper to show Image in view, and to call controller action when user clicks it.

@Html.ActionImage("../../Images/logo.png", "Site Logo", "siteLogo", "About", "Home", null)
Here, "Home" is a controller name, and "About" is an action, and it does not have any routeValues so passed as null.

This is all we need to do.
Following is the HTML generated by using this HTML helper:
<a href="/Home/About"><img alt="Site Logo" class="siteLogo" src="../../Images/logo.png" /></a>

You can take advantage of this HTML helper in any view within your MVC application and it will generate HTML image and anchor tags every time for you!

Monday, May 21, 2012

Remove HTML tags from a text string in ASP.NET using RegularExpression

This article aims to explain a very simple method to remove HTML tags from a text using RegularExpressions.

This will remove all HTML tags, or character references (like, &nbsp, &amp) from a text, and will return plain text.

    public static string RemoveHtmlTags(string htmlText, bool preserveNewLine)
    {
        System.Web.UI.HtmlControls.HtmlGenericControl divNew = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
        divNew.InnerHtml = htmlText;
        if (preserveNewLine)
        {
            divNew.InnerHtml = divNew.InnerHtml.Replace("<br>", "\n");
            divNew.InnerHtml = divNew.InnerHtml.Replace("<br/>", "\n");
            divNew.InnerHtml = divNew.InnerHtml.Replace("<br />", "\n");
        }
        return System.Text.RegularExpressions.Regex.Replace(divNew.InnerText, "<[^>]*>", "");
    }

if there is a requirement to preserve new line, then we need to convert HTML line breaks into new line character (which is "\n" in C#).

Example output using above method:
Input: &nbsp;Take one <strong>notebook</strong>, pen, <u>pencil</u> and <em>eraser</em> with you.
Result:  Take one notebook, pen, pencil and eraser with you.
(In above example, &nbsp; is replaced by a space character in the beginning)

Saturday, May 19, 2012

JavaScript Injection: Dealing with Java Scripts supplied through QueryString in URL while requesting for an ASP.NET or MVC webpage.

QueryStrings are very common in websites.
But they are very much prone to JavaScript injection. And if they are not handled properly, they can easily temper your webpage/ data.

For example, you have a label in your webpage, and you are setting it's text property according to a particular QueryString supplied (say, querystring UserName).

Now, if a user requests following URL in a browser:
http://myWebsite/myPage.aspx?UserName=Sample<script type="text/Javascript">alert('a')</script>

In this case, if you have not taken handled QueryString properly in your page, it will execute this javascript. Though, alert is a harmless script, but a user can really pass some dangerous javascripts and they will get executed.

Following is what we need to avoid this -

1. If your page has ValidateRequest attribute is set to True (which is a default value), it will anyway not allow such querystrings to be processed. Instead it will show user Browser's error page indicating potentially dangerous request.

2. But if you do not want to display Browser's error page (Browser's error page does not give good impression to user) or if you absolutely had to set ValidateRequest to False according to your requirements, then you need to do an HTML encoding of supplied querystring.

lblQueryString.Text = HttpUtility.HtmlEncode(Request.QueryString["UserName"].ToString());

This will treat the querystring as plain text (no execution of any script at all)!

Also, if you are using .NET v4.0, it will show you browser's error page even if you have "ValidateRequest" to False in your page. To avoid that, add following line under "<system.web>" section within your web.config file.

<httpRuntime requestValidationMode="2.0"/>





Friday, May 18, 2012

Execute client-side code/ validation on RadioButton/ CheckBox before executing their server-side CheckedChanged (Click) event.


We very much know if we want to perform client-side validations before the server-side Click event of button, we need to write our javascript in “onclientclick” event, and if it returns False, the server-side Click event will not be executed. As easy as it sounds!
But it’s not that straight-forward for controls like RadioButton and Checkbox.

Our article will take example of RadioButton, and will show you how to achieve this.

Consider a webpage showing user following options:

Now, if a user clicks either “Movie”, or “Dinner Out” option, it should show fire some server-side code directly.
But, if user clicks on “Touring Europe” option, it should first ask for confirmation from user and if user agrees, then only should fire server-side code written in “CheckedChanged” event.
Just like –

At first it may look that the job is only to add “onclick” attribute and to call a javascript method for that. But it requires a little more than that.
If you see HTML source of page at this point, it will show there is already a javascript method which is assigned to execute on click.







Now, if you simply add your own JavaScript code for “onclick” method, your code will be executed before existing code of click method (i.e. before setTimeout(..__doPostBack)… ). And if you have “return” statement in your code (to return True/ False), the PostBack will not be hit, and your “CheckedChanged” event will never be called.

If you say, why this behavior is different from “onclientclick” of button control? It’s simply because Button is a submit/ postback control, and so it does not rely on javascript. So you will never see javascript __doPostBack call in its HTML source

To overcome this, you will need to do the postback from your javascript method
Following is a step-by-step instruction to achieve the same in our example. (You can easily correlate it with your requirements)

            STEP 1
            Assign javascript method to “onclick” attribute of radiobutton in Page_Load.
if (!Page.IsPostBack)
        {
            rbOptEuropeTour.Attributes.Add("onclick", "return checkBudgetForEuropeTour('" + rbOpt3.ClientID + "')");
       }

checkBudgetForEuropeTour is a javascript method which we will write in next step.
Here, rpOpt3 is the radio button for “Touring Europe” option. Here, client id of this control is passed to help the code in identifying which control has caused postback, and using that we will call a corresponding server-side event. (Explained in later steps).

STEP 2
      Write javascript method in your aspx page, or script file (.js) according to your preferences.
function checkBudgetForEuropeTour(rbOptClientId) {
        if (confirm('You do not have enough budget for Europe Tour. Do you still want to go with this option?') == true) {
            __doPostBack(rbOptClientId, 'CheckedChanged');           
        }
        else {
            return false;
        }
    }

We are explicitly doing postback from the javascript method (using __doPostBack).

      STEP3
      Last step, is to identify which control has caused the postback, and to call server-side event accordingly.
It should be written ideally in Page_Load event.
if (Page.IsPostBack)
        {
            if (Request["__EVENTTARGET"].Contains("rbOptEuropeTour") == true && Request["__EVENTARGUMENT"] == "CheckedChanged")
            {
                rbOptEuropeTour_CheckedChanged(sender, e);
            }
        }

“rbOptEuropeTour_CheckedChanged” is the server-side OnCheckedChanged event for radio button “rbOptEuropeTour”.

That’s all we need to do!

Tuesday, May 15, 2012

Find pattern-matched duplicate rows within a group using PARTITION BY in SQL Server.


This article aims to explain how to find duplicate rows within a group matching patterns.

Scenario:
Following is the structure and data of “tblPeople” table:

PersonID
CityName
ForeNames    
LastName
1
Ahmedabad
Bhavin
Parikh
2
Ahmedabad
Bharat
Soni
3
Ahmedabad
Jagdish
Shah
4
Ahmedabad
Byomesh
Soni
5
Ahmedabad
Jagdish
Shyam
6
Vadodara
Bhavin
Patel
7
Vadodara
Amay
Kapoor
8
Vadodara
Ajit
Shah
9
Vadodara
Ajit
Shah
10
Vadodara
Soham
Patel
11
Vadodara
Amay
Shah
12
Surat
Rajesh
Desai
13
Surat
Bhavin
Parikh
14
Surat
Bhaumik
Shah

Our SQL should have input parameters for ForeNames, LastName, and CityName to define search pattern for duplicate items search.
Parameters - @foreNames, @lastName, @cityName
Our objective is as follow:
1.       If none of the above parameters are supplied, find all persons having same ForeNames and LastName within same City.
Expected result:

2.       If @cityName is only supplied, then find all persons having same ForeNames within particular City.
Expected result: (input >> @cityName = ‘%Vad%’)


3.       If @foreNames is supplied (with/ without @cityName supplied), but @lastName is not supplied.
In that case, consider all persons matching @foreNames within same city as duplicates, and return them.
Expected result: (input >> @foreNames=’%Bh%’)


4.       If @lastName is supplied (with /without @cityName supplied), but @foreNames is not supplied.
In that case, consider all persons matching @lastName within same city as duplicates, and return them.
Expected result: (input >> @lastName=’%Sh%’)



5.       If @foreNames and @lastName are supplied (with/without @cityName supplied).
In that case, consider all persons matching @foreNames, and @lastName within same city as duplicates, and return them.
Expected result: (input >> @foreNames=’%A%’, @lastName=’%S%’, @cityName=’%Ahm%’)

Solution:
First of all, we cannot use GROUP BY here, because we need also need ID, and other details of the rows which are duplicated. GROUP BY simply cannot allow selecting those columns which are not used in grouping or aggregate.

To achieve this, we need to partition the rows in groups of “City”, “Forenames”, and “LastName”. Here, while partitioning the rows we also need to consider pattern-matching for ForeNames and LastName.

This can be done using PARTITION BY in SELECT query, and within single T-SQL statement only.

Following is the T-SQL serving the purpose:

WITH CTE AS
(SELECT ID, City, ForeNames, LastName,
COUNT(*) OVER(PARTITION BY City,
CASE WHEN @foreNames <> '' THEN @foreNames
ELSE
CASE WHEN @lastName <> '' THEN '' ELSE ForeNames END
END,
CASE WHEN @lastName <> ''  THEN @lastName
ELSE
CASE WHEN @foreNames <> '' THEN ''
ELSE
CASE WHEN @cityName <> '' THEN ''
ELSE LastName
END
END
END) as MAXCount
FROM tblPeople
WHERE ForeNames LIKE  
CASE WHEN @foreNames <> '' THEN @foreNames ELSE '%' END
AND LastName LIKE CASE WHEN @lastName <> '' THEN @lastName ELSE '%' END
AND City LIKE CASE WHEN @cityName <> '' THEN @cityName ELSE '%' END
)

SELECT * FROM CTE WHERE MAXCount > 1

Here, I have used Common Table Expression (but you can use temporary table to store the result of SELECT statement) before further narrowing down the resultset to exclude non-duplicates.

The point of interest in above T-SQL is how the field “MAXCount” gets generated.
COUNT(*) – indicates the total number of rows in each partition. This is what ultimately will be stored in “MAXCount” field.

Now, each partition is built using following values:
City >> Because we need to find duplicates within the same city always, so this column is an obvious choice in partition.

ForeNames >> Partition on this column depends upon @foreNames parameter.
·         If it is not supplied, and neither @lastName is supplied it will create partition by grouping rows having exact ForeNames.
·         If it is not supplied, but @lastName is supplied, it will ignore ForeNames column in partitioning.
·         If supplied, it will create partition by grouping all matching rows in a single partition.

LastName >> Partition on this column depends upon @lastName parameter.
·         If it is not supplied, and not even @foreNames, and @cityName, then it will create partition by grouping rows having same LastName.
·         If it is not supplied, but either @foreNames, or @cityName is supplied, it will ignore LastName column in partitioning.
·         If supplied, it will create partition by grouping all matching rows in single partition.