Способы интеграции
==================

Существует два основных подхода для интеграции с Synergy:

* Прямая интеграция — интеграционные модули разрабатываются
  с использованием API Synergy и интегрируемых систем.
  Синхронизация данных между системами и координация обмена между ними
  остаётся за разработчиком интеграционного модуля

* Событийная интеграция — когда какая-либо из подсистем Synergy
  генерирует различные события, связанные с какими-либо данными.
  Обработчики этих событий (на стороне Synergy) при необходимости
  преобразовывают данные событий и передают их интегрируемой системе
  через какой-либо транспортный уровень

Прямая интеграция
-----------------

ARTA Synergy предоставляет API для доступа к своим функциям
с помощью rest сервисов. Описание методов REST API можно
найти в `данном разделе`_.
Авторизация для всех методов API — ``Basic HTTP``.

Событийная интеграция
---------------------

Под «событием» мы будем подразумевать сообщение о каком-либо изменении
в ARTA Synergy, содержащее тип события
и минимально необходимые для получения связанной с событием информации
либо воздействия на Synergy данные. Обработчик события (или событий) —
программный модуль, читающий сообщения о событиях из ``JMS Queue`` или ``JMS Topic``
и осуществляющий, при необходимости, доступ к экземпляру Synergy,
сгенерировавшему сообщение, с помощью API Synergy.

Обработчик событий является отдельным от ARTA Synergy приложением,
которое может работать как на том же сервере приложений,
что и ARTA Synergy, так и на удалённом.

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

Обработчик событий может обрабатывать как конкретное событие
(например, ``event.registers.formdata.add``),
так и класс событий (например, ``event.registers.*``).

Обработка события может происходить в 3 этапа:

1. Получение события
2. Получение и преобразование необходимых обработчику данных
3. Передача сформированного пакета данных далее (опционально)

ARTA Synergy генерирует событие в случае,
если для этого события настроены обработчики.
Обработчики событий настраиваются в конфигурационном файле
``${jboss.home}/standalone/configuration/arta/api-observation-configuration.xml``.

Сообщение, помещаемое в очередь ``JMS``,
представляет собой экземпляр ``javax.jms.TextMessage``.
Тело сообщения зависит от типа события,
его описание можно посмотреть ниже среди описаний типов событий.
Каждое событие содержит свойство ``api_event``, указывающее на тип события,
вызвавшего его (содержимое тега ``<event>event.registers.formdata.add</event>`` в конфигурационном файле).

Например:

.. code-block:: xml

    <configuration>
        <listener>
            <queue>java:jboss/queues/Synergy/UsersQueue</queue>
            <event>event.users.*</event>
        </listener>
        <listener>
            <queue>java:jboss/queues/Synergy/RegisterCreateDocQueue</queue>
            <event>event.registers.formdata.add</event>
        </listener>
    </configuration>

В этом примере настроены обработчики:

1. ``java:jboss/queues/Synergy/UsersQueue`` для всех событий класса
   ``event.users.*``, т.е. всех событий, связанных с пользователями:
   ``event.users.account.change``,
   ``event.users.formdata.change``,
   ``event.users.account.add`` и т.д.
2. ``java:jboss/queues/Synergy/RegisterCreateDocQueue``
   для события добавления записи реестра ``event.registers.formdata.add``.

Рассмотрим, например, код обработчика очереди ``UsersQueue``:

.. code-block:: java

    public class UsersMessagesListener implements MessageListener {

        public void onMessage(Message message) {
            //Получаем идентификатор пользователя, для которого
            //сгенерировано событие
            String userID = ((TextMessage) message).getText();
            //Получаем тип события
            String eventType = message.getStringProperty("api_event");

            //Выполнение действия по получению дополнительных данных через API
            //и прочих операций, зависящих от условий решаемой задачи
        }
    }

Ниже описаны типы событий, которые могут быть сгенерированы ARTA Synergy.

Для события ``[event.orgstructure.department.formdata.change]`` -
идентификатор подразделения, для события
``[event.orgstructure.position.formdata.change]`` - идентификатор
должности, для события ``[event.users.formdata.change]`` - идентификатор
пользователя будет передаваться как основной параметр, остальные как
свойства. Получить их можно следующим образом:

