<h1>ACIS::Web - ACIS веб-интерфейс, Perl module</h1>

Ответственность - общее управление, глобальные данные о запросе и о формировании
ответа. Интерфейс модуля - объектно-ориентированный.

метод new:

- пытается получить свою домашнюю директорию, иначе руководствуется
$ACIS::LocalConfig::local_path.
- читает конфигурацию системы и экранов с помощью ACIS::Web::Config.
- пытается получить из запроса имя экрана и идентификатор сессии.
- создает объект CGI

Метод handle_request:

- используя конфигурацию (ACIS::Web::Config), находит соответствующий список
обработчиков.

- передает управление первому обработчику из списка обработчиков для этого
экрана. 
  - в результате работы обработчика может поменять назначенного презентера или
  вообще сразу сгенерировать и отправить ответ (например, HTTP Redirect). кроме
  этого обработчик может добавить обработчик в список обработчиков или вызвать
  исключение, заместив все имеющиеся обработчики каким-то одним.
  - на всех экранах, требующих аутентификации, требуется присутствие обработчика
  ACIS::Web::Session. 

- после того как последний обработчик закончил свою работу и перед тем, как
передать xml документ presenter'у, метод ACIS::Web::handle_request запишет
данные сессии и пользователя (если нужно), в xml файлы.

- передает данные презентеру для генерации содержимого экрана

- выводит экран

<h2>обработка запроса классом ACIS::Web</h2>

для обработки запроса ACIS::Web создает объект модуля CGI и получает собственную
конфигурацию. на основании запроса и того, по какому URL расположена система,
ACIS::Web пытается получить название экрана и имя пользователя (login). потом
название экрана и имя пользователя сохраняются в ветке request/user и
session/request/screen.

<h2>конфигурация экранов</h2>

ACIS::Web::Config - соответствие страница - обработчик - презентер

это класс, объект которого хранит в себе хэш.  ключи хэша - идентификаторы
"экранов", значения, соответствующие ключам, - список структур, описывающих
экран.

такая структура содержит по крайней мере два параметра "экрана": обработчик
(имя perl-функции) и презентер (имя файла презентации и его тип)

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


<h2>Обработка запроса обработчиками.</h2>

обработчик получает объект ACIS::Web при запуске. допустим, этот объект - в
переменной $acis.

<h3>Переменные запроса, данные сессии и пользователя.</h3>

Переменные запроса используются обработчиками для передачи данных 
презентеру.  Кроме того, они содержат информацию о самом запросе 
(HTTP запросе), информацию о пользователе системы, если он идентифицирован.
Они нигде не сохраняются.

Данные сессии содержат информацию о сессии, как то:
  когда она открыта,  
  когда последний раз использовалась и откуда (IP)
  кому принадлежит (user id)

также, обработчики могут писать туда все, что им нужно сохранить 
в течении сессии.

Данные пользователя включают самую общую информацию о 
пользователе системы (full name, user id, login id, password, user type) 
и, конечно, сами записи, созданные пользователем.


Структура переменных запроса - хэш.

	'request'

		"CGI" 	объект класса CGI
		'screen'	название текущего экрана
		'session-id'

добавляется при условии успешно пройденной аутентификации:

данные сессии - класс ACIS::Web::Session.

        'created'	время создания
	'user-id'
	'IP'			IP адрес пользователя
	'last-interaction'	time, seconds since the epoch
			(interaction - взаимодействие, операция)

загружается при условии успешно пройденной аутентификации:

        'current-record-number'		integer - номер текущей записи

#        'search'  - результаты поиска.  Создается обработчиками, 
#                       используется презентерами.

Структура данных пользователя:

	'user'
		'user'			string, human readable name
		'login'			string, email
		'type'			???
		'file'			???
#		'password'		пароль пользователя 

	'records'	список (ARRAY)
		для каждой записи:
		'id'
		'type' FIXED "person"
		'name'
			'first'
			'middle'
			'last'
		'email' - string, email
		'email-publish' - boolean
		'name-variations' - array of strings
		'affiliations' - array of institution identifiers or institution records
		'contributions' - array of [role (string), document (id)]
		'sci-fields' - array of strings
		'photo' - string, filename or URL

	'records-hash' - hash (auxiliary - вспомогательный)
    RECORDID => RECORD, as described above


