A beginner ASP.NET developer also knows pretty well that each ASP.NET page needs to have a <form runat=”server”> tag on the page and that all server controls need to be present inside this tag (although this is not entirely true; you can place server controls outside <form runat=”server”> but then they cannot participate in regular ASP.NET functionality like PostBacks, Viewstate etc).
A problem thus often arises in ASP.NET when you need to add another <form> tag to the page. Html/Xhtml specifications do not allow nested <form> tags. A typical trick I use in these scenarios when writing ASP.NET pages is to put the second <form> tag on the .aspx page outside the <form runat=”server”> tag and then use some CSS tricks (and sometimes javascript) to make the second <form> tag appear at its desired location on the page.
But this is not possible in DotNetNuke when writing custom modules as you are basically writing an .ascx UserControl in a module and DotNetNuke will load that dynamically on the page as a descendant of <form runat=”server”> tag, which means your second <form> tag present inside the .ascx control would be nested inside a parent <form> tag. Although this would work server-side with no exceptions, most browsers would create issues rendering or submitting such pages with nested <form> tags.
I was in one such tight situation recently. I needed to have a PayPal pay button on a DotNetNuke page and if you have used PayPal buttons before, you would know they are rendered inside a <form> tag. Adding such a <form> tag for PayPal payment button from a DNN module clearly meant it won’t work in browsers due to nesting of <form> tags as discussed above. Further I was decently sure that the client would not agree to regular workarounds normally used in such scenarios (like using a PayPal payment link instead of a button, or redirecting to another regular .aspx page with the PayPal button).
It thus appeared that the only workable solution was to have the PayPal button inside an <iframe> on the DotNetNuke page itself. Although implementing this solution is pretty straightforward, I thought of sharing the same on the blog just in case it would help someone.
The process starts with rendering an <iframe> from inside your DNN module pointing to another stand-alone .aspx page that would render the PayPal button. Here’s some sample code:
Inside your .ascx file of DNN module:
<asp:Literal runat="server" ID="ctlPaypalHolder" />
Inside your .ascx.cs code-behind:
string iframeUrl=UriUtil.resolveAbsoluteUrl("~/DesktopModules/MyModule/PaypalPayment.aspx"); string paypalForm="<iframe src='" + iframeUrl + "' frameborder='0'></iframe>"; this.ctlPaypalHolder.Text = paypalForm;
UriUtil.resolveAbsoluteUrl is a utility method I use very commonly in ASP.NET. This is its code:
{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }public static string resolveAbsoluteUrl (string url)
{
if (url.ToLower().StartsWith(“http”))
{
return (url);
}
HttpContext context = HttpContext.Current;
if (url[0] == ‘~’)
{
url = url.Substring(1);
}
if (url.Length > 0 && url[0] != ‘/’)
{
url = ‘/’ + url;
}
if (context.Request.ApplicationPath != “/”)
{
url = context.Request.ApplicationPath + url;
}
return (string.Format(“{0}://{1}{2}{3}”,
context.Request.IsSecureConnection ? “https” : “http”,
context.Request.Url.Host,
context.Request.Url.IsDefaultPort ? “” : “:” + context.Request.Url.Port,
url));
}{/syntaxhighlighter}
Next is the PayPalPayment.aspx markup:
<%@ Page Language="C#" AutoEventWireup="true" Inherits="PayPalPayment" Codebehind="PayPalPayment.aspx.cs" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <asp:PlaceHolder runat="server" ID="ctlPayCodePlaceholder"> </asp:PlaceHolder> <form id="form1" runat="server"> <div> </div> </form> </body> </html>
With this being the code-behind (PayPalPayment.aspx.cs):
{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }public partial class PayPalPayment : PaymentPageBase
{
protected void Page_Load (object sender, EventArgs e)
{
Payment payment=Payment.currentPayment;
if (payment == null)
{
this.ctlPayCodePlaceholder.Controls.Add(new LiteralControl(“Access Denied…”));
return;
}
string url=UriUtil.resolveAbsoluteUrl(“~/DesktopModules/MyModule/PayPalPayment.aspx”);
if (!UriUtil.areSameDomainUrls(new Uri(uri), this.Context.Request.Url))
{
this.doAccessDenied();
return;
}
string paypalForm=@”
<form action=’https://www.sandbox.paypal.com/cgi-bin/webscr’ method=’post’ target=’_top’>
<input type=’hidden’ name=’cmd’ value=’_xclick’>
<input type=’hidden’ name=’business’ value=’2YTGJLJJGCBNY’>
<input type=’hidden’ name=’lc’ value=’US’>
<input type=’hidden’ name=’button_subtype’ value=’services’>
<input type=’hidden’ name=’no_note’ value=’0′>
<input type=’hidden’ name=’cn’ value=’Add special instructions to the seller’>
<input type=’hidden’ name=’no_shipping’ value=’2′>
<input type=’hidden’ name=’undefined_quantity’ value=’1′>
{0}
<input type=’hidden’ name=’bn’ value=’PP-BuyNowBF:btn_paynowCC_LG.gif:NonHosted’>
<input type=’image’ src=’https://www.sandbox.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif’ border=’0′ name=’submit’ alt=’PayPal – The safer, easier way to pay online!’>
<img alt=” border=’0′ src=’https://www.sandbox.paypal.com/en_US/i/scr/pixel.gif’ width=’1′ height=’1′>
</form>”;
string variables = “”;
variables += “<input type=’hidden’ name=’rm’ value=’2′>”;
variables += “<input type=’hidden’ name=’currency_ code’ value=’USD’>”;
variables += “<input type=’hidden’ name=’item_number’ value='” + payment.paymentId.ToString() + “‘>”;
variables += “<input type=’hidden’ name=’item_name’ value='” + payment.paymentTypeDesc + ” – ICSI Student Registration” + “‘>”;
variables += “<input type=’hidden’ name=’amount’ value='” + payment.paymentAmount.ToString(“n2”) + “‘>”;
paypalForm = String.Format(paypalForm, variables);
this.ctlPayCodePlaceholder.Controls.Add(new LiteralControl(paypalForm));
}
}{/syntaxhighlighter}
There are a couple of important points you would need to adapt before using this code-behind directly:
- Most probably the payment information (for example amount to be paid, transaction number etc) would be known in your DNN module’s user control. You would need to pass that securely to the PayPalPayment page to be able to generate button properly.
In the above sample code, Payment.currentPayment does exactly that. It fetches information from Session (that was stored by the .ascx control) and uses it to create appropriate parameters for the PayPal button.
As this part (fetching Payment information from Session) was specific to our app, I have not reproduced it here. You would need to adapt this portion for your situation to make the above sample code run. - You would want to perform security checks to prevent common expolits, e.g. the above code verifies that the PayPalPayment.aspx page is being invoked from the application’s domain. You might want to add additional checks (e.g. the UrlReferrer belongs to the same domain) as per your needs.
- Lastly you would need to adapt the variables rendered for the PayPal button. In particular, do not forget to change the “business” hidden variable (or you would end up on my Sandbox PayPal merchant page). Also change the PayPal’s Sandbox url to the PayPal url.
As I already said, no rocket science in this code. Just available out-of-the-box to adapt and save some time.
Hi Rahul,
Thanks a lot for such a useful blog. Certainly it saved lot of time.
Regards,
Anand
This is truly helpful for those programmers out there. You need to follow the steps given in order to come up to the correct result.