Разработка расширения для программы DVWebTool

Программа DVWebTool устанавливается на клиентскую машину только в ОС Microsoft Windows. Для ОС Linux используется DVSupService, см. подробнее Установка и запуск службы DVSupService.

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

Программа DVWebTool является приложением для рабочего стола с возможностью расширения функциональности. Расширение DVWebTool представляет собой DLL-файл содержащий ожидаемый класс с веб-методами. Данные методы могут вызываться клиентскими компонентами Web-клиента (есть стандартный метод JSAPI для вызова) или другими способами.

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

Алгоритм работы расширения:
  1. При вызове функции расширения Клиент передаёт идентификаторы PDF файлов карточки Документ (идентификаторы карточек Файл с версиями, а не идентификаторы собственно файлов) и идентификатор самой карточки Документ.

  2. Расширение DVWebTool загружает PDF-файлы на компьютер пользователя с помощью функций, предоставляемых Web-клиентом.

  3. Расширение DVWebTool добавляет в полученные PDF-файлы водяные знаки используя функции сторонней библиотеки для работы с PDF.

  4. Расширение DVWebTool прикрепляет полученные файлы с водяными знаками с помощью функций, предоставляемых Web-клиентом.

    Расширение не должно изменять существующие файлы карточки: полученные PDF-файлы с водяными знаками должны быть добавлены в карточку в качестве дополнительных.

Для реализации функций, указанных в пп. 2 и 4, потребуется разработать расширение Web-клиента с соответствующей функциональностью.

Также для примера необходимо разработать клиентское расширение, которое будет вызывать функции расширения DVWebTool с передачей им идентификаторов PDF-файлов Документа, в которые нужно добавить водяные знаки.

Полный код примера доступен в репозитории на GitHub.

Расширение Web-клиента

Для работы расширения DVWebTool необходимо, чтобы Web-клиент по запросу возвращал файл из карточки Файл с версиями с переданным идентификатором, а также мог прикреплять переданные файлы к карточке Документ.

Требуемая функциональность может быть реализована с помощью расширения Web-клиента.

Ниже приведена часть исходного кода контроллера, реализованного в серверном расширении.