Переменные запроса должны быть доступны как хэш 
через объект ACIS::Web, метод variables.

  my $vars = $acis -> variables;
  my $request = $acis -> request;
  my $user = $acis -> user;

Данные сессии должны быть доступны как хэш 
через объект ACIS::Web, метод session.

  my $session = $acis -> session;

Класс ACIS::Web::Session будет отвечать за загрузку и 
сохранение данных сессии.

Данные пользователя должны быть доступны как хэш 
через объект ACIS::Web, метод user_data.

  my $userdata = $acis -> user_data;

Класс ACIS::Web::UserData будет отвечать за загрузку и 
сохранение данных пользователя.

<h4>процедура аутентификации</h4>

процедура аутентификации состоит из двух частей:

а) проверка имени и пароля пользователя на соответствие тем, которые хранятся в базе данных: 
в базе данных BerkeleyDB хранится список идентификаторов пользователей и их паролей. если полученные идентификатор пользователя и пароль не совпадают с теми, что есть в базе данных, процедура должна вернуть информацию об ошибке

б) проверка данных сессии на то, можно ли ее использовать применительно к данному пользователю:
последняя сессия для каждого из пользователей хранится в
"$acis->{home}/sessions/pAB101", где:
  p - префикс типа записи (первый символ short_id),
  AB - префикс фамилии (второй и третий символы short_id),
  101 - порядковый номер записи в пространстве имен pAB (номер в short_id)
  $acis -> {home} указывается в конфигурации.

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

рассматриваются 2 случая:
пользователь может запросить экран, указав идентификатор пользователя в URL, либо не указав его.

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

<h4>записи</h4>

Данные пользователя могут включать в себя несколько записей.
Если записей несколько и пользователь выбрал работу с определённой
записью, то в $session->{'current-record-number'} будет ее порядковый номер в
списке records.

Доступ к самой записи: 

  my $record = $userdata -> {records} -> [ $record_number ];

для сохранения почтового адреса в записи пользователя:

  $record -> {'postal-address'} = $post;

для получения e-mail адреса пользователя:

  $email = $record -> {"e-mail"};

для получения полного имени пользователя:

  $full_user_name = $record -> {"full-name"};

для сохранения (изменения) полного имени пользователя:

  $record -> {"full-name"} = $full_user_name;
  $userdata -> changed;

Данные пользователя будут сохраняться в свой XML файл, только
в том случае, если с тех пор, как они были загружены, был вызван
метод $userdata -> changed;  Можно было бы избавиться от этого
используя для $userdata связанный хэш (tied hash), но, кажется, это
ненужное усложнение.

<h4>данные запроса</h4>

обработчику экрана нужно узнать имя экрана, для обработки которого он вызван.

  $acis-> request -> {screen}

обработчик хочет получить идентификатор пользователя.

  $acis -> user -> {id}


<h3> объект (my) sql </h3>

для получения объекта sql_helper обработчик вызовет для полученного объекта ACIS::Web метод sql_object. метод sql_object проверит на определенность ключ 'sql-object' в объекте ACIS::Web, и, если он не определен, то создаст объект sql_helper и поместит его в значение ключа 'sql-object'. потом объект вернет значение ключа 'sql-object' объекта ACIS::Web

  $acis -> sql_object

<h3>контекст и взаимодействие с себе подобными</h3>

<h4>экраны</h4>

данные о конфигурации экранов хранятся в объекте ACIS::Web::Config, доступ к которому можно получить
  
  my $config = $acis -> config;

а доступ к конфигурации определенного экрана:

  $config -> screen ($screen_id)

<h4>взаимодействие с другими обработчиками</h4>

для того, чтобы сразу после обработчика управление передалось presenter'у:

  $acis -> clear_process_queue( );

для того, чтобы ответ пользователю генерировал другой presenter:

  $acis -> set_presenter( $screen_name );

для того, чтобы сразу после обработчика управление передалось presenter'у 
другого экрана:

  $acis -> clear_process_queue( );
  $acis -> set_presenter( $screen_name );
  
для того, чтобы добавить обработчики какого-то экрана в очередь:

  $acis -> add_to_process_queue ( $screen_name ); 

