ДокументацияПрограммирование → web-модулей
Открываем файл /usr/local/nodeny/cfg/web_plugins.list и видим:
demo   0   demo    Тестовый модуль

Первый параметр (demo) код модуля, который передается в адресной строке в параметре «a»: http://xxx/cgi-bin/stat.pl?a=demo. 3й параметр указывает на файл без расширения pl. Т.е. если в адресной строке, кроме прочих параметров будет присутствовать a=demo, то будет выполнен скрипт demo.pl:

Создаем файл /usr/local/nodeny/web/demo.pl
use strict;
sub go
{
    ToTop 'Вас приветствует тестовый модуль';
    Error 'Он ничего не делает, но выводит окно с ошибкой';
}
1;
В html, который формирует NoDeny, есть 4 основных области: верхняя, левая, правая и центральная:
use strict;

sub go
{
    ToTop 'Строка вверху страницы';
    ToLeft 'Строка в левой части экрана';
    ToRight 'Строка в правой части экрана';
    ToTop 'Еще одна строка вверху страницы';
    Error 'Ошибка';
    Show 'Эта строка не будет выведена т.к. Error() не только выводит ошибку, '.
         'но и завершает выполнение модуля';
}
1;

Движок NoDeny позволяет формировать html разными способами.

Например, непосредственная вставка html-кода:
Show "<p><b>Не рекомендую писать html в коде.</b> ".
     "<span class='error'>Это строка напечатана красным цветом</b></p>";
Более сложный пример:
use strict;

sub go
{
    my($Url) = @_;
    if( ses::input_exists('num') )
    {
        my $num = ses::input('num');
        $num =~ /^\d+$/ or Error 'Вы не ввели целое положительное число';
        Show Center "OK. Вы ввели $num";
        return;
    }


    my $form = $Url->form(
        _('[p][p h_center][p h_center]',
            'Введите целое положительное число:',
            v::input_t(name=>'num'),
            v::submit('Далее')
        )
    );

    Show Center MessageBox( $form );
}
1;

Переменные, которые были переданы браузером (POST или GET параметры) доступны через ses::input('имя переменной'). В примере ses::input('num') возвращает параметр с именем num. Т.е. если ввести в адресной строке http://../stat.pl?a=demo&num=55, то ses::input('num') вернет 55.

ses::input_exists('num') проверяет была ли вообще передана переменная num. При открытии нашего плагина, в адресной строке не будет num, поэтому условие if( ses::input_exists('num') ) не выполнится и сработает код:

my $form = создание формы ввода;
Show отображение формы ввода;

my $form = $Url->form(...) создает в переменной $form код html-формы, в которой находится текстовое поле ввода с именем num, а также кнопка «Далее». Читайте здесь о создании форм. Функция _() похожа на sprintf - в первом параметре задается шаблон, остальные параметры подставляются в этот шаблон. Шаблон кодируется квадратными скобками и в большинстве случаев конвертируется по такому правилу:

[имя_тега класс]<имя_тега class='класс'></имя_тега>

код perl
ToTop _('Здравствуйте, [span bold]. Сегодня [span big]', 'Администратор', 'Пятница!');

преобразуется в Здравствуйте, <span class='bold'>Администратор</span>. Сегодня <span class='big'>Пятница!</span>



Задача: вывести ФИО всех клиентов, у которых баланс меньше нуля
use strict;

sub go
{
    my($Url) = @_;
    Adm->chk_privil_or_die(30);

    my $db = Db->sql("SELECT id, name, balance FROM users WHERE balance<0 ORDER BY name");
    $db->ok or Error('не выполнился sql');
    $db->rows > 0 or Error('Нет ни одного клиента с отрицательным балансом');

    my $tbl = tbl->new( -class=>'td_wide td_medium' );
    while( my %p = $db->line )
    {
        my $err_msg = Adm->why_no_usr_access($p{id});
        $err_msg && next;
        $tbl->add('*', 'll', $p{name}, $p{balance});
    }
    Show Center $tbl->show;
}
1;

Adm->chk_privil_or_die проверяет есть ли у текущего администратора определенная привилегия. В данном случае проверяется привилегия «просмотр платежей» (код 30, см. файл RU_admin.pl). Если привилегии нет, действие скрипта будет прекращено с выводом сообщения об отсутствии прав доступа, расширенная ошибка пойдет в debug.

