Комментарии - это html-формы и их обработка, а в PmWiki есть два принципиальных подхода к работе с формами:
- в которых результат обработки сохраняется в отдельной сущности;
- в которых результат обработки просто записывается в контент существующей страницы.
Исходя из принципа наименьшего сопротивления, рассмотрим второй - более простой случай.
Есть два рецепта для решения этой задачи: Cookbook:CommentBoxPlus и его производные и Cookbook:PmForm . Первый рецепт более популярен, второй находится в стадии бетатестирования, однако написан самим разработчиком PmWiki в соответствии с концепцией ядра системы и от того ближе к его философии и более конфигурабелен. Я выбрал второй вариант.
Процессинг форм с записью в контент страницы: PmForm
Установка PmForm и капчи
Установка формы стандартна. Интерес вызывает только последняя строчка, где описывается процессинг формы с именем savedata: результат обработки формы записывается в страницу ее вызова, форма генерируется по шаблону dataform, а результат форматируется по шаблону datapost (шаблоны описаны в Site.LocalTemplates):
include_once("$FarmD/cookbook/pmform.php");
$PmForm['savedata'] = 'saveto={$FullName} form=#dataform fmt=#datapost';
Сразу после установки рецепта Cookbook:Captcha , капча начинает по-умолчанию применяться ко всем формам, которые только найдет на сайте. Поэтому отредактировать страницу, например, станет невозможно - нужно будет вводить код. Нам не нужна защита от самих себя, а только от посетителей сайта, заполняющих форму комментариев. Забегая вперед, вот как это реализуется (другие предложения со страницы рецепта , почему-то не зарабаботали):
if ($action == 'browse' || $action == '' || $action == 'pmform')
$EnablePostCaptchaRequired = true; #требовать ввода для всех форм страницы только при перечисленных действиях
include_once("$FarmD/cookbook/captcha.php");
Вызов на странице
Чтобы вызвать форму на странице, воспользуемся синтаксисом (:pmform savedata:)
, где 'savedata' описана в config.php выше. Это надо будет поместить в GroupFooter, например, обрамив условием (:if exists {$FullName}:)
(чтобы форма не выводилась на несуществующей странице). Синтаксис (:messages:)
отвечает за обратную связь с пользователем - сюда будут выводиться ошибки, если они возникнут при заполнении формы (неверная капча, не заполнено обязательное поле), ее стоит поместить в GroupHeader.
Важное замечание: если по-умолчанию ваш сайт имеет пароль на редактирование, то перемещение (:pmform savedata:)
в GroupFooter или GroupHeader автоматически приведет к тому, что анонимусы потеряют возможность добавлять комментарии. Логика здесь PmForms такая: скрипт дает возможность изменять страницы пользователям с правами "read", но только на тех страницах, где собственно установлена форма. Иначе, путем подмены POST-запроса, теоретически, можно будет через форму осуществить запись в любую страницу сайта без прав на то. ОК, но только как же быть, если вызов формы находится в GroupFooter? Не знаю, что придумает PM, а пока он не придумал ничего, а я - ничего лучше, как не параноидальничать и вырезать эту проверку из исходника pmform.php вот так:
# if (!@$mark) {
# $page = RetrieveAuthPage($saveto, 'edit', true);
# if (!$page) return '$[Edit permission required]';
# }
Выход не очень хороший, зато действенный.
Шаблон формы
Самое хитрое: создаем, собственно, саму форму на системной странице шаблонов Site.LocalTemplates. Вот как это выглядело на момент написания статьи:
- [@
- [[#dataform]]
- (:div class="comment_post_block":)
- ''[++(:toggle show="Оставить комментарий " hide="Оставить комментарий:" commentpost:)++]''
- (:div2 id="commentpost" class="comment_post_form":)
- (:input pmform target=savedata :)
- (:input default request=1 :)
- (:input hidden author "{*$LastModifiedBy}" size=25 :)
- (:input hidden title "{*$Title}" size=25 :)
- (:input hidden ctime "{*$Created}" size=25 :)
- (:input hidden csum "новый комментарий" size=25 :)
- (:input hidden description "{*$Description}" size=25 :)
- (:input textarea text rows=5 cols=50:) \\
- Ваше имя: (:input text name:) \\
- {$Captcha} -> (:input captcha:)
- (:input submit name=post value="$[Отправить]" :)
- (:input end:)
- (:div2end:)
- (:divend:)
- [[#dataformend]]
- @]
Первые пять, а также 19 и 20 строчки относятся к внешнему представлению в виде кнопки и двух слоев, один из которых вложен в другой. Это оформление, к делу не имеет отношения. Следующие две строчки - инициализация формы, детали можно узнать на странице рецепта.
Особый интерес представляют строчки с 8 по 12, включительно. Дело в том, что почему-то и PmForm, и CommentBox при добавлении поста на страницу не просто дописывают новый блок информации в контент, но переписывают всю страницу, удаляя при этом все переменные страницы (PV), которые на ней присутствовали до того! Это кажется очень странным и не логичным, и, наверное, будет как-то пофиксено и учтено при дальнешей разработке PmForm, по крайней мере запрос я отправил.
Чтобы обойти данную особенность, в 8-12 строчках все переменные страниц, введенные на сайте, считываются с текущей страницы и записываются в форму, в скрытом виде. Также туда записывается значение для csum, которое на моем сайте особо не используется, но удобно для отслеживания событий на сайте на странице Site.AllRecentChanges, например.
Строчки 13-17, по-моему, очевидны: это собственно и есть простое и прозрачное, практически визуальное программирование формы. Именно ради этой простоты мы и прошли все предыдущие козни.
Шаблон поста
- [@
- [[#datapost]]
- (:template defaults where=bottom :)
- (:template require name errmsg="$[Незакомментилось: представьтесь, пожалуйста]":)
- (:template require text errmsg="$[Незакомментилось: напишите что-нибудь]":)
- (:nl:)>>messagehead<<
- !!!!!{$$name} — [-{$$CurrentTime}-]
- >>messageitem<<
- {$$text}
- >><<
- [[#datapostend]]
- @]
Здесь 3 строчка описывает координаты для помещения комментария, важно указать значение "bottom" или "top", если вы собираетесь размещать вызов формы в GroupFooter, ведь для возможных значений "above" или "below" (над или под собственно формой) у скрипта не будет привязки, так как вызова формы в этом случае на самой странице не будет. 4 и 5 строчки перечисляют поля, обязательные для заполнения и текст, который выведется в (:messages:)
, если они не будут заполнены. 6-10 строчки - это формат результирующего поста, который, собственно, и будет записан на страницу.
Комментарии готовы.
ToDo-лист
Пока что данный функционал не предоставляется движком, но его частично можно внести самостоятельно, либо дождаться, пока его внедрит разработчик. Вот что хотелось бы добавить:
- факт всякого постинга с любым результатом должен приводить к редиректу на якорь
#commentform
этой же страницы, стоящий прямо перед вызовом формы, где и должен отображаться результат операции. Это более юзабильно, чем размещение(:messages:)
в GroupHeader. - в формы надо научиться внедрять javascript, чтобы они заполнялись тектом по умолчанию (например, "ваше имя"), исчезающим при наведении фокуса
- ну и, самое главное, хотелось бы переделать комментарии так, чтобы они умели просто добавлять новый блок информации на страницу, не трогая существующие переменные страницы вообще. Существующее решение хоть и работоспособно, но архитектурно криво и сомнительно с точки зрения безопасности.
- выход из положения с правами при вызове из GroupFooter
Оставить комментарий