для того, чтобы заменить очередь обработчиков на обработчики другого экрана:

  $acis -> clear_process_queue( );
  $acis -> add_to_process_queue ( $screen_name );

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

  $acis -> redirect( $URL );

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

  $acis -> redirect_to_screen( $screen_name );

<h2>сохранение обработанных данных</h2>

ACIS::Web::UserData - 

методы класса:
 load( user_id )  - загружает данные пользователя с переданным user_id из xml файла.
 save( ) - сохраняет данные пользователя

my $user_data = load ACIS::Web::UserData ( user_id );
$user_data -> save;

<h2>presenters</h2>

для передачи presenter количества полученных результатов поиска:

  $acis -> variables -> {search} -> {results} = ...;

необходимо передать presenter'у записи, найденные в результате поиска, в которых содержится название и идентификаторы документов.

  my $search = {};
  $search -> {start_from} = 10;
  $search -> {results} = 58;
  while (my ($id, $title) = each %search_results)
    {
     push @{$search -> {items}},
                {
                  id => $id,
                  title => $title,
                };
    }
  $acis -> variables -> {search} = $search;

С точки зрения presenter'а это будет доступно как следующие XML файлы:

user_data.xml:

 <data ...>
  ...
  <records>
   <list-item pos='1'>
    <email>...</email>
    <name first="..." last ="..." full="..."/>
    <name-variations>
    ...
    </name-variations>
   </list-item>
  </records>
 ...
 </data>

в генерируемом "на лету" XML с переменными запроса:

<data>
 <search>
  <results>58</results>
  <items>
   <list-item>
    <handle></handle>
    <name></name>
    ...
   </list-item> 
  </items>
 </search>
 <form-data>
    ...
 </form-data>
</data>

presenter'у будет передан этот XML с переменными запроса.

Если ему необходимо, presenter (т.е. xslt документ) сам найдет 
файл user_data.xml. 


все пути, необходимые обработчикам, генерируются в 
таком порядке:
при создании объекта acis::web из конфигурации будут 
получены пути, соответствующие домашней директории, 
пути к статическим файлам (в файловой системе и в 
web), путь к директории презентеров, и пути зависящие 
от сеанса, собственно путь к сеансу работы с 
пользователем и путь к файлу неподтвержденной 
user-data.

при авторизации, вследствие того, что авторизация 
производится по user-data, обработчик 
acis::web::handler::authenticate добавляет пути: к 
user-data, к lock-файлу данной user-data, к файлу, в 
котором должна содержаться удаленная user-data.
если сеанс работы с пользователем не определен или уже 
истек, то создается новый сеанс. метод create модуля 
acis::web::session пытается создать новый сеанс работы 
с пользователем, и если создание проходит удачно, то 
он изменяет пути соответственно с идентификатором 
нового сеанса.

пути к персональной странице и фотографии могут быть 
определены только после того, как известен порядковый 
номер текущей записи, поэтому они будут сгенерированы 

только после вызова метода current_record модуля 
acis::web::session

итак:

ACIS::Web::new
'home'              => ,
'shared'            => ,
'static'            => ,
'session'           => ,
'unconfirmed'       => ,
'presenters'        => ,

ACIS::Web::Handler::Authenticate::authorize
'user-data'         => ,
'lock'              => ,
'deleted-user-data' => ,

ACIS::Web::Session::create
'session'           => ,
'unconfirmed'       => ,

ACIS::Web::Session::current_record
'personal-path'     => ,
'personal-url'      => ,


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

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

 <screen id='personal-data'>
   
   <param name='full-name'       required='yes' type='name'      place='record:full-name'/>
   ...
   <param name='postal'          type='printable'                place='record:postal'/>

   <call function='ACIS::Web::Handler::Authenticate::authenticate' />
   
   <init>
     <call function='ACIS::Web::Handler::Main::prepare_form_data' />
   </init>
   
   <process>
     <call function='ACIS::Web::Handler::Main::check_input_parameters' />
     <call function='ACIS::Web::Handler::Main::commit_form_data' />
     <call function='ACIS::Web::Handler::User::personal_data' />
   </process>
   
   <presentation type='xslt' filename='user/personal-data.xsl' />
 </screen>
 