Db->sql применяется когда нужно получить выборку нескольких строк, Db->line - одной строки. Разница в том, что в первом случае возвращается объект, из которого можно запрашивать строку за строкой, во втором случае сразу возвращается строка в виде хеша. Если выборка не возвращает ни одной строки, причина может быть в том, что условию не соответствует ни одна строка либо же ошибочный sql, дисконнект БД и др. ошибки. Хорошим тоном является обработка ошибок, в примере это $db->ok or Error(...).

tbl->new создает объект html-таблица с css 'td_wide td_medium'. Класс td_wide делает широкими ячейки таблицы (большой padding по ширине), td_medium делает средний вертикальный padding. Обычно таких параметров достаточно, чтобы данные в разных столбцах не сливались, т.е. между ними был достаточный промежуток. Документация по таблицам.

Adm->why_no_usr_access проверяет есть ли у текущего админа доступ к учетной записи клиента с заданным id. Если у админа нет доступа к группе клиента, то возвращается строка «нет доступа к группе». Если никаких ошибок при проверке не возникло и доступ есть, возвращается пустая строка.



Еще один пример. Модуль выводит список клиентов в виде таблицы, максимально 10 строк за раз, вверху отображая постраничную навигацию. Заголовки столбцов - ссылки, при нажатии на которые происходит сортировка по текущему столбцу.

код perl
use strict;

sub go
{
    my($Url) = @_;
    my %fields = (
        id   => 'id',
        fio  => 'ФИО',
        name => 'логин',
    );

    my $order = ses::input('order');
    $order = 'id' if !exists $fields{$order};

    my $sql = 'SELECT * FROM users ORDER BY '.$order;
    $Url->{order} = $order;
    my($sql, $page_buttons, $rows, $db) = Show_navigate_list($sql, ses::input_int('start'), 10, $Url);
    my $tbl = tbl->new( -class=>'td_ok' );

    $tbl->add('head', 'lll', map{ 
        [ $Url->a($fields{$_}, order=>$_) ]
    } sort{ $a cmp $b } keys %fields);

    while( my %p = $db->line )
    {
        $tbl->add('*', 'lll', map{ $p{$_} } sort{ $a cmp $b } keys %fields);
    }
    Show Center $page_buttons;
    Show Center MessageBox( $tbl->show );
}
1;

Хеш %fields содержит поля таблицы клиентов, которые будут выводится, соответственно по этим полям возможна сортировка. $order = 'id' if !exists $fields{$order} - делает скрипт безопасным, если оператор в адресной строке подменит имя реального поля на недействительное, например sql инъекцию, то оно будет заменено на дефолтное: id.

$Url->{order} = $order добавляет в объект $Url параметр order - поскольку при постраничной навигации необходимо сохранять сортировку. В противном случае, при нажатии на любую страницу в навигации, была бы принята сортировка по умолчанию, в нашем случае по id.

$Url->a() рендерит объект $Url в гиперрсылку. Первый параметр - текст гиперссылки, остальные параметры добавляются в url. Например,

$Url->a('Привет', hi=>1);

преобразуется в <a href='?a=demo&hi=1'>Привет</a>. Почему в url появился параметр a=demo? Потому что при передаче управления модулю, в Url по умолчанию находится параметр `a`, ссылающийся на данный модуль.

код perl
my $url = url->new( a=>'users' );
Show $url->a('Список клиентов').' '.$url->a('Модуль карточек', a=>'cards');

Получим ссылки: <a href='?a=users'>Список клиентов</a> и <a href='?a=cards'>Модуль карточек</a>. Видно, что параметр `a` переопределяется во втором $url->a().



Написание реального модуля требует изучения документации, в частности структуры базы и модулей calls.pm:

Ajax

Модуль, обрабатывающий ajax запросы должен возвращать данные в массив @$ses::cmd:

Вывод фразы 'Ok' в элемент с id='test div'
    push @$ses::cmd, {
        id   => 'test div',
        data => 'ok',
    };
Добавление фразы 'Ready' в элемент с id='test div'
    push @$ses::cmd, {
        id     => 'test div',
        action => 'add',
        data   => 'ok',
    };
Выполнение js-кода
    push @$ses::cmd, {
        type   => 'js',
        data   => 'alert("Hi, man!")',
    };

Команды выполняются в таком же порядке, как они записаны в @$ses::cmd, поэтому, если требуется, протолкнуть команду вперед, то вместо push следует применить unshift.

Если посылка данных идет в элемент с id='modal_window' (см. подпрограмму ajModal_window) - выводится модальное окно. Посылка пустой строки закрывает модальное окно.