.. code-block:: java

    public void onMessage(Message message) {
        //Получение идентификатора пользователя/должности/подразделения (В зависимости от события на которое подписаны)
        String userID = ((TextMessage) message).getText();
        //Получаем идентификатор формы
        String formUUID = message.getStringProperty("formUUID");
    }

.. toctree::
  :maxdepth: 2
  :numbered:

  integration/events

Блокирующий процесс
-------------------

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

* при использовании блокирующего процесса маршрут реестра
  дожидается ответа о результате выполнения операции внешним модулем
* блокирующий процесс может завершиться положительно или отрицательно,
  что повлияет на дальнейшую работу маршрута
  (Если блокирующий процесс завершится отрицательно —
  процесс остановится, если положительно — то продолжит работу дальше)

Модуль, реализующий блокирующий процесс, должен представлять собой отдельное приложение,
задеплоенное на jboss в соответствии с правилами,
описанными в разделе `Как задеплоить интеграционное приложение`_.

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

Конфигурация блокирующего процесса
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Для того, чтобы добавить блокирующий процесс,
необходимо выполнить следующие действия:

1. Добавить процесс с в маршрут реестра в конфигураторе:

      .. figure:: _static/img/integration/blocking-add.png
         :scale: 68%

   Название процесса должно начинаться с ``event.blocking.``
   и далее строка, характеризующая суть блокирующего процесса.

2. Создать очередь JMS для блокирующего процесса.
   Для этого необходимо в конфигурационный файл
   (в стандартной установке это ``/opt/synergy/jboss/standalone/configuration/standalone-onesynergy.xml``)
   в секцию ``<subsystem xmlns="urn:jboss:domain:messaging:1.2">`` добавить:

   .. code-block:: xml

      <jms-queue name="ExampleQueue">
          <entry name="java:jboss/queues/Integration/ExampleQueue"/>
          <durable>true</durable>
      </jms-queue>


3. Связать очередь и процесс через конфигурационный файл
   ``{$jboss.home}/standalone/configuration/arta/api-observation-configuration.xml``,
   добавив в него следующее:

   .. code-block:: xml

      <listener>
           <queue>java:jboss/queues/Integration/ExampleQueue</queue>
           <event>event.blocking.example</event>
      </listener>

Обратите внимание, что название блокирующего процесса,
указанное в маршруте в конфигураторе должно быть
равно значению тега в конфигурационном файле
``api-observation-configuration.xml`` (в данном примере: ``event.blocking.example``)
и название очереди должно совпадать со значением тега
``queue`` конфигурационного файла
``api-observation-configuration.xml`` (в данном примере: ``java:jboss/queues/Integration/ExampleQueue``)

Сообщение, передаваемое в очередь, является экземпляром ``TextMessage``.
Содержимым сообщения является объект JSON с полями:

    1. dataUUID — идентификатор данных по форме записи реестра
    2. executionID — идентификатор блокирующего процесса
    3. documentID — идентификатор документа реестра

После того, как модуль обратится к внешней системе
и выполнит необходимые действия, он должен вызвать метод API Synergy
для того, чтобы возвратить результат выполнения процесса и продолжить работу маршрута.
Для того, чтобы это сделать, необходимо вызвать метод API
``rest/api/processes/signal``.

.. note:: Сигнал блокирующему процессу для его разблокировки/блокировки
          нужно отправлять после того, как этот процесс был запущен, то есть после того как
          транзакция с запуском процесса была завершена. Для этого, перед отправкой сигнала,
          проверяйте на наличие такого процесса в БД. В противном случае, блокирующий процесс
          может завершиться в транзакции, но в маршруте нет.

В примере кода ниже разблокировка маршрута осуществляется в методе ``onMessage``.
Если время выполнения действия значительно или зависит от внешних факторов
(например, доступность интегрируемой системы,
или необходимость ввода пользователем данных в интегрируемой системе),
то разблокировка маршрута может произойти позже,
в любой другой момент времени из другого метода,
а сам метод onMessage должен завершиться без ошибок, «запомнив» переданные параметры.

