Разработка расширения для DVWebTool и DVSupService
Программа DVWebTool устанавливается на клиентскую машину только в ОС Microsoft Windows. Для ОС Linux используется DVSupService, см. подробнее Установка и запуск службы DVSupService. |
Ссылка на пример на GitHub: Watermark.
Расширения для DVSupService и DVWebTool можно распространять с помощью пакетов Debian, с подробной инструкцией и необходимыми материалами можно ознакомиться в репозитории на GitHub.
Дополнительным компонентом модуля Web-клиент является программа DVWebTool, которая предоставляет функциям Web-клиента доступ к устройствам и программному обеспечению, установленному на компьютере пользователя Web-клиента. Программа DVWebTool также может быть использована для обработки произвольных задач на пользовательском компьютере.
Программа DVWebTool является приложением для рабочего стола с возможностью расширения функциональности. Расширение DVWebTool представляет собой DLL-файл содержащий ожидаемый класс с веб-методами. Данные методы могут вызываться клиентскими компонентами Web-клиента (есть стандартный метод JSAPI для вызова) или другими способами.
В качестве примера будет разработано расширение, реализующее функцию добавления водяных знаков в PDF-файлы карточки Документ.
-
При вызове функции расширения Клиент передаёт идентификаторы PDF файлов карточки Документ (идентификаторы карточек Файл с версиями, а не идентификаторы собственно файлов) и идентификатор самой карточки Документ.
-
Расширение DVWebTool загружает PDF-файлы на компьютер пользователя с помощью функций, предоставляемых Web-клиентом.
-
Расширение DVWebTool добавляет в полученные PDF-файлы водяные знаки используя функции сторонней библиотеки для работы с PDF.
-
Расширение DVWebTool прикрепляет полученные файлы с водяными знаками с помощью функций, предоставляемых Web-клиентом.
Расширение не должно изменять существующие файлы карточки: полученные PDF-файлы с водяными знаками должны быть добавлены в карточку в качестве дополнительных.
Для реализации функций, указанных в пп. 2 и 4, потребуется разработать расширение Web-клиента с соответствующей функциональностью.
Также для примера необходимо разработать клиентское расширение, которое будет вызывать функции расширения DVWebTool с передачей им идентификаторов PDF-файлов Документа, в которые нужно добавить водяные знаки.
Полный код примера доступен в репозитории на GitHub. |
Расширение Web-клиента
Для работы расширения DVWebTool необходимо, чтобы Web-клиент по запросу возвращал файл из карточки Файл с версиями с переданным идентификатором, а также мог прикреплять переданные файлы к карточке Документ.
Требуемая функциональность может быть реализована с помощью расширения Web-клиента.
Ниже приведена часть исходного кода контроллера, реализованного в серверном расширении.
Others/Watermark/WatermarkServerExtension/Controllers/FileOperationsController.cs
public HttpResponseMessage GetFile(Guid fileCardID)
{
FileReader fileReader = fileService.GetFileReader(fileCardID);
if (fileReader.FileID == Guid.Empty)
return new HttpResponseMessage(HttpStatusCode.NotFound);
HttpResponseMessage response = CreateResponseForFile(fileReader);
return response; (1)
}
[HttpPost]
public async Task<ActionResult> AddFile([FromForm(Name = "file")] FormFileCollection formFiles, Guid cardId) (2)
{
string rootPath = CreateAndGetTempFolder();
try
{
List<string> files = await SaveFilesFromResponse(formFiles); (3)
await fileService.AddFilesToCard(cardId, files);
return Ok();
}
catch (Exception e)
{
return Problem(statusCode: (int)HttpStatusCode.InternalServerError, detail: e.Message);
}
}
1 | Создаёт ответ с файлом. |
2 | Добавляет в карточку файлы из запроса. |
3 | Добавляемые файлы. |
В контроллере используется сервис 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()
};
}
1 | Добавляет файлы с файловой системы в карточку cardID . |
2 | Получает карточку, к которой прикрепляются файлы. |
3 | Возвращает идентификаторы добавленных файлов с версиями. |
4 | Получает указатель для чтения файла из карточки файла с версиями fileCardID . |
5 | Получение файла карточки с версиями. |
6 | Получение идентификатора файла из последней версии. |
7 | Если файла нет, возвращается пустой указатель. |
8 | Запрашивается файл текущей версии. |
9 | Возвращается указатель для чтения файла. |
Расширение программы 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
{
private readonly ServiceProvider serviceProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private string WATERMARK = "Секретно"; (1)
public WatermarkController()
{
serviceProvider = new ServiceProvider();
Init();
}
private void Init() (2)
{
actions.Add(nameof(AddWatermarkToFiles), AddWatermarkToFiles);
}
private WebServiceResponse AddWatermarkToFiles(WebServiceRequest webServiceRequest, JObject data) (3)
{
Logger.Info("Получено задание на добавление водяного знака");
if (data == null)
{
return CreateBadResponse("С клиента не переданы данные для работы");
}
AddWatermarkRequest request; (4)
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) (5)
{
var connectionService = new ConnectionToWebClient(request.ServerAddress, request.AccessToken);
List<string> files = new List<string>();
foreach (var fileId in request.FileIDs) (6)
{
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(); (7)
List<Task<string>> processes = new List<Task<string>>(); (8)
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); (9)
}
catch (Exception ex)
{
Logger.Error(ex.Message);
throw new Exception($"Не удалось сохранить файлы в карточке");
}
return doneInfo;
}
1 | Текст водяного знака. |
2 | Регистрация методов контроллера PDFWatermarkController . Название метода регистрозависимое. |
3 | Веб-метод добавления водяного знака. Метод должен принимать два параметра: WebServiceRequest и JObject . Данные передаются в data . |
4 | Загружаем данные из полученного запроса в модель AddWatermarkRequest . |
5 | Обработчик запроса на добавление водяного знака. Получает данные запроса. Возвращает строку с названиями файлов, в которые добавлены запросы. |
6 | Загружаем с Web-клиента файлы, идентификаторы которых переданы в запросе. |
7 | Сервис для работы с водяными знаками. |
8 | Добавление водяных знаков в файлы из списка files . |
9 | Отправка запроса на прикрепление файлов filesWithWatermark к карточке request.CardID . |
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 & $JwtTokenController) { (2)
}
async AddWatermarkToFiles(cardID: string, fileIDs: string[]): Promise<IWebServicesResponse<any>> { (3)
const accessToken = await this.services.jwtTokenController.getAccessToken(); (4)
const data: any = { (5)
data: {
cardID: cardID,
fileIDs: fileIDs,
userID: this.services.currentEmployeeId,
serverAddress: this.services.siteUrl,
accessToken
},
action: 'AddWatermarkToFiles', (6)
locale: this.services.applicationSettings.culture.twoLetterISOLanguageName (7)
};
return DVWebToolConnection.trySendData("Watermark", data, this.services); (8)
}
}
export type $WatermarkService = { watermarkService: WatermarkService }; (9)
export const $WatermarkService = serviceName((s: $WatermarkService) => s.watermarkService);
1 | Клиентский сервис, предоставляющий доступ к методу добавления водяных знаков, предоставляемому расширением DVWebTool. | ||
2 | Метод принимает идентификатор карточки и идентификатор её конвертируемых файлов. | ||
3 | Токен доступа, с помощью которого DVWebTool будет выполнять запросы к серверу Web-клиента.
|
||
4 | В данных нужно также передать:
|
||
5 | Название метода, вызываемого из расширения DVWebTool. | ||
6 | Обязательное для передачи название локали. | ||
7 | Вызываем метод AddWatermarkToFiles из контроллера Watermark расширения DVWebTool. Тип DVWebToolConnection предоставляет методы для работы с DVWebTool. |
||
8 | Регистрируем сервис 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 | Получаем ID текущей карточки и список файлов из элемента FileList . |
3 | Получаем из модели files только идентификаторы файлов. |
4 | Обрабатываем только основные файлы с расширением PDF. |
5 | Получаем реализованный сервис для работы с водяными знаками. |
6 | Вызываем функцию добавления водяных знаков для файлов с ИД из списка fileIDs . |
7 | Обновляем список файлов, если есть. Возможно уже открыта другая карточка, но в данном случае это не существенно. |
Настройка времени действия токена
Время жизни токена доступа зависит от значений параметров JwtAccessTokenLifetime
и JwtClockSkew
в конфигурационном файле модуля.
"Docsvision": {
"WebClient": {
"SettingGroups": {
"System": {
"JwtAccessTokenLifetime": 300, (1)
"JwtClockSkew": 300 (2)
}
}
}
}
1 | Время жизни токена. По умолчанию 5 минут (300 секунд). |
2 | Допустимая погрешность часов при проверке времени жизни токена. Значение прибавляется к JwtAccessTokenLifetime . По умолчанию 5 минут (300 секунд). |
Сборка проекта
-
Откройте решение
Samples.sln
. -
Соберите проект
. -
Соберите проект
.
-
Откройте в командной строке папку
. -
Выполните команды:
npm install npm update npm run build:prod
-
Остановите dvwebclient.
-
Скопируйте папку
SamplesOutput/Content/Modules/WatermarkWebExtension/
в/lib/docsvision/webclient/Content/Modules
. -
Скопируйте папку
SamplesOutput/Extensions/WatermarkServerExtension
в/lib/docsvision/webclient/Extensions
. -
Для работы пользователей на Windows скопируйте файлы из папки
SamplesOutput/Site/Content/Tools/DVWebTool/Application Files/
вПуть к папке с панелью управления Web-клиентом/Site/Content/Tools/DVWebTool/
, напримерC:/Program Files (x86)/Docsvision6/WebClient/Site/Content/Tools/DVWebTool
. -
Запустите dvwebclient.
-
Запустите программу Панель управления Web-клиентом.
-
Нажмите кнопку Собрать DVWebTool.
-
В диалоге введите адрес публикации Web-клиента, при необходимости установите другие опции, и нажмите кнопку ОК.
-
Установите собранную утилиту DvWebTool на сервер Web-клиента, см. подробнее "Установка и запуск программы DVWebTool.
-
Перезапустите dvwebclient.
Сборка и установка пакета для DvSupService
Для работы пользователей на Linux нужно обеспечить копирование файлов расширения в папку установки DvSupService — /lib/docsvision/dvsupservice
. Рекомендуемый способ это сделать — собрать deb-пакет[1] и распространить его любым удобным способом:
-
Убедитесь, что собран проект
Other > Watermark > WatermarkWebToolExtension
. В результате в папкеOthers/Watermark/DebPackage/Build/DvSupServiceExtension
должны оказаться файлы расширения в папкахAssemblies
иServices
. ПараметрExtensionsPath
в конфигурационном файле DVSupService позволяет изменять папку для расширений, по-умолчанию этоAssemblies
. -
Скопировать папку
Others\Watermark\DebPackage
на Linux в каталог, где планируется выполнять сборку пакета (расположение папки значения не имеет). -
Установите необходимые для сборки компоненты следующей командой:
sudo apt-get install make debhelper dpkg-dev
-
Откройте консоль в папке
Build
и соберите пакет с помощью следующей команды:cd DebPackage/Build make
В результате в папке
DebPackage
появится файлdvsupservice-extension_1.0_amd64.deb
. -
Остановите сервис dvsupservice командой:
sudo systemctl stop dvsupservice
-
Распространите и установите на машины пользователей собранный пакет. Для этого можно создать репозиторий, чтобы можно было установить расширение с помощью команды
sudo apt-get install dvsupservice-extension
. См. "Создание простого репозитория". Либо просто скопируйте файл на клиентскую машину и выполните команду:sudo dpkg -i dvsupservice-extension_1.0_amd64.deb
-
Запустите сервис dvsupservice командой:
sudo systemctl start dvsupservice
Проверка примера
-
В программе Конструктор Web-разметок добавьте элемент Кнопка в любую разметку просмотра карточки Документ.
-
Укажите для события
При щелчке
обработчикaddWatermark
. -
Сохраните разметку.
-
Установите или обновите программу DVWebTool. Cм. пункт Установка и запуск программы DVWebTool в руководстве пользователя Web-клиент.
-
Запустите программу DVWebTool. Убедитесь, что программа DVWebTool и Web-клиент будут запущены от имени одного пользователя.
-
В Web-клиенте перейдите в раздел О программе.
В разделе Подключенные расширения должны быть указаны расширения:-
WatermarkServerExtension (Сборка <номер сборки>).
-
Watermark to PDF <номер сборки> — web-расширение.
-
-
Откройте панель About из меню Docsvision DVWebTool.
В списке установленных расширений должно быть расширение:-
Watermark to PDF <версия>.
-
-
Откройте для просмотра любую карточку с разметкой, настроенной в п. 1.
-
Добавьте один или несколько основных файлов с расширением
.pdf
. -
Нажмите на добавленную кнопку с обработчиком
addWatermark
.
Появится сообщение Запущен процесс добавления водяных знаков
— начнётся процесс добавления водяных знаков.
После завершения процедуры появится сообщение Водяные знаки добавлены в файлы:
"список PDF-файлов, в которые добавлены водяные знаки"
.
В карточку будут добавлены дополнительные файлы с постфиксом _marked
, являющиеся копиями оригинальных файлов с добавленным водяным знаком: слово Секретно
.
Особенности обновления
При обновлении версии Web-клиента регистрация расширения в программе DVWebTool будет отменена (конфигурационные файлы программы перезаписываются).
После установки новой версии Web-клиента необходимо повторно зарегистрировать расширения DVWebTool на сервере Web-клиент.