как я ЭТО делал, или немного записок о p3
vasyan 18.07.2003 13:04
короче все это - мое знакомство с p3 и как оно сделано.(шаз несколько поменял)
буду рад конструктивной критике....
В файле auto.p общедоступного сайта определены следующие переменные:
$connect_string - строка, подключения к базе данных
$web_url - url сайта в сети internet
$title - заголовок окна web-браузера
$url -запрос, передаваемый браузером web-серверу
$parts -массив, содержащий части запроса
$last[] - содержит текущий номер пункта меню в базе(определяется по url)
Так же определены процедуры:
@dbconnect[code] - процедура подключения к базе. Внутри этой процедуры следует производить все
манипуляции с базой.
Пример использования процедуры:
^dbconnect{
$menu_table[^table::sql{SELECT nmbr, name, url, hint, parent, child
FROM menu
ORDER BY nmbr}]
$menu_kind[^table::sql{SELECT header
FROM menu_kind}]
}
@terminator[width;height] - процедура, используемая для оформления сайта
@html[code;nmbr]- процедура используется в некоторый классах, для введения псевдо-тегов.
производит следующие замены:
1) Замена [img] на <img src="/path/count.jpg"> , где count порядковый номер вхождения [img]
2) Замена [url="link"] *** [/url] на<A HREF="link"> *** </A>
3) Автообрамление url, т.е. если в тексте находится ссылка в виде текста, то она автоматически
становится гиперссылкой. Пример:
http://asd.ru заменяется на <A
href=
http://asd.ru>
http://asd.ru</A>
4) Замена [br] на<br>
5) Замена
*** на<b>***</b>
6) Замена
*** на<i>***</i>
7) Замена [u]***[/u] на<u>***</u>
8) Замена [p] на<p>
9) Замена [p] на</p>
@detect_tree[] - определяет номер меню для текущего адреса в таблице menu и заносит полученный
ответ в переменную $last
@detect_uri[nmbr] - определяет строку запроса для номера меню $nmbr
@rusage[comment][v;now;prefix;message;line;usec] - процедура записывающая в файл rusage.log
данные о быстродействии программы и ведущая некоторую другую статистику.
Тело документа разделено на две части:
1) head
2) body
Это хорошо видно из определения класса @main[]
@main[]
^detect_tree[]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML LANG="ru">
<HEAD>
^head[]
</HEAD>
<BODY LEFTMARGIN="0" TOPMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0">
<CENTER>
^body[]
</CENTER>
</BODY>
</HTML>
^rusage[]
Класс @main[] - это основной класс, текст ответа, полученный от этого класса, будет передан
web-браузеру как гипертекстовый документ. Иными словами, все то, что выдаст этот класс, будет
интерпретировано web-браузером пользователя как обычный html-документ.
Вызов процедур ^detect_tree[] и ^rusage[] не возвращает текста, поэтому результат работы этих
процедур не будет влиять на текст ответа.
Процедура ^head[] создает объект класса class_head и вызывает метод @head_show[] этого
класса.
В методе этого класса содержится текст, который по стандарту HTML 4.0 [37] может содержаться
в теле тега <HEAD>
Пример:
<TITLE> $title </TITLE>
<!--META-->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1251">
<META NAME="discription" CONTENT="Рубцовский городской сайт">
<META NAME="org" CONTENT="ЗАО РУБЦОВСК">
<META NAME="keywords" CONTENT="">
<META NAME="distribution" CONTENT= "Global">
<META NAME="rating" CONTENT= "General">
<META NAME="resource-type" CONTENT="document">
<META NAME="revisit" CONTENT="15 days">
<META NAME="revisit-after" CONTENT= "15 days">
<META NAME="robots" CONTENT="ALL">
<META NAME="copyright" CONTENT="ЗАО РУБЦОВСК">
<META NAME="robots" CONTENT="all">
<!--LINK-->
<LINK HREF="/style-all.css" REL="stylesheet" TYPE="text/css">
<LINK HREF="/rubtsovsk.ico" REL="shortcut icon">
<!--SCRIPT-->
<script language="JavaScript" type="text/javascript">
var canRollOver = false;
var canClickDown = false;
</script>
Очевидно, что этот текст для разных сайтов будет различаться, по этому для простоты восприятия
текста, содержимое тела тега <HEAD> помешено в отдельный класс.
Процедура ^body[] возвращает текст, который может содержаться в теле тега <BODY>. Все
содержимое разбито на логические части:
1) Top
2) Body
3) Bottom
Для каждого из этих частей существует соответственно классы class_top, class_body,
class_bottom. В классах class_top, class_bottom, содержится текст, который привилегированный
пользователь не может изменить. Однако при переходе со страницы на страницу некоторые изменения
все же происходят. Например: при стандартном использовании, в верхней части сайта высвечивается
текущей url, с возможностью перехода на более высокие уровни. А класс class_bottom содержит
объект класса class_navigat, который отображает список меню, верхнего уровня. Однако системный
программист имеет доступ к этим классам, по этому изменение любого класса не составляет особой
проблемы.
Класс class_body, в свою очередь разбит на три части
1) Left
2) Center
3) Right
Все вместе это выглядит так:
тут типа картинка [ top ]
[l][cent][r]
[ bottom ]
Это далеко не обязательное представление вида сайта, все легко меняется при изменение класса
class_body и процедуры @body[] основного файла auto.p . Вот пример метода @body_show[] класса
class_body:
@body_show[]
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" WIDTH="100%" >
<TR>
<TD CLASS="global" ID="left" WIDTH="21%" VALIGN="top" >
<!--LEFT-->
^MAIN:terminator[164;6]
^show_left[]
</TD>
^if(def $MAIN:last){
^dbconnect{
$last_menu_item[^table::sql{SELECT nmbr
FROM menu_item
WHERE menu_nmbr=$MAIN:last and align=4}]
}}
^if(def $last_menu_item.nmbr || $MAIN:right is junction){
<TD CLASS="global" ID="center" WIDTH="58%" VALIGN="top">
<!--CENTER-->
^MAIN:terminator[442;6]
^show_center[]
</TD>
<TD CLASS="global" ID="right" WIDTH="21%" VALIGN="top">
<!--RIGHT-->
^MAIN:terminator(164;6)
^show_right[]
</TD>
}{<TD CLASS="global" ID="center" WIDTH="79%" VALIGN="top">
<!--CENTER-->
^MAIN:terminator[606;6]
^show_center[]
</TD>
}
</TABLE>
@show_left[]
$temp_menu[^class_menu::create[]]
^temp_menu.menu_show[]
^switcher[2]
^if($MAIN:left is junction){^MAIN:left[]}
@show_center[]
^switcher[3]
^if($MAIN:center is junction){^MAIN:center[]}
@show_right[]
^switcher[4]
^if($MAIN:right is junction){^MAIN:right[]}
При использовании этого метода и методов классов class_top и class_bottom в процедуре @body[]
получаем текст, который непосредственно отображается web-браузером пользователя. Метод класса
@body_show[] построен таким образом, что текст ответа для каждого url можно изменить
непосредственно в файле index.html. При этом если в файле index.html будут определены процедуры
@left, @center, @right, то текст этих процедур будет добавлен к ответу процедур @show_left,
@show_center, @show_right. Однако текст процедур@show_left, @show_center, @show_right также
можно переопределить в файле index.html! При этом все данные, которые были получены из базы и
оформлены в виде гипертекста, будут изменены на текст процедур из этого файла. Таким образом,
такой механизм удобно использовать для написания сложного гипертекста с использованием процедур
parser'a для конкретного url. Существуют другие методы написания не стандартного и сложного
гипертекста, однако они не предоставляют возможности использовать процедур parser'a. Это
сделано для разграничения полномочий между системным программистом и обычным привилегированным
пользователем, имеющим, доступ к сайту администрирования.
Определение оператора @body[] имеет вид:
@body[]
$temp_top[^class_top::create[]]
^temp_top.top_show[]
$temp_body[^class_body::create[$last]]
^temp_body.body_show[]
$temp_bottom[^class_bottom::create[]]
^temp_bottom.bottom_show[]
В данном случае создаются объекты классов class_top, class_body, class_bottom и вызываются
методы этих классов, отвечающих за отображение необходимой информации. В методе body_show класса
class_body подключаются необходимые классы для отображения специфичной информации из базы
данных. Для этого используется процедура @switcher[]. Эта процедура просматривает базу данных и
выбирает необходимые классы, которые обрабатывают информацию, присущую для данного url. Т.е.
каждому url соответствует выборка данных, которые нужно отобразить. Эти данные можно изменять,
добавлять и удалять с использованием сайта администрирования.
Процедура @switcher[] объявляется в файле auto.p и содержит тест:
@switcher[align]
^if(def $last){
^dbconnect{
$last_menu_item[^table::sql{SELECT kind, menu_nmbr, kind_nmbr, align
FROM menu_item
WHERE menu_nmbr=$last and align=$align
ORDER BY nmbr}]
}
^if($last_menu_item.align == $align){
^last_menu_item.menu{
$temp[$last_menu_item.kind]
$temp[^temp.split[ ;h]]
^if(-f "/classes/class_${temp.0}.p"){
^use[/classes/class_${temp.0}.p]
$temp_[^process{^^class_${temp.0}::create[]}]
^if($temp_.show_$align is junction){
^temp_.show_$align[$last_menu_item.kind_nmbr;$align]
}
}
}
}
}
Для каждого url в таблице menu_item содержится информация о тех классах, которые нужно
использовать для отображения информации. Процедура @switcher[] проверяет существование этих
классов и метод отображения, если класс и метод существуют, то управление передается методу
соответствующего класса и ответ метода этого класса добавляется в текст ответа сервера. Т.е.
если процедура @switcher[] вызвана из метода @show_left класса @body, и существует информация
для отображения, то будет вызван метод необходимого класса, а его ответ будет добавлен к ответу
метода @show_left.
Переменная $align процедуры@switcher[], содержит номер, соответствующий методу вызова. В данном
случае методы @show_left, @show_center, @show_right, имеют номера 2,3,4 соответственно. 1,5
зарезервированы для методов отображения классов class_top, и class_bottom. Кроме того, в
методе @show_left вызывается метод @menu_show[] класса class_menu. Этот метод создает пункты
меню и добавляет гипертекст, отображающий меню, в метод @show_left. Это также можно легко
изменить, если перенести создание объекта и вызов метода отображение меню в интересующую
процедуру или метод:
$temp_menu[^class_menu::create[]]
^temp_menu.menu_show[]
Таким образом, для корректной работы программы, необходимы следующие классы:
1) class_body
2) class_botom
3) class_head
4) class_menu
5) class_top
6) class_navigat
В базе, кроме того, необходимы таблицы: menu, menu_item, menu_kind, stat. При этом нет
возможности работать со специфической информацией.
Для использования специфических данных, необходима структура данных, записанная в базе в виде
таблицы или набора таблиц, а также класс и метод способный отобразить эти данные. В дальнейшем
совокупность таблиц и классов, работающих с специфической информацией, будем называть сущностью.
Имя класса выбирается для текущего url'а из базы, и при помощи процедуры @switcher[] программа
пытается его отобразить. Если такого класса или метода класса нет, то объект класса
уничтожается, т.е. сущность не отображается.
Все специфичные классы сущностей должны быть оформлены в следующем виде:
@CLASS
#служебное имя, только прописными буквами!
Имя_класса
# имя класса без пробелов, желательно английскими буквами, должно совпадать с именем файла.
@USE
#Служебное имя, только прописными буквами! Необязательный параметр.
Файл_с_родительским_классом
#методы родительского класса могут быть свободно использованы в данном классе, причем вызов
происходит динамически.
@BASE
#Служебное имя, только прописными буквами! Необязательный параметр.
Имя_родительского_класса
#В том случае, когда в качестве родительского класса выступает другой пользовательский класс,
необходимо подключить модуль, в котором он находится(@USE), а также объявить класс базовым
(@BASE).
@create[]
#конструктор класса, в этом методе обычно помешаются обращения к базе данных, однако он может
быть пустым.
@show_2[nmbr;align]
#Метод отображения данных заданного типа вызываемый из процедуры @show_left[] процедурой
@switcher[]. Поля $nmbr и $align передаются методу процедурой @switcher[]. $nmbr-уникальный
порядковый номер сущности в базе menu_item, а $align номер процедуры, из которой была вызвана
процедура @switcher[]
@show_3[nmbr;align]
#Метод отображения данных заданного типа вызываемый из процедуры @show_center[] процедурой
@switcher[]. Поля $nmbr и $align передаются методу процедурой @switcher[]. $nmbr-уникальный
порядковый номер сущности в базе menu_item, а $align номер процедуры, из которой была вызвана
процедура @switcher[]
@show_4[nmbr;align]
#Метод отображения данных заданного типа вызываемый из процедуры @show_right[] процедурой
@switcher[]. Поля $nmbr и $align передаются методу процедурой @switcher[]. $nmbr-уникальный
порядковый номер сущности в базе menu_item, а $align номер процедуры, из которой была вызвана
процедура @switcher[]
Должен существовать хотя бы один метод отображения!
Однако это только рекомендуемый метод описания класса. Можно использовать подклассы для выбора
вида отображения для одной и той же сущности. Например таким образом:
@CLASS
class_task
@create[]
@show_3[nmbr;align]
^show[$nmbr;$align]
@show[nmbr;align]
^dbconnect{
$type[^table::sql{SELECT type
FROM task_kind
WHERE nmbr =$nmbr}]
}
^if(-f "/classes/class_task_type_${type.type}.p"){
^use[/classes/class_task_type_${type.type}.p]
$temp_[^process{^^class_task_type_${type.type}::create[]}]
^if($temp_.show_$align is junction){
^temp_.show_$align[$nmbr]}
}
Как видно из примера создается один метод отображения @show_3[], таким образом, только вызов из
процедуры @show_center[] будет успешно завершен, вызовы из всех других процедур будут
проигнорированы. В методе @show[] мы обращаемся к базе и делаем выборку вида отображения.
Согласно этому виду, мы вызываем необходимый подкласс, проверяем подкласс на существование и
существование необходимых методов. Если все проходит успешно, то создается объект данного
подкласса и вызывается метод отображения, который возвращает некоторый текст.
Вернемся к файлу auto.p корневого каталог web- пространства. В этом файле так же определена
процедура @postprocess[body]. Эта процедура вызывается после формирования текста ответа, и может
выполнять с ответом любые манипуляции. В частности, по умолчанию, процедура имеет вид:
@postprocess[body]
^body.match[\n][g]{}
В таком виде процедура удаляет все символы конца строки из ответа, для уменьшения трафика.
Для обработки ошибки 404 (документ не найден) используются совместно средства parser'a и
web-серверa Apache. Так если не существует файл с расширением *.html то управление передается
parser'у. Для обработки этой ошибки в конфигурационном методе сайта auto.p, лежащем в каталоге
установки parser'а, используется процедура @unhandled_exception:
@unhandled_exception[exception;stack]
^if($exception.type eq 'file.missing' && !$stack){
## 404 handler
$exception.handled(1)
$response:location[/404.html]
}{
^unhandled_exception_release[$exception;$stack]
}
Т.е. если не найден документ, то отображается документ 404.html, иначе если другой тип ошибки,
то вызывается процедура ^unhandled_exception_release.
Со всеми другими типами расширений работает web-сервер Apache. Для обработки ошибки 404
используется директива ErrorDocument 404, которая прописывается внутри директивы <Virtualhost
*>.
Сайт администрирования. Процедуры, переменные, классы, методы и поля.
В файле auto.p сайта администрирования определены дополнительные переменные:
1) $adm_url[] - url сайта администрирования
2) $class[] - список сущностей доступных для использования.
Так же несколько отличаются и процедуры. Так класс @MAIN[] теперь строится с использованием
трех процедур:
1) @head[]
2) @config[]
3) @body[]
@main[]
^detect_tree[]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML LANG="ru">
^head[]
<BODY LEFTMARGIN="0" TOPMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0">
<CENTER>
^config[]
^body[]
</CENTER>
</BODY>
</HTML>
^rusage[]
Процедура @config[] используется для вызова конфигурационных методов. Иными словами, все данные
которые передаются web-браузером web-серверу Apache из форм, обрабатываются методами классов
вызываемых из данной процедуры. Процедура @config[] имеет вид:
@config[]
^config_top[]
^config_left[]
^config_center[]
^config_bottom[]
А процедуры, вызываемые из процедуры @config[] имею вид:
@config_top[]
$temp_top[^class_top::create[]]
@config_left[]
$temp_menu[^class_menu::create[]]
@config_center[]
^temp_menu.menu_config[]
@config_bottom[]
Таким образом, все изменения общедоступного сайта выполняются методом @menu_config[] класса
class_menu. Однако для изменения сущностей используются методы соответствующих классов,
вызываемых из метода @menu_config[] класса class_menu.
Процедура @body[] также несколько отличается от процедуры @body[] общедоступного сайта и имеет
вид:
@body[]
^show_top[]
$temp_body[^class_body::create[]]
^temp_body.body_show[]
^show_bottom[]
@show_top[]
^temp_top.top_show[]
@show_left[]
^temp_menu.menu_show[]
@show_center[]
^temp_menu.menu_show_config[]
@show_bottom[]
$temp_bottom[^class_bottom::create[]]
^temp_bottom.bottom_show[]
Такие изменения вызваны другими требованиями, предоставляемые к сайту администрирования. В
первую очередь это быстродействие. Второе - это изменение требований к возможности использовать
разный дизайн для отдельных url'ов. По этому все максимально упрошено. Все параметры передаются
в виде строки запроса методом get. Это является не безопасным при использовании на общедоступных
сайтах, однако доступ к данному сайту имеет ограниченное число привилегированных пользователей.
Класс @head[] остался прежним, однако произошли изменения в гипертексте. В случае неправильной
настройки, сайт администрирования может стать общедоступным, поэтому заголовок документа не
разрешает поисковым серверам индексировать данный сайт.
Метод @menu_config[] класса class_menu содержит процедуры, вызывающие методы администрирования
сущностей:
#add module
^if($form:module_type eq add){
^use[/classes/class_${form:module_name}.p]
$temp_[^process{^^class_${form:module_name}::create[]}]
^temp_.add[$nmbr]
}
#delete module
^if($form:module_type eq delete){
$temp[$form:module_name]
$temp[^temp.split[ ;h]]
^use[/classes/class_${temp.0}.p]
$temp_[^process{^^class_${temp.0}::create[]}]
^temp_.delete[$form:module_nmbr]
}
#config module
^if($form:module_type eq config){
$temp[$form:module_name]
$temp[^temp.split[ ;h]]
^use[/classes/class_${temp.0}.p]
$temp_[^process{^^class_${temp.0}::create[]}]
^temp_.config[$form:module_nmbr]
}
Каждая сущность должна иметь класс администрирования, содержащий методы @add[], @delete[],
@config[], @show[]. Именно эти методы вызываются в методе @menu_config[] класса class_menu.
Таким образом, класс администрирования сущности должен иметь вид:
@CLASS
#служебное имя, только прописными буквами!
Имя_класса
# имя класса без пробелов, желательно английскими буквами, должно совпадать с именем файла.
@USE
#Служебное имя, только прописными буквами! Необязательный параметр.
Файл_с_родительским_классом
#методы родительского класса могут быть свободно использованы в данном классе, причем вызов
происходит динамически.
@BASE
#Служебное имя, только прописными буквами! Необязательный параметр.
Имя_родительского_класса
#В том случае, когда в качестве родительского класса выступает другой пользовательский класс,
необходимо подключить модуль, в котором он находится(@USE), а также объявить класс базовым
(@BASE).
@create[]
#Конструктор класса, может быть пустым.
@add[nmbr]
#Метод добавления сущности к элементу меню с порядковым номером $nmbr
@delete[nmbr]
#Метод удаления сущности с порядковым номером $nmbr
@show[nmbr]
#метод отображения настроек сущности c порядковым номером $nmbr
@config[nmbr]
#метод настройки сущности с порядковым номером $nmbr
Пример простого класса администрирования:
@CLASS
class_navigat
@create[]
@add[nmbr]
^dbconnect{
^void:sql{INSERT INTO menu_item (menu_nmbr, kind, align)
VALUES ('$nmbr','navigat','4')}}
@delete[nmbr]
^dbconnect{
^void:sql{DELETE FROM menu_item
WHERE nmbr=$nmbr}}
@show[nmbr]
^dbconnect{
$navigat_table[^table::sql{SELECT nmbr, menu_nmbr, kind, kind_nmbr,align
FROM menu_item
WHERE nmbr=$nmbr}]}
<fieldset CLASS="module">
<FORM METHOD="post">
<BR>Центровка
$align[^table::create{align}]
^for[i](1;5){^if(-f "/html/classes/class_navigat.p"){
^use[/html/classes/class_navigat.p]
$temp_[^process{^^class_navigat::create[]}]
^if($temp_.show_$i is junction){
^align.append{$i}
} } }
<SELECT NAME="navigat_align" SIZE="1">
<OPTION SELECTED VALUE="$navigat_table.align">
^switch[$navigat_table.align]{
^case[1]{top}
^case[2]{left}
^case[3]{center}
^case[4]{right}
^case[5]{bottom}
}
</OPTION>
^align.menu{
<OPTION VALUE="$align.align">
^switch[$align.align]{
^case[1]{>^; top}
^case[2]{>^;left}
^case[3]{>^;center}
^case[4]{>^;right}
^case[5]{>^;bottom}
} </OPTION>
} </SELECT>
<br>
<INPUT VALUE="Изменить" TYPE="submit">
</FORM>
</fieldset>
@config[nmbr]
^if(def $form:navigat_align){
^dbconnect{^void:sql{UPDATE menu_item
SET align = '$form:navigat_align'
WHERE nmbr=$nmbr} }}
В данном примере пользователь может изменить только один параметр в базе. Имена всех
дополнительных сущностей должны быть прописаны в файле class.cfg. Формат файла tab-delimited.
Столбцы разделяются знаком табуляции, а строки -переводом каретки.
Таким образом, для полной работы с сущностью необходима структура данных в базе, класс
отображения информации и класс администрирования.
Разделение на классы администрирования и отображения информации кроме повышения уровня
безопасности позволяет разделить работу между сотрудниками и использовать сравнительно
неквалифицированных работников при изменении и добавлении определенных данных на сайт. Кроме
того, классы отображения информации содержат минимум программного кода, поэтому системному
программисту легко изменять методы отображения информации этих классов. Классы администрирования
содержат код, не изменяющий структуры данных и не нарушающий целостность данных. Эти классы
достаточно сложны для понимания, однако они могут быть использованы для множества разных сайтов.
Пакет программ может быть полностью изменен, так как доступен в виде исходных кодов. Так же
могут быть легко добавлены новые сущности. Для этого необходимо написать два класса, которые
будут работать с набором таблиц из базы данных. Рекомендуемая структура классов описана в данной
документации. При добавлении нового класса необходимо выполнить следующие действия:
1) Добавить класс администрирования в каталог ./classes сайта администрирования;
2) Добавить класс отображения информации в каталог ./classes общедоступного сайта;
3) Добавить имя класса в файл class.cfg в каталоге ./classes сайта администрирования;
4) Создать структуру данных и уровни доступа в базе данных при помощи пакета pgadmin2 или любым
другим доступным методом.
- как я ЭТО делал, или немного записок о p3, vasyan 18.07.2003 13:04