oldi

<% ASP на блюдечке %>. Часть 8

Форум своими силами

Единственная роскошь на свете — это роскошь человеческого общения.
Антуан де Сент-Экзюпери

Рубен Садоян

Введение

Концепция

Постановка задачи

Что нам понадобится

Создание и подготовка базы данных

   Универсальный набор функций (файл utils.asp)

   Главная страничка — выбор форума (файл index.asp)

   Просмотр списка сообщений форума — (файл list.asp)

   Показ текущего сообщения (файл Show.asp)

   Отправка сообщения (файл Post.asp)

Заключение

Введение

Во всех предыдущих частях “<% ASP на блюдечке %>” мы еще не касались форума (конференции) — неотъемлемого компонента современного Интернета. Освоив построение таких непростых механизмов, как электронный магазин (часть 4 — “Виртуальный магазин”, часть 5 — “Виртуальный магазин (продолжение)”, часть 7 — “Классический Интернет-магазин”), перейти к проектированию форум системы будет несложно.

Настоящая публикация призвана пролить свет на эту отрасль Интернет-строительства — на форумостроительство.

В начало

В начало

Концепция

Для начала попытаемся сформулировать концепцию простейшего форума с точки зрения пользователя, что позволит нам в дальнейшем определить задачу его построения для программиста. Зададимся вопросом: “Что же такое форум?” Некоторые читатели могут сразу же ответить: “Это же всем известно!” Однако советую не спешить с утверждениями такого рода, а попытаться упорядочить свои представления о форуме. Надеюсь, что данный раздел поможет вам в этом.

Итак, форум — это некая область экрана (окна браузера), где различные его участники (будем называть их пользователями форум-системы, или просто пользователями) могут излагать свои мысли на заданную тему (под мыслями в настоящей статье имеются в виду текстовые сообщения). Просматривая сообщения, каждый пользователь волен написать ответ на чье-либо уже написанное сообщение, которое, в свою очередь, может являться ответом на сообщение, и т.д.

Обобщая, можно сказать, что форум представляет собой двухмерный поток текстовых сообщений. Под вторым измерением следует понимать сообщения-ответы, ответы на ответы и т.д.

Не правда ли, нам с вами стала гораздо понятнее и доступнее концепция форума? Теперь можно переходить к рассмотрению некоторых отличительных особенностей форумов. Конечно, это не догма, поскольку не каждый форум обладает всеми нижеперечисленными особенностями, но все эти особенности, как правило, присутствуют в современных форум-системах.

Во-первых, это система идентификации пользователей, призванная регистрировать имя (псевдоним, nickname), пароль, электронный адрес и пр. пользователя в целях обеспечения его уникальности в форум-системе.

Во-вторых, это, как правило, система управления форумом, позволяющая осуществлять контроль за потоком текстовых сообщений. В современных системах эта функция возложена на так называемых модераторов, или форум-менеджеров.

Вкратце список функций контроля и управления форумами можно представить следующим образом:

  1. Контроль тематики — модератор следит за соблюдением определенной тематики и удаляет (предупреждает конкретного пользователя о нарушении им заданной тематики) ненужные сообщения. Согласитесь, что такие сообщения досадно отвлекают от предмета обсуждения (впрочем, существуют и форумы с весьма неопределенной тематической ориентацией).
  2. Контроль ненормативной лексики — фильтруются бранные слова и выражения.
  3. “Поддержка” форума — умышленная посылка модератором множества сообщений от имени разных пользователей на интригующие читателей форума темы. Хотя такое порой и случается, все же не следует воспринимать форум как место, где можно всегда пообщаться лишь с модератором или с системным администратором.
  4. Организация общего управления форумом — порождение или удаление форумов заданной тематики, контроль, назначение и управление работой модераторов. Выполняется администратором форума.

Данный список можно продолжать, но, на мой взгляд, это то, без чего не обходится ни одна форум-система.

Во-третьих, это система безопасности, обеспечивающая доступ модераторов и администраторов форума к функциям, недоступным для других пользователей.

В-четвертых, это система поиска сообщений и/или их авторов — как по тематике, так и по содержимому сообщения.

Совокупность этих трех компонентов и является, по сути дела, самым обычным и зачастую самым распространенным Интернет-форумом. Конечно, существуют варианты с выбором картинки-смайлика и цвета текстового сообщения, но сути дела это не меняет.

В начало

В начало

Постановка задачи

Теперь, когда мы уяснили, что представляет собой форум-система с точки зрения пользователя, можно переходить к постановке и формализации программной задачи, для чего необходимо разработать:

  1. Базу данных с таблицами сообщений, форумов, пользователей, модераторов и администраторов.
  2. Модули идентификации пользователей.
  3. Поисковый модуль, осуществляющий поиск сообщений.
  4. Систему контроля за потоком текстовых сообщений, позволяющую модератору удалять или редактировать нужные сообщения.
В начало

В начало

Что нам понадобится

Предполагается, что читатель знаком с основами 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 & "&nbsp;&nbsp;&nbsp;"
      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>&nbsp;"& 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>Имя:&nbsp;</b>" & "<a href='mailto:" & email &_
          "'>" & name & "</a><br>" & "<b>Тема:&nbsp;</b>" & subject &_
         "<br>" & "<b>Дата:&nbsp;</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='Отправить сообщение'>&nbsp;&nbsp;"
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