Файл Others/Watermark/WatermarkServerExtension/Controllers/FileOperationsController.cs
    public class FileOperationsController : ControllerBase (1)
    {
        private readonly IFileService fileService;
        private readonly IEnvironmentService environmentService;

        public FileOperationsController(IFileService fileService, IEnvironmentService environmentService)
        {
            this.fileService = fileService;
            this.environmentService = environmentService;
        }

        [HttpGet]
        public HttpResponseMessage GetFile(Guid fileCardID) (2)
        {
            FileReader fileReader = fileService.GetFileReader(fileCardID);

            if (fileReader.FileID == Guid.Empty)
                return new HttpResponseMessage(HttpStatusCode.NotFound);

            HttpResponseMessage response = CreateResponseForFile(fileReader); (3)
            return response;
        }

        [HttpPost]
        public async Task<ActionResult> AddFile([FromForm(Name = "file")] FormFileCollection formFiles, Guid cardId) (4)
        {

            string rootPath = CreateAndGetTempFolder();
            try
            {
                List<string> files = await SaveFilesFromResponse(formFiles); (5)

                await fileService.AddFilesToCard(cardId, files);
                return Ok();

            }
            catch (Exception e)
            {
                return Problem(statusCode: (int)HttpStatusCode.InternalServerError, detail: e.Message);
            }
        }
1 Контроллер получения-записи файлов карточки.
2 Возвращает файл из карточки файла с версиями с fileCardID.
3 Создаёт ответ с файлом.
4 Добавляет в карточку файлы из запроса.
5 Добавляемые файлы.

В контроллере используется сервис IFileService, реализация которого приведена ниже. Код дополнительных методов смотрите в полном примере на GitHub.

Файл Others/Watermark/WatermarkServerExtension/Services/FileService.cs
    public class FileService : IFileService
    {
        private readonly ICurrentObjectContextProvider currentObjectContextProvider;
        private Guid DOCUMENT_CARD_ID = new Guid("B9F7BFD7-7429-455E-A3F1-94FFB569C794");

        public FileService(ICurrentObjectContextProvider currentObjectContextProvider) {
            this.currentObjectContextProvider = currentObjectContextProvider;
        }

        public async System.Threading.Tasks.Task<IEnumerable<Guid>> AddFilesToCard(Guid cardID, List<string> files) (1)
        {
            var document = GetDocumentCard(cardID); (2)
            ObjectContext objectContext = GetObjectContext();

            ILockService lockService = GetLockService(objectContext);

            if (lockService.IsObjectLockedByAnotherUser(document)) {
                throw new Exception($"Карточка {cardID} заблокирована другим пользователем");
            }

            if (lockService.LockObjectBase(document) == false) {
                throw new Exception($"Не удалось заблокировать карточку {cardID}");
            }

            IDocumentService documentService = GetDocumentService(objectContext);

            return await System.Threading.Tasks.Task.Run(() =>
            {
                IEnumerable<Guid> documentFileIds;
                try
                {
                    IEnumerable<DocumentFile> documentsFiles = documentService.AddAdditionalFiles(document, files);
                    objectContext.AcceptChanges();
                    documentFileIds = documentsFiles.Select(t => t.FileId);
                }
                catch (Exception ex)
                {
                    throw new Exception($"Ошибка при добавлении файлов в карточку {cardID}/n {ex.Message}");
                }
                finally {
                    lockService.UnlockObject(document);
                }

                return documentFileIds; (3)
            });
        }

        public FileReader GetFileReader(Guid fileCardID) (4)
        {
            ObjectContext objectContext = GetObjectContext();
            IVersionedFileCardService versionedFileCardService = GetVersionedFileCardService(objectContext);

            VersionedFileCard fileCard = versionedFileCardService.OpenCard(fileCardID); (5)

            Guid fileID = fileCard.CurrentVersion.Id; (6)

            UserSession userSession = GetUserSession();

            if (userSession.FileManager.FileExists(fileID) == false)  (7)
                return new FileReader();

            var file = userSession.FileManager.GetFile(fileID); (8)

            return new FileReader() { (9)
                FileID = fileID,
                FileName = file.Name,
                Stream = file.OpenReadStream()
            };
        }
(10)
1 Добавляет файлы из файловой системы в карточку cardID.
2 Получает карточку, к которой прикрепляются файлы.
3 Возвращает идентификаторы добавленных файлов с версиями.
4 Получает указатель для чтения файла из карточки файла с версиями fileCardID.
5 Получение файла карточки с версиями.
6 Получение идентификатора файла из последней версии.
7 Если файла нет, возвращается пустой указатель.
8 Запрашивается файл текущей версии.
9 Возвращается указатель для чтения файла.
10 Код вспомогательных функций приведён в полном примере.

Расширение программы DVWebTool

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

Расширение DVWebTool представляет собой сборку, в которой реализован интерфейс Docsvision.DVWebTool.WebServices.IServiceManager. Данный интерфейс определяет метод Register, который регистрирует контроллеры с необходимыми функциями во внутреннем веб-сервере DVWebTool, и поле DisplayName с названием расширения.

Ниже приведён код класса, реализующего интерфейс IServiceManager в данном примере.

Файл Others/Watermark/WatermarkWebToolExtension/WatermarkManager.cs
 public class WatermarkManager : IServiceManager
    {
        public string DisplayName => "Watermark to PDF"; (1)

        public void Register(WebSocketSharper.Server.WebSocketServer server) (2)
        {
            server.AddWebSocketService<WatermarkController>("/Watermark"); (3)
        }
    }
1 Название расширения для информации в окне "О программе".
2 Регистрация контроллера расширения.
3 Регистрация контроллера PDFWatermarkController для маршрута Watermark.

В данном примере выполняется регистрация контроллера WatermarkController для обработки запросов, поступающих по пути /Watermark.

Контроллер, передаваемый в AddWebSocketService, должен быть производным типа Docsvision.DVWebTool.WebServices.BaseService. При его реализации необходимо зарегистрировать в BaseService.actions веб-методы, с помощью которых будут вызываться функции DVWebTool, предоставляемые расширением. Данные методы будут доступны для вызова по протоколу WebSocket по адресу ws://localhost:/%Адрес контроллера%/%Название метода%.

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

Файл Others/Watermark/WatermarkWebToolExtension/WatermarkController.cs
    public class WatermarkController : BaseService (1)
    {
        private readonly ServiceProvider serviceProvider;
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

        private string WATERMARK = "Секретно"; (2)

        public WatermarkController()
        {
            serviceProvider = new ServiceProvider();
            Init();
        }

        private void Init() (3)
        {
            actions.Add(nameof(AddWatermarkToFiles), AddWatermarkToFiles);
        }

        private WebServiceResponse AddWatermarkToFiles(WebServiceRequest webServiceRequest, JObject data) (4)
        {
            Logger.Info("Получено задание на добавление водяного знака");

            if (data == null)
            {
                return CreateBadResponse("С клиента не переданы данные для работы");
            }

            AddWatermarkRequest request; (5)
            try
            {
                request = data.ToObject<AddWatermarkRequest>();
            }
            catch
            {
                Logger.Error($"Ошибка преобразования полученного сообщения: {data}");
                return CreateBadResponse("Поступивший запрос не соответствует ожидаемому формату");
            }

            string doneInfo;

            try
            {
                doneInfo = HandleRequest(request).Result;
            }
            catch (Exception ex)
            {
                return CreateBadResponse(ex.Message);
            }

            return CreateEndProcessResponse(request.CardID, $"Водяные знаки добавлены в файлы:<p/>{doneInfo}");
        }

        private async Task<string> HandleRequest(AddWatermarkRequest request) (6)
        {
            var connectionService = new ConnectionToWebClient(request.ServerAddress);

            try
            {
                await connectionService.Authentificate();
            }
            catch (Exception ex)
            {
                Logger.Error(ex.Message);
                throw new Exception("Не удалось подключиться к серверу Web-клиента");
            }

            List<string> files = new List<string>();

            foreach (var fileId in request.FileIDs) (7)
            {
                try
                {
                    string pathToFile = await connectionService.PullFile(fileId);
                    files.Add(pathToFile);
                }
                catch (Exception ex)
                {
                    Logger.Error(ex.Message);
                    throw new Exception($"Не удалось получить из карточки файл с идентификатором {fileId}");
                }
            }

            var watermarkService = new WatermarkService(); (8)

            List<Task<string>> processes = new List<Task<string>>(); (9)
            string doneInfo = "";

            foreach (var file in files)
            {
                try
                {
                    processes.Add(watermarkService.AddWatermark(file, WATERMARK));
                    doneInfo += $" {Path.GetFileName(file)}<p/>";
                }
                catch (Exception ex)
                {
                    Logger.Error(ex.Message);
                    throw new Exception($"Не удалось добавить водяной знак в файл {Path.GetFileName(file)}");
                }
            }

            var filesWithWatermark = await Task.WhenAll(processes);

            try
            {
                await connectionService.PushFiles(request.CardID, filesWithWatermark); (10)
            }
            catch (Exception ex)
            {
                Logger.Error(ex.Message);
                throw new Exception($"Не удалось сохранить файлы в карточке");
            }
            return doneInfo;
        }
(11)
1 Реализация контроллера PDFWatermarkController
2 Текст водяного знака.
3 Регистрация методов контроллера PDFWatermarkController. Название метода регистрозависимое.
4 Веб-метод добавления водяного знака. Метод должен принимать два параметра: WebServiceRequest и JObject. Данные передаются в data.
5 Загружаем данные из полученного запроса в модель AddWatermarkRequest.
6 Обработчик запроса на добавление водяного знака. Получает данные запроса. Возвращает строку с названиями файлов, в которые добавлены запросы.
7 Загружаем с Web-клиента файлы, идентификаторы которых переданы в запросе.
8 Сервис для работы с водяными знаками.
9 Добавление водяных знаков в файлы из списка files.
10 Отправка запроса на прикрепление файлов filesWithWatermark к карточке request.CardID.
11 Код вспомогательных функций приведён в полном примере.
Контроллер WatermarkController использует функции двух сервисов:
  • ConnectionToWebClient для получения файлов из карточек Файл с версиями и загрузки файлов в карточки Документ. Данный сервис использует функции, реализованного серверного расширения Web-клиента.

  • WatermarkService для добавления водяных знаков в PDF файлы.

Реализации данных сервисов смотрите в исходных кодах примера на GitHub.

После публикации расширения DVWebTool, зарегистрированный в расширении метод AddWatermarkToFiles контроллера Watermark может быть вызван из клиентского расширения Web-клиента с помощью сервиса DVWebToolConnection.

Клиентское расширение

В качестве примера использования функций расширения Watermark to PDF программы DVWebTool было разработано клиентское расширение, которое выполняет две задачи:

  • Получает из текущей открытой карточки Документа идентификаторы основных файлов формата PDF.

  • Отправляет идентификаторы файлов расширению Watermark to PDF.

Общие требования к реализации клиентских расширений приведены в пункте Расширение возможностей клиентской части Web-клиента.

Прежде всего реализуем сервис, получающий список идентификаторов файлов, в которые нужно добавить водяной знак и передавать его в метод AddWatermarkToFiles расширения Watermark to PDF. Ниже приведён исходный код данного сервиса.

Файл Others/Watermark/WatermarkWebExtension/src/WatermarkService.tsx
export class WatermarkService { (1)

    constructor(private services: $RequestManager & $WebServices & $ApplicationSettings & $MessageBox & $SiteUrl & $CurrentEmployeeId) {
    }

    AddWatermarkToFiles(cardID: string, fileIDs: string[]): Promise<IWebServicesResponse<any>> { (2)

        const data: any = { (3)
            data: {
                cardID: cardID,
                fileIDs: fileIDs,
                userID: this.services.currentEmployeeId,
                serverAddress: this.services.siteUrl
            },
            action: 'AddWatermarkToFiles', (4)
            locale: this.services.applicationSettings.culture.twoLetterISOLanguageName  (5)
        };

        return DVWebToolConnection.trySendData("Watermark", data, this.services); (6)
    }
}


export type $WatermarkService = { watermarkService: WatermarkService }; (7)
export const $WatermarkService = serviceName((s: $WatermarkService) => s.watermarkService);
1 Клиентский сервис, предоставляющий доступ к методу добавления водяных знаков, предоставляемому расширением DVWebTool.
2 Метод принимает идентификатор карточки и идентификатор её конвертируемых файлов.
3 В данных нужно также передать:
  • Адрес сервера Web-клиента (DVWebTool должен подключиться к Web-клиенту для получения и сохранения файлов карточки).

  • Идентификатор пользователя (для отправки оповещения о завершении процесс).

4 Название метода, вызываемого из расширения DVWebTool.
5 Обязательное для передачи название локали.
6 Вызываем метод AddWatermarkToFiles из контроллера Watermark расширения DVWebTool. Тип DVWebToolConnection предоставляет методы для работы с DVWebTool.
7 Регистрируем сервис WatermarkService.

Сервис $WatermarkService предоставляет единственный метод AddWatermarkToFiles, который вызывает функцию программы DVWebTool с помощью метода DVWebToolConnection.trySendData. При вызове данного метода нужно передать название контроллера и данные, в которых должно быть название вызываемого метода контроллера — в поле action передаваемых данных.

В качестве метода, использующего сервис $WatermarkService реализуем обработчик нажатия кнопки разметки карточки Документ, который будет получать идентификаторы основных PDF-файлов карточки, вызывать метод $WatermarkService.AddWatermarkToFiles и после завершения его работы обновлять содержимое элемента управления Список файлов, или отображать ошибку.

Файл Others/Watermark/WatermarkWebExtension/src/EventHandlers.tsx
export async function addWatermark(sender: LayoutControl) { (1)
   showNotify("Запущен процесс добавления водяных знаков");

   let cardId = sender.layout.getService($CardId); (2)
   let files = sender.layout.getService($FileService).getFiles();

   let fileIDs = new Array();

   files.forEach((item) => { (3)
      if (item.data.isMain && item.data.name.toLowerCase().endsWith(".pdf")) { (4)
         fileIDs.push(item.data.fileId);
      }
   })

   let watermarkService = sender.layout.getService($WatermarkService); (5)

   let response = await watermarkService.AddWatermarkToFiles(cardId, fileIDs); (6)

   if (response.success == false) {
      showError(response.errorMessage);
   } else {
      showNotify(response.data.message);

      if (layoutManager.cardLayout == null)
         return;

      let currentCardId = sender.layout.getService($CardId);
      if (currentCardId == cardId) {
         let fileList = layoutManager.cardLayout.controls.get<FileListControl>("fileList"); (7)
         await fileList.reloadFromServer();
      }
   }
}
1 Обработчик для события нажатия иконки добавления водяных знаков
2 Получение идентификатора текущей карточки и список файлов из элемента FileList
3 Обработка только основных файлов с расширением .pdf
4 Получение из модели files только идентификаторы файлов
5 Получение реализованный сервис для работы с водяными знаками
6 Вызов функцию добавления водяных знаков для файлов с идентификаторами из списка fileIDs
7 Обновление список файлов, если есть. Возможно, уже открыта другая карточка, но в данном случае это не существенно.

Сборка проекта

Сборка серверного расширения Web-клиента и расширения DVWebTool.
  1. Откройте решение Samples.sln.

  2. Соберите проект Other  Watermark  WatermarkServerExtension.

  3. Соберите проект Other  Watermark  WatermarkWebToolExtension.

Сборка клиентской части.
  1. Откройте в командной строке папку Others  Watermark  WatermarkWebExtension.

  2. Выполните команды:

    npm install
    npm update
    npm run build:prod

Публикация компонентов на сервере Web-клиента

  1. Остановите dvwebclient.

  2. Скопируйте папку SamplesOutput/Content/Modules/WatermarkWebExtension/ в /lib/docsvision/webclient/Content/Modules на Linux.

  3. Скопируйте папку SamplesOutput/Content/Tools/DVWebTool/Application Files/ в Путь-к-папке-с панелью-управления-Web-клиентом/Content/Tools/DVWebTool/ на Windows.

  4. Скопируйте папку SamplesOutput/Extensions/WatermarkServerExtension в Каталог-установки-Web-клиента/Extensions на Windows.

  5. Запустите dvwebclient.

Регистрация расширения DVWebTool на сервере Web-клиент.
  1. Запустите "Панель управления Web-клиентом".

  2. Нажмите кнопку Собрать DVWebTool

  3. В диалоге введите адрес публикации Web-клиента, при необходимости установите другие опции, нажмите кнопку ОК.

  4. Установите собранную программу DVWebTool на сервер Web-клиент, см. подробнее "Установка и запуск службы DVWebTool".

  5. Рекомендуется перезапустить dvwebclient.

Проверка примера

Настройте разметку:
  1. В программе Конструктор Web-разметок добавьте элемент Кнопка в любую разметку просмотра карточки Документ.

  2. Укажите для события При щелчке обработчик addWatermark.

  3. Сохраните разметку.

  4. Установите или обновите программу DVWebTool. Cм. пункт Установка и запуск программы DVWebTool в руководстве пользователя Web-клиент.

  5. Запустите программу DVWebTool. Убедитесь, что программа DVWebTool и Web-клиент будут запущены от имени одного пользователя.

Убедитесь, что все расширения установлены:
  1. В Web-клиенте перейдите в раздел О программе.

    В разделе Подключенные расширения должны быть указаны расширения:
    • WatermarkServerExtension (Сборка %номер сборки%).

    • Watermark to PDF %номер сборки% — web-расширение.

  2. Откройте панель About из меню Docsvision DVWebTool.

    В списке установленных расширений должно быть расширение:
    • Watermark to PDF <версия>.

  3. Откройте для просмотра любую карточку с разметкой, настроенной в п. 1.

  4. Добавьте один или несколько основных файлов с расширением .pdf.

  5. Нажмите на добавленную кнопку с обработчиком addWatermark.

Появится сообщение Запущен процесс добавления водяных знаков — начнётся процесс добавления водяных знаков.

После завершения процедуры появится сообщение Водяные знаки добавлены в файлы: %список PDF-файлов, в которые добавлены водяные знаки%.

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

Особенности обновления

При обновлении версии Web-клиента регистрация расширения в программе DVWebTool будет отменена (конфигурационные файлы программы перезаписываются).

После установки новой версии Web-клиента необходимо повторно зарегистрировать расширения DVWebTool на сервере Web-клиент.