<% 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