So, I tried to use the contact topic but was too constricted on what I wanted to accomplish. I had few goals in mind:
1. Add captcha so that bots couldn't use our form to spam
2. Hide email addresses so they could not be harvested
3. Support contacting different departments from the same form
4. Make as configurable as possible in the Admin site
I first tried adding captcha to the contact topic but was unhappy with the results. I then decided it was time to write my own page. Here's how I did it.
First I created some new AppConfigs:
SecurityCodeRequiredOnContact set to True
ContactCount set to 12 (I'll explain this later)
ContactAddress set to our address (surprised this doesn't exist out of the box)
XmlPackage.ContactEmail set to notification.contactus.xml.config
FaxNumber set to our fax number
TollFreeNumber set to our toll free number
Now I needed to create some string resources:
contact.1.display set to Support
contact.1.email set to support@mydomain.com
contact.2.display set to Finance
contact.2.email set to finace@mydomain.com
contact.3.display set to Orders
contact.3.email set to orders@mydomain.com
(Note: For every department just increment the x - contact.x.display/email)
I added these strings in addition to the regular department contacts as an Admin "backdoor" for testing the form if I need to make changes. They are only used if the person viewing the page is SuperAdmin.
contact.1000.display set to Admin Test
contact.1000.email set to myadminemail@mydomain.com
However many departments you end up with, set the value of your AppConfig ContactCount to that number (3 for the example above Support, Finance, Orders...don't include the admin backdoor "department".)
Create some more string resources for your validation fields:
contact.aspx.1 set to Name Is Required
contact.aspx.2 set to Phone Is Required (Note: I don't use this)
contact.aspx.3 set to Email Is Required
contact.aspx.4 set to Subject Is Required
contact.aspx.5 set to Message Is Required
contact.aspx.6 set to Mail To Is Required
contact.aspx.7 set to Submitting Your Information. You Will Be Redirected To The Home Page Upon Completion.
I also created some new string resources for the form labels:
common.faxprompt set to Fax:
common.tollfreeprompt set to Toll Free:
Here's the code for the page contactus.aspx:
Code:
<%@ Page Language="vb" Inherits="AspDotNetStorefront.contactus" CodeFile="contactus.aspx.vb" %>
<%@ Register TagPrefix="aspdnsf" TagName="Topic" src="TopicControl.ascx" %>
<html>
<head>
<title></title>
</head>
<body>
<form runat="Server" method="POST" id="ContactForm" name="ContactForm">
<div align="center">
<asp:Panel ID="FormPanel" runat="server" Width="90%">
<asp:Label ID="ContactAddress" runat="server" EnableViewState="false"></asp:Label>
<br/>
<br/>
<asp:Label ID="TollFree" runat="server" EnableViewState="false"></asp:Label>
<br/>
<asp:Label ID="FaxNumber" runat="server" EnableViewState="false"></asp:Label>
<br/><br/>
<b>Use the form below to send us a message</b>
<br/><br/>
<hr size="1"/>
<asp:Table runat="server" EnableViewState="false" CellPadding="3" CellSpacing="0" Width="600" BorderStyle="None">
<asp:TableRow>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Right" Width="148">
<label>*Mail To:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:PlaceHolder ID="ContactListHolder" runat="server"></asp:PlaceHolder>
<asp:RequiredFieldValidator ID="RequiredFieldValidator7" runat="server" ControlToValidate="ContactList" ErrorMessage="(!contact.aspx.6!)" ValidationGroup="Group1" Enabled="True" InitialValue="-1"></asp:RequiredFieldValidator>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Right" Width="148">
<label>*Your Name:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:TextBox ID="MailName" runat="server" Visible="True" ValidationGroup="Group1" CausesValidation="True" Width="200px" EnableViewState="False" MaxLength="50" CssClass="InputBox"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="MailName" ErrorMessage="(!contact.aspx.1!)" ValidationGroup="Group1" Enabled="True"></asp:RequiredFieldValidator>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Right" Width="148">
<label>Your Phone:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:TextBox ID="MailPhone" runat="server" Visible="True" Width="200px" EnableViewState="False" MaxLength="50" CssClass="InputBox"></asp:TextBox>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Right" Width="148">
<label>*Your E-Mail:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:TextBox ID="MailEmail" runat="server" Visible="True" ValidationGroup="Group1" CausesValidation="True" Width="200px" EnableViewState="False" MaxLength="50" CssClass="InputBox"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" ControlToValidate="MailEmail" ErrorMessage="(!contact.aspx.3!)" ValidationGroup="Group1" Enabled="True"></asp:RequiredFieldValidator>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Right" Width="148">
<label>*Subject:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:TextBox ID="MailSubject" runat="server" Visible="True" ValidationGroup="Group1" CausesValidation="True" Width="300px" EnableViewState="False" MaxLength="50" CssClass="InputBox"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator5" runat="server" ControlToValidate="MailSubject" ErrorMessage="(!contact.aspx.4!)" ValidationGroup="Group1" Enabled="True"></asp:RequiredFieldValidator>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell VerticalAlign="Top" HorizontalAlign="Right" Width="148">
<label>*Message:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:TextBox ID="MailMessage" runat="server" Visible="True" ValidationGroup="Group1" CausesValidation="True" Width="350px" EnableViewState="False" Height="200px" MaxLength="500" CssClass="InputBox" TextMode="MultiLine"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator6" runat="server" ControlToValidate="MailMessage" ErrorMessage="(!contact.aspx.5!)" ValidationGroup="Group1" Enabled="True"></asp:RequiredFieldValidator>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow ID="EnterSecurityCode" Visible="false">
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Right" Width="148">
<label>*Enter Code Below:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:TextBox ID="SecurityCode" runat="server" ValidationGroup="Group1" CausesValidation="True" Width="45px" EnableViewState="False" MaxLength="6" CssClass="InputBox" TextMode="SingleLine"></asp:TextBox>
<asp:Label CssClass="errorLg" ID="ErrorMsgLabel" runat="server" Visible="false"></asp:Label>
<asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" ControlToValidate="SecurityCode" ErrorMessage="(!signin.aspx.20!)" ValidationGroup="Group1" Enabled="False"></asp:RequiredFieldValidator>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow ID="ShowSecurityCode" Visible="false">
<asp:TableCell VerticalAlign="Top" HorizontalAlign="Right" Width="148">
<label>Security Code:</label>
</asp:TableCell>
<asp:TableCell VerticalAlign="Middle" HorizontalAlign="Left" Width="448">
<asp:Image ID="SecurityImage" runat="server"></asp:Image>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell ColumnSpan="2" Width="100%" HorizontalAlign="Center">
<asp:Panel ID="SubmitPanel" runat="server" Width="100%" Visible="true">
<asp:Button ID="SubmitButton" OnClick="SubmitButton_Click" runat="server" Text="(!common.cs.61!)" ValidationGroup="Group1"></asp:Button>
</asp:Panel>
<asp:Panel ID="ExecutePanel" runat="server" Width="100%" Visible="false">
<div align="center">
<br />
<b>
<asp:Literal ID="SignInExecuteLabel" runat="server" Text="(!contact.aspx.7!)"></asp:Literal>
</b>
<br />
</div>
</asp:Panel>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
</asp:Panel>
</div>
</form>
</body>
</html>
And the code for the code-behind contactus.asxp.vb:
Code:
Imports Microsoft.VisualBasic
Imports System
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Web.Security
Imports System.Data
Imports System.Data.SqlClient
Imports System.Globalization
Imports AspDotNetStorefrontCommon
Namespace AspDotNetStorefront
Partial Public Class contactus
Inherits SkinBase
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
'Dynamically Create Our Contacts DropDownList
Dim ContactDropDown As New DropDownList()
ContactDropDown.ID = "ContactList"
ContactDropDown.EnableViewState = False
ContactDropDown.ValidationGroup = "Group1"
ContactDropDown.Items.Add(New ListItem("", "-1"))
Dim x As Integer
For x = 1 To AppLogic.AppConfig("ContactCount")
If (AppLogic.GetString("contact." & x & ".display", SkinID, ThisCustomer.LocaleSetting) <> "") AndAlso (AppLogic.GetString("contact." & x & ".email", SkinID, ThisCustomer.LocaleSetting) <> "") Then
ContactDropDown.Items.Add(New ListItem(AppLogic.GetString("contact." & x & ".display", SkinID, ThisCustomer.LocaleSetting), x))
End If
Next
If ThisCustomer.IsAdminSuperUser Then
ContactDropDown.Items.Add(New ListItem(AppLogic.GetString("contact.1000.display", SkinID, ThisCustomer.LocaleSetting), 1000))
End If
'Add Our Dynamically Created DropDownList To The Page
ContactListHolder.Controls.Add(ContactDropDown)
ContactAddress.Text = AppLogic.AppConfig("StoreName") & "<br/>" & AppLogic.AppConfig("ContactAddress")
TollFree.Text = AppLogic.GetString("common.tollfreeprompt", SkinID, ThisCustomer.LocaleSetting) & " " & AppLogic.AppConfig("TollFreeNumber")
FaxNumber.Text = AppLogic.GetString("common.faxprompt", SkinID, ThisCustomer.LocaleSetting) & " " & AppLogic.AppConfig("FaxNumber")
SectionTitle = AppLogic.GetString("sendform.aspx.1", SkinID, ThisCustomer.LocaleSetting)
If (Not Page.IsPostBack) Then
If AppLogic.AppConfigBool("SecurityCodeRequiredOnContact") Then
' Create a random code and store it in the Session object.
Session("SecurityCode") = CommonLogic.GenerateRandomCode(6)
End If
'Pre-fill some form info if our customer is registered
If ThisCustomer.IsRegistered Then
MailName.Text = ThisCustomer.FirstName & " " & ThisCustomer.LastName
MailPhone.Text = ThisCustomer.Phone
MailEmail.Text = ThisCustomer.Email
End If
End If
If AppLogic.AppConfigBool("SecurityCodeRequiredOnContact") Then
EnterSecurityCode.Visible = True
ShowSecurityCode.Visible = True
RequiredFieldValidator4.Enabled = True
SecurityImage.ImageUrl = "jpegimage.aspx"
End If
End Sub
Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As EventArgs)
If AppLogic.AppConfigBool("SecurityCodeRequiredOnContact") Then
Dim sCode As String = Session("SecurityCode").ToString()
Dim fCode As String = SecurityCode.Text
If fCode <> sCode Then
ErrorMsgLabel.Text = String.Format(AppLogic.GetString("lat_signin_process.aspx.5", SkinID, ThisCustomer.LocaleSetting), sCode, fCode)
ErrorMsgLabel.Visible = True
SecurityCode.Text = String.Empty
Session("SecurityCode") = CommonLogic.GenerateRandomCode(6)
Return
End If
End If
SubmitPanel.visible = False
ExecutePanel.visible = True
Dim FormContents As String = String.Empty
Dim Subject As String = "Contact Form: " & CommonLogic.FormCanBeDangerousContent("MailSubject")
Dim SendTo As String = AppLogic.GetString("contact." & CommonLogic.FormCanBeDangerousContent("ContactList") & ".email", SkinID, ThisCustomer.LocaleSetting)
Dim SendFrom As String = CommonLogic.FormCanBeDangerousContent("MailEmail")
Dim SendName As String = CommonLogic.FormCanBeDangerousContent("MailName")
Dim UseXmlPackage As String = AppLogic.AppConfig("XmlPackage.ContactEmail").Trim()
If UseXmlPackage.Length <> 0 Then
FormContents = AppLogic.RunXmlPackage(UseXmlPackage, MyBase.GetParser, ThisCustomer, ThisCustomer.SkinID, String.Empty, String.Empty, True, False)
Else
FormContents = CommonLogic.GetFormInput(True, "<br/>")
End If
AppLogic.SendMail(Subject, FormContents, True, SendFrom, SendName, SendTo, SendTo, "", AppLogic.MailServer())
Response.AddHeader("REFRESH", "1; URL=" & Server.UrlDecode("default.aspx"))
End Sub
End Class
End Namespace
And finally, the code for the xml package notification.contactus.xml.config :
Code:
<?xml version="1.0" encoding="utf-8" ?>
<package displayname="ContactUs Email" version="2.1" debug="false">
<!-- ###################################################################################################### -->
<!-- Copyright AspDotNetStorefront.com, 1995-2008. All Rights Reserved. -->
<!-- http://www.aspdotnetstorefront.com -->
<!-- For details on this license please visit the product homepage at the URL above. -->
<!-- THE ABOVE NOTICE MUST REMAIN INTACT. -->
<!-- -->
<!-- ###################################################################################################### -->
<PackageTransform>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aspdnsf="urn:aspdnsf" exclude-result-prefixes="aspdnsf">
<xsl:output method="html" omit-xml-declaration="yes" encoding="ISO-8859-1" />
<xsl:param name="StoreURL"><xsl:value-of select="/root/Runtime/StoreUrl" /></xsl:param>
<xsl:param name="StyleURL"><xsl:value-of select="$StoreURL" />skins/skin_<xsl:value-of select="aspdnsf:SkinID()" />/style.css</xsl:param>
<xsl:template match="/">
<html>
<head>
<link rel="rel" type="text/css" href="{$StyleURL}"></link>
<title>ContactUs Form Contents</title>
</head>
<body>
The Contact Form Was Submitted With The Following Information:
<br/>
<br/>
<table style="width: 100%" cellspacing="0" cellpadding="0" align="left">
<tr>
<td style="width: 80px"><b>Name:</b></td>
<td><xsl:value-of select="/root/Form/mailname" /></td>
</tr>
<tr>
<td style="width: 80px"><b>Email:</b></td>
<td><xsl:value-of select="/root/Form/mailemail" /></td>
</tr>
<tr>
<td style="width: 80px"><b>Phone:</b></td>
<xsl:choose>
<xsl:when test="/root/Form/mailphone=''">
<td>Not Submitted</td>
</xsl:when>
<xsl:otherwise>
<td><xsl:value-of select="/root/Form/mailphone" /></td>
</xsl:otherwise>
</xsl:choose>
</tr>
<tr>
<td style="width: 80px" colspan="2"> </td>
</tr>
<tr>
<td style="width: 80px"><b>Subject:</b></td>
<td><xsl:value-of select="/root/Form/mailsubject" /></td>
</tr>
<tr>
<td style="width: 80px" colspan="2"> </td>
</tr>
<tr>
<td style="width: 80px" colspan="2"><b>Message:</b></td>
</tr>
<tr>
<td style="width: 80px" colspan="2"> </td>
</tr>
<tr>
<td style="width: 80px" colspan="2">
<xsl:value-of select="/root/Form/mailmessage" />
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
</PackageTransform>
</package>
I wanted to note also that when accessing form fields in an xml package, the field names will always be lowercase no matter what the case is in your aspx page. Since the xml is case sensitive, this is an important note.
I looked at the codebehind for signin.aspx and sendform.aspx to get an idea of how those two pages worked out of the box to help me come up with the code for contactus.aspx so there may be a couple of lines of code borrowed from either.
In the contactus page, I also pre-fill some form elements if you're a already a customer and are logged in. No need to make you retype your email address or name if we already know it. Also, different from sigin and sendform, I wrapped the securitycode stuff an asp table so I can flip a row's visibility off or on based on the AppConfig rather than have to mess with changing properties for multiple labels.
After submitting the form, the user is redirected to the home page. This can be changed to whatever and can even be managed by an AppConfig if you wanted to add that tiny bit of code.
This form addressed all four of my original goals. I see a few posts where people are wanting to add captcha to the contact topic or change the format of the email that the contact topic sends. I've gotten a lot of ideas from reading these forums on a daily basis and I hope that I can give back and help someone else in the future with this code.