Подпрограммы calls.pm

Глобальные переменные


В любой модуль, кроме авторизации, можно попасть лишь будучи авторизованным. Авторизация бывает доверенной и недоверенной. Если источник авторизации надежный, например, авторизация по логину и паролю, он устанавливает параметр trust в 1. Опасные функции следует разрешать только при доверенном соединении:

    $ses::auth->{trust} or Error('Соединение не доверенное. Необходимо перелогиниться');

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

    if( 1 + 1 > 3 )
    {
        Error( Adm->id? 'Паника! Perl неправильно считает' : 'Сервис временно недоступен');
    }
    # Пополним счет клиента, при этом Pay_to_DB сама проставит автора
    Pay_to_DB(uid=>$uid, cash=>10, category=>1);

POST/GET параметры

Получить параметры, переданные через браузер, можно с помощью ses::input:

http://xxx/?a=test&b=0&uid=33
    if( ses::input('a') eq 'test' && ses::input_exists('b') )
    {
        # Условие сработает т.к. в url a=test и параметр b присутствует
        # Параметр uid на всякий случай принудительно приведем к целому числу
        my $uid = ses::input_int('uid');
        $uid or Error('Введите целое число, не равное нулю');
    }

Хеш всех переданных параметров хранится в $ses::input_orig:

    # Если параметр yes не существует либо не установлен,
    # отредиректимся сами на себя и пошлем все параметры + установим yes
    ses::input('yes') or url->redirect( { %$ses::input_orig, yes=>1 } );

Существует альтернативный способ передачи параметров - данные записываются в базу данных в таблицу webses_data, доступ к которым осуществляется по ключу unikey таблицы.

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

Допустим, клиент посылает данные. Модуль их проверяет. Находит ошибку. Если вывести ошибку, то клиент будет раздражен тем, что ввел много данных и ему снова понадобится их вводить. Поэтому переданные данные записываеются в webses_data и происходит редирект на страницу ввода, при этом параметр _unikey устанавливается в значение поля unikey сохраненных данных.

Перед загрузкой модуля, если calls.pm видит параметр _unikey, он автоматически извлекает из БД связанную с ключем строку и дешифрует ее в переменную $ses::data. Далее, если существует ключ $ses::data->{-input}, то calls.pm «далает вид», что браузер послал POST/GET данные, находящиеся $ses::data->{-input}.

В большинстве случаев эти действия происходят прозрачно, т.е. не требуется думать о ключе -input. Например, редирект всегда осуществляется через webses_data:

    $Url->redirect(a=>'main', uid=>15);

В данном случае произойдет запись {a=>'main', uid=>15} в webses_data, после чего произойдет переход http:://xxx/?_unikey=yyyyyy. Здесь yyyyyy - ключ к данным, записанным в таблицу.

Раз уж мы заговорили о редиректе, то отметим 2 полезных его параметра -made и -error. Первый предназначен для того, чтобы вывести сообщение вверху страницы после редиректа, при этом если будет установлен -error, то красным цветом:

    $Url->redirect(a=>'main', -error=>1, 
        -made=>'Ошибочная операция. Редиректимся на титульную страницу');

Подпрограммы модуля main

ToLeft, ToRight, ToTop
Выводят данные в левую, правую и соответсвенно в верхнюю часть формируемой html-страницы.
ToLeft 'Текст слева';
MessageBox выводит сообщение в рамке
ToLeft MessageBox('Сообщение 1');
ToRight MessageBox('Сообщение 2');
MessageWideBox выводит сообщение в рамке, растянутой по ширине
Show MessageWideBox('Сообщение');
Box выводит темплейт box.html. Все параметры необязательные
Show Box(
    title => 'Заголовок',
    wide  => 1,
    msg   => 'Текст сообщения внутри рамки',
    css_class => 'error txtpadding',
);
WideBox тоже, что и Box, но параметр wide установлен в 1
Show WideBox();
Menu
Menu тоже, что и MessageWideBox, но сообщение имеет css class `navmenu`, что предписывает выводить гиперссылки на всю ширину блока, одна под другой.
ToLeft Menu(
    url->a( 'Список клиентов', a=>'users' ).
    url->a( 'Статистика трафика', a=>'traf_log' ).
    url->a( 'Google', -base=>'http://google.com' )
);
Center выводит информацию отцентрированной по горизонтали
Show Center MessageBox('Center');
Отличие от css h_center в том, что позволяет центрировать не только текст.
Error выводит сообщение об ошибке и завершает выполнение скриптов
1 > 2 && Error('1 > 2 !!!');
Error_ тоже, что и Error, но сообщение обрабатывается подпрограммой _()
$lang::hello_msg = 'Здравствуйте [filtr|bold], на вашем счету [bold] $';
Error_($lang::hello_msg, 'администратор', 1000);
ErrorMess тоже, что и Error, но выполнение скрипта не приостанавливается
ErrorMess('Что-то не так!');
Error('Точно что-то не так...');
Pay_to_DB создает запись в таблице pays по входным параметрам:
Возвращает 1 в случае успешной записи. В параметрах записи автоматически устанавливается автор админ или клиент в зависимости от того, кто авторизован. Также устанавливается ip.

