This question came in to me today, and as a former library worker (well, I was a shelver in high school - one of the best jobs I ever had), I wanted to give her a hand and thought I'd share the question/solution.
Susan wrote:
Programming challenged librarian needs a contact form that includes radio buttons for different departments. Depending on which radio button is selected, the completed form is sent to a different email address.(ex: I want to ask about my overdues (email to overdues@mylibrary... I want to ask about children's programs (email to kids@mylibrary)...)
I can get a basic contact/feedback form to work for one email but I'm clueless on how to have it select different options. If you have any suggestions/directions for me I would really appreciate it. THANKS!
So a basic contact form will ask for your name, email address, and comments. It will then verify the form fields, and if all is well, mail the results:
<cfmail to="admin@mysite.com" from="#form.email#" subject="Site Comments" wrap="80">
#form.comments#
</cfmail>
So to properly route the email, our librarian had added radio controls to her form. Most likely something like this:
Your question concerns...
<input type="radio" name="department" value="overdue">Overdue Fines
<input type="radio" name="department" value="kids">Children Programs
<input type="radio" name="department" value="dhamra">Life Extension
First - you need to remember that a radio field will not exist in the form scope if the user doesn't select anything. You need to use isDefined or structKeyExists to ensure the user picked something. If you have, then routing the email is easy. Here is one possible way to handle it:
<cfif form.department is "overdue">
<cfset to="overdue@mysite.com">
<cfelseif form.department is "children">
<cfset to="children@mysite.com">
<cfelseif form.department is "dharma">
<cfset to="dharmainiative@mysite.com">
<cfelse>
<cfset to="root@mysite.com">
</cfif>
I check for the possible values of the form field (and note I have a last ditch check in case the form value is not recognized). I set a value to a variable named to. Once I have it, I can change my cfmail tag rather easily:
<cfmail to="#to#" from="#form.email#" subject="Site Comments" wrap="80">
#form.comments#
</cfmail>
You could also make the subject dynamic as well. Anyway - I hope this is helpful.
Archived Comments
You could always use a:
[code]
<input type="hidden" name="department_required" value="You must choose a department for your comment/questions">
[/code]
to insure that they chose one
Ugh no. Don't use CF's built in form checker.
I'd simplify by putting the email addresses in the values for the radio buttons and use CFPARAM as a catch-all on the action page.
Or, if not, I'd definitely use a CFSWITCH.
Or, perhaps, #form.department#@mysite.com
That poor cat.
You absolutely don't want to make the radio buttons include the TO addresses. If so - it could be used to spam people.
Now _maybe_ the front part of the @, but even with that a spammer could use it to spam folks at mysite.com.
Using a CFSWITCH is something I considered - but as this was a beginner user, I wanted ot keep it simple.
Sorry, Ray, but I was looking at it from the "novice programmer" perspective... It would be quick, simple, and it's really nicely documented in the "Using Coldfusion" docs in DW8. Most of the beginners I know don't understand using isDefined or structKeyExists. I have to spend at least an hour explaining the uses of those to "beginning" programmers (as well as many other "advanced" features such as structures and arrays, etc)... I realy wonder about people who come on board as "programmers" but that can't tell me what a 2-dimensional array is. I guess I have gottent to used to dealing with REALLY low-end programmers.
I do agree with you that CF's built-in form validation leaves much to be desired (especially in an environment where you are hosting multiple domains that are NOT in a J2EE environment). I, also, like that fact that you were being spam-conscious by keeping the e-mail address processing on the server side of the house.
An additional reason for not having the email address as part of the form that is submitted. Is someone can use your form for malicious spam themselves. By spoofing the to address.
Hi Ray,
I really like your blog, I find it nice and easy to read.
I was curious as to why you used:
form.department is "overdue"
rather than something like:
form.department eq "overdue"
in your code? Are there any benefits to using "is". I have never used the "is" comparison operator before - actually havent come across - so was wondering on your opinion of it and pros/cons?
Just personal preference. See:
http://www.houseoffusion.co...
I find the shortest route is sometimes the best. Why not just make the value of the radio button the email address?
<input type="radio" name="department" value="overdue@library.com">
<input type="radio" name="kids" value="overdue@library.com">
<cfmail to="#form.department#" from="#form.email#" subject="Site Comments">
#form.comments#
</cfmail>
Typo
the second radio value should be value="kids@library.com".
Dan - see the earlier comments. If you do this, someone can use this as a way to spamothers. I'd build my own form, set my own TO, and POST to your box.
I know that your answer was posted for novice readers, so why not take the time to introduce them to the CFSWITCH/CFCASE tags?
<cfswitch expression="#form.department#">
<cfcase value="overdue">
<cfset to="overdue@mysite.com">
</cfcase>
<cfcase value="children">
<cfset to="kids@mysite.com">
</cfcase>
<cfcase value="dharma">
<cfset to="darmainitiative@mysite.com">
</cfcase>
<cfdefaultcase>
<cfset to="root@mysite.com">
</cfdefaultcase>
</cfswitch>
I actually ran into a similar situation just recently when working on a newsletter system. The difference was that depending on the newsletter signup, it had to go to multiple different people.
I handled it this way - not exactly a "novice" oriented approach, but certainly it worked great for me.
<cfset objEmails = StructNew() />
<cfif (FORM.newsletter EQ "foo")>
...<cfset objEmails[ "ben@test.com" ] = true />
...<cfset objEmails[ "ben2@test.com" ] = true />
<cfelseif (FORM.newsletter EQ "bar")>
...<cfset objEmails[ "ben@test.com" ] = true />
...<cfset objEmails[ "suzzie@test.com" ] = true />
...<cfset objEmails[ "larry@test.com" ] = true />
<cfelse>
...<cfset objEmails[ "ben@test.com" ] = true />
</cfif>
<cfmail to="#StructKeyList( objEmails )#" ....></cfmail>
What I liked about this was you don't have to maintain a "list" of emails as the Struct does this for you. Also, I found the code to be quite readable and easy to maintain.
Anyway, this worked great for me.
and don't forget CFFormProtect!
http://cfformprotect.riafor...
I wouldn't suggest the "FROM" field being the #form.email#, since most advanced emailservers block email if it's not from the correct IP-address. Instead use the FROM address being a "website@library.com" address and use the #form.email# in the REPLYTO="" field. That way if receivers press REPLY they will send to the #form.email# address still. It's just good practice.
Say for instance I would enter bill@gates.com as my emailaddress in the form, and the mailserver that handles the email for the CF-server has DNS-lookup to prevent spam, it will refuse to accept this mail (since GATES.COM doesn't resolve to the local CF-server) and it will end up in the undeliverable folder of CFMAIL. You could setup a trusted-ip for the CF-server in the mailserver-configuration, but not all builders have control over this configuration and it's not manageble across servers.
the other advantage of using a hardcoded from address is that if the user enters something not technically valid (e.g. 'n/a'), there's a good chance your mailserver won't deliver it.
@Wizard: I disagree. Using the from as the user's email address makes it easier to reply to the person. Plus, the mails ervers I've dealt with have had no problem with this since CF was using an authenticated connection.
@Duncan: That's what isValid("email") is for. :)
@Raymond: true, but we are building portable applications that run on any system or could be easily ported. Why bother remembering that if you port your CFM page you have to setup a mailserver to trust your server.
I have several CFC which practice this policy, and they are portable amongst clients and addresses. Some of our clients tend to setup email rules to deliver message from "website@....com" to a specific website folder inside Outlook.
I don't mean the replyto should be strict policy, it's just better practice in the long run.
I'm all for portability, but I don't agree with this. As I said - every mail server I've dealt with lets you set any FROM as long as you local to the server itself. I've never run into a problem with this. We shall have to agree to disagree.
Raymond, not sure if i trust IsValid() for this.
http://www.bennadel.com/ind...
The regex seems overly simple:
^[a-zA-Z0-9-'\+~]+(\.[a-zA-Z0-9-'\+~]+)*@([a-zA-Z_0-9-]+\.)+[a-zA-Z]{2,7}$
to be RFC2822 compliant, it should be more like:
^[-!#$%&'*+/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+/0-9=?A-Z^_a-z{|}~])*@[a-zA-Z](-?[a-zA-Z0-9])*(\.[a-zA-Z](-?[a-zA-Z0-9])*)+$
to simply do nothing but expound on the email regex... this is by far, my favorite one...
(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})
Noob question here in reply to Ray's response to the first comment:
"Ugh no. Don't use CF's built in form checker."
Why not? Does this apply to both validateAt="onServer" and validateAt="onSubmit"??
So you're saying the best way to check forms is on the server side using cfparam an isDefined?
Thanks.
Because you don't get the fine control over handling the errors.
If a user wants to send email to many people, do you think it will work?
Many as in a whole heck of a lot? I'd probably use a dedicated solution for mail lists.