<% ASP на блюдечке %>. Часть 8
Форум своими силами
Единственная роскошь на свете — это роскошь человеческого общения.
Антуан де Сент-Экзюпери
Создание и подготовка базы данных
Универсальный набор функций (файл utils.asp)
Главная страничка — выбор форума (файл index.asp)
Просмотр списка сообщений форума — (файл list.asp)
Показ текущего сообщения (файл Show.asp)
Отправка сообщения (файл Post.asp)
Введение
Во всех предыдущих частях “<% ASP на блюдечке %>” мы еще не касались форума (конференции) — неотъемлемого компонента современного Интернета. Освоив построение таких непростых механизмов, как электронный магазин (часть 4 — “Виртуальный магазин”, часть 5 — “Виртуальный магазин (продолжение)”, часть 7 — “Классический Интернет-магазин”), перейти к проектированию форум системы будет несложно.
Настоящая публикация призвана пролить свет на эту отрасль Интернет-строительства — на форумостроительство.
Концепция
Для начала попытаемся сформулировать концепцию простейшего форума с точки зрения пользователя, что позволит нам в дальнейшем определить задачу его построения для программиста. Зададимся вопросом: “Что же такое форум?” Некоторые читатели могут сразу же ответить: “Это же всем известно!” Однако советую не спешить с утверждениями такого рода, а попытаться упорядочить свои представления о форуме. Надеюсь, что данный раздел поможет вам в этом.
Итак, форум — это некая область экрана (окна браузера), где различные его участники (будем называть их пользователями форум-системы, или просто пользователями) могут излагать свои мысли на заданную тему (под мыслями в настоящей статье имеются в виду текстовые сообщения). Просматривая сообщения, каждый пользователь волен написать ответ на чье-либо уже написанное сообщение, которое, в свою очередь, может являться ответом на сообщение, и т.д.
Обобщая, можно сказать, что форум представляет собой двухмерный поток текстовых сообщений. Под вторым измерением следует понимать сообщения-ответы, ответы на ответы и т.д.
Не правда ли, нам с вами стала гораздо понятнее и доступнее концепция форума? Теперь можно переходить к рассмотрению некоторых отличительных особенностей форумов. Конечно, это не догма, поскольку не каждый форум обладает всеми нижеперечисленными особенностями, но все эти особенности, как правило, присутствуют в современных форум-системах.
Во-первых, это система идентификации пользователей, призванная регистрировать имя (псевдоним, nickname), пароль, электронный адрес и пр. пользователя в целях обеспечения его уникальности в форум-системе.
Во-вторых, это, как правило, система управления форумом, позволяющая осуществлять контроль за потоком текстовых сообщений. В современных системах эта функция возложена на так называемых модераторов, или форум-менеджеров.
Вкратце список функций контроля и управления форумами можно представить следующим образом:
- Контроль тематики — модератор следит за соблюдением определенной тематики и удаляет (предупреждает конкретного пользователя о нарушении им заданной тематики) ненужные сообщения. Согласитесь, что такие сообщения досадно отвлекают от предмета обсуждения (впрочем, существуют и форумы с весьма неопределенной тематической ориентацией).
- Контроль ненормативной лексики — фильтруются бранные слова и выражения.
- “Поддержка” форума — умышленная посылка модератором множества сообщений от имени разных пользователей на интригующие читателей форума темы. Хотя такое порой и случается, все же не следует воспринимать форум как место, где можно всегда пообщаться лишь с модератором или с системным администратором.
- Организация общего управления форумом — порождение или удаление форумов заданной тематики, контроль, назначение и управление работой модераторов. Выполняется администратором форума.
Данный список можно продолжать, но, на мой взгляд, это то, без чего не обходится ни одна форум-система.
Во-третьих, это система безопасности, обеспечивающая доступ модераторов и администраторов форума к функциям, недоступным для других пользователей.
В-четвертых, это система поиска сообщений и/или их авторов — как по тематике, так и по содержимому сообщения.
Совокупность этих трех компонентов и является, по сути дела, самым обычным и зачастую самым распространенным Интернет-форумом. Конечно, существуют варианты с выбором картинки-смайлика и цвета текстового сообщения, но сути дела это не меняет.
Постановка задачи
Теперь, когда мы уяснили, что представляет собой форум-система с точки зрения пользователя, можно переходить к постановке и формализации программной задачи, для чего необходимо разработать:
- Базу данных с таблицами сообщений, форумов, пользователей, модераторов и администраторов.
- Модули идентификации пользователей.
- Поисковый модуль, осуществляющий поиск сообщений.
- Систему контроля за потоком текстовых сообщений, позволяющую модератору удалять или редактировать нужные сообщения.
Что нам понадобится
Предполагается, что читатель знаком с основами ASP- и SQL-программирования (первых частей настоящей статьи для этого будет вполне достаточно). Кроме того, вам потребуется Microsoft Access 97 или 2000, какой-нибудь HTML- или текстовый редактор (рекомендую использовать Macromedia Dreamweaver UltraDev 4.0) и немного терпения.
Создание и подготовка базы данных
Создадим три таблицы: таблицу пользователей (ForumUsers), таблицу форумов (ForumsTable) и таблицу текстовых сообщений (MessagesTable). Представим себе их взаимосвязь так, как показано ра рис. 1.
Ключевыми здесь являются столбцы thread_parent и thread_id. Их взаимосвязь, по сути, отражает взаимосвязь сообщений и ответов на сообщения. Так, поле thread_parent указывает на принадлежность данного сообщения к определенному “родителю”, который, в свою очередь, тоже может принадлежать определенному “предку”, и т.д.
Теперь, когда база данных готова, можно переходить к созданию самого форума.
Универсальный набор функций (файл utils.asp)
Прежде чем приступить непосредственно к разработке форума, вынесем все типовые функции, которые нам понадобятся многократно, в один файл, который будем впоследствии по мере необходимости “включать” в разрабатываемые странички. Разберемся, что нам для этого понадобится.
Прежде всего заголовок и “концевик” HTML-файлов наших ASP-страниц (их придется создавать достаточно часто); затем — функции соединения и закрытия соединения с базой данных; далее — функция, упорядочивающая дочерние сообщения (сообщения-ответы на сообщения); функция посылки уведомления пользователям по электронной почте; функция выбора последнего введенного значения:
<%
…
'***Формируем заголовок (шапку) HTML-файлов***Sub Header()
Response.Write "<HTML><HEAD><link rel='stylesheet' href='style.css'>”
Response.Write ”<title>ASP — Форум своими силами</title>"
Response.Write "<TITLE>ASP — Форум своими силами — Главная страница</TITLE>"
Response.Write "<meta http-equiv='Content-Type' content='text/html; "
Response.Write "charset=windows-1251'></HEAD><body>"
Response.Write "<table border=0 width=600 cellpadding=0 cellspacing=0>"
end sub
'***Формируем ссылку на компонент администрирования форума
Sub Admin()
Response.Write "<br>Для <b>администрирования системы</b> жми <A HREF='Administration/index.asp'>сюда</a>"
End Sub
Response.Buffer = true
'**Строим строку соединения с базой данных
dim dsn ,conn, rs, sql
dsn = "DBQ=" & Server.Mappath("Data/Forum.mdb") & ";Driver={Microsoft Access Driver (*.mdb)};"
'**Подсоединяемся к базе данных форума
Sub Connect()
set conn = server.createobject("adodb.connection")
set rs = server.createobject("adodb.recordset")
conn.open dsn
End Sub
'**Закрываем соединение с базой данных форума
Sub Close()
rs.close
set rs = nothing
conn.close
set conn = nothing
End Sub
'***Формируем нижнюю шапку HTML-файлов
Sub Footer()
Response.Write "<table><tr><td><br>"
Response.Write "<img src='images/line.gif' width=600 height=1><br><br>"
Response.Write "</table></body></html>"
End Sub
'***Сортируем дочерние сообщения
Sub displayMessages (layer,id)
dim sql_order, rs_order, image, str, MessageSpacing, mesid, spaceing, tid
'Создаем экземпляр объекта набора данных ( recordset )
set rs_order=server.createObject("adodb.recordset")
'Ищем дочерние сообщения
sql_order = "SELECT * FROM MessagesTable WHERE thread_parent = " & id & "and thread_parent <> 0"
rs_order.open sql_order,conn
Do until rs_order.eof
mesid = rs_order("id")
name = rs_order("name")
email = rs_order("email")
subject = rs_order("subject")
mesdate = rs_order("mesdate")
fid = rs_order("forumID")
tid = rs_order("thread_id")
image = "<img src='images/arr1.gif' border=0>"
str = ""
spaceing = ""
'Формируем отступ сообщения в зависимости от уровня его дочерности
For MessageSpacing = 1 To layer
spaceing = spaceing & " "
Next
str = "<tr><td>"
str = str & spaceing & image & "<a href='show.asp?id=" & mesid &_
"&fid="&fid&"&tid="&tid&"'>" & subject & "</a>" & "...." & mesdate
str = str & "...." & name
str = str & "</tr></td>"
str=str & "</b>"
Response.Write str
'Рекурсивный вызов для обнаружения дочерних сообщений пока rs_order <> nothing
call displayMessages (layer + 1, mesid)
rs_order.movenext
Loop
'Закрытие объекта
rs_order.close
set rs_order = nothing
End Sub
'***Посылка уведомления об ответах на сообщение по электронной почте
Sub sendAllFamily (tid,maxid,fid)
dim body, email, link, rs_send
set rs_send = server.createObject("adodb.recordset")
sql = "select email,id,thread_id from MessagesTable"
sql = sql & " where (thread_id="& tid & " or id="& tid & ") and id<>" & maxid &""
sql = sql & "and forumid="&fid&" and isChecked=1"
rs_send.open sql,conn
Do While Not rs_send.eof
Link = "http://localhost/show.asp?fid=" & fid & "&id=" & maxid & "&tid=" & tid
Email = rs_send("email")
Response.Write Email
Body = "Поступило новое сообщение на форум"
Body = Body & "Адрес поступившего сообщения: "
Body = Body & link
Dim objCDO
Set objCDO = Server.CreateObject("CDONTS.NewMail")
objCDO.To = email
objCDO.From = "rouben@iname.com"
objCDO.Subject = "Уведомление о поступлении сообщения!"
objCDO.Body = body
objCDO.Send
rs_send.MoveNext
Loop
rs_send.close
set rs_send = nothing
End Sub
'***Получаем ID самой последней записи из таблицы сообщений
Sub maxRec()
set rs_maxid = server.createobject("adodb.recordset")
sql_maxid = "select max(id) as maxid from MessagesTable"
Set rs_maxid = Conn.Execute(sql_maxid)
maxid = rs_maxid("maxid")'new id
rs_maxid.close
set rs_maxid = nothing
End Sub
…
%>
Главная страничка — выбор форума (файл index.asp)
Для начала создадим страничку, где пользователь сможет выбрать форум по своему усмотрению. Данная страница должна содержать ссылки на все форумы (на страничку, представляющую содержание форума, которой в качестве параметра передается идентификатор выбранного пользователем форума):
<!--#include file="utils.asp"-->
<%
Call Header()
response.write "<tr><td><b>Выберите тему обсуждения:</b><br><br></tr></td></table>"
sql = "select forumid, forumname from ForumsTable order by forumid"
Call Connect()
rs.open sql,conn
If rs.eof and rs.bof then ' Если в форуме нет сообщений
Response.Write "Нет Сообщений"
Else ' В противном случае показ всех сообщений
Dim Id, Name
Do While Not rs.eof
Id = rs("forumid")
Name = rs("forumname")
Response.write "<tr><td>"
Response.write "<a href='list.asp?fid=" & id & "'>" & name & "</a><br>"
Response.write "</tr></td>"
rs.MoveNext
Loop
End if
call Footer()
call Admin()
call Close()
%>
Просмотр списка сообщений форума — (файл list.asp)
Теперь нам необходима страничка просмотра списка сообщений, которая из таблицы всех сообщений извлекает сообщения выбранного пользователем форума и организующая показ сообщений иерархически, сдвигая сообщения-ответы вправо, сообщения-ответы на ответы — еще правее и т.д.:
<!--#include file="utils.asp"-->
<SCRIPT LANGUAGE = VBScript RUNAT=Server>
<!-- Metadata type="typelib" File="c:\program files\common files\system\ado\msado15.dll" —->
</SCRIPT>
<%
Response.CacheControl = "no-cache"
Response.AddHeader "Pragma", "no-cache"
Response.Expires = -1
dim fid,tp,tid
dim rs_fn
dim ParentID,name,email,subject,mes,mesdate,image
Dim iPageSize 'Размер страницы
Dim iPageCount 'На сколько страниц может быть осуществлен возврат
Dim iPageCurrent 'Идентификатор текущей страницы
dim iRecordsShown,i
'Количество сообщений на одной странице
iPageSize = 30
If request("page")="" then
iPageCurrent = 1
Else
iPageCurrent = CInt(Request.QueryString("page"))
End If
Function GetForumName(id)
set rs_fn = server.createObject("adodb.recordset")
sql = "select forumid,forumname from ForumsTable where forumid="&id
rs_fn.open sql,conn
response.write "<tr><td width=300><b>" & rs_fn("forumname") & "<br><br></b></TR></TD>"
rs_fn.close
set rs_fn=nothing
End Function
'Извлекаем ID форума из строки посредством QueryString
fid = request("fid")
image = "<img src='images/arr.gif' border=0>"
Call Header()
sql = "select id,thread_id,name,email,subject,mesdate,forumID,thread_parent from MessagesTable WHERE (forumid = " & fid & ") and thread_parent=0 order by id desc"
Call Connect()
Rs.Open sql, conn, adOpenStatic, adLockReadOnly, adCmdText
RS.PageSize = iPageSize
RS.CacheSize = iPageSize
iPageCount = RS.PageCount
iRecordsShown = 0
If iPageCount = 0 Then ' Если в форуме нет сообщений
Response.Write "<tr><td>Нет Сообщений</tr</td>"
Else ' В противном случае показ всех сообщений
RS.AbsolutePage = iPageCurrent
Response.write getForumName(fid)
Do While iRecordsShown < iPageSize And Not RS.EOF
tid=rs("thread_id")
ParentID=rs("id")
name=rs("name")
email=rs("email")
subject=rs("subject")
mesdate=rs("mesdate")
response.write "<tr><td>"
response.write image & "<a href='show.asp?id=" & parentID & "&fid="&fid&"&tid="&tid&"'>" & subject &
"</a>" & "...." & mesdate & "...." & name & "</a>"
response.write "</tr></td>"
'Показ всех дочерних сообщений
Call displayMessages(1,ParentID)
rs.MoveNext
iRecordsShown = iRecordsShown+1
Loop
End If
Call Close()
Response.Write "<tr><td><br>"
If iPageCount > 1 Then
Response.Write "Страницы"
For i = 1 To ipagecount
Response.Write " ... <a href='list.asp?fid="&fid&"&page="&i&"'>" & i & "</a>"
Next
Response.Write "<br>Page <B>" & iPageCurrent & "</B> of <B> "& iPageCount & "</B>"
End If
Response.Write "<tr><td><br>"
Response.Write "<a href='post.asp?fid=" & fid & "'>Новое сообщение</a> | <a href='index.asp'>Назад</a> | <a href='search.asp?fid="&fid&"'>Поиск сообщений</a>"
Response.Write "</table><br>"
Call Footer()
%>
Как видите, все просто. Результатом формирования странички списка по существу будет являться набор ссылок на странички просмотра отдельных сообщений (на страничку show.asp, которой в качестве параметра передается строка с идентификатором сообщения, и уровнем вложенности: show.asp?id=" & parentID & "&fid="& fid &"&tid=" & tid"
Самое главное в данной страничке — вывод дочерних сообщений. Осуществляется это посредством вызова функции displayMessages.
Указанная функция производит вывод всех дочерних сообщений, причем в качестве параметров этой функции передается уровень, с которого необходимо начать вывод дочерний сообщений, а также идентификатор сообщения-родителя.
Показ текущего сообщения (файл Show.asp)
Далее, как уже говорилось, нам необходимо организовать показ отдельного сообщения. Здесь нет никаких сложностей, и требуется только извлечь заголовок (тему) заданного сообщения, а также показать его тело:
<!--#include file="utils.asp"-->
<%
dim fid, id
dim name, email, subject, mesdate, mesbody, tid, tl
dim rs_order, image, parentid, sql1, urHereImg, idFromq
image = "<img src='images/arr.gif' border=0>"
urHereImg = "<img src='images/arr2.gif' border=0>"
'Извлечем ID текущего форума
fid = request("fid")
'Извлечем ID текущего сообщения
id = request("id")
idFromq = request("id")
'Извлечем идентификатор вложенности сообщения — thread_id
tid = request("tid")
Call Header()
sql = "select * from MessagesTable WHERE forumid = " & fid & " and id = " & id &" order by id"
Call Connect()
rs.open sql,conn
name = rs("name")
email = rs("email")
subject = rs("subject")
mesdate = rs("mesdate")
mesbody = rs("mesbody")
Response.Write "<tr><td width=100><B>Сообщение</B><br><br></TR></TD>"
Response.Write "<tr><td>"
Response.Write "<b>Имя: </b>" & "<a href='mailto:" & email &_
"'>" & name & "</a><br>" & "<b>Тема: </b>" & subject &_
"<br>" & "<b>Дата: </b>" & mesdate & "<br><br>" & mesbody
Response.Write "</tr></td>"
Response.Write "<tr><td><br>"
Response.Write "<a href='post.asp?id="&id&"&fid="&fid&"&tid="&tid&_
"'>Ответить на это сообщение</a> | <a href='javascript:history.back(-1)'>"&_
"Назад</a> | <a href='index.asp'>"&_
"Главная страница</a> | <a href='search.asp?fid="&fid&"'>Поиск</a>"
Response.Write "</table><br>"
set rs_order = server.createobject("adodb.recordset")
If tid = 0 Then
sql = "select * from MessagesTable where id="& id &" and forumid=" & fid & ""
Else
sql = "select * from MessagesTable where id="& tid &" and forumid=" & fid & ""
End If
rs_order.open sql,conn
parentID = rs_order("id")
subject = rs_order("subject")
fid = rs_order("forumid")
tid = rs_order("thread_id")
mesdate = rs_order("mesdate")
name = rs_order("name")
Response.Write line
Response.Write "<table cellpadding=0 cellspacing=0><tr><td>"
Response.Write image & "<a href='show.asp?id=" & parentID & "&fid="&fid&"&tid="&tid&"'>" & subject &_
"</a>" & "...." & mesdate & "...." & name & "</a>"
if CInt(idfromq) = CInt(parentid) Then
Response.Write urHereImg
End If
Response.Write "</tr></td>"
Call displayMessages1(1,ParentID)
Response.Write "</table>"
Call Footer()
rs_order.close
set rs_order = nothing
Call Close()
%>
Однако, как видите, в том случае, если идентификатор текущего уровня вложенности отличен от нуля (когда сообщение является ответом), то выборка формируется таким образом, чтобы содержать всю ветвь сообщений:
If tid = 0 Then sql = "select * from MessagesTable where id="& id &" and forumid=" & fid & "" Else sql = "select * from MessagesTable where id="& tid &" and forumid=" & fid & "" End If
Далее формируются ссылки на дочерние сообщения.
Для удобства все сообщения сопровождаются изображением стрелки слева:
image = "<img src='images/arr.gif' border=0>"
А текущее сообщение сопровождается стрелкой справа:
urHereImg = "<img src='images/arr2.gif' border=0>"
Отправка сообщения (файл Post.asp)
Теперь надлежит сформировать формы посылки нового сообщения или сообщения-ответа, организовать проверку корректности их заполнения, обработку введенных значений и реализовать механизм добавления новых сообщений в соответствующую таблицу базы данных.
Прежде всего извлечем идентификатор текущего форума, сообщения и уровня вложенности, далее опросим действия пользователя. Данные, введенные пользователем в качестве тела сообщения, а также его заголовка (темы) и имени пользователя, обработаем с использованием серверного метода Server.HtmlEncode: mesbody = Server.HtmlEncode(mesbody), после чего можно добавлять новую запись в таблицу:
<!--#include file="utils.asp"-->
<SCRIPT LANGUAGE = VBScript RUNAT=Server>
<!-- Metadata type="typelib" File="c:\program files\common files\system\ado\msado15.dll" —->
</SCRIPT>
<%
dim useraction, fid, action, id, tid, mailme
dim mesbody, name, subject
dim sql_maxid, rs_maxid, maxid
fid = request("fid")
id = request("id")
tid = request("tid")
If id = "" Then
action = "post.asp?action=save"
Else
action = "post.asp?action=replay"
End If
'Организуем опрос действий пользователя
userAction = request("action")
Select Case userAction
case "save" ' Случай добавления нового сообщения
Call Connect()
sql = "select * from MessagesTable"
RS.Open sql, Conn, 3, adLockOptimistic
mesbody = request.form("message")
name = request.form("name")
name = replace(name,"*","")
subject = request.form("subject")
mailme = request.form("mailme")
'Используем метод объекта Server для кодирования в формате HTML
mesbody = Server.HtmlEncode(mesbody)
name = Server.HtmlEncode(name)
subject = Server.HtmlEncode(subject)
'Заменяем символы br символами новой строки
mesbody = Replace(mesbody, vbCrLf, "<br>")
'Добавляем новую запись
rs.AddNew
rs("name") = name
rs("subject") = subject
rs("email") = request.form("email")
rs("mesdate") = now()
rs("mesbody") = mesbody
rs("thread_parent") = 0
rs("thread_id") = 0
rs("forumID") = request.form("fid")
rs("isChecked") = mailme
rs.Update
Call Close()
'Передача управления в List.asp
Response.Redirect "list.asp?fid="&fid
case "replay" 'Случай ответа на уже существующее сообщение
fid = request.form("fid")
id = request.form("id")
mailme = request.form("mailme")
'Добавляем сообщение-ответ в таблицу
Call Connect()
sql = "select * from MessagesTable"
RS.Open sql, Conn, adOpenStatic, adLockOptimistic
mesbody = request.form("message")
name = request.form("name")
subject = request.form("subject")
tid = request.form("tid")
If tid = 0 Then
tid = id
End If
'Используем метод обекта Server для кодирования в формате HTML
mesbody = Server.HtmlEncode(mesbody)
name = Server.HtmlEncode(name)
subject = Server.HtmlEncode(subject)
'Заменяем символы br символами новой строки
mesbody = Replace(mesbody, vbCrLf, "<br>")
rs.addnew
rs("name")=name
rs("subject")=subject
rs("email")=request.form("email")
rs("mesdate")=now()
rs("mesbody")=mesbody
rs("forumID")=fid
rs("thread_parent")=id
rs("thread_id")=tid
rs("isChecked")=mailme
rs.update
'Посылаем E-mail всему "семейству" авторов родительских сообщений
Call maxRec()
Call sendAllFamily(tid,maxid,fid)
Call Close()
'Передача управления в List.asp
Response.Redirect "list.asp?fid="&fid
End Select
dim requireFldSign
requireFldSign = "<font color='red'>*</font>"
response.write "<HTML><HEAD><link rel='stylesheet' href='style.css'>"
%>
<%
'Организуем JavaScript-функцию анализа корректности ввода пользователя
%>
<script language="javascript">
function submitit()
{
var name = document.myform.name.value;
var subject = document.myform.subject.value;
var email = document.myform.email.value;
if (name =="") {
alert("Введите Ваше имя")
document.myform.name.focus()
return false
}
if (subject =="") {
alert("Введите тему сообщения")
document.myform.subject.focus()
return false
}
if (email != ""){
if (email.indexOf('@', 0) == -1 || email.indexOf('.', 0) == -1) {
alert("Некорректный формат электронного адреса!");
document.myform.email.focus()
return false
}
}
}
</script>
<%
Response.Write "<TITLE>Сообщения</TITLE></HEAD><body>"
Response.Write "<table border=0 width=600 cellpadding=0 cellspacing=0>"
Response.Write "<tr><td bgcolor='black' colspan=2>"
Response.Write "</tr></td>"
Response.Write "<tr><td width=100>"
Response.Write "<b>Посылка сообщения</b><br><br></tr></td>"
Response.Write "<form name='myform' action='" &action & "' method='post' onsubmit='return submitit()'>"
Response.Write "<input type=hidden name='fid' value='"&fid&"'>"
Response.Write "<input type=hidden name='id' value='"&id&"'>"
Response.Write "<input type=hidden name='tid' value='"&tid&"'>"
Response.Write "<tr><td width=100>"
Response.Write requireFldSign & "Имя:</td><td><input type='text' name='name' maxlength='20' size=23></tr></td>"
Response.Write "<tr><td>"
Response.Write "Email:</td><td><input type='text' name='email' maxlength='28' size=23></tr></td>"
Response.Write "<tr><td>"
Response.Write requireFldSign & "Тема:</td><td><input type='text' name='subject' maxlength='25' size=23></tr></td>"
Response.Write "<tr><td valign=top>"
Response.Write "Сообщение:</td><td><textarea cols=25 rows=8 name='message' maxlength='20' size=23 wrap='virtual'></textarea></tr></td>"
Response.Write "<tr><td colspan=2><br>"
Response.Write "<input type='checkbox' name='mailme' value='1'>"
Response.Write "Отправьте мне электронное письмо с уведомлением об ответах на мое сообщение.</tr></td>"
Response.Write "<tr><td colspan=2><br>"
Response.Write "<input type='submit' name='submit' value='Отправить сообщение'> "
Response.Write "<input type='reset' name='reset' value='Сброс'>"
Response.Write "</form>"
Response.Write "</tr></td></table></body></html>"
Response.Write "<table border=0 width=600 cellpadding=0 cellspacing=0><tr><td>"
Call Footer()
Response.Write "</tr></td></table>"
%>
Остановимся на извлечении идентификатора последнего введенного значения и на функции посылки уведомлений о поступлении нового сообщения по электронной почте. Первая функция, которая была описана выше, просто извлекает максимальное значение идентификатора сообщения. А поскольку идентификаторы (identity) таблиц баз данных уникальны и постоянно инкрементируются, то фактически данная функция возвращает идентификатор последнего сообщения. Если это действие выполняется непосредственно после добавления в таблицу (а в данном случае это именно так), то извлекаемое значение совпадает с идентификатором последнего введенного значения, то есть последнего сообщения.
По сути функция посылки уведомительных сообщений о поступлении новых сообщений в цикле по результатам запроса сообщений, авторы которых пожелали получать уведомления по электронной почте, посылает сообщения, используя серверный объект CDONTS. Выборка этих сообщений из таблицы сообщений выглядит следующим образом:
sql = "select email,id,thread_id from MessagesTable" sql = sql & " where (thread_id="& tid & " or id="& tid & ") and id<>" & maxid &"" sql = sql & "and forumid="&fid&" and isChecked=1"
Здесь флажок isChecked отражает пожелание пользователя получать уведомления по электронной почте.
Далее представлена сама форма посылки нового сообщения и реализована функция проверки корректности ее заполнения.
Поиск сообщений (файл Search.asp)
Может создаться ошибочное впечатление, что поиск сообщений — не такая уж необходимая вещь, ведь сообщения все равно уже отсортированы по дате их поступления, а актуальны, как правило, лишь самые последние. Это может быть справедливо только в том случае, если конференция посвящена определенной теме. Если, к примеру, она касается купли-продажи, то актуальна лишь небольшая часть последних сообщений и вряд ли имеет смысл просматривать более старые сообщения. Однако это не распространяется на разнообразные тематические конференции, в которых информация постоянно накапливается и устаревает гораздо медленнее. Яркий тому пример — технические конференции по программированию или, допустим, по ремонту автомобилей. К конференциям такого рода пользователи обращаются в основном за советом, а дельный совет подчас несет важнейшую информацию и может оказаться не менее полезным для новых читателей спустя достаточно долгое время после опубликования. Таким образом, поиск сообщений может принести много пользы.
Итак приступим к его созданию:
<!--#include file="utils.asp"-->
<%
dim flag, color, userAction, element, myarray, t, OR_CheckBox, p, id, fid, tid
userAction = request.querystring("action")
fid = request("fid")
'Организуем опрос действий пользователя
Select Case userAction
Case "search"
sql = "select * from MessagesTable where id>0 AND forumid="&fid&" "
Call Connect()
OR_CheckBox = request.form("OR_CheckBox")
fid = request.form("fid")
'Опрос элементов формы
For each element in request.form
If request.form(element)<>"" then
Select Case element
Case "name"
sql=sql & " and name like '%" & Request.form(element) & "%'"
Case "fsearch"
myarray=split(Request.form(element)," ")
If OR_CheckBox = "" Then
For t = 0 To ubound(myarray)
sql = sql & " and mesbody like '%" & myarray(t) & "%'"
Next
Else
p = 0
For t = 0 To ubound(myarray)
If p = 0 Then
sql = sql & " and mesbody like '%" & myarray(t) & "%'"
p = 1
Else
sql = sql & " or mesbody like '%" & myarray(t) & "%'"
End If
Next
End If
End Select
End If
Next
sql = sql & " order by mesdate"
rs.open sql,conn
If rs.eof and rs.bof Then
Response.Write "<br><b>По результатам вашего запроса сообщений не обнаружено</b>"
Else
Response.Write "<table width=400 border=0><th></th><th align=left>Имя</th>"
Response.Write "<th align=left>Тема</TH><th align=left>Дата</TH>"
Do While Not rs.eof
id = rs("id")
tid = rs("thread_id")
If flag = 0 Then
flag = 1
color = "#D7D7D7"
Else
flag = 0
color = "#B0C4EC"
End If
Response.Write "<TR bgcolor="& color & "><TD><img src=images/search.gif></td><td>"
Response.Write RS("name") & "</td><td>" & "<a ref='show.asp?id="& id &_
"&fid="&fid&"&tid="&tid&"'>" & RS("subject") & "</td><td>"
Response.Write RS("mesdate") & "</td></TR>"
rs.MoveNext
Loop
Response.Write "</table>"
End If
End Select
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<style>a:hover{color:red}</style>
<title>Поиск сообщений</title>
</head>
<body>
<table border=0 width=450>
<tr>
<td colspan=2 align=center><b>Поиск сообщений</b><br>
</td>
</tr>
<form action="search.asp?action=search" method = post>
<input type="hidden" name="fid" value='<%= fid %>'>
<tr><td>
Поиск по имени:</td><td><input type="text" name="name" size=20 maxlength=20>
</tr></td>
<tr>
<td>
Поиск по ключевым словам:<br>
Разделите слова пробелами<br>
Оператор И по умолчанию
</td>
<td>
<input type="text" name="fsearch" size=30 maxlength=30>
</td>
</tr>
<tr>
<td>
Оператор ИЛИ
</td>
<td>
<input type="checkbox" name="OR_CheckBox" value="1">
</td>
</tr>
<tr>
<td colspan=2 align="center">
<input type="submit" value="search">
</td>
</tr>
</table>
</body>
</html>
Теперь давайте разберемся, каким образом работает страничка нашего пользователя.
Для начала, как видите, опрашивается действие пользователя:
userAction = request.querystring("action")
Далее, в зависимости от действия пользователя, опрашивается форма, причем делается это довольно хитроумным способом (с помощью оператора выбора — Select). Ключевой строкой, позволяющей извлекать значения из формы-родителя (из массива представления этих данных), в данном случае является строка:
myarray = split(Request.form(element)," ")
Эта строка, по сути, разбивает все переданные текущей форме формой-предком данные, реагируя на пробелы как на разделители.
После того как значения полей нашего поискового компонента считываются в переменные, производится анализ пользовательского ввода и формируется строка запроса к базе данных, исходя из введенных значений.
Вот, в общем-то, и все. Форум готов, осталось только впечатать несколько сообщений и убедиться, что все его функции работают (рис. 2).
Заключение
В заключение хотелось бы еще раз напомнить читателям о форум-управлении. Дело в том, что вопросам процесса автоматизации мы планируем посвятить одну из следующих частей статьи. Иначе говоря, обо всем, что лежит в рассмотренной нами главной страничке под ссылкой: “Для администрирования системы жми сюда”, то есть об инструментах контроля и администрирования форум-системы, мы расскажем позднее.
Полный архив исходных текстов ASP-страниц к настоящей статье лежит здесь.
С автором статьи можно связаться по следующему адресу: rouben@iname.com
КомпьютерПресс 5'2001