Если запись финансовая, то должна быть выполнена в транзакции, например:
 Db->begin_work or Error($lang::err_try_again);
 my $rows1 = Db->do("UPDATE users SET balance=balance+(?) WHERE id=?", $money, $uid);
 my $rows2 = Pay_to_DB(uid=>$uid, cash=>$money, category=>1);
 if( $rows1 < 1 || $rows2 < 1 || !Db->commit )
 {
    Db->rollback;
    Error($lang::err_try_again);
 }
Здесь Db->begin_work переключает Db в режим транзакций. Начиная с этого момента sql update/insert будут выполняться, но не фиксироваться в БД пока не будет выполнен Db->commit. В условии проверяется, что оба запроса были выполнены успешно, т.е. если хотя бы один из запросов вернул меньше единицы - выполняется откат Db->rollback. Откат гарантирует, что ни один из запросов не будет зафиксирован в БД. Также он отключает режим транзакций.


Get_usr_info
Получает данные клиента по его id. В случае успеха, возвращает ссылку на хеш с данными, иначе сообщение об ошибке.
my $info = Get_usr_info(15);
ref $info or Error $info;
Show 'ФИО клиента с id=15: '.v::filtr($info->{fio});
Подпрограмма возвращает не только основные данные, но и дополнительные поля, а также список ip, принадлежащих клиенту.
debug('pre', $info);


_()
Подпрограмма, состоящая из одного символа подчеркивания вставляет в заданную строку параметры:
Show _('[div][p h_center][div bold h_right]', 'Текст1', 'Текст2', 'Текст3');
Преобразуется в:
<div>Текст1</div><p class='h_center'>Текст2</p><div class='bold h_right'>Текст3</div>
Как видно, квадратные скобки указывают на вставку очередного параметра в текущее место. При этом первое слово в квадратных скобках указывает на тег. Есть несколько зарезервированных слов, которые не указывают на тег, например, filtr - будет эскейпить html-спецсимволы в параметре. trim - удаляет пробелы по краям параметра. Кроме того, вертикальная черта позволяет вставлять несколько управляющих последовательностей:
Show _('Вы ввели [filtr|trim|span bold]', '   xxx  ');
Преобразуется в строку «Вы ввели <span class='bold'>xxx</span>»

Таблицы

perl код
    # Создаем таблицу
    my $tbl = tbl->new( -class=>'td_wide' );
    # Первая строка
    $tbl->add( '*', 'lll', 'ячейка 1', 'ячейка 2', 'ячейка 3' );
    # Вторая строка
    $tbl->add( '*', 'lll', 'ячейка 1', 'ячейка 2', 'ячейка 3' );
    # Выводим таблицу
    Show $tbl->show;

Методом new создается новая пустая таблица. Входные параметры:

-class  : css class таблицы
-row1   : css class первого ряда
-row2   : css class второго ряда

Параметры не обязательны. Могут быть взяты из другой:

perl код
    my $tbl1 = tbl->new( -class=>'td_wide td_ok' );
    # Class для таблицы 2 будет 'td_wide td_ok'
    my $tbl2 = $tb1->new( -row1=>'row5' );
Примеры классов для таблиц
td_wide     : таблица максимальной ширины
fade_border : ячейки отделяются полупрозрачными линиями
pretty      : если в таблице будет мало строк, то они будут больше по высоте
td_ok       : оптимальный паддинг для ячеек таблицы
td_tall     : высокие строки
td_medium   : строки средней высоты
td_wide     : широкие ячейки
td_narrow   : узкие ячейки

Строки добавляются методами add (в конец таблицы) и ins (в начало таблицы.). Формат параметров:

Если в css будет присутствовать символ звездочка - это указание дать ряду css из параметра -row1, после чего значения в -row1 и -row2 обменяются местами. Таким образом можно организовать «зебру».

Карта ячеек - это строка, которая описывает выравнивание каждой ячейки:

l : выравнивание по левому краю
r : по правому
c : по центру
L : по левому и 2 ячейки объединяются в одну
R : по правому и 2 ячейки объединяются в одну
C : по центру и 2 ячейки объединяются в одну
E : по левому и 3 ячейки объединяются в одну
3 : по центру и 3 ячейки объединяются в одну
4 : по центру и 4 ячейки объединяются в одну
..
9 : по центру и 9 ячеек объединяются в одну
t : по центру и вертикальное выравнивание top
T : 2 ячейки в одну, по центру и вертикальное выравнивание top
пробел : пустая ячейка
perl код
    my $tbl = tbl->new( -class=>'td_wide td_ok' );
    $tbl->add( '*', 'rcl', 'равнение на право', 'центр', 'лево');
    $tbl->add( '*', 'rL', 'текст в 1й ячейке', 'ячейка 2 и 3 объеденена в 1');
    $tbl->add( '*', '3', 'текст по центру и все ячейки объеденены в одну');
    Show $tbl->show;

Если какая-либо ячейка будет в квадратных скобках - это отключит фильтрацию html-спецсимволов. Если вы отображаете данные, посланные клиентом, вы не должны отключать фильтрацию.

perl код
    $tbl->add( '*', 'rl', 'вы прислали строку', ses::input('str'));
    $tbl->add( '*', 'rl', 'поле ввода', [ v::input_t(name=>'str') ]);

Автоматически ведется учет строк и столбцов таблицы. Метод rows позволяет получить текущее количество рядов. Если при вставке строк выясняется, что ячеек меньше чем в предыдущей, то идет автоматическое расширение последней путем объединения с оставшимися:

perl код
    $tbl->add( '*', 'll', 'Здравствуйте', 'Станислав');
    # ячейка будет объеденена с последней т.к. в предыдущей строке 2 столбца
    $tbl->add( '*', 'l', 'сегодня все ок');
    # return не сработает т.к. в таблице 2 строки
    $tbl->rows < 1 && return;

Альтернативный способ вставки строк позволяет присваивать css класс персонально каждой ячейке (а не только всей строке в целом). Кроме того, для каждой ячейки указывается заголовок. Каждую строку необходимо обернуть в квадратные скобки:

perl код
    $tbl->add( '*', [
        [ 'bold h_left', 'Заголовок 1й колонки', 'Внимание' ],
        [ 'h_right',     'Заголовок 2й колонки', 'всем' ],
    ]);

В результате будет вставлена строка с двумя ячейками: в первой выравнивание по левому краю, во второй - по правому. В левой выделенный (класс bold) текст «Внимание», в правой текст «всем». Когда таблица будет выведена, то в ее шапке в первой ячейке будет текст «Заголовок 1й колонки», во 2й - «Заголовок 2й колонки».



Package Url

Предназначен для формирования гиперссылок.

Методы
new     : создание нового объекта
a       : рендеринг url-объекта в гиперрсылку
post_a  : рендеринг в гипперссылку, при нажатии на которую
            данные будут отправленны post-методом
form    : рендеринг url-объекта в форму
url     : получение url из объекта
redirect: редирект

Если параметр не начинается со знака тире - это параметр url-а:

perl код
    my $url = url->new( a=>1, b=>2, -class=>'nav', -style=>'color:#000' );
    Show $url->a('Текст ссылки');

Будет преобразовано в <a href='?a=1&b=2' class='nav' style='color:#000'>Текст ссылки</a>

Одну ссылку можно создать на основе другой, можно переопределять параметры и т.д.:

perl код
    my $url = url->new( a=>1, b=>2, -class=>'nav', -style=>'color:#000' );
    my $url2 = url->new( b=>3 ); #?a=1&b=3
    $url2->{a} = 'demo'; #?a=demo&b=3
    Show $url2->a('OK', b=>4); #?a=demo&b=4
    $url->{b} = undef;
    Show $url2->a('OK'); #?a=demo

Во всех параметрах эскейпятся html-спецсимволы. Квадратные скобки отключают это, чаще всего необходимо для текста ссылки:

perl код
    my $url = url->new( -class=>'nav' );
    # Но лучше это сделать через стиль или класс
    Show $url->a( [ '<b>Bold text</b>' ] );

В формах порядок параметров отличается от гиперрсылки, сначала параметры, затем содержимое формы:

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

Параметр -ajax автоматически конвертируется в -class='ajax'. При этом JS NoDeny все ссылки, у которых присутствует такой класс, делает «аяксовыми» - при клике на такие ссылки не загружается новая страница, но идет http-запрос на сервер, а ответ обрабатывается скриптом nody.js в браузере.



Package Db

Предназначен для работы с базой данных. Основной метод - sql.

2 формата вызова

Если необходима выборка только одной строки:

my %p = Db->line( параметры );
Хеш %p пустой если

Чтобы уточнить: Db->ok возвращает 1, если не было ошибок.

Выборка одной строки
my %p = Db->line("SELECT * FROM users WHERE id=? AND grp=?", $id, $grp);
Show %p? "$p{name}, $p{fio}" : Db->ok? 'пустая выборка' : 'внутренняя ошибка';
Выборка нескольких строк
my $db = Db->sql("SELECT id, name FROM users WHERE field=?", $unfiltered_field);
while( my %p = $db->line )
{
    Show "$p{id} = $p{name}<br>";
}
Выборка нескольких строк с иным форматом вызова
my $db = Db->sql(
    sql     => "SELECT * FROM tbl WHERE field=? AND val=?",
    param   => [ $filed, $val ],
    comment => 'Выборка номер 2',
);
while( my %p = $db->line ) { ... }
UPDATE/INSERT
my $rows = Db->do("UPDATE websessions SET uid=?, role=? WHERE ses=? LIMIT 1", $id, $role, $ses);
$rows>0 or Error('Ошибка!'); # не делайте $rows or Error() т.к. rows может = -1
Выполнение нескольких запросов в транзакции
Db->do_all(
    [$sql1, $param1, $param2 ],
    [$sql2, $param3 ],
);

Проверяется, что каждый запрос затронул как минимум 1 строку. Внимание! Если запрос выполнился, но не затронул ни одну строку (ни одного совпадения по условию WHERE), то будет откат транзакции.

Дополнительные методы