www.textpattern.ru

Статьи и Документация

Пишем плагины для Textpattern

Автор: nicck
Дата статьи: 23.10.2005

По традиции следует сказать, что статья не претендует на роль полной документации по написанию плагинов, многие лучше знают систему, и вам решать верить тому, что написано ниже, или нет. В ходе повествования я планирую провести вас по процессу написания плагина и рассказать о тех шагах, что пришлось мне предпринять для получения результата.

Подготовка

Итак, что же вам понадобится для написания плагина:

  • Установленный Textpattern1
  • Ваш любимый редактор кода с подсветкой синтаксиса и прочими полезностями
  • Что-либо для компиляции плагина. Я использовал Textpattern plugin template от товарища zem
  • phpMyAdmin или что-то, чем можно посмотреть нашу БД (например, плагин rss_admin_db_manager)
  • Знание PHP, не обязательно глубокие, достаточно того, что вы когда-то с помощью PHP обрабатывали данные, полученные из формы, или что-то в этом духе.

Далее, имея в виду инструкции Scencer’a, сделаем следующее:

  • создадим папку /full/path/www/txp/dev2
  • Перейдём к расширенным настройкам: Admin › Preferences › Advanced preferences
  • Установим параметр plugin_cache_dir равным /full/path/www/txp/dev

Теперь код плагинов будет браться не только из базы, но и из php файлов, помещенных в папку, на которую указывает plugin_cache_dir. Т.о. мы сможем проверить, как работает наш плагин, не компилируя и не устанавливая его в систему, что значительно ускорит отладку.

1 При написании плагинов, рассматриваемых в этой статье, использовалась версия 4.0.1 от 6 сентября 2005 г.

2 Здесь и далее /full/path/www/txp это путь к корню сайта и папка, в которую установлен Textpattern.

Тэги

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

Одиночный

Тэг, который сам себя закрывает, так сказать self-closing tag, например <txp:permlink /> или <txp:css n="print" />. Мой самый первый плагин реализовывал именно такой:

<txp:nck_plink title="мой title" />

Предполагалось использовать тэг в шаблоне форм, он должен был работать, как и <txp:permlink /> с той лишь разницей, что title для ссылки я указываю свой и генерируется следующий html:

<a href="тут/ссылка" title="мой title">01 января 05</a> | понедельник

Сейчас, конечно, уже можно решить эту задачу куда правильней, чем через новый одиночный тэг, но тогда было проще именно так.

Начнём. Когда система встречает в шаблоне тэг <txp:nck_plink title="мой title" /> она пытается вызвать функцию nck_plink передав ей в качестве аргумента ассоциативный массив с атрибутами тэга, если конечно эти атрибуты есть.
В нашем случае они есть и значит, передаётся массив array("title"=>"мой title")

Для извлечения атрибутов есть уже устоявшаяся конструкция:

