Разработка расширения для программы DVWebTool
Дополнительным компонентом модуля 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 class FileOperationsController : ApiController
{
IFileService fileService; (1)
public FileOperationsController(IFileService fileService)
{
this.fileService = fileService;
}
[HttpGet]
public HttpResponseMessage GetFile(Guid fileCardID) (2)
{
FileReader fileReader = fileService.GetFileReader(fileCardID); (3)
if (fileReader.FileID == Guid.Empty)
return new HttpResponseMessage(HttpStatusCode.NotFound);
HttpResponseMessage response = CreateResponseForFile(fileReader); (4)
return response;
}
[HttpPost]
public async Task<HttpResponseMessage> AddFile() (5)
{
if (!Request.Content.IsMimeMultipartContent()) (6)
{
return Request.CreateErrorResponse(HttpStatusCode.UnsupportedMediaType, "Ошибка в формате входных данных");
}
string rootPath = CreateAndGetTempFolder(); (7)
MultipartFormDataStreamProvider provider; (8)
try
{
provider = new MultipartFormDataStreamProvider(rootPath);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, $"Ошибка при работе с временными данными: {ex.Message}");
}
try
{
await Request.Content.ReadAsMultipartAsync(provider); (9)
if (provider.FileData.Count < 1) (10)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "В запросе не переданы файлы для получения");
}
Guid cardId = GetCardIdFromResponse(provider.FormData); (11)
List<string> files = await SaveFilesFromResponse(provider.FileData); (12)
await fileService.AddFilesToCard(cardId, files); (13)
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e.Message);
}
}
}
1 | Отдельный сервис для работы с файлами карточек Документ. Реализация приведена ниже. |
2 | Возвращает файл из карточки файла с версиями с идентификатором fileCardID . |
3 | Метод GetFileReader получает файл из карточки файла с версиями с ID fileCardID и передаёт указатель для его чтения. |
4 | Метод CreateResponseForFile создаёт HTTP-ответ, в который включает данные файла. |
5 | В карточку добавляются файлы из запроса. Данные добавляемых файлов и идентификатор карточки поступают в теле запроса. |
6 | Проверяется формат входных данных. Ожидается составной тип. |
7 | Создаётся временный каталог для оперативного сохранения файлов из запроса |
8 | Инициализируется доступ к входным данным. |
9 | Загружаются данные из запроса. |
10 | Если в запросе нет файлов, возвращается ошибка. |
11 | Считывается идентификатор карточки. |
12 | Полученные файлы сохраняются в файловую систему сервера. |
13 | Полученные файлы прикрепляются к карточке с cardId . |
В контроллере используется сервис 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)
ILockService lockService = GetLockService();
if (lockService.IsObjectLockedByAnotherUser(document)) {
throw new Exception($"Карточка {cardID} заблокирована другим пользователем");
}
if (lockService.LockObjectBase(document) == false) {
throw new Exception($"Не удалось заблокировать карточку {cardID}");
}
IDocumentService documentService = GetDocumentService();
return await System.Threading.Tasks.Task.Run(() =>
{
IEnumerable<Guid> documentFileIds;
try
{
IEnumerable<DocumentFile> documentsFiles = documentService.AddAdditionalFiles(document, files);
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)
{
IVersionedFileCardService versionedFileCardService = GetVersionedFileCardService();
VersionedFileCard fileCard = versionedFileCardService.OpenCard(fileCardID); (4)
Guid fileID = fileCard.CurrentVersion.Id; (5)
UserSession userSession = GetUserSession();
if (userSession.FileManager.FileExists(fileID) == false) (6)
return new FileReader();
var file = userSession.FileManager.GetFile(fileID); (7)
return new FileReader() { (8)
FileID = fileID,
FileName = file.Name,
Stream = file.OpenReadStream()
};
}
(9)
}
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(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 async Task<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 = await HandleRequest(request);
}
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("Не удалось подключиться к серверу {wc}а");
}
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>>();
string doneInfo = "";
foreach (var file in files) (9)
{
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 = {
data: { (3)
cardID: cardID,
fileIDs: fileIDs,
userID: this.services.currentEmployeeId, (4)
serverAddress: this.services.siteUrl (5)
},
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 | Метод принимает ID карточки и ID её конвертируемых файлов |
3 | Передача данных, включая следующие два пункта: |
4 | ID пользователя (для отправки оповещения о завершении процесс). |
5 | Адрес сервера Web-клиента. DVWebTool должна подключиться к Web-клиенту для получения и сохранения файлов карточки. |
6 | Название метода, вызываемого из расширения DVWebTool |
7 | Обязательное для передачи название локали. |
8 | Вызываем метод AddWatermarkToFiles из контроллера Watermark расширения DVWebTool. Тип DVWebToolConnection предоставляет методы для работы с DVWebTool. |
9 | Регистрируем сервис WatermarkService . |
Сервис $WatermarkService
предоставляет единственный метод AddWatermarkToFiles
, который вызывает функцию программы DVWebTool с помощью метода DVWebToolConnection.trySendData
. При вызове данного метода нужно передать название контроллера и данные, в которых должно быть название вызываемого метода контроллера — в поле action
передаваемых данных.
В качестве метода, использующего сервис $WatermarkService
реализуем обработчик нажатия кнопки разметки карточки Документ, который будет получать идентификаторы основных PDF-файлов карточки, вызывать метод $WatermarkService.AddWatermarkToFiles
и после завершения его работы обновлять содержимое элемента управления Список файлов, или отображать ошибку.
Others\Watermark\WatermarkWebExtension\src\EventHandlers.tsx
export async function addWatermark(sender: LayoutControl) {
showNotify("Запущен процесс добавления водяных знаков"); (1)
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 | Обрабатываем только основные файлы с расширением .pdf . |
4 | Получаем из модели files только идентификаторы файлов. |
5 | Получаем реализованный сервис для работы с водяными знаками. |
6 | Вызываем функцию добавления водяных знаков для файлов с ID из списка fileIDs . |
7 | Обновляем список файлов, если есть (возможно уже открыта другая карточка, но в данном случае это не существенно) |
Сборка проекта
-
Откройте решение
Samples.sln
. -
Соберите проект
. -
Соберите проект
.
-
Откройте в командной строке папку
. -
Выполните команды:
npm install npm update npm run build:prod
-
Остановите IIS.
-
Скопируйте папку
SamplesOutput\Site\Content\Modules\WatermarkWebExtension\
в\%WebCinstallDir%\Site\Content\Modules
. -
Скопируйте папку
SamplesOutput\Site\Content\Tools\DVWebTool\Application Files\
в\%WebCinstallDir%\Site\Content\Tools\DVWebTool\
. -
Скопируйте папку
SamplesOutput\Site\Extensions\WatermarkServerExtension
в\%WebCinstallDir%\Site\Extensions
. -
Запустите IIS.
-
Запустите программу
\%WebCinstallDir%\Tools\mageui.exe
. -
Обновите манифест программы:
-
Нажмите
и выберите файл\%WebCinstallDir%\Site\Content\Tools\DVWebTool\Application FilesDocsvision.DVWebTool.exe.manifest
. -
Перейдите в раздел Name и в поле
Version
увеличьте номер сборки. Например,5.5.5531.0
до5.5.5531.1
.Не изменяйте мажорную и минорную версии, и версию исправления. -
Перейдите в раздел Files.
-
Нажмите кнопку Populate.
-
Нажмите
. -
Нажмите … (три точки) в поле File, выберите файл сертификата
\%WebCinstallDir%/DVWebTool.pfx
, затем нажмите OK в основном окне подписания манифеста (пароль указывать не нужно). Файл манифеста будет подписан сертификатомDVWebTool.pfx
.
-
-
Обновите файл развертывания программы:
-
Нажмите
и выберите файл\%WebCinstallDir%\Site\Content\Tools\DVWebToolDocsvision.DVWebTool.application
. -
Перейдите в раздел Name и в поле
Version
увеличьте номер сборки. Например,5.5.5531.0
до5.5.5531.1
.Не изменяйте мажорную и минорную версии, и версию исправления. -
Перейдите в раздел Update option и в поле
Version
введите номер версии, который был получен в разделе Name. Например,5.5.5531.1
. -
Перейдите в раздел Application Reference, нажмите кнопку Select Manifest и выберите файл
\%WebCinstallDir%\Content\Tools\DVWebTool\Application FilesDocsvision.DVWebTool.exe.manifest
. В поле Version будет указана версия, полученная при обновлении файла манифеста. -
Нажмите
. Будет предложено подписать файл развёртывания. -
Нажмите … (три точки) в поле File, выберите файл сертификата
\%WebCinstallDir%/DVWebTool.pfx
, затем нажмите OK в основном окне подписания (пароль указывать не нужно).
-
-
Закройте программу
mageui.exe
. -
Рекомендуется перезапустить IIS.
Проверка примера
-
В программе Конструктор 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-клиент.