.. code-block:: java

    package kz.arta.synergy.samples.processes.blocking;

    import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
    import org.codehaus.jackson.JsonFactory;
    import org.codehaus.jackson.JsonParser;
    import org.codehaus.jackson.JsonToken;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import javax.jms.Message;
    import javax.jms.MessageListener;
    import javax.jms.TextMessage;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;

    /**
     * <p><Пример блокирующего процесса</p>
     */
    public class BlockingQueueListener implements MessageListener {

        public void onMessage(Message message) {

            String dataUUID = null;
            String executionID = null;
            String documentID = null;

            if (!(message instanceof TextMessage)){
                return;
            }

            try {

                JsonFactory factory = new JsonFactory();
                JsonParser parser = factory.createJsonParser(((TextMessage) message).getText());
                JsonToken token = null;

                while ((token = parser.nextToken()) != null) {
                    if (token == JsonToken.FIELD_NAME) {
                        String fieldName = parser.getText();
                        parser.nextToken();
                        String value = parser.getText();
                        if (fieldName.equals("dataUUID")){
                            dataUUID = value;
                        } else if (fieldName.equals("executionID")){
                            executionID = value;
                        } else if (fieldName.equals("documentID")){
                            documentID = value;
                        }
                    }
                }

             //Выполнение каких-либо действий
             ….......

                //Разблокировка маршрута

                String address = "http://127.0.0.1:8080/Synergy";
                String login = "1";
                String password = "1";
                String signal = "got_agree";
                boolean isSuccess = false;

                try {
                    URL url = new URL(address + "/rest/api/processes/signal?signal=" + signal + "&executionID=" + executionID + "&param1=resolution&value1=signal_is_" + signal);

                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setRequestProperty("Accept", "application/json; charset=utf-8");

                    String encoded = Base64.encode((login + ":" + password).getBytes());
                    conn.setRequestProperty("Authorization", "Basic " + encoded);

                    String output;
                    StringBuffer result = new StringBuffer();

                    BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

                    while ((output = br.readLine()) != null) {
                        result.append(output);
                    }

                    conn.disconnect();

                    JsonFactory factory = new JsonFactory();
                    JsonParser parser = factory.createJsonParser(result.toString());
                    JsonToken token = null;

                    while ((token = parser.nextToken()) != null) {
                        if (token == JsonToken.FIELD_NAME) {
                            String fieldName = parser.getText();
                            token = parser.nextToken();
                            if (fieldName.equals("errorCode") && parser.getText().equals("0")){
                                isSuccess = true;
                            }
                        }
                    }
                } catch (Exception exc){
                    logger.error(exc.getMessage(), exc);
                }

            } catch (Exception exc){
                logger.error(exc.getMessage(), exc);
            }
        }
    }

Дополнительный обработчик для стандартного процесса
---------------------------------------------------

Цель данного вида интеграции — дать возможность повлиять на
запуск стандартного процесса и, при необходимости, прервать его.

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

Обработчик может быть применён к процессам:

*   «работа» (`assignment-single`)
*   «согласование» (`agreement-single`)
*   «утверждение» (`approval-single`)
*   «ознакомление» (`acquaintance-single`)
*   «отправка документа» (`send-document`)
*   «общий процесс при запуске по формам» (`common-process-by-form`)
*   «отправка документа по форме» (`send-document-by-form`)

Обработчик представляет собой Java-класс, реализующий интерфейс

``kz.arta.synergy.integration.api.bp.StartHandlerIF``

Данный интерфейс находится в библиотеке ``integration-api.jar``,
которую можно найти в установленном экземпляре ARTA Synergy в директории
``/opt/synergy/jboss/standalone/deployments/Synergy.ear/lib``.

Интерфейс содержит два метода:

*   ``makeDecision()`` — проверяет возможно ли выполнение процесса
*   ``getResolution()`` — возвращает текст, который должен быть записан в ход
    исполнения

Более подробную информацию о полях методов можно посмотреть в javadoc
к этим методам, которые доступны в ``integration-api.jar`` (библиотека
содержит и скомпилированные классы, и исходный код).

Установка обработчика для процесса осуществляется с помощью
конфигурационного файла
``${jboss.server.config.dir}/arta/process-handlers-configuration.xml``,
имеющего следующий формат:

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8"?>
    <process-handlers-configuration
      xmlns="http://www.arta.kz/xml/ns/ai"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.arta.kz/xml/ns/arta
      http://www.arta.kz/xml/ns/ai/process-handlers-configuration .xsd">
     <handlers>
         <handler>
             <process>assignment-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>agreement-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>approval-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>acquaintance-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>send-document</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
     </handlers>
    </process-handlers-configuration>


