Welcome to the second part of my series on how to create a simple mailing list in ColdFusion. Be sure to read the first entry in the series before starting this one. As previously mentioned, the goal of this application is to create a simple way for users to sign up at your web site. An administrator can then use a tool to send an email to folks who have signed up. Today's entry will deal with the administrator a bit. Now I'm going to cheat a bit. I don't want to spend a lot of time on security and all that, so I'm going to write a script and place it in the same folder as my other files. Obviously in a real world application this file would be placed in a protected folder. The specific item to add to our application today is a simple interface to list the subscribers and add/delete folks. Later in the series I'll discuss how folks can delete themselves, but the honest truth is that even if you provide such a method, folks will still email you (or call you) and demand that you remove them. So lets work on a tool that will make that simple.
The following script will handle listing subscribers, removing subscribers, as well as adding them:2 <cfset application.maillist.unsubscribe(url.delete)>
3 </cfif>
4
5 <cfif structKeyExists(form, "add") and len(trim(form.email)) and isValid("email", form.email)>
6 <cfset application.maillist.subscribe(form.email)>
7 </cfif>
8
9 <cfset members = application.maillist.getMembers()>
10
11 <cfoutput>
12 <p>
13 Your mail list has
14 <cfif members.recordCount is 0>
15 no members
16 <cfelseif members.recordCount is 1>
17 1 member
18 <cfelse>
19 #members.recordCount# members
20 </cfif>. You may use the table below to remove any member, or the form to add a new member.
21 </p>
22 </cfoutput>
23
24 <cfif members.recordCount gte 1>
25
26 <p>
27 <table border="1">
28 <tr>
29 <th>Email Address</th>
30 <td> </td>
31 </tr>
32 <cfloop query="members">
33 <tr <cfif currentRow mod 2>bgcolor="yellow"</cfif>>
34 <cfoutput>
35 <td>#email#</td>
36 <td><a href="listmembers.cfm?delete=#token#">Delete</a></td>
37 </cfoutput>
38 </tr>
39 </cfloop>
40 </table>
41 </p>
42
43 </cfif>
44
45 <form action="listmembers.cfm" method="post">
46 <input type="text" name="email"> <input type="submit" name="add" value="Add Subscriber">
47 </form>
2
3 <cffunction name="init" returnType="maillist" output="false" access="public">
4 <cfargument name="dsn" type="string" required="true">
5
6 <cfset variables.dsn = arguments.dsn>
7
8 <cfreturn this>
9 </cffunction>
10
11 <cffunction name="getMembers" returnType="query" output="false" access="public"
12 hint="Returns a query of everyone subscribed.">
13 <cfset var q = "">
14
15 <cfquery name="q" datasource="#variables.dsn#">
16 select email, token
17 from subscribers
18 order by email asc
19 </cfquery>
20
21 <cfreturn q>
22 </cffunction>
23
24 <cffunction name="subscribe" returnType="boolean" output="false" access="public"
25 hint="Adds a user to the mailinst list, if and only if the person wasn't already on the list.">
26 <cfargument name="email" type="string" required="true">
27 <cfset var checkIt = "">
28
29 <cfif not isValid("email", arguments.email)>
30 <cfthrow message="#arguments.email# is not a valid email address.">
31 </cfif>
32
33 <!--- only add if the user doesn't already exist. --->
34 <cflock name="maillist" type="exclusive" timeout="30">
35 <cfquery name="checkIt" datasource="#variables.dsn#">
36 select email
37 from subscribers
38 where email = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.email#">
39 </cfquery>
40
41 <cfif checkIt.recordCount is 0>
42
43 <cfquery datasource="#variables.dsn#">
44 insert into subscribers(email,token)
45 values(<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.email#">,<cfqueryparam cfsqltype="cf_sql_varchar" value="#createUUID()#">)
46 </cfquery>
47
48 <cfreturn true>
49
50 <cfelse>
51
52 <cfreturn false>
53
54 </cfif>
55
56 </cflock>
57
58 </cffunction>
59
60 <cffunction name="unsubscribe" returnType="void" output="false" access="public"
61 hint="Removes a user to the mailinst list.">
62 <cfargument name="token" type="uuid" required="true">
63
64 <cfquery datasource="#variables.dsn#">
65 delete from subscribers
66 where token = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.token#">
67 </cfquery>
68 </cffunction>
69
70 </cfcomponent>
Comment 1 written by Sean Coyne on 23 May 2006, at 8:53 AM
The download link to the zip isn't showing.
Comment 2 written by Raymond Camden on 23 May 2006, at 8:57 AM
Comment 3 written by ulirc on 13 March 2009, at 10:43 AM
Anyway all of your tutorial makes sense and works at my end except for when the subscriber them self (i.e not via the mailing list admin page/s) tries to unsubscribe
using your template TEMPUnsubscribe.cfm included in your archive file causes the problem. You have not included a way for the user to pass the token accros in teh URL query string.
If the user enters his email address blah@blah.net
the result sent to unsubscribe.cfm will be:
unsubscribe.cfm?emailaddress=ulric@uk2.net&unsubscribe=Unsubscribe
and this will cause the database deletion to fail and we get the result "Sorry, but you were not unsubscribed. Please ensure that you have copied the URL correctly from your mail client.".
Would it be possible for you to update the unsubscribe pages so that token is included somehow. I cant figure it out :(
many thanks
Comment 4 written by ulric on 13 March 2009, at 3:06 PM
Major apologies!
Ulric
Comment 5 written by Raymond Camden on 13 March 2009, at 3:14 PM
Comment 6 written by Sean Budlong on 12 February 2010, at 7:16 PM
One thing I noticed is the behavior of the app if someone is already subscribed. If the email address already exists, the visitor gets the same message about thank you for registering. Is it possible to tell the visitor that he/she is already registered?
Comment 7 written by Sean Budlong on 12 February 2010, at 7:33 PM
Comment 4 written by ulric on 13 March 2009, at 3:06 PM
Sorry Ray! I did not figure out all i needed was unsubscribe.cfm?token=%token% for user self service unsubscribe to work, and that I should ignore template TEMPunsubscribe.cfm.
Major apologies!
Comment 8 written by Raymond Camden on 12 February 2010, at 8:11 PM
Comment 9 written by Sean Budlong on 13 February 2010, at 6:58 AM
Comment 10 written by Raymond Camden on 13 February 2010, at 8:48 AM
Comment 11 written by Sean Budlong on 14 February 2010, at 12:51 PM
<cfquery datasource="maillist">
UPDATE subscribers
SET token = <cfqueryparam cfsqltype="cf_sql_varchar" value="#createUUID()#">
WHERE token IS NULL
</cfquery>
Comment 12 written by Sean Budlong on 14 February 2010, at 1:03 PM
<cfquery datasource="maillist">
UPDATE subscribers
SET token = <cfqueryparam cfsqltype="cf_sql_varchar" value="#createUUID()#">
WHERE id > 14
</cfquery>
and it did create UUIDs, but they were all the same. If this is at all possible, it probably involves some kind of LOOP, doesn't it?
Comment 13 written by Raymond Camden on 15 February 2010, at 7:43 AM
Comment 14 written by Sean Budlong on 15 February 2010, at 1:26 PM
[Add Comment] [Subscribe to Comments]