function nсk_plink($atts)
{   
    extract(lAtts(array(    // извлекаем атрибуты
        'title'  => '',     // "атрибут" => "значение по умолчанию"
    ),$atts));
...

Она используется при обработке стандартных тэгов системы, поэтому, если Вы вдруг забыли, как это делается, её всегда можно найти в publish/taghandlers.php

Итак, title у нас есть. Осталось получить данные о статье: ссылку и дату. Хранятся они в глобальной переменной $thisarticle которая представляет собой ассоциативный массив. В publish.php в функции populateArticleData можно посмотреть, чем заполняется массив $thisarticle. Видим, что из него мы можем получить только дату: $thisarticle['posted']; URL’а там нет. Откуда брать? Смотрим откуда его берут авторы CMS, когда обрабатывают тэг permlink. Для этого ищем в taghandlers.php функцию permlink и видим сразу после извлечения атрибутов строку $url = permlinkurl($thisarticle); Отлично, поступим точно так же:

...
    global $thisarticle;
    $date = $thisarticle['posted'];   // дата статьи
    $url = permlinkurl($thisarticle); // ссылка на статью
...

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

function nсk_plink($atts)
{   
    global $thisarticle;

    extract(lAtts(array(   // извлекаем атрибуты
        'title'  => '',    // "атрибут" => "значение по умолчанию"
    ),$atts));

    $wdayarr = array ('понедельник', 'вторник', 'среда', 'четверг',
                      'пятница', 'суббота', 'воскресенье');
    $wmonarr = array ('января', 'февраля', 'марта', 'апреля',
                      'мая', 'июня', 'июля', 'сетября', 'октября',
                      'ноября', 'декабря', 'января');

    $date = $thisarticle['posted'];   // дата статьи

    $url = permlinkurl($thisarticle); // ссылка на статью

    $day = date("d", $date);          // день месяца
    $year = date("Y", $date);         // год с тысячелетием

    $wmon = $wmonarr[date("m",$date)-1]; // название месяца
    $wday = $wdayarr[date("w",$date)];   // день недели

    // title, если был задан
    $title = !empty($title)?' title="'.$title.'"':'';

    return("<a href="$url"$title>$day $wmon $year</a> | $wday");
}

Этот код замечательно работает и по сей день, но его не следует использовать, и вот почему:

  • значение по умолчанию нужно бы брать из языкового файла (это сделать не долго, нужно использовать функцию gTxt() из txplib_misc.php)
  • надо бы добавить возможнсть применять тэг ещё и как парный:
    <txp:nck_plink title="Cцылка на криатифф">Зоголовок</txp:nck_plink>
  • можно просто переопределить функцию, которая вызывается при обработке тэга txp:permlink и нам не придётся вводить в шаблоны новый тэг

Сейчас мы делть этого не будем, но непременно рассмотрим всё пречисленное, позже.

Парный

Что такое парный тэг вы уже, я надеюсь, понимаете. Пример:

<txp:link_to_home>Home</txp:link_to_home>

Отличие при обработке от одиночного состоит в том, что в функцию вторым аргументом передаётся строка, которую тэг содержит. Всё, что касалось одиночного тега естественно справедливо и для парного.

Вот пример плагина реализующего парный тэг:

function nck_comment($atts, $thing) {
    return("<!-- $thing -->");
}

Используем в тексте статьи так:

<txp:nck_comment>Текст комментария</txp:nck_comment>

В результате имеем:

<!-- Текст комментария -->

Что замечательно, можно реализовать тэг, который будет возвращать разный результат в зависимости от того как мы его используем: как одиночный или как парный. Таким свойством обладает, в частности, тэг <txp:permlink />. Посмотрим в paghandlers.php как это сделано

function permlink($atts,$thing=NULL) // если тэг одиночный,
                                     // то при вызове
                                     // процедуры второй параметр передан
                                     // не будет и переменной $thing будет
                                     // присвоено значение NULL
{
...
if ($thing===NULL) {                 // если тэг одиночный
    return $url;                     // просто возвращаем url
}
                                     // иначе делаем ещё 
                                     // и <a href="$url"...

В результате получаем очень полезный тэг. Напомню его поведение:

В шаблоне В результате
<txp:permlink /> http://localhost/news/11/sometitle
<txp:permlink>link text</txp:permlink> <a href="http://localhost/news/11/sometitle" title="Permanent link to this article">link text</a>

Как вы понимаете, мы, получив текст, который содержится внутри нашего тэга, вольны делать с ним, что захотим:

  • вернуть не тронутым
  • вернуть изменённым
  • не вернуть (вернуть ничего)
  • вернуть обработанным

Под последним пунктом подразумевается вот что:

<txp:наш_тэг>текст<txp:nck_comment>Текст комментария</txp:nck_comment>текст</txp:наш_тэг>

Как вы понимаете, можно вернуть текст:

текст<txp:nck_comment>Текст комментария</txp:nck_comment>текст

Но логичнее конечно позволить системе обработать txp:nck_comment:

текст<!-- Текст комментария -->текст

Занимается эти функция parse($thing). Хочется только предупредить, что тэги наши, если используются в статье, окружают уже “оттекстиленный” текст и, как следствие, предотвратить превращение < в &lt; с помощью подобного плагина не получится.

Итак, возвращаясь к тому, что мы вольны делать с $thing всё, что захотим, мы можем отдать parse($thing), а можем и не отдать, в зависимости от, например, какого либо атрибута. Вот тут мы подошли к тэгам, реализующим условие.

Условие

Тэг выглядит примерно следующим образом:

<txp:if_something cond="yes">
если да
<txp:else/>
если не да
<txp:if_something>

Единственное, что отличает этот тэг от парного – содержащийся в нём тэг <txp:else />

Для его обработки в системе существует функция Evalelse($thing, $condition) которая возвращает текст до тэга <txp:else /> если условие condition истинно или текст после, если ложно. Условие не обязано зависеть от атрибута. В нашем очередном примере оно и не зависит, у тэга даже нет этих атрибутов.

Не так давно на форуме один участник хотел выводить на определённой странице только статьи с датой публикации как у последней.

Как быть? Казалось бы, нужно найти все статьи с подходящей датой. Но я предлагаю ничего не искать, а просто взять и написать плагин реализующий тэг который будет определять является ли текущая статья подходящей или нет. Текущая – это какая? Это та, которую система обрабатывает в данный момент. Значит, в другой момент она обрабатываем другую. Значит, система обрабатывает много статей. А когда система обрабатывает много статей? Когда выводит список статей. А где список статей, там и шаблон формы каждой статьи. Через этот то шаблон и проходят все выводимые статьи. Сделаем так чтоб проходили лишь подходящие. Вот так этот шаблон будет выглядеть:

<txp:nck_if_article_day>
шаблон формы статьи с датой как у последней,
например, нас интересуют только заголовки: <txp:title />
[ </txp:else />
шаблон формы статьи с датой НЕ как у последней,
в нашем случае, мы выводим "ничего"
и потому эту часть формы можно опустить ]
</txp:nck_if_article_day>

Собственно, что нам нужно:

  1. вычислить условие (дата статьи равна дате последней публикации?)
  2. return parse(Evalelse($thing, $condition));

Итак, код плагина с подробными комментариями:

function nck_if_article_day($atts,$thing) 
{
// нам понадобятся данные о статье, чтоб определить, выводить её или нет
  global $thisarticle;
//
// Теперь выясним дату последней статьи.
// В таблице textpattern (содержит все статьи) поле Posted определяет
// дату статьи. Выбираем из таблицы textpattern поля Posted причём
// отсортированные в порядке убывания, нам же нужна последняя?
// Нужна только одна дата. Получаем примерно следующий запрос:
//    SELECT Posted
//    FROM textpattern
//    ORDER BY Posted DESC 
//    LIMIT 1
// В классе lib/txplib_db.php есть вобще несколько функций выборки данных,
// но я выбрал почему-то safe_row($things, $table, $where, $debug='')
// Вот это:
//    ORDER BY Posted DESC 
//    LIMIT 1
// мы засунем в $where. Оно там(в функции safe_row) внутри используется
// как "where $where" потомуконечный запрос будет выглядеть так:
//    SELECT Posted
//    FROM textpattern
//    WHERE 1
//    ORDER BY Posted DESC 
//    LIMIT 1
// Ну, вообщем вызываем:
//
  $rs=safe_row("Posted","textpattern","1 order by 'Posted' desc limit 1");
//
// в $rs["Posted"] мы получили что-то вроде 2005-07-09 10:11:12,
// нас интересует только дата, то, что до пробела. Выкусим:
//
  list($date,$time) = split(' ',$rs["Posted"],2);
//
// UNIX timestamp текущей статьи содержит дату и время публикации
// текущей статьи находится в $thisarticle['posted'].
// Преобразуем к полученному из базы формату и сравниваем:
//
  $condition = date("Y-m-d",$thisarticle['posted'])==$date;
//
// Вот собственно и всё, теперь парсим то, что до <txp:else /> если
// даты совпадают и то, что после него если не совпадают.
//
  return parse(Evalelse($thing, $condition));
}

Вы этот код могли видеть на форуме, я его уже публиковал. Там же я говорил, что есть способ оптимизировать функцию. Повторюсь.
Допустим, мы в шаблоне страницы вызываем форму так:
<txp:article listform="only_last_day" limit="10" />
Тогда функция nck_if_article_day вызывается 10 раз и 10 же раз выполняется запрос к базе для выяснения последней даты. Вообще говоря, результат возвращается один и тот-же, и потому достаточно делать запрос лишь первый раз. Для того, чтоб определить что функция вызвана первый раз используем static переменную с первоначальным значением "first", затем будет хранить в ней дату последней публикации. Прочтите о использовании статических переменных если не до конца понимаете их поведение.

Окончательный код:

function nck_if_article_day($atts,$thing)
{
  static $date="first";  // переменная, содержащая дату последней статьи
  global $thisarticle;
  if("first"==$date)     // если функция вызвана первый раз
  {                      // узнаем дату последней статьи
    $rs = safe_row("Posted",
                   "textpattern",
                   "1 order by 'Posted' desc limit 1");
    list($date,$time) = split(' ',$rs["Posted"],2); // запомнили дату
  }
  $condition = date("Y-m-d",$thisarticle['posted'])==$date;
  return parse(Evalelse($thing, $condition));
}

Вот. Не сложно, правда? Вообще, лучшим учебником на эту тему является файл taghandlers.php, очень рекомендую.

Плагины админ. части

Тут всё ещё интереснее. Вообще говоря, код плагина подключается всегда: и когда мы смотрим на сайт как посетители, и когда заходим как пользователи системы textpattern. Но часто нужно бывает определять некоторые функции лишь для админ-части системы. Разделить код мы можем так:

// общий код (выполняется и там и там)
// например, инициализация каких-либо глобальных переменных плагина
if ('admin' == @txpinterface)
{
// admin-side
// код, добавляющий новый интерфейс для пользователя системы
// и обеспечивающий обработку его действий
}
else
{
// publish-side
// преимущественно код реализующий тэги или код который зависит
// от посетителей (того, что они запрашивают и т.п.)
}
// общий код

Учиться будем опять на примере. Разберём плагин nck_olm (override link mode).
Вот кусок из хелпа, чтоб было понятно, о чём речь:

Плагин nck_olm добавляет новую закладку в интерфейс администратора Textpattern и даёт вам возможность переопределить permanent link mode для любой секции. Ссылки на статьи выбранных секций будут сгенерированы (тегом txp:permlink) в соответствии с выбранной схемой. Созданные ссылки будут нормально обрабатываться без дополнительных плагинов.

Итак, что он будет делать:

  • помнить настройки
  • переопределять поведение стандартного тэга txp:permlink
  • обрабатывать url не соответствующий link mode по умолчанию

Начнём с настроек. Самый простой вариант, взать, да и там где я обозначил ”// общий код” написать:

$nck_olm_prefs = array("about"=>"section_title","news"=>"id_title");

И очень часто так и делают, вполне разумно и универсально, но есть недостатки:

  • тот, кто может изменять настройку плагина обязан иметь права на редактирование плагина
  • можно ввести не валидное значение (не существующей секции или не существующий link mode)

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

Первым делом определим глобальные переменные плагина:

global $nck_olm, $nck_olm_prefs;
$nck_olm = array (
    "ver" => "1.1",
);

В $nck_olm_prefs будем держать настройки, в $nck_olm всяческие текущие вещи, как то версия плагина, текущая ошибка и пр.

Если мы собрались хранить настройки плагина в базе, то следует держать их в таблице txp_prefs, именно там хранятся настройки системы. Значения из этой таблицы извлекаются системой в область глобальных переменных (это происходит в publish.php).

Структура таблицы txp_prefs:

prefs_id name* val* type event html position
номер набора настроек глобальная переменная в которую будет извлечено значение значение,
255 символов
тип событие системы представление
в html
позиция

* Вообще говоря, только эти поля имеют значение.

т.о. если $nck_olm_prefs у нас не определено, значит в таблице этого значения нет, и следует его в таблицу добаваить (вероятно плагин запущен первый раз).

// if prefs is absent insert it in txp_prefs table
if (!isset($nck_olm_prefs))
{
    $nck_olm_prefs = serialize(array());
    //:KLUDGE:надо бы не всё в одной строке хранить, 
    // ибо в 255 всё не влезет
    safe_insert("txp_prefs",
                "prefs_id = '1',
                 name     = 'nck_olm_prefs',
                 val      = '$nck_olm_prefs',
                 event    = 'nck_olm'"
    ) or $nck_olm['err'] = "Can't save preferences"; //:TODO: gTxt()
}

Теперь $nck_olm_prefs определено и содержит “serializ’ованный” набор настроек. Извлекаем:

// get preferernces
$nck_olm_prefs = unserialize($nck_olm_prefs);

т.к. сохранение настроек происходит при нажатии на кнопку Save на закладке настроек плагина, а до интерфейса мы ещё не дошли. т.е. достать из базы ничего интересного мы не можем, т.к. не можем ничего интересного туда записать. Хотя есть phpMyAdmin, можем записать, но не будем. Просто пока поставим затычку, притворимся, будто достали из базы array("about"=>"section_title"):

$nck_olm_prefs = array("about"=>"section_title")

В админке же установим режим по умолчанию: section/id/title
т.е. мы хотим использовать section_id_title для всех ссылок, кроме ссылок на статьи секции about.

С настройками пока разобрались. Теперь сделаем так чтоб txp:permlink генерировал ссылки в соответствии с нашей схемой:

  • вида section/title для статей секции about
  • вида section/id/title для всех прочих

Ищем в yaghandlers.php функцию, реализующую интересующий нас тэг. Находим. Видим, что в функции permlink для генерации url используется функция permlinkurl, вот её начало:

function permlinkurl($article_array) 
{
  global $permlink_mode, $prefs;
  if (isset($prefs['custom_url_func'])
      and is_callable($prefs['custom_url_func']))
    return call_user_func($prefs['custom_url_func'],$article_array);
  if (empty($article_array)) return;
...

Как видите, если пользовательская функция определена, то именно она и вызывается. Определим её.

// define custom permlinkurl() function
$prefs['custom_url_func'] = "nck_olm_permlinkurl";

Теперь нужно написать функцию nck_olm_permlinkurl. Для начала изучим оригинальную функцию и обнаружим, что вид ссылки зависит от глобальной переменной $permlink_mode которая содержит значение link mode установленное в админке. Нам нужна функция точно такая как оригинальная permlinkur, не генерирующая ссылку не в зависимости от установленного в системе значения, а исходя из секции статьи на которую ссылаются, а так же массива $nck_olm_prefs содержащего настройки.

Для того, чтоб не переписывать всю функцию мы просто на время изменим глобальный режим permanent link mode на нужный нам и вызовем permlinkurl(). Это приводит так же к тому, что если вдруг в будущих версиях добавятся новые permlink mode плагин будет их поддерживать автоматически! Ближе к коду:

// generate per section permanent link mode
function nck_olm_permlinkurl($article_array) {
  if(empty($article_array)) return;
  global $nck_olm_prefs, $permlink_mode, $prefs;
// сохраним default link mode
  $def_mode = $permlink_mode;
// определим секцию статьи
  $section = $article_array['section'];
  if(empty($section)) $section = $Section;
// если для этой секции в настройках определён link mode
// меняем глобальный на него
  if(isset($nck_olm_prefs[$section]))
    $permlink_mode = $nck_olm_prefs[$section];
  unset($prefs['custom_url_func']); // для предотвращения рекурсии
  // генерим url
    $url = permlinkurl($article_array);
  $prefs['custom_url_func'] = "nck_olm_permlinkurl";
// восстановим default link mode
  $permlink_mode = $def_mode;
  return($url);
}

Вот так, теперь ссылки генерируются. Пора подумать о том, как заставить систему адекватно их обрабатывать. Для этого смотрим исходники плагинов, которые этим занимаются, исходники системы, и находим в publish.php это:

    // if messy vars exist, bypass url parsing
  if (!$out['id'] && !$out['s']) {

Кав видно из кода и комментария, url обрабатывается только если не определён $out['id']. ID попадает туда так:

    // set messy variables
  $out =  makeOut('id','s','c','q','pg','p','month','author');

А т.к. код плагина подключается и выполняется до этих манипуляций, мы, записав $_GET['id'] ID нашей статьи заставим систему сразу отдать нам нужную статью. Теперь другая проблема: надо как-то узнать из url ID статьи. Как обычно смотрим как это реализовано в самой системе. Обнаруживаем, что занимается этим функция pretext($s,$prefs), она, исходя из установленного link mode (передаётся со всеми настройками вторым параметром), находит статью и возвращает массив с кучей полезной информации (её мы обычно используем в тэгах) из которой нас интересует ID статьи и секция (для того, чтоб переопределённый link mode применялся только к той секции, для которой мы его переопределили).

Теперь то же самое на более понятном языке, хотя не факт, что это поможет.

if ('admin' = = @txpinterface) // между равенствами нет пробела
                               // textile что-то глючит
{
// это мы будем писать чуть позже
}
else // отработаем url для publish-side 
{
  // пока мы не нашли ID, для какждой переопределённой секции
  while (!is_numeric($_GET['id']) 
                       and list($name,$mode)=each($nck_olm_prefs))
  {
    // достанем информацию о статье
    $pretext = pretext('',array('permlink_mode' => $mode));
    // и если статья доступная по этому url принадлежит
    // секции для кторой мы переопределили url на подобный
    // укажем ID
    if ($name = = $pretext['s']) $_GET['id'] = $pretext['id'];id's
               // между равенствами нет пробела, textile глючит
  }
}

Этот код позволяет отдавать статьи определённых секций по переопределённой схеме. Они так же будут отдаваться и по схеме по умолчанию, это не очень хорошо, но не страшно. Я решил этого не исправлять. Как и в прошлый раз, мы опять использовали функции системы и, как следствие, в случае увеличения количества доступных в textpattern url-схем не заставит нас дописывать что-то в плагине.

Вот собственно и всё, если хранить настройки в коде плагина. Но мы решили хранить в базе и делать интерфейс. Приступим.

admin-side интерфейс

Добавим закладку и определим функцию, вызываемую при переходе на неё:

if ('admin' = = @txpinterface)
{
// Добавим закладку 'Link mode' для события 'nck_olm'
    register_tab("extensions", "nck_olm", "Link mode");
// по событию 'nck_olm' будем вызывать функцию 'nck_olm_adm'
    register_callback("nck_olm_adm", "nck_olm");
}
// дальше мы уже писали...

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

// admin-side interface
function nck_olm_adm($event, $step)
{
  // в этой переменной будем держать строку которая выводится
  //   в левом верхнем углу интерфейса (назовём её строкой статуса)
  // Обычно там пишут "Статья сохранена" и т.п. 
  // Так вот, ошибки мы будем хранить в $nck_olm['err']
  // и если они есть то выводить мы их будем жирным и красным
  $stat = isset($nck_olm['err'])
        ? $stat=tag($nck_olm['err'],'b',' style="color:red"')
: '';
  // ну и выводим значит "Override link mode" в заголовке окна браузера
  // и $stat в статусной строке
  pagetop("Override link mode", $stat );
...

Да, чтоб создавать интерфейс в функциях содержащихся в lib xplib_html.php вам нужно ориентироваться. Они предназначены для того чтоб быстрого и удобного заворачивания данных в HTML. Например, hed("text",4) вернёт <h4>text</h4>. Так же следует обратить внимание на gTxt(). Работает она так: gTxt('password_changed') Вернёт Password changed если выбран английский или Пароль изменён если выбран русский.

Выведем заголовок что-ли:

  echo "<div align="center">",
  hed("Override Link Mode",3);

Так, пора форму делать. Предполагается, что она будет представлять собой таблицу: первый столбец – имя секции, второй – select с link mode для неё. Секции, для которых режим не переопределён, отображаются серым.

Элемент select содержащий список доступных режимов вобще-то уже есть в системе, и создаёт его функция permlinkmodes() из файла /include/txp_prefs.php. Переписывать не будем, просто подключим его и используем эту функцию.

$fs = array(); // тут будем накапливать "тело формы"
  include_once txpath.'/include/txp_prefs.php';
  // выбираем из базы список секций (кроме default)
  $rs = safe_rows_start("name,title", "txp_section",
                        "name != 'default' order by name");
  if ($rs)
  {
  $fs[] = startTable('list'); // начинаем рисовать таблицу
  $fs[] = assHead(            // шапка
    gTxt('section_name'),     // Section name
    gTxt('permlink_mode')     // Permanent link mode
  );
  while ($a = nextRow($rs))
  {
    extract($a);              // extract $name and $title
    // если для секции перегружен link mode
    if (isset($nck_olm_prefs[$name]))
    {
      $cur = $nck_olm_prefs[$name]; // по умолчанию в селекте
      $attr = ' style="background:#fcfcfc"'; // выделим фон цветом
    }
    else // если для секции ничего не перегружено
    {
      $cur = $permlink_mode; // в селекте будет режим системы
      $attr = ' style="color:#ccc"'; // и серым цветом название
    }
    $title = ''; // на будущее, не берите в голову. ;) 
//:TODO:    $title = $title = = $name ? '' : " | $title" ;
    $fs[] = tr(
      // собственно добавляем строку в таблицу
      // вот тут-то и используем функцию из подключенного выше файла
      tda($name.$title,$attr).tda( permlinkmodes($name,$cur), $attr )
    );
  }
  $fs[] = tr(  // кнопка 'Save' или 'Сохранить', от языка зависит
    tda(
      fInput("submit","save_button_submit",gTxt('save_button'),"publish"),
      ' colspan="2" class="noline"'
    )
  );
  $fs[] = endTable(); // конец таблицы
}
  // очень важны элементы формы eInput и sInput
  // собственно с их помощью система и определяет
  // какая функция должна обрабатывать данные формы (eInput)
  // и какой собственно это шаг (sInput)
echo form(
  join("
", $fs).
  eInput("nck_olm").sInput("save")
  ," style='text-align:center'"
);

Вывели форму. Теперь по нажатию кнопки Save данные отправятся в событие nck_olm т.е. в функцию nck_olm_adm. Вторым параметром уйдёт шаг. Реагируем так:

if ("save" = = $step)
{
  // получим имена секций
  $rs=safe_column('name','txp_section',"name!='default' order by name");
  // переопределим настройки нашего плагина
  foreach ($rs as $val)
  if ($rs[$val]=ps($val)
    and ($permlink_mode = = $nck_olm_prefs[$val]=$rs[$val]) )
unset($nck_olm_prefs[$val]);
  // созраняем в базу с serialize'ованном виде
  $rs = safe_update(
    "txp_prefs",
    "val = '".serialize($nck_olm_prefs)."'",
    "name = 'nck_olm_prefs'"
  );
  $stat = ( $rs ) // ну и формируем текст статусной строки
    ? gTxt('preferences_saved')
    : tag(gTxt('not_saved'),'b',' style="color:red"');
}

Всё вроде. Осталось всё в кучу собрать.

А статья мне эта уже надоела, да и чем дольше я её пишу, тем больше она мне не нравится. Не буду больше писать, не получается, но вот на конкретные вопросы на форуме отвечу, если не понятно что-то.

P.S. Textile для написания таких статей лучше не использовать, код вставлять очень неудобно. Надо какое-то другое решение искать.

Обсуждение этой темы в форуме

 
Rambler's Top100