Обработчики выполняются последовательно до тех пор, пока метод
``makeDecision()`` одного из них не вернет ``false``, после этого процесс
прерывается.

Библиотеку, содержащую обработчик необходимо скопировать в папку
``/opt/synergy/jboss/standalone/deployments/Synergy.ear/lib``.

После копирования библиотеки обработчика и изменения файла
``process-handlers-configuration.xml`` необходимо перезапустить JBoss.

.. attention:: Процесс ``common-process-by-form`` запускает процессы
               ``agreement-single``, ``approval-single``, ``acquaintance-single``,
               ``assignment-single`` (подпроцессы). Поэтому, если обработчик будет
               запрещать выполнение подпроцесса и при этом разрешать выполнение процесса
               ``common-process-by-form``, то подпроцессы все равно будут прерваны.
               Аналогично, если выполнение ``common-process-by-form`` разрешено, а
               выполнение подпроцесса запрещено, подпроцессы будут прерваны.

Пример использования
~~~~~~~~~~~~~~~~~~~~

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

Для установки внешнего модуля из репозитория необходимо установить пакет
``arta-synergy-ext-sendcontrol``.

Далее на остановленном JBoss в конфигурационном файле
``${jboss.server.config.dir}/arta/process-handlers-configuration.xml``
необходимо прописать следующие обработчики процесса:

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8"?>
    <process-handlers-configuration
      xmlns="http://www.arta.kz/xml/ns/ai"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.arta.kz/xml/ns/arta
      http://www.arta.kz/xml/ns/ai/process-handlers-configuration .xsd">
     <handlers>
         <handler>
             <process>assignment-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>agreement-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
     </handlers>
    </process-handlers-configuration>

Установка групп (каким группам пользователей Synergy) разрешать либо
блокировать процессы осуществляется с помощью конфигурационного файла
``${jboss.server.config.dir}/arta/ext/send-control.xml``. Пример настройки:

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8"?>
    <send-control>

        <!--
            Идентификаторы групп, членам которых разрешено отправлять
            что-либо из блоков `deny`
        -->
        <from>
            <allow>35</allow>
        </from>

        <!--
            Идентификаторы групп, членам которых могут отправлять что-либо
            только пользователи групп из блоков `allow`, если таковые есть.
            В противном случае, никто ничего этим пользователям отправить
            не сможет.
        -->
        <to>
            <deny comment="Вы не можете отправлять что-либо на согласование данному пользователю">111</deny>
        </to>
    </send-control>

Способы авторизации в ARTA Synergy
----------------------------------

REST API ARTA Synergy доступно только авторизованным пользователям.
Тип авторизации — ``BASIC HTTP``.
Методы API выполняются от имени того пользователя,
который авторизован. Имеются следующие типы авторизации:

Авторизация по логину и паролю
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

* Приложение предоставляет альтернативный интерфейс
  к некоторым модулям Synergy
  (мобильное приложение, десктопный клиент для хранилища)
* Приложение представляет собой `server-side` утилиту для синхронизации,
  для которого создан выделенный пользователь,
  и его логин и пароль хранятся в конфигурационном файле на сервере.

Для реализации данного типа авторизации надо передать в запросе заголовок
``Authorization`` со значением:

    ``"Basic " + Base64("login" + ":" + "password")``

Например:

+--------------------+--------------------------------------+
| Логин              | `Administrator`                      |
+--------------------+--------------------------------------+
| Пароль             | `123456`                             |
+--------------------+--------------------------------------+
| Значение заголовка | `Basic QWRtaW5pc3RyYXRvcjoxMjM0NTY=` |
+--------------------+--------------------------------------+

.. code-block::
    :emphasize-lines: 3

    POST /Synergy/rest/api/report/do?reportID=daa64ab2-1a79-4ea2-9c24-372e35d9da3e&amp;fileName=report.pdf HTTP/1.1
    Host: demo.arta.kz
    Authorization: Basic QWRtaW5pc3RyYXRvcjoxMjM0NTY=
    Cache-Control: no-cache
    Content-Type: multipart/form-data

Пример кода на JAVA:

