Отображает новейший заголовок каждой страницы сайта
page.php
Приложение
Выводит список заголовков и текст для определенной страницы
resize_image.php
Приложение
Изменяет на лету размеры изображений для сценария headlines. php
search_form.php
Приложение
Форма ввода ключевых слов для ведения поиска в содержимом сайта
search.php
Приложение
Отображает заголовки содержимого, соответствующего ключевым словам
login.php
Приложение
Выполняет аутентификацию пароля пользователя и вводит его в систему
logout.php
Приложение
Выводит пользователя из системы
stories. php
Приложение
Перечисляет статьи, написанные вошедшим в систему пользователем. Ему предоставляются опции добавления, модификации и удаления статей
Имя
Тип
Описание
story.php
Приложение
Окно информации о статьях для редактирования, либо добавления новых статей
story _submit.php
Приложение
Добавляет новую статью либо передает изменения на основе данных, введенных в окно story.php
delete_story.php
Приложение
Обрабатывает запрос на удаление статьи, переданный из сценария stories. php
keywords.php
Приложение
Выводит список ключевых слов для статьи с опцией их добавления либо удаления
keyword_add.php
Приложение
Обрабатывает запрос на добавление ключевого слова, переданный из сценария keywords.php
keyword_delete.php
Приложение
Обрабатывает запрос на удаление ключевого слова, переданный из сценария keywords. php
publish.php
Приложение
Список статей для редактора с указанием, какие из них опубликованы, а также опцией переключения статуса каждой из них
publish_story.php
Приложение
Обрабатывает запрос на публикацию, переданный из сценария publish. php
unpublish_story.php
Приложение
Обрабатывает запрос на отмену публикации, переданный из сценария publish. php
Создание базы данных.Листинг 6. 22 отображает SQL-запросы, используемые для создания базы данных содержимого системы. Этот листинг демонстрирует часть файла create_database.sql. Файл в приложении кроме этого содержит запросы для заполнения базы данных примерами пользователей и статей.
Листинг 6. 22.Фрагмент create_database.sql —создание содержимого базы данных
DROP DATABASE IF EXISTS content;
CREATE DATABASE content;
USE content;
DROP TABLE IF EXISTS writers;
CREATE TABLE writers (
username varchar(16) PRIMARY KEY,
password varchar(16) NOT NULL,
full_name text);
DROP TABLE IF EXISTS stories;
CREATE TABLE stories (
id int PRIMARY KEY auto_increment,
writer varchar(16) NOT NULL, # FOREIGN KEY writers.username
page varchar(16) NOT NULL, # FOREIGN KEY pages.code
headline text,
story_text text,
picture text,
created int,
modified int,
published int);
DROP TABLE IF EXISTS pages;
CREATE TABLE pages (
code varchar(16) PRIMARY KEY,
description text);
DROP TABLE IF EXISTS writer_permissions;
CREATE TABLE writer_permissions (
writer varchar(16) NOT NULL, # FOREIGN KEY writers.username
page varchar(16) NOT NULL # FOREIGN KEY pages.code
);
DROP TABLE IF EXISTS keywords;
CREATE TABLE keywords (
story int NOT NULL, # FOREIGN KEY stories.id
keyword varchar(32) NOT NULL,
weight int NOT NULL);
GRANT select, insert, update, delete ON content.*
TO content@localhost identified by 'password';
GRANT all ON content.* to root@localhost;
Необходимо сохранить краткие сведения о каждом авторе, включая входное имя и пароль, в таблице writers. Полные имена авторов будут храниться для вывода после каждой статьи, а также приветствия авторов после входа в систему.
Таблица pages содержит заголовки каждой страницы, в которых будут отображаться статьи. Таблица writer_permissions реализует отношение многие ко многим с указанием, для каких страниц автор может передавать статьи.
Таблица stories содержит отдельные поля для компонентов headline (заголовок), story_text (текст статьи) и picture (изображение), о чем речь шла выше. Поля created (создана), modified (модифицирована) и published (опубликована) имеют целочисленный тип данных и хранят метки времени UNIX соответствующих событий.
Для создания базы данных необходимо выполнить следующую команду:
mysql -u root < create_database.sql
Реализация.Теперь, когда база данных и функция изменения размеров созданы, приступим к построению основной части системы.
Интерфейсная часть.Начнем с обзора сценария headlines.php, показанного в листинге 6. 23. Он выводит первую страницу, которая будет отображаться посетителям сайта. Необходимо отобразить заголовки последних статей из каждой страницы.
Листинг 6. 23.Сценарий headlines.php отображает новейшие заголовки каждой страницы
<?php
include "include_fns.php";
include "header.php";
$conn = db_connect();
$pages_sql = "select * from pages order by code";
$pages_result = mysql_query($pages_sql, $conn);
while ($pages = mysql_fetch_array($pages_result)) {
$story_sql = "select * from stories where page = '$pages[code]'
and published is not null order by published desc";
print "<FONT SIZE=1>Read more $pages[code] ...</FONT>";
print "</A></TABLE>";
}
}
include "footer.php";
?>
Этот сценарий, как и все общедоступные сценарии, подключает файл header.php в начале и файл footer.php в конце. В результате любой вывод, генерируемый сценарием, отображается в ячейке основного содержимого страницы.
Основную нагрузку несут два запроса базы данных. Первый
select * from pages order by code
извлекает список страниц, содержащихся в базе данных Затем выполняется тело цикла
select * from stories where page = ' $pages [code] '
and published is not null order by published desc
для поиска статей данной страницы в обратном порядке дат публикации.
Рядом с каждым заголовком генерируется ссылка в следующей форме:
<А HREF="page.php?page=l"><FONT SIZE=1>Read more news..</FONT></A>
Рассмотренный выше цикл обеспечивает вывод значения строки запроса $page и имени страницы рядом с соответствующим заголовком. В результате щелчка на ссылке выводится страница, генерируемая сценарием page.php. Она содержит полный список статей определенной страницы. Код сценария page.php приводится в листинге 6. 24.
Листинг 6. 24.Сценарий page.php отображает на странице все опубликованные статьи
<?php
include "include_fns.php";
include "header.php";
$conn = db_connect();
if (!$page) { header("Location: headlines.php"); exit; }
$sql = "select * from stories where page = '$page'
and published is not null order by published desc";
Обратите внимание, что сценарий page.php требует наличия значения у переменной $page, чтобы правильно сформировать первый запрос. Когда сценарий page.php вызывается непосредственно, без строки запроса, первый условный оператор
if (!$раgе) { header("Location: headlines.php"); exit; }
отсылает посетителя обратно к странице заголовков, чтобы отсутствие значения $page не привело к ошибке. Первый запрос
select * from stories where раgе = ' $раgе'
and published is not null order by published desc
выбирает все статьи, опубликованные на указанной странице, располагая их в обратном хронологическом порядке (самые свежие статьи следуют в начале). Внутри каждого цикла загруженное изображение и текст отображаются на экране, затем следует имя автора и дата последнего изменения.
Прикладная часть.Теперь рассмотрим метод добавления статей в систему. Сначала для авторов запускается сценарий stories.php. Этот сценарий после аутентификации автора отображает список написанных им статей и дату публикации и либо опции добавления новой статьи, редактирования или удаления существующей, либо установки ключевых слов поиска.
Эти окна не форматируются внутри файлов заголовка и нижнего колонтитула, хотя такое и возможно. Поскольку данные сценарии применяют только авторы и редактор, в нашем примере использован лишь необходимый объем форматирования для создания работоспособной системы. Код сценария stories.php показан в листинге 6. 25.
Листинг 6. 25.Сценарий stories.php - интерфейс авторов для управления своими статьями
На первом этапе проверяется, выполнена ли аутентификация пользователя. Если нет, отображается только форма входной регистрации. После входа автора в систему переменной сеанса $auth_user присваивается значение. Используемая здесь аутентификация не очень надежна. В реальной ситуации необходимо обеспечить, чтобы аутентификация пользователей выполнялась должным образом.
Форма входной регистрации передает данные сценарию login.php, который сравнивает имя пользователя и пароль с соответствующими значениями базы данных. В случае успешности входной регистрации пользователю возвращается предыдущая страница с помощью значения $HTTP_REFERER. Это означает, что сценарий регистрации может вызываться из любой страницы системы. Затем автор приветствуется по имени и предоставляется возможность выхода из системы. Эта ссылка всегда отображается в верхней части страницы stories.php, что позволяет легко выйти из системы в любой момент.
$w = get_writer_record($auth_user);
echo "Welcome, ".$w[full_name];
echo " (<A HREF=\"logout.php\">Logout</A>)";
Функция get_writer_record() описана в библиотеке db_fns.php и возвращает массив полей таблицы автора на основе переданного имени пользователя. Сценарий logout.php просто сбрасывает значение переменной $auth_user.
SQL-запрос выбирает все статьи автора, начиная с добавленных в последнее время:
select * from stories where writer = '$auth_user'
order by created desc
Для каждой записи, связанной со статьей, хранятся метки времени добавления, модификации и публикации. Когда добавляется новая статья, меткам создания и модификации присваивается текущее системное время. Каждое последующее изменение статьи будет вызывать обновление лишь метки модификации.
Вся эта информация выводится в окне статей сначала с помощью кода:
Последний фрагмент отображает лишь дату публикации, если она имеет смысл. В противном случае выводятся ссылки для редактирования или удаления статьи, а также установки ключевых слов поискового механизма.
Сценарий ввода или редактирования статьи содержится в файле story.php.
Листинг 6. 26.Сценарий story.php служит для создания или редактирования статьи
Для добавления статей и их редактирования может использоваться один и тот же сценарий. Выполняемое действие зависит от того, установлено ли значение переменой $story при вызове сценария.
if (isset($story))
$s = get_story_record($story);
Функция get_story_record() описана в db_fns.php. Она возвращает массив всех полей таблицы статей для указанного идентификатора статьи. Если идентификатор не передан, переменная $story имеет значение NULL, а переменная $s не содержит элементов массива.
Если значение переменной $story не установлено, предыдущий фрагмент кода не создаст значения из оператора РНР, поэтому поле ввода заголовка будет пустым. Когда значение переменной $story установлено, она содержит текст заголовка для редактируемой статьи.
where p.code = w.page and w.writer = ' $auth_user'", $s[page]); "
Функция query_select() описана в select_fns.php. Она возвращает HTML-код вывода списка SELECT на основе данного SQL-запроса. Первый параметр представляет собой атрибут NAME для оператора SELECT. SQL-запрос из второго параметра выбирает два столбца, где первый является составляющей VALUE каждой опции, а второй следует после дескриптора OPTION и представляет собой текст, отображаемый в списке. Третий параметр необязателен. Он добавляет атрибут SELECTED к опции, значение которой совпадает с указанным.
Здесь создается переменная-заполнитель путем установки нового значения для статьи из переданного в переменной $story. После передачи формы сценарий story_submit.php проверяет, существует ли значение переменной $story, и генерирует в соответствии с этим SQL-оператор UPDATE либо INSERT.
Код сценария story_submit.php показан в листинге 6. 27.
Листинг 6. 27.Сценарий story_submit.php - вставка или обновление статей в базе данных
<?php
include "include_fns.php";
$conn = db_connect();
$time = time();
if ( ($html) && (dirname($html_type) == "text") ) {
Ссылка удаления статьи вызывает сценарий delete_story.php. Он просто реализует оператор DELETE и возвращает автора к вызывающей странице. Код сценария delete_story.php показан в листинге 6. 28.
Листинг 6. 28.Сценарий delete story.php удаляет статью из базы данных
<?php
include "include_fns.php";
$conn = db_connect(); $now = time();
$sql = "delete from stories where id = $story";
$result = mysql_query($sql, $conn);
header("Location: $HTTP_REFERER"); ?>
Поиск статей.В результате щелчка на ссылке ключевых слов в списке статей вызывается новая форма для ввода ключевых слов, связанных со статьей. Количество ключевых слов не ограничено. Каждому из них присваивается значение весомости. Более высокие значения соответствуют более важным ключевым словам.
Сценарий keywords.php довольно прост, поэтому мы не будем его подробно рассматривать. Он содержится в приложении. Он запускает сценарии keyword_add.php и keyword_delete.php. Они также просты и здесь не рассматриваются.
Для ввода новых ключевых слов в базу данных сценарий keyword_add.php использует следующий запрос:
insert into keywords (story, keyword, weight)
values ($story, '$keyword', $weight)
Подобным же образом, сценарий keyword_delete.php для удаления ключевого слова использует следующий запрос:
delete from keywords where story = $story and keyword = '$keyword'
Интерес представляет способ использования показателей весомости для вычисления процентного отношения значимости в процессе поиска.
Форма поиска, генерируемая сценарием search_form.php, содержит поле для ключевых слов и передается в сценарий search.php, который запрашивает базу данных статей для поиска соответствующего содержимого. Код сценария search.php показан в листинге 6.29.
Листинг 6. 29.Сценарий search.php - поиск статей по ключевым словам
as score from stories s, keywords k where s.id = k.story $and
group by s.id, s.headline order by score desc, s.id desc";
$result = mysql_query($sql, $conn);
print "<H2>Search results</H2>";
if (mysql_num_rows($result)) {
print "<TABLE>";
while ($qry = mysql_fetch_array($result)) {
print "<TR><TD>";
print $qry[headline];
print "</TD><TD>";
print floor($qry[score])."%";
print "</TD></TR>";
}
print "</TABLE>";
}
else { print "No matching stories found"; }
include "footer.php";
?>
Сначала строка ключевого слова, передаваемая в сценарий, разбивается на отдельные слова для поиска. В этом примере не используются какие-либо расширенные методы поиска, такие как возможность применения операторов AND и OR, либо объединение слов в фразу.
В этом коде используется РНР-функция split() для создания массива, содержащего каждое слово строки $keyword, разделенное пробелами. Если указано только одно слово, возвращается массив с единственным элементом, и последующий цикл выполняется один раз. В результате условие, хранимое в переменной Sand, будет иметь следующий вид:
and (k.keyword = 'keyword1' or k.keyword = 'keyword2' or
k.keyword = ' keyword3')
Ниже приводится запрос поиска, основанный на рассмотренном ранее коде:
select s.id, s.headline, 10 * sum(k.weight)/$num_keywords as score
from stories s, keywords k
where s.id = k.story and (k.keyword = 'keyword1'
or k.keyword = ' keyword2' or k.keyword = 'keyword3' )
group by s.id, s.headline order by score desc, s.id desc
Общий показатель значимости определяется как сумма весовых значений всех соответствующих ключевых слов, деленная на количество искомых ключевых слов и умноженная на десять. Эта формула пригодна для поисков, когда все введенные ключевые слова соответствуют ключевым словам базы данных.
Поскольку показатели весомости находятся в диапазоне от 1 до 10, максимальное значение общего показателя значимости составляет 100. Эта величина для поиска по трем ключевым словам достигается, когда все три слова найдены и каждое из них имеет весовое значение 10.
Окно редактора.Осталась нерассмотренной одна составляющая проекта, отвечающая за публикацию статьи после ее написания. Это осуществляет сценарий publish.php, показанный в листинге 6. 20.
Листинг 6. 20.Сценарий publish.php - выбор из списка документов публикации
<?php
include "include_fns.php";
$conn = db_connect();
$sql = "select * from stories order by modified desc";
Данный сценарий должен быть доступным только для лиц, уполномоченных публиковать статьи на сайте. В нашем приложении это редактор сайта. Однако в целях простоты здесь не реализовано управление доступом. В реальной ситуации сценарий должен быть защищен.
Он очень похож на сценарий stories.php с той разницей, что для редактора отображаются статьи всех авторов, а не только его собственные. Оператор if обеспечивает предоставление для каждой статьи необходимых опций. Публикация статей может отменяться, а неопубликованные статьи могут публиковаться либо удаляться.
Связанные с этим оператором три ссылки вызывают, соответственно, сценарии unpublish_story.php, publish_story.php и delete_story.php. Сценарий publish_story.php использует следующий SQL-запрос:
update stories set published = $now where id = $story
Он помечает статью как опубликованную и делает ее общедоступной для просмотра.
Подобно этому, сценарий unpublish_story.php использует следующий запрос, чтобы пометить статью как неопубликованную и прекратить ее отображение для всеобщего просмотра:
update stories set published = null where id = $story
Ссылка редактирования отображается независимо от факта публикации статьи, поэтому редактор всегда может внести в нее изменения. В этом состоит отличие от уровня доступа автора, который может изменять статью лишь до ее публикации.