Академический Документы
Профессиональный Документы
Культура Документы
In part II of our membership and personalization series, we are going to enhance the
membership database introduced in our first article with a flexible discussion forum
built using SQL Server and ASP. By the end of this article, you will have the
knowledge and scripts necessary to add multiple forums to your web-site.
The Discussion Forum is built using the same methodology as the Site Membership
database. All logic that manipulates the database is contained within stored
procedures and database triggers. The ASP scripts are simply a middle tier for
interfacing the user with the database. The front-end client is a simple, script-free
HTML user-interface.
On the last page you will be able to download all the necessary SQL scripts and
sample files to start adding site membership and discussion forums. These scripts
were run and tested against IIS 4.0 with SQL Server 6.5. If you are familiar with SQL
you should be able to adapt them for other SQL backends.
Interface
Our goal is to provide you with a framework for building your own interactive and
personalized community. For this reason, the sample code merely demonstrates
each feature. We leave the task of clean integration with your system and backend
up to you.
For example, lets look at the loop that generates the list of forums and see how
the URL for viewing a forum is constructed:
This is the simplest integration point. This gets slightly more complex when you
want to interact with the user. For example, let's imagine you are creating a new
message page for a specific discussion forum. First, looking at the stored procedure
for adding messages, sp_starttopic, you will notice that the stored procedure
requires the forum id, a parent id if this is a reply, the user id posting the message, a
message name, and the message itself.
If you were to create a form for starting new topics, you need to make sure all this
information is specified and then passed back to the server. When creating the form,
we use hidden fields for contextual information (forum id, etc) the user does not
need to see. For example:
' BuildForm and BuildInput are helper functions for generating the FORM
and INPUT tags.
Response.Write("<P>Start new topic in " & sForumName)
Response.Write(BuildForm("startTopic","post"))
Response.Write("<TABLE>")
Response.Write("<TR><TD>Forum:</TD><TD>" &_
sForumName & "</TD></TR>")
Response.Write("<TR><TD>User Name:</TD><TD>" &_
Session("u_name") & "</TD></TR>")
Response.Write("<TR><TD>Topic:</TD><TD>" &_
BuildInput("text","topic_name","",20,200) & "</TD></TR>")
Response.Write("<TR><TD valign=top>Message</TD><TD>" &_
BuildTextArea("topic_message","",10,35) & "</TD></TR>")
Response.Write("<TR><TD COLSPAN=2>" &_
BuildInput("submit","action",ACTIONSAVE,"","") & "</TD></TR>")
Response.Write("</TABLE>")
This creates the front-end interface. The next step is to write the interface
between the user's input and the backend database. This is as simple as transferring
the user's arguments into the sp_starttopic stored procedure:
if (request.form("action")=ACTIONSAVE) then
Set oCmd = GetStoredProcedure(getConnection(),"sp_StartTopic")
oCmd.Parameters.append oCmd.CreateParameter("forum_id", adInteger,
adParamInput,10,forumID)
oCmd.Parameters.append oCmd.CreateParameter("topic_parent",
adInteger, adParamInput,10,-1)
oCmd.Parameters.append oCmd.CreateParameter("u_id", adInteger,
adParamInput,10,session("u_id"))
oCmd.Parameters.append oCmd.CreateParameter("topic_name", adVarChar,
adParamInput,200,escapeString(Request.Form("topic_name")))
oCmd.Parameters.append oCmd.CreateParameter("topic_message",
adLongVarChar,
adParamInput,32000,escapeStringWithCR(Request.Form("topic_message")))
oCmd.execute()
end if
By building your server interfaces correctly, you are essentially creating an HTTP
based API for your web-site. With a little more work, you create an interface page
that could allow external agents to manipulate and query your backend database.
With this approach, you would most likely return structured information instead of
traditional web pages. We will be pursuing this concept more in future articles.
Schema
The discussion forum consists of two tables (three if you include the user
membership table):
t_user Table
This is the user membership table from the first article. This table stores information
about each registered user. The most important field in the t_user table is u_id which
represents an individual user. This field is used to associate a user with a particular
message.
t_forums Table
This table contains the list of message forums. Adding new forums is as simple as
adding new records to this table.
t_topics Table
This table contains the list of messages. Each message is associated with a specific
forum through the forum_id field. We designed this table so that it can be extended
to support threaded messages. However, for this example, we limit all discussions to
a single thread (just like the SiteExperts.com discussion forums).
This table also contains two triggers for handling message deletions, message
counts and last updates. We will explain the triggers in more detail when we walk
through the stored procedures.
Data Type
Column Name Special Notes
(length)
forum_id Integer The forum the message is posted in.
Auto-incrementing primary key representing the
topic_id Integer
individual message.
u_id Integer The ID of the user who posted the message.
topic_name String (200) The name of the discussion thread.
topic_create DateTime The time and date the message was posted.
The time and date the thread was responded to. Only
topic_update DateTime
updated for the original message of the thread.
topic_count Integer The number of replies to the message.
The topic_id of the parent message. Defaults to -1 for new
topic_parent Integer
threads.
topic_message Text The actual message.
Create Table dbo.t_topics
(
forum_id int Not Null,
topic_id int Not Null Identity (1, 1),
u_id int Not Null,
topic_name varchar(200) Null,
topic_create datetime Not Null Constraint DF_t_topics_topic_date
Default (getdate()),
topic_update datetime Not Null Constraint DF_t_topics_topic_update
Default (getdate()),
topic_count int Not Null Constraint DF_t_topics_topic_count Default
(0),
topic_parent int Not Null Constraint DF_t_topics_topic_parent Default
((-1)),
topic_message text Null
)
Go
Alter Table dbo.t_topics Add Constraint
FK_t_topics_t_forums Foreign Key
(
forum_id
) References dbo.t_forums
(
forum_id
)
Go
Alter Table dbo.t_topics Add Constraint
FK_t_topics_t_user Foreign Key
(
u_id
) References dbo.t_user
(
u_id
)
Go
Table Relationships
The relationships between each table is fairly straightforward. We reuse the same
field name in all tables to represent the relationship. All our relationships are one-to-
many relationships between the primary and foreign key. This means that each
primary key can be related with any number of records in the related table. For
example, looking at the first relationship below, an individual user can post any
number of messages.
Stored Procedures
The discussion forum uses a collection of stored procedures for posting and editing
messages.
sp_gettopicsforforum
This stored procedure returns two recordsets given a forum id: one
containing the forum information and the other containing the list of topics
for the specified forum. The forum information allows you to access meta-
information (eg., forum name and description). The list of topics is based on
all messages in the forum that have a topic_parent of -1. This defines that
the message is the first message in the topic. All replies have a topic_parent
pointing to the originating message.
sp_getmessagesfortopic
This stored procedure returns two recordsets representing a topic and all its replies.
The first recordset returns the original message with information about the forum the
message is contained within. The second recordset returns all the replies to the
original message. Message replies are easily retrieved by finding messages with the
topic_parent equal to the starting topic's ID.
sp_getforum
This stored procedure is very simple and just returns all the meta-information for a
particular forum.
Message Manipulation
The remaining stored procedures are used to post, edit, and delete messages.
sp_deletemessage
This stored procedure is used to delete an individual message from the message
board. This stored procedure is very simple and only deletes a single message.
If you delete the first message in a topic, we go ahead and delete all replies. If the
message you are deleting is a reply, then we decrement the counter that tracks how
many replies have been posted.
sp_editmessage
This stored procedure is used by the administration screens to allow you to update
an existing message.
sp_starttopic
This stored procedure is used to post new messages to a specific forum. By
specifying a topic_parent, you can use this procedure to reply to a specific message.
return
Whenever a message is posted, we call an update trigger on the t_topics table.
This trigger updates the message counter and last update information:
We are now ready to take you through the ASP Scripts that interact with the
database and expose a simple user-interface.
User Pages
forums.asp Dipslay a list of each discussion forum and a description.
viewForum.asp Display a list of messages within a specific forum.
Displays an individual message with all the replies. This page includes
viewTopic.asp
a form for replying to the thread.
startTopic.asp Displays a form for starting a new topic.
Administration Pages
editForum.asp Allows you to select a forum to edit or to add new forums.
AdminForum.asp Used to edit the messages within an individual forum.
adminTopic.asp Used to edit or delete an individual message or reply.
The user pages integrate with the site membership information introduced in
our first article. If you are not logged in, you can only read the articles. To post or
reply to an article, you must be logged into the system.
The entry page to the discussion forum is forums.asp. This page simply enumerates
and outputs all the discussion forums with their descriptions. From this page, the
user can select an individual forum taking them to the viewForum.asp.
Forums.asp
The script for forums.asp is very simple. Rather than execute a query, we merely
open the t_forums table and output each record. In a production system, you may
want to add ordering information to the table and apply a sort, or depending upon
your layout needs, you may want to create an index page manually.
<%
Dim oRS, oCmd, oConn
Const ACTIONSAVE = "Save..."
Response.Write("<H2>" & HOMEPAGE & " : " & SITENAME & " Discussion
Forums</H2>")
Set oRS = GetTable(oConn,"t_forums")
Response.Write("<UL>")
while not oRS.eof
Response.Write("<LI><A HREF=""viewForum.asp?forum_id=" &
oRS.fields("forum_id") & """>")
Response.write(oRS.fields("forum_name") & "</A>")
if not isnull(oRS.fields("forum_update")) then
' Output date of last update if one exists
Response.Write(" (last updated " & oRS.fields("forum_update") &
")")
end if
Response.Write("<BR>" & oRS.fields("forum_desc"))
oRS.movenext
wend
Response.Write("</UL>")
%>
</BODY>
</HTML>
viewForum.asp
viewForum.asp is a much more interesting script. This page outputs the topics within
a particular forum. To accomodate larger topic lists, this page also supports paging
by displaying 20 topics at a time (this value can be changed).
Below we take you through the commented script.
%>
<HTML>
<HEAD>
<!-- #include virtual="/inc/utility.asp" -->
<TITLE><%=sForumName%></TITLE>
</HEAD>
<BODY>
<%
' Output each topic until no more topics or reach per page record limit
Response.Write("<UL>")
while not oRS.eof and iCount<chunking
' Topic Name
Response.Write("<LI><A HREF=""viewtopic.asp?topic_id=" &
oRS.fields("topic_id") & """>" & oRS.fields("topic_name") & "</A>")
viewTopic.asp
When you view an individual topic, we first display the original message then we
display all the replies. For this page, we did not implement chunking of the replies.
In an active message board you may want to enhance this page with message
chunking. You can easily add chunking by copying the script from the previous page.
dim topicid
if Request.Form("action")=actionSave then
' Save a message reply
' Replies are submitted directly back to the viewing page.
' This reply is automatically returned with the query that populates
the page.
Set oCmd = GetStoredProcedure(getConnection(),"sp_StartTopic")
oCmd.Parameters.append oCmd.CreateParameter("forum_id", adInteger,
adParamInput,10,Request.Form("forum_id"))
oCmd.Parameters.append oCmd.CreateParameter("topic_parent",
adInteger, adParamInput,10,Request.Form("topic_id"))
oCmd.Parameters.append oCmd.CreateParameter("u_id", adInteger,
adParamInput,10,session("u_id"))
oCmd.Parameters.append oCmd.CreateParameter("topic_name", adVarChar,
adParamInput,200,"")
oCmd.Parameters.append oCmd.CreateParameter("topic_message",
adLongVarChar,
adParamInput,32000,escapeStringWithCR(Request.Form("topic_message")))
oCmd.execute()
Response.Write("Message Posted")
end if
%>
<HTML>
<HEAD>
<!-- #include virtual="/inc/utility.asp" -->
<TITLE><%=sForumName%></TITLE>
</HEAD>
<BODY>
<%
startTopic.asp
Compared to the previous page, this page is quite simple. It contains a form for
starting new topics. For simplicity, we filter and automatically escape all HTML. This
can be easily changed by modifying the escape* functions contained within the
utility.asp file. Allowing HTML can create a real challenge to administrating and
managing your discussion forums. We allow HTML on our message boards. However,
our algorithm for checking HTML is very simplistic so we often have to manually fix
messages that unintentionally disrupt the page's layout.
dim forumID
oRS.close()
%>
<HTML>
<HEAD>
<!-- #include virtual="/inc/utility.asp" -->
<TITLE><%=sForumName%></TITLE>
</HEAD>
<BODY>
<%
if Request.Form("action")=actionSave then
' Save new topic
Set oCmd = GetStoredProcedure(getConnection(),"sp_StartTopic")
oCmd.Parameters.append oCmd.CreateParameter("forum_id", adInteger,
adParamInput,10,forumID)
oCmd.Parameters.append oCmd.CreateParameter("topic_parent",
adInteger, adParamInput,10,-1)
oCmd.Parameters.append oCmd.CreateParameter("u_id", adInteger,
adParamInput,10,session("u_id"))
' Escape all HTML
oCmd.Parameters.append oCmd.CreateParameter("topic_name", adVarChar,
adParamInput,200,escapeString(Request.Form("topic_name")))
oCmd.Parameters.append oCmd.CreateParameter("topic_message",
adLongVarChar,
adParamInput,32000,escapeStringWithCR(Request.Form("topic_message")))
oCmd.execute()
Response.Write("<B>Topic: " &
escapeString(Request.Form("topic_name")) & "</B><BR>")
Response.Write(escapeStringwithCR(Request.Form("topic_message")))
Response.Write("<P><A HREF=""viewForum.asp?forum_id=" & forumID &
""">View Messages...</A>")
else
' Output form for posting message
Response.Write("<P>Start new topic in " & sForumName)
Response.Write(BuildForm("startTopic","post"))
Response.Write("<TABLE>")
Response.Write("<TR><TD>Forum:</TD><TD>" & sForumName & "</TD></TR>")
Response.Write("<TR><TD>User Name:</TD><TD>" & Session("u_name") &
"</TD></TR>")
Response.Write("<TR><TD>Topic:</TD><TD>" &
BuildInput("text","topic_name","",20,200) & "</TD></TR>")
Response.Write("<TR><TD valign=top>Message</TD><TD>" &
BuildTextArea("topic_message","",10,35) & "</TD></TR>")
Response.Write("<TR><TD COLSPAN=2>" &
BuildInput("submit","action",ACTIONSAVE,"","") & "</TD></TR>")
Response.Write("</TABLE>")
end if
%>
</BODY>
</HTML>
This defines all the user pages for the discussion forum. Next we take you through
the pages for administrating and managing the message boards.
Administration Pages
Equally important as the user pages are the administration pages. From these pages
you can create new forums and edit or delete existing messages. The pages for
creating forums and choosing a topic are very similar to the pages used to view
forums and topics. Therefore, we are not going to go into those pages details (they
are included in the download). However, we are going to show you the script for the
edit topic page. This page is interesting because it provides one-click in place editing
of each message.
if Request.Form("action")=ACTIONDELETE then
' Delete the message
Set oCmd = GetStoredProcedure(getConnection(),"sp_DeleteMessage")
oCmd.Parameters.append oCmd.CreateParameter("topic_id", adInteger,
adParamInput,200,Request.Form("topic_id"))
oCmd.execute()
end if
if Request.Form("action")=ACTIONSAVE then
' Save Updated Message
Set oCmd = GetStoredProcedure(getConnection(),"sp_EditMessage")
oCmd.Parameters.append oCmd.CreateParameter("topic_id", adInteger,
adParamInput,10,Request.Form("topic_id"))
oCmd.Parameters.append oCmd.CreateParameter("topic_name", adVarChar,
adParamInput,200,escapeString(Request.Form("topic_name")))
oCmd.Parameters.append oCmd.CreateParameter("topic_message",
adLongVarChar,
adParamInput,32000,escapeStringWithCR(Request.Form("topic_message")))
oCmd.execute()
end if
%>
<HTML>
<HEAD>
<!-- #include virtual="/inc/utility.asp" -->
<TITLE><%=sForumName%></TITLE>
</HEAD>
<BODY>
<%
We built this demonstration using SQL Server 6.5 but there is no reason it should
not run with SQL Server 7.0. To make it easy to create the database, you will find a
file called discussion.sql that contains all the SQL commands necessary to create the
tables and stored procedures. You just need to execute this SQL file against a new
SQL Server database. (In SQL Server 6.5, after creating the database, open the SQL
Enterprise Manager, select the database, choose SQL Query Tool from the Tools
menu and open and run the discussion.sql file).
After creating your database you need to create an ODBC DSN (from the ODBC
control panel) so the ASP scripts can connect to the database. After creating the
DSN, you need to update the GetConnection function inside of utility.asp (in the inc
directory) with the correct DSN name, user, and password:
function GetConnection()
const DSN = "membershipdb"
const UID = "webuser"
const PASSWORD = "password"
As with all articles in this series we are merely providing the core pieces for adding
membership and community features. Before introducing this into a production
system you need to evaluate the performance, security and error handling of the
scripts. We permit you to reuse these scripts as a learning device. To reuse this work
in a production system, we ask that you make available to us for possible publishing
any updates or changes you make to the database or interactions with the database.
This includes any extensions that are built over this code.
Adding Site Registration
By Scott Isaacs
This is the first article in a new series demonstrating how to personalization and
community features to your web-site. Each article will contain all the ASP scripts and
SQL queries for a basic implementation. However, as with all database and
transaction-based systems, you should do a careful review to meet an appropriate
level of security, performance and error handling.
In writing the samples we are focusing on simplicity and clarity sometimes at the
expense of robustness. To keep things straightforward, the user-interface used in all
samples is very minimalistic. The web pages merely act as a demonstration on how
to interface with the backend, rather than how to create a rich user experience.
In this article we are going to cover the first step and show you how to create a
membership database. The membership directory supports the following features:
We organized this article into two parts. First we explain the backend database.
The backend database includes the user table and all the stored procedures used to
manipulate the data. Once finished, we explain the ASP scripts which interact with
the backend database and produce the web-pages for the user.
For the SQL guru's, below is the SQL queries that generates the user table.
Create Table dbo.t_user
(
u_id int Not Null Identity (1, 1),
u_name varchar(50) Not Null,
u_password varchar(12) Not Null,
u_firstname varchar(50) Not Null,
u_lastname varchar(50) Not Null,
u_email varchar(50) Not Null,
u_logcount int Not Null Constraint DF_t_user_u_logcount Default (1),
u_createDate datetime Not Null Constraint DF_t_user_u_createDate
Default (getdate()),
u_lastDate datetime Not Null Constraint DF_t_user_u_lastDate Default
(getdate())
)
Go
Alter Table dbo.t_user Add Constraint
PK_t_user_name Primary Key Nonclustered
(
u_id
)
Go
Alter Table dbo.t_user Add Constraint
IX_t_user_name Unique Nonclustered
(
u_name
)
Go
Create Nonclustered Index IX_t_user_email On dbo.t_user
(
u_email
)
Go
Depending upon your needs, this table can also be extended with additional
information. For example, you can have fields for the user's address, phone number,
etc.
Now that this table is created, the next step is to write the necessary scripts that
interact with the user table. Our approach to building a web-based database
application is to encapsulate all the database manipulations and queries into stored
procedures and use ASP scripts to enumerate and process query results. This
separation allows us to build and test our database interactions separate from the
testing the web-site. Next we explain the stored procedures used to add, edit, log-in,
and find users.
Once we are finish exploring the stored procedures, we explain how these
procedures are manipulated from the ASP scripts. For the most part, the ASP scripts
are quite straightforward, but they do demonstrate techniques for processing forms
on the server. However, in a few cases we did include validation logic that could
easily be incorporated into the backend database rather than manipulated through
script.
First, let's look at the stored procedures we created for our membership database:
We start by explaining the two stored procedures for adding and logging in users.
sp_adduser
Adding a user to the database is a very simple process with one simple caveat - you
must first ensure the specified user name is available. We accomplish this with a
simple check against the user table before adding the record. If no record exists for
the specified user-name we add the user. If a user exists, we return a user-id of -1
which signifies the duplicate.
sp_logonuser
This stored procedure is very simple. Given a user name and password, we locate
and return the corresponding record:
Once we are finish exploring the stored procedures, we explain how these
procedures are manipulated from the ASP scripts. For the most part, the ASP scripts
are quite straightforward, but they do demonstrate techniques for processing forms
on the server. However, in a few cases we did include validation logic that could
easily be incorporated into the backend database rather than manipulated through
script.
First, let's look at the stored procedures we created for our membership database:
We start by explaining the two stored procedures for adding and logging in users.
sp_adduser
Adding a user to the database is a very simple process with one simple caveat - you
must first ensure the specified user name is available. We accomplish this with a
simple check against the user table before adding the record. If no record exists for
the specified user-name we add the user. If a user exists, we return a user-id of -1
which signifies the duplicate.
sp_logonuser
This stored procedure is very simple. Given a user name and password, we locate
and return the corresponding record:
Administration Procedures
There are three stored procedures related to site administration. One,
sp_changepassword, is intended for use by your members, and the other two,
sp_getuser and sp_saveuser are intended for use by the site administrator.
sp_changepassword
This simple stored procedure is used to change the user's password:
sp_getuser
This is a very simple and self-explanatory stored procedure that returns the complete
user record for a specified user-id.
sp_saveuser
This stored procedure is used to update all the fields (except the user's id) for any
particular user:
This concludes our introduction to all the stored procedures. Next we walk you
through the ASP scripts used to coordinate and inteface with the backend database.
Below are the commented functions used that interact with the database.
function GetConnection()
' GetConnection
'Used to obtain a connection to the backend database
function BuildInput(sType,sName,sValue,iSize,iLength)
' A helper function for building input boxes.
select case sType
case "text":
BuildInput = "<INPUT TYPE=""text"" NAME=""" & sName & """
VALUE=""" &
sValue & """ SIZE=""" & iSize & """ MAXLENGTH=""" & iLength &
""">"
case "password"
BuildInput = "<INPUT TYPE=""password"" NAME=""" & sName & """
VALUE=""" &
sValue & """ SIZE=""" & iSize & """ MAXLENGTH=""" & iLength &
""">"
case "submit"
BuildInput = "<INPUT TYPE=""submit"" NAME=""" & sName & """
VALUE=""" &
sValue & """>"
end select
end function
function EscapeString(str)
' Used to prevent HTML from being entered and
' and to escape any quotation marks.
function BuildForm(sName,sMethod)
' Returns a simple form tag. Used to ensure tha the appropriate
' submission method is specified
BuildForm = "<FORM NAME=""" & sName & """ METHOD=""" & sMethod &
""">"
end function
This file also includes the constants necessary for interacting with the stored
procedures. These constants are a subset of all the constants available for
manipulating databases and recordsets. We created our own subset to reduce the
overall size of our pages. Rather than list the constants, they are in the download
available at the end of the article. In addition, we defined two additional constants
that help define the site's name and home page:
function OutputHeader()
OutputHeader="<H2>" & HOMEPAGE & "</H2>"
end function
We included this to demonstrate ways to more generically define your web-site. By
defining globally used as constants, you can quickly change aspects of your site. You
will see these constants used to generate our page headers and other items.
First, we track user information using ASP session objects. Therefore, the first test
on the join.asp page is to make sure the user is not already logged on. If they are,
we redirect them to the logon.asp screen (which will also detect the user is logged on
and just display the user's information). Since we are doing a redirect, this test must
be done before any HTML is sent to the client:
We came up with a unique technique for managing form actions on the same
page. We take advantage of the SUBMIT buttons name and value fields. When a
submit button is pressed, it's name and value field are sent to the server. This
makes it easy to distinguish between multiple actions on the same form or even the
same page. For example, you can easily distinguish between an UPDATE and DELETE
operation as follows:
<%
select case Request.Form("action")
case "Update...":
' update
case "Delete...":
' delete
end select
%>
<FORM ACTION=post>
<INPUT TYPE=submit NAME=action VALUE="Update...">
<INPUT TYPE=submit NAME=action VALUE="Delete...">
</FORM>
One problem with the above approach is that you need to remember to update the
script if the submit button's text ever changes. To avoid this, we store the value in a
constant and use this constant throughout the page. With this approach, we can
easily update the button's value without worrying about not updating the script.
Continuing with the join.asp page, you will see we declare a constant
ACTIONJOIN. This constant represents the submit button text. Below we outline the
remainder of the page's logic:
<BODY>
<%
Response.Write(outputHeader())
' The text for the submit button
' Initialize
bJoin = false
sName=""
sFirstName=""
sLastName=""
sEmailName=""
sError=""
if (Request.Form("action")=ACTIONJOIN) then
' Joining
Dim sPassword
sName = Request.Form("u_name")
sFirstName = Request.Form("u_firstname")
sLastName = Request.Form("u_lastname")
sEmailName = Request.Form("u_emailname")
sPassword = Request.Form("u_password")
' In a production system, this validation may be better served inside
' the database itself.
logon.asp
The logon.asp page serves two purposes. To allow existing users to log into the
system and to allow users to manually log themselves off. Since the session object
eventually times out automatically logging the user off, the log out functionality is
provided mostly as a convenience to the user.
Since this page supports two distinct actions, we use the form management
technique presented on the previous page. Below we walk you through the complete
logon.asp script:
<TITLE>Logon <%=SITENAME%></TITLE>
</HEAD>
<BODY>
<%=OutputHeader()%>
<%
' The text for the submit button and to differentiate
' form actions
const LOGON_ACTION = "Logon..."
const LOGOFF_ACTION = "Logoff..."
if Request.Form("action")=LOGOFF_ACTION then
' User chose to log-off.
' Just abandon the session object
Session.Abandon()
bLogon = true
elseif Request.Form("action")=LOGON_ACTION then
' Logon
Dim sPassword, sName
sName = Trim(Request.Form("u_name"))
sPassword = Trim(Request.Form("u_password"))
if sName="" or sPassword="" then
sError = "<BR>You did not enter a name and/ or password."
else
' Try to log the user in
Dim oCmd, oConn, oRS
set oConn = GetConnection()
Set oCmd = GetStoredProcedure(oConn,"sp_logonuser")
oCmd.Parameters.append oCmd.CreateParameter("u_name", adVarChar,
adParamInput,50,sName)
oCmd.Parameters.append oCmd.CreateParameter("u_password",
adVarChar, adParamInput,12,spassword)
set oRS = oCmd.execute()
' If no record returned - login failed
if oRS.eof then
sError = "<BR>Invalid User Name or Password."
else
' Store user information in session object
session("u_id") = oRS.fields("u_id")
session("u_name") = oRS.fields("u_name")
session("u_lastdate") = oRS.fields("u_lastdate")
session("u_logcount") = oRS.fields("u_logcount")
bLogon = false
end if
end if
end if
if bLogon then
Response.Write("<P>Logon " & SITENAME & " or <A
HREF=""join.asp"">Join Now</A>.")
if sError<>"" then
' Report any errors
Response.Write("<P><B>You were not logged in because:</B><FONT
COLOR=darkred>" & sError & "</FONT>")
end if
Response.Write(BuildForm("username","post"))
Response.Write("<TABLE>")
Response.Write("<TR><TD>User Name</TD>")
Response.Write("<TD>" & BuildInput("text","u_name",sName,20,50) &
"</TD></TR>")
Response.Write("<TR><TD>Password</TD>")
Response.Write("<TD>" & BuildInput("password","u_password","",12,12)
& "</TD></TR>")
Response.Write("<TR><TD COLSPAN=2>" &
BuildInput("submit","action",LOGON_ACTION,"","") & "</TD></TR>")
Response.Write("</TABLE>")
Response.Write("</FORM>")
else
' User logged in - output welcome back message
Response.Write("<H2>Welcome Back " & session("u_name") & "</H2>")
Response.Write(BuildForm("","post"))
Response.Write(BuildInput("submit","action",LOGOFF_ACTION,"",""))
Response.Write("</FORM>")
end if
%>
</FORM>
</BODY>
</HTML>
changepassword.asp
All membership related sites should allow the user to update their password.
Creating the change password page is very simple. First, you make sure the user is
logged in, then you ask the user for their old password and a new one. Below is the
complete script for the changepassword.asp page:
sError = ""
Response.write(outputHeader())
User Management
The amount of site management needed is dependent upon your site's purpose and
goals. For this demonstration we create a very simple site management page,
userlist.asp, that allows you to select and edit any user. Since this page gives you
complete access to all users, it should be appropriately secured on any production
system.
<%
Dim oRS, oCmd, oConn
Const ACTIONSAVE = "Save..."
if Request.Form("action")=ACTIONSAVE then
' Update user
Set oCmd = GetStoredProcedure(oConn,"sp_SaveUser")
oCmd.Parameters.append oCmd.CreateParameter("u_id", adInteger,
adParamInput,10,Request.QueryString("u_id"))
oCmd.Parameters.append oCmd.CreateParameter("u_name", adVarChar,
adParamInput,50,Request.Form("u_name"))
oCmd.Parameters.append oCmd.CreateParameter("u_password", adVarChar,
adParamInput,50,Request.Form("u_password"))
oCmd.Parameters.append oCmd.CreateParameter("u_firstname", adVarChar,
adParamInput,50,Request.Form("u_firstname"))
oCmd.Parameters.append oCmd.CreateParameter("u_lastname", adVarChar,
adParamInput,50,Request.Form("u_lastname"))
oCmd.Parameters.append oCmd.CreateParameter("u_email", adVarChar,
adParamInput,50,Request.Form("u_email"))
oCmd.execute()
end if
if Request.QueryString("u_id")<>"" then
' Get user information
Set oCmd = GetStoredProcedure(oConn,"sp_GetUser")
oCmd.Parameters.append oCmd.CreateParameter("u_name", adInteger,
adParamInput,10,Request.QueryString("u_id"))
set oRS = oCmd.execute()
Response.Write("<A HREF=""userlist.asp"">User List</A>")
if oRS.eof then
' Bad user id
Response.Write("User not found")
else
Response.Write(BuildForm("edituser","post"))
Response.Write(BuildInput("hidden","u_id",Request.QueryString("u_id
"),"",""))
Response.Write("<TABLE>")
Response.Write("<TR><TD>User Name</TD>")
Response.Write("<TD>" &
BuildInput("text","u_name",escapeString(oRS.fields("u_name")),20,50) &
"</TD></TR>")
Response.Write("<TR><TD>Password</TD>")
Response.Write("<TD>" &
BuildInput("text","u_password",oRS.fields("u_password"),12,12) &
"</TD></TR>")
Response.Write("<TR><TD>First Name</TD>")
Response.Write("<TD>" &
BuildInput("text","u_firstname",escapeString(oRS.fields("u_firstname"))
,20,50) & "</TD></TR>")
Response.Write("<TR><TD>Last Name</TD>")
Response.Write("<TD>" &
BuildInput("text","u_lastname",escapeString(oRS.fields("u_lastname")),2
0,50) & "</TD></TR>")
Response.Write("<TR><TD>Email Name</TD>")
Response.Write("<TD>" &
BuildInput("text","u_email",escapeString(oRS.fields("u_email")),20,50)
& "</TD></TR>")
Response.Write("<TR><TD COLSPAN=2>"
Response.Write(BuildInput("submit","action",ACTIONSAVE,"","") &
"</TD></TR>")
Response.Write("</TABLE>")
end if
else
' Output all users.
Set oRS = GetTable(oConn,"t_user")
Response.Write("<UL>")
while not oRS.eof
Response.Write("<LI><A HREF=""?u_id=" & oRS.fields("u_id") & """>"
& oRS.fields("u_name"))
oRS.movenext
wend
Response.Write("</UL>")
end if
%>
</BODY>
</HTML>
This page is interesting in that it provides three pieces of functionality. In its
default form (userlist.asp) it displays a list of all the site's users. When called with a
user id (userlist.asp?u_id=23) it returns a form for editing the user. If called with a
user id plus the appropriate form submission, the user's data will be updated and the
form will be redisplayed. All of this is handled by the large if-then blocks:
if Request.Form("action")=ACTIONSAVE then
' Save user information
end if
if Request.QueryString("u_id")<>"" then
' Output form
else
' Output list of users
end if
Next we wrap up our introduction to adding site membership and explain how
to download and install the demonstration
We built this demonstration using SQL Server 6.5 but there is no reason it should
not run with SQL Server 7.0. To make it easy to create the database, you will find a
file called membership.sql that contains all the SQL commands necessary to create
the tables and stored procedures. You just need to execute this SQL file against a
new SQL Server database. (In SQL Server 6.5, after creating the database, open the
SQL Enterprise Manager, select the database, choose SQL Query Tool from the Tools
menu and open and run the membership.sql file).
As we said in the beginning of this article, by itself this code is not intended to
serve as the foundation for a user membership system. Before introducing this into a
production system you need to evaluate the performance, security and error
handling of the scripts. We permit you to reuse these scripts as a learning device. To
reuse this work in a production system, we ask that you make available to us for
possible publishing any updates or changes you make to the database or interactions
with the database. This includes any extensions built into the system (eg., adding
message boards, etc).
THE END
Thanks