.. code-block:: java
    :emphasize-lines: 5

    String login = "Administrator";
    String password = "123456";

    HttpResponse<String> response = Unirest.get("http://demo.arta.kz/Synergy/rest/api/admin/db/current_version")
    .header("authorization", Base64.getEncoder().encodeToString((login + ":" + password).getBytes(‌"UTF‌​-8"​)))
    .asString();

Пример кода на JavaScript (jQuery):

.. code-block:: js
    :emphasize-lines: 10

    var login = "Administrator";
    var password = "123456";

    var settings = {
      "async": true,
      "crossDomain": true,
      "url": "http://demo.arta.kz/Synergy/rest/api/admin/db/current_version",
      "method": "GET",
      "headers": {
        "authorization": ("Basic " + btoa(login + ":" + password))
      }
    }

    $.ajax(settings).done(function (response) {
      console.log(response);
    });

Пример кода на PHP:

.. code-block:: php
    :emphasize-lines: 11

    <?php

    $login = "Administrator";
    $password = "123456";

    $request = new HttpRequest();
    $request->setUrl('http://demo.arta.kz/Synergy/rest/api/admin/db/current_version');
    $request->setMethod(HTTP_METH_GET);

    $request->setHeaders(array(
      'authorization' => "Basic " . base64_encode("$login:$password")
    ));

    try {
      $response = $request->send();

      echo $response->getBody();
    } catch (HttpException $ex) {
      echo $ex;
    }

Сессионная авторизация
~~~~~~~~~~~~~~~~~~~~~~

Сессионная авторизации используется для встроенных WEB-модулей.
При cессионной авторизации также используется тип — ``BASIC HTTP``,
но в качестве логина пользователя необходимо использовать
значение ``$session`` и в качестве пароля —
полученное значение ``sso_hash``.

Таким образом заголовок ``Authorization`` должен иметь значение:

    ``"Basic " + Base64("$session" + ":" + "sso_hash")``

Например:

+-----------------------+--------------------------------------------------------+
| Значение ``sso_hash`` | ``D3RONfC52dtJO5XgDyn5qUMv``                           |
+-----------------------+--------------------------------------------------------+
| Значение заголовка    | ``Basic JHNlc3Npb246RDNST05mQzUyZHRKTzVYZ0R5bjVxVU12`` |
+-----------------------+--------------------------------------------------------+

Получить значение ``sso_hash`` авторизованного пользователя можно
следующими способами:

#.  В случае если приложение представляет собой `Внешний WEB-модуль`_, получить
    значение ``sso_hash`` можно из строки запроса.

    Пример кода на JavaScript (jQuery):

    .. code-block:: js
        :emphasize-lines: 13

        function getURLParameter(name) {
          return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)')
          .exec(location.search) || [null, ''])[1]
          .replace(/\+/g, '%20')) || null;
        }

        var settings = {
          "async": true,
          "crossDomain": true,
          "url": "http://demo.arta.kz/Synergy/rest/api/admin/db/current_version",
          "method": "GET",
          "headers": {
            "authorization": ("Basic " + btoa("$session" + ":" + getURLParameter('sso_hash')))
          }
        }

        $.ajax(settings).done(function (response) {
          console.log(response);
        });

#.  С помощью переменной окружения ``$CURRENT_USER`` основного WEB-приложения Synergy,
    которая представляет собой JSON-объект следующего вида:

    .. code-block:: json

        {
          "id": "Идентификатор текущего пользователя",
          "sso_hash": "hash-сумма для идентификации пользователя",
          "surname": "Фамилия текущего пользователя",
          "name": "Имя текущего пользователя",
          "patronymic": "Отчество текущего пользователя"
        }

    Пример кода на JavaScript (jQuery):

    .. code-block:: js
        :emphasize-lines: 7

        var settings = {
          "async": true,
          "crossDomain": true,
          "url": "http://demo.arta.kz/Synergy/rest/api/admin/db/current_version",
          "method": "GET",
          "headers": {
            "authorization": ("Basic " + btoa("$session" + ":" + $CURRENT_USER.sso_hash))
          }
        }

        $.ajax(settings).done(function (response) {
          console.log(response);
        });

    .. note:: Данный способ можно использовать только если код исполняется
              в основном приложении Synergy. Например, приложение представляет
              собой :ref:`extcmp-label` .


