Биллинговая система NoDeny. Программирование плагинов клиентской статистики.

NoDeny дает возможность самостоятельно нарастить функционал клиентской статистики с помощью плагинов. Если вы уверенно программируете на perl, можете взяться за работу, предварительно изучив следующее:

Клиентская статистика предоставляется скриптом stat.pl, который загружает необходимый плагин и выполняет его. Скрипт существует в двух абсолютно одинаковых экземплярах: http://..../cgi-bin/stat.pl и http://..../cgi-bin/adm/stat.pl - обусловлнено тем, что папка adm является административной и запаролена, т.е клиенты не имеют к ней доступ, поэтому получают статистику через /cgi-bin/stat.pl. С другой стороны, у администратора должна быть возможность посмотреть «глазами клиента» на его статистику, что он и делает через /cgi-bin/adm/stat.pl.

В задачи скрипта stat.pl входит: проверка авторизации, подготовка данных для плагинов, запуск плагина.

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

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

Данные о плагине содержатся в реестре плагинов - в файле plugin_reestr.cfg. Каждая строчка файла описывает параметры плагина: имя файла, уникальный номер плагина, название плагина, разрешить ли доступ к плагину только администратору и некоторые другие данные, которые будут описаны ниже. В самом реестре даны комментарии по полям.

Каким образом скрипту stat.pl сообщается какой плагин необходимо запустить? Для этого в запросе, переданным через браузер, в переменной a указывается уникальный код плагина, например:
http://.../stat.pl?a=102
что указывает выполнить плагин с номером 102. Если переменная a не будет определена, то будет запущен плагин с кодом 101 - отображена главная страница статистики.

Перед тем как начать программировать плагины, изучите скрипт calls.pl - в нем содержится много полезных подпрограмм.

Что должно быть результатом выполнения плагина?

Существует глобальная переменная $OUT, которая является фактически html-страницей, которая будет возвращена клиенту. Код:
$OUT.='Привет!'
Добавит к формирующейся html-странице текст «Привет!». После того, как плагин завершит свою работу (выполнит return в скрипт stat.pl), stat.pl закончит формирование html-страницы - закроет открытые теги, после чего пошлет страницу в бразуер клиенту.

Последние действия в виде кода:
$OUT.=$EOUT;
&Exit;
Здесь $EOUT - переменная, содержащая список закрывающих тегов, а &Exit - подпрограмма из calls.pl, которая «приклеивает» заголовок к $OUT, отсылает все клиенту в браузер и выполняет exit.

Нормальное завершение плагина - это return в stat.pl, однако в случае, если плагин фиксирует проблему или ошибочное действие, наилучшим вариантом будет запуск подпрограммы Error, которая выведет сообщение об ошибке и сделает &Exit:
&Error('Ошибка! Нет связи с ФБР!',$EOUT) unless &Connect_FBR;
- обратите внимание на присутствие $EOUT вторым параметром в &Error. Если заглянуть в calls.pl и изучить описание к &Error, то станет понятно, что 2й параметр добавляется к $OUT сразу после вывода ошибки.

Изучите глобальные переменные, доступные внутри плагина:
Перечисленные переменные модифицировать не рекомендуется.

%Adm - хеш данных администратора, просматривающего статистику. Поскольку статистику обычно просматривает не администратор, а клиент, то в таком случае хеш содержит всего одно значение: $Adm{id}=0. Создавая плагины вы всегда должны помнить о том, что он может быть запущен как администратором так и клиентом, поэтому хорошим стилем, а также условием безопасности, является пометка кем было осуществлено действие клиентом или администратором.

$Adm{id} - id администратора, либо 0, если статистику просматривает клиент.

$Adm{office} - отдел администратора.

$Adm{login} - логин администратора.

%F - хеш, содержащий данные, которые клиент послал через браузер. Например, клиент послал запрос:
http://.../stat.pl?a=101&day=10
Хеш %F будет заполнен такими значениями:

$F{a}=101
$F{day}=10

Если в запросе одна и таже переменная встречается больше одного раза, то за основу берется последнее значение, например:
http://.../stat.pl?a=101&a=102

Приведет к результату: $F{a}=102

Эта особенность применяется, когда необходимо изменить уже существующий запрос. Например, глобальная переменная $scrpt содержит url текущей страницы вместе с переданными данными, скажем:
$scrpt = 'stat.pl?pp=...&a=101&day=10&mon=2&year=2008'