Авторизация по ключам
~~~~~~~~~~~~~~~~~~~~~

Модуль, который хочет авторизоваться от имени какого-либо пользователя таким способом,
должен сгенерировать для него ключевую пару,
обеспечив сохранность закрытого ключа.
Затем модуль сохраняет получивший открытый ключ для пользователя в Synergy,
используя следующий вызов API:

    ``rest/api/person/generate_auth_key``

Этот вызов назначает ключ тому пользователю, от имени которого выполняется.

Параметр ``user_token_expire_interval`` регулирует интервал устаревания ключей авторизации.
Пример настройки интервала:

.. code-block:: sql

	   insert into options (id, value) values ('user_token_expire_interval', '5256000'); -- 10 лет

.. note:: Интервал устаревания ключа указывается в минутах. Значение по умолчанию ``0``, то есть если ранее для
          данного пользователя был сгенерирован другой ключ, то предыдущий автоматически становится недействительным.

Создать ключ можно только для существующего WEB-модуля,
так как для этого требуется идентификатор приложения.

.. tip::  Если у вас нет необходимости разрабатывать WEB модуль,
          но есть необходимость в использовании авторизации по ключам,
          можно добавить внешний модуль и отключить его использование
          в административном приложении SynergyAdmin для всех элементов оргструктуры.

Использование этого ключа для авторизации аналогично использованию сессионного ключа.
Тип авторизации ``Basic HTTP``,
в качестве логина пользователя надо использовать строку ``$key``,
в качестве пароля — полученный с помощью API ключ.

Таким образом заголовок ``Authorization`` должен иметь значение:

    ``"Basic " + Base64("$key" + ":" + "значение_ключа")``

Например:

+---------------------+------------------------------------------------------------------------------------------------------------------------+
| Значение ключа      | ``MS03Y2Q0ZGU3YS0zYjRkLTQ2NjgtYWIyOC0zZDI1YzgxZGNmOGZfMjAxMy0xMC0zMSAxNzo0Mg==``                                       |
+---------------------+------------------------------------------------------------------------------------------------------------------------+
| Значение заголовка  | ``Basic JGtleTpNUzAzWTJRMFpHVTNZUzB6WWpSa0xUUTJOamd0WVdJeU9DMHpaREkxWXpneFpHTm1PR1pmTWpBeE15MHhNQzB6TVNBeE56bzBNZz09`` |
+---------------------+------------------------------------------------------------------------------------------------------------------------+

Внешний WEB-модуль
------------------

Web-приложение внешнего модуля открывается
в ``iframe`` в окне основного приложения.
При этом рабочая область внешнего модуля
занимает всю область страницы, кроме панели меню и панели задач:

.. figure:: _static/img/integration/web-module.png
   :scale: 50%

   Внешний WEB-модуль

Для добавления нового модуля нужно перейти в

:menuselection:`Конфигуратор --> Настройки системы --> Управление модулями --> Внешние модули`

и нажать на кнопку "Добавить".

.. figure:: _static/img/integration/web-modules.png

   Внешние модули

В открывшемся окне нужно заполнить следующие поля:

* **«Название»** - название модуля в соответствующем интерфейсе.
* **«Код»** - поле должно содержать уникальное значение.
* **«Адрес приложения»** - поле для ввода URL.
* **«Описание модуля»** - поле для описания данного модуля.
* **«Иконка»** - задает иконку модуля в пользовательской подсистеме
  (по умолчанию внешний модуль имеет стандартную иконку). Для того,
  чтобы изменить стандартную иконку, нужно кликнуть по кнопке
  «Выберите файл» и в диалоге выбора файла указать файл формата PNG,
  размер которого не превышает 28х26.

  .. figure:: _static/img/integration/web-module-add.png

     Добавление нового внешнего модуля

.. hint:: Добавить внешний web-модуль можно с помощью SQL-запроса
          в БД ``synergy.outer_modules``, вставив запись со следующими полями:

              * ``id`` — идентификатор модуля, должен совпадать с идентификатором
                 вашего проекта в репозитории проектов
              * ``nameru``, ``namekz``, ``nameen`` — название модуля на русском,
                казахском и английском языках соответственно
              * ``url`` — адрес приложения
              * ``description`` — описание модуля
              * ``active`` — активен ли модуль, 1/0.

Для реализации механизма :abbr:`SSO (Single Sign-On)` приложений,
ARTA Synergy при загрузке внешнего web-модуля будет в строку URL
добавлять три параметра:

#. ``locale`` — локаль авторизованного пользователя
#. ``sso_hash`` — hash-сумма для идентификации пользователя.
#. ``host`` — адрес, с которого загружено приложение Synergy

Например, если URL приложения

    ``http://host:port/plans_module``,

то при обращении к модулю будет вызываться

    ``http://host:port/plans_module?locale=locale_value&sso_hash=sso_hash_value``

Интегрированный модуль должен будет будет получить из URL
параметр ``sso_hash`` и запросить по REST API у ARTA Synergy
информацию об авторизованном пользователе (идентификатор, имя).
Если метод REST API возвращает информацию о пользователе,
это подтверждает, что данный пользователь
действительно авторизован с данного хоста, в данном браузере.

Далее строка ``sso_hash`` может быть использована для
`Сессионная авторизация`_ и вызова REST API Arta Synergy.

В ARTA Synergy реализована возможность обращения к ее модулям по относительной ссылке.
Такая же возможность существует для внешних web-модулей. Переход по ссылке вида:

``#submodule=outer&outerModuleID='код_модуля'&прочие_параметры_по_желанию_модуля``

активирует в Synergy заданный модуль и передаст ему заданные в url-e параметры
(параметры ``locale``, ``sso_hash``, ``host`` также будут переданы,
несмотря на то, что они отсутствуют в ссылке).

Часто возникает необходимость в этой ссылке передать ссылку на текущий документ.
Для этого можно добавить в ссылку параметр,
значение которого будет равно ``${docID}`` —
эта строка в web-интерфейсе проигрывателя форм Synergy
будет заменена на идентификатор данного документа.

Внешний проигрыватель форм
--------------------------

В бизнес-приложениях на базе Synergy может возникнуть необходимость работы из
внешних систем с формами Synergy. В этом случае можно использовать внешний проигрыватель форм.

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

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

.. note:: Проигрыватель форм запускается на стороне клиента, поэтому все события
          и скрипты срабатывают только при открытом проигрывателе.

Подключение проигрывателя форм
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#. Для подключения проигрывателя на страницу необходимо добавить код в раздел ``head``:

    .. code-block:: xml

        <script>
         FORM_PLAYER_URL_PREFIX = "http://127.0.0.1:8080/Synergy/"; <!--служебная переменная для корректной работы компонента "HTD-редактор"-->
        </script>
        <link rel="stylesheet" href="http://127.0.0.1:8080/Synergy/js/form.player.css"/> <!--стандартный стиль компонентов формы-->
        <script src="http://127.0.0.1:8080/Synergy/js/vendor.js" type="text/javascript"></script> <!--ссылка на сторонние библиотеки-->
        <script src="http://127.0.0.1:8080/Synergy/js/form.player.js" type="text/javascript"></script> <!--ссылка на проигрыватель форм-->

   и вставить элемент для размещения проигрывателя в тело страницы:

   .. code-block:: xml

       <div id="form_player_container">
           <div id="form_player_div"></div>
       </div>

#. Код скрипта, который использует проигрыватель, должен содержать ссылку на Synergy:

   .. code-block:: javascript

      AS.OPTIONS.coreUrl = "http://127.0.0.1:8080/Synergy/"; //ссылка на экземпляр Synergy

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

   .. code-block:: javascript

       'use strict';

        AS.OPTIONS.locale = "ru";
        AS.OPTIONS.coreUrl = "http://127.0.0.1:8080/Synergy/";


        var portal = {
            player : null,

            /**
             * очистить текущий проигрыватель форм
             */
            clearPlayer : function() {
                if(portal.player) {
                    portal.player.destroy();
                }
            },
            /**
             * добавить новый проигрыватель форм
             */
            createPlayer : function(formCode){

                portal.clearPlayer();
                portal.player = AS.FORMS.createPlayer();

                portal.player.showFormByCode(formCode);

                portal.player.view.appendTo($('#form_player_div'));

            }

        };

        $(document).ready(function(){

            AS.OPTIONS.login = "login";
            AS.OPTIONS.password = "password";
            portal.createPlayer("formCode");

        });

См. также документацию по `Скриптинг в формах`_ .

Варианты использования внешнего проигрывателя форм
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

В данном разделе описываются основные примеры использования внешнего
проигрывателя форм. Каждый пример отражает одно из базовых требований
к внешнему проигрывателю и содержит:

* описание примера;
* реализованный пример внешнего проигрывателя форм, встроенного во внешний портал (в данном случае - в портал документации);
* а также исходные коды JavaScript и CSS этих примеров.

Примеры использования внешнего проигрывателя форм:

.. toctree::
  :maxdepth: 2
  :numbered:

  integration/extfp-usecases/uc1
  integration/extfp-usecases/uc2
  integration/extfp-usecases/uc3
  integration/extfp-usecases/uc4
  integration/extfp-usecases/uc5

Ссылки на модули системы и их внутренние элементы
-------------------------------------------------

Ссылки на модули и различные объекты Synergy
можно использовать как внутри основного web-приложения
(в этом случае предпочтительно использовать относительные
ссылки, чтобы не перезагружать страницу), так и во внешних
системах.

Общий вид ссылок:

    http[s]://**host***[:port]*/**Application**?*param1*=*value1*&*param2*=*value2*#*param3*=*value3*&*param4*=*value4*

где

* **host** - доменное имя или ip-адрес сервера Synergy
* *port* - порт
* **Application**:
    * `Synergy` - основное приложение
    * `Configurator` - Конфигуратор
    * `SynergyAdmin` - административное приложение
* *param1*, *param2* - параметры абсолютной ссылки
* *param3*, *param4* - параметры относительной ссылки

Параметры абсолютной ссылки - это, как правило:

* **locale** - локаль загружаемой системы
* **nocache** - специальный параметр, предотвращающий случайное кэширование

остальные параметры можно передавать как параметры относительной ссылки.

Ниже для краткости будем приводить образец относительной ссылки

Ссылка на модуль системы
~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=module_id``


где ``module_id``:

* **workflow** - Потоки работ
* **calendar** - Ежедневник
* **repository** - Хранилище
* **plans** - Проекты
* **pointers** - Цели и показатели
* **employees** - Сотрудники

При переходе по ссылке откроется указанный модуль.

Ссылка на документ и файл в нём
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=common&file_identifier=some_file_id&action=open_document&document_identifier=some_doc_id``

При переходе по такой ссылке откроется указанный документ с основным файлом,
а если указан ``file_identifier`` - то откроется документ с этим файлом.

Ссылка на проект и мероприятие в нем
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=plans&action=open_action&action_identifier=some_action_id&project_identifier=some_project_id``

При переходе по такой ссылке откроется указанный проект,
а если указан ``action_identifier`` - то в проекте будет выделено
это мероприятие.

Ссылка на профиль пользователя
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=employees&innermodule=structure&action=open_user&user_identifier=some_user_id``

При переходе по такой ссылке будет открыт модуль «Сотрудники»,
а в нем - профиль указанного пользователя

Отключение всего пользовательского клиентского скриптинга
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Если в абсолютной ссылке указать параметр ``noCustomScripting``,
то все пользовательские ВМК, скрипты в формах и пользовательских
компонентах будут отключены. Это можно использовать для отладки
пользовательских компонентов, ВМК и скриптов на форме.

Как задеплоить интеграционное приложение
----------------------------------------

ARTA Synergy работает на сервере приложений JBoss AS7.
Интеграционное приложение может представлять собой
*jar*-файл либо *war*-файл либо их комбинацию.

Если приложение является одиночным файлом, его можно задеплоить,
скопировав в директорию ``${jboss.home}/standalone/deployments``.
Если приложение состоит из нескольких файлов,
необходимо создать `*.ear` приложение.

Если приложение имеет зависимости на внешние библиотеки
и они находятся в модулях JBoss-а (``${jboss.home}/modules``),
необходимо использовать их, прочие зависимости — помещать внутрь приложения.

.. danger:: В целях безопасности работы приложения Synergy и сервера приложений
            категорически запрещается помещать артефакты интеграционного модуля
            в приложение ``Synergy.ear`` и изменять состав модулей (``${jboss.home}/modules``).