мы хотим сформировать ссылку на страницу для отображения статистики не на 10 февраля, а на 11 марта:
&ahref("$scrpt&day=11&mon=3",'показать статистику');
$RealIp - ip с которого в данный момент просматривается статистика. Обратите внимание, что это не ip клиентской записи, это ip с которого произведен "заход" в клиентскую статистику.
$start - номер отображаемой страницы, когда система выводит фрагмент таблицы (определенную страницу). Найдите подпрограмму &Show_navigate_list в каком-нибудь плагине, чтобы понять где и как используется $start.
$Sel_id используется тогда, когда у клиента несколько учетных записей и статистика может выводиться отдельно для каждой учетной записи либо же суммарная для всех. Например, статистику трафика можно показать для основной записи либо же для алиасной, либо же суммарную. В переменной $Sel_id содержится список id, разделенных запятыми, для которых клиент запросил статистику.
$OUT.=$For_U? "Запрошен вывод телефона для $For_U" : 'Запрошен вывод телефонов всех записей клиента';
$sth=&sql("SELECT telefon FROM users WHERE id IN ($Sel_id)");
$Er_Mess_for_Client - сообщение «Данные недоступны - ведутся технические работы на сервере.». Это «отмазка» для клиентов, когда произошла критическая ошибка, которую нельзя сообщать клиенту, администратору же ошибка выводится - смотри описание к переменной $V.
$day_now
$day_now	- текущий день
$mon_now	- текущий месяц (1..12)
$year_now	- текущий год (от 1970!)
$p - ссылка на выборку из таблицы БД для текущей клиентской записи
$OUT.='Квартира клиента: '.$p->{room};
$pm - ссылка на выборку для основной записи клиента. Если просматривается основная запись, то $p и $pm указывают на одну и туже структуру данных.
$Mid - id основной записи клиента. Пример:
$h=&sql_select_line($dbh,"SELECT COUNT(mid) FROM pays WHERE mid=$Mid AND type=30');
$OUT.='Клиент отправил/получил '.$h->{'COUNT(mid)'}.' сообщений за все время сущестрования его учетной записи';
Обратите внимание - у клиента может быть несколько учетных записей (алиасов). Клиентстая статистика может просматриваться от имени алиаса. Поэтому чтобы отобразить некоторые данные, которые существуют только для основной записи (к примеру платежи), необходимо использовать $Mid.
$id - id текущей записи. Если просматривается основная запись, то $Mid=$id
%U - данные всех учетных записей клиента.
$U{$id}{name}	- логин
$U{$id}{ip}	- ip
$U{$id}{fio}	- фио
$U{$id}{state}	- состояние записи заблокирована/не заблокирована
$U{$id}{o_name}	- логин с отфильтрованными спецсимволами html
$U{$id}{m_name}	- логин с отфильтрованными спецсимволами mysql
$U{$id}{o_fio}	- фио с отфильтрованными спецсимволами html
$U{$id}{m_fio}	- фио с отфильтрованными спецсимволами mysql
Пример:

$OUT.='Логин основной записи: '.$U{$Mid}{o_name};

Важно.

Не забывайте о безопасности, а именно о фильтрации спецсимволов в непроверенных данных, в противном случае злоумышленник сможет повредить/получить данные из БД путем посылки некорректных данных. Не исключайте, что злоумышленником может быть и ненадежный работник. Учтите, в данных таблиц базы данных содержатся нефильтрованные значения. Например, допускается фамилия <b>Иванов</b>. Поэтому при выводе данные в виде html всегда применяйте фильтр &Filtr_out, а при выполнении sql-запроса &Filtr_mysql. Это разные фильтры!

$OUT.='В переменной privet вы прислали: '.&Filtr_out($F{privet});
$nAlias - количество учетных записей клиента. Если нет алиасов, то = 1.
$OUTLEFT - всеб что будет добавлено к этой переменной, будет выведено в левую колонку клиентской статистики. Аналогично как все, что добавляется в $OUT - выводится в правую колонку. Однако, есть нюанс. В левую колонку можно добавлять данные строго до выполнения основного кода плагина, имеется ввиду подпрограмма, которая описывается в реестре.

Чтобы было понятней, необходимо описать детальнее процесс запуска плагина:
1) stat.pl производит авторизацию.
2) stat.pl получает данные клиента.
3) stat.pl формирует меню плагинов (левую колонку статистики). В этот момент происходит импортирование плагина. Т.е плагин загружается, но подпрограмма, указанная в реестре, не выполняется. Выполняется лишь код, оформленный вне подпрограмм плагина. В этом коде и предусматривается обновление $OUTLEFT. Смотрите Sdemo.pl для примера.
4) stat.pl завершает формирование левой колонки, начинает формирование правой.
5) выполняется заданная подпрограмма плагина.
$scrpt - url текущей страницы со ссылкой на текущий плагин (с установленным уникальным номером плагина a).
&ahref($scrpt,'Запустить текущий плагин без параметров');
&ahref("$scrpt&a=101",'Титульная страница статистики');
$t - время на сервере основной БД. Это время принято за основу, поскольку в системе с большим количеством серверов могут быть значительные расхождения во времени. Во всех расчетах применяйте переменную $t. Для записи в БД используйте значение unix_timestamp()
&sql_do($dbh,"INSERT INTO pays SET mid=$Mid,type=30,coment='Привет!',time=unix_timestamp()");
$ut - строка 'unix_timestamp()'
$V - если установлена, то означает, что статистику просматривает не клиент, а администратор. Предназначено для того, чтобы администратору сообщать более детальные сообщения об ошибках.

Когда переменная $V установлена, то содержит такое сообщение:

«Вы в режиме администрирования, поэтому вам видны более детальные описания ошибок:»

Таким образом, если в плагине ввести следующую строку:
&Error($V? "$V повреждение данных!" : 'Временная ошибка. Повторите запрос позже');
То при запуске плагина клиентом, ему будет показано сообщение:

Временная ошибка. Повторите запрос позже

Администратору же:

Вы в режиме администрирования, поэтому вам видны более детальные описания ошибок:

повреждение данных!