Перейти к содержимому

Функциональные модули (.portalmod)

Функциональный модуль — ZIP-архив .portalmod, который разворачивает на портале заказчика готовое решение одним файлом:

  • вложенные .portalpart / .portalevent / .portaltimer;
  • узлы навигации, списки с полями, библиотеки, страницы с WebPart;
  • права доступа, привязки Event Receivers и экземпляры Timer Jobs;
  • опционально C# provisioner — демо-данные, пользователи, файлы в библиотеках.

Установка: Админка → Модули или POST /api/v1/modules/install.

Отладка provisioner с брейкпоинтами: Отладка расширений.

Готовые демо-модули с исходниками: korport.ru/modules.

ПодходКогда использовать
Отдельный пакет (.portalpart, .portalevent, .portaltimer)Одно расширение без инфраструктуры; быстрые эксперименты
Функциональный модуль (.portalmod)Поставка заказчику: UI + данные + структура портала в одном артефакте

Интегратор или разработчик описывает инфраструктуру декларативно в module.json, вкладывает собранные пакеты в packages/ — заказчик загружает один файл, без ручного создания узлов и списков в админке.

my-solution.portalmod (ZIP)
├── module.json # манифест (обязательно)
├── packages/ # вложенные расширения
│ ├── widget.portalpart
│ ├── on-change.portalevent
│ └── nightly.portaltimer
├── seed/ # опционально: JSON с начальными данными
│ └── items.json
└── provisioner/ # опционально: C# DLL для сложной логики
└── MyModule.dll

JSON Schema: module.schema.json.

Ограничения: размер архива до 50 МБ; корень ZIP — module.json (не вложенная папка).

При загрузке .portalmod платформа выполняет шаги в фиксированном порядке:

  1. Проверка зависимостей (requires)
  2. Установка пакетов из packages/ (WebPart, Event Receiver, Timer Job)
  3. Создание узлов (nodes)
  4. Создание списков (lists) и библиотек (libraries)
  5. Создание страниц (pages) и назначение страниц по умолчанию (defaultPage)
  6. Назначение прав (permissions)
  7. Регистрация Event Receivers и Timer Jobs из манифеста
  8. Запуск C# provisioner (если указан)

Повторная установка той же версии — пропуск (skipped: true). Новая версия — обновление с вызовом UpgradeAsync у provisioner.

ПолеОписание
idУникальный ключ модуля, напр. contoso.tasks или demo.address-book
versionSemver, напр. 1.0.0
titleНазвание в админке
descriptionКраткое описание (опционально)
requiresЗависимости от других модулей, напр. ["demo.address-book@>=1.0.0"]
"packages": [
{ "path": "packages/tasks.portalpart", "type": "webpart" },
{ "path": "packages/list-handler.portalevent", "type": "event-receiver" },
{ "path": "packages/heartbeat.portaltimer", "type": "timer-job" }
]

Пути — относительно корня ZIP. Типы: webpart, event-receiver, timer-job. Пакеты устанавливаются до создания узлов и страниц, чтобы WebPart уже был в каталоге.

"nodes": [
{
"key": "tasks",
"slug": "tasks",
"title": "Задачи",
"description": "Модуль управления задачами",
"parent": { "slug": "portal" },
"sortOrder": 0,
"addToSideMenu": true,
"defaultPage": "home"
}
]
ПолеОписание
keyСимвольный ключ для $ref внутри модуля
slugURL-сегмент: /portal/tasks/...
parent{ "slug": "portal" } или { "$ref": "nodes.demo" }
defaultPageslug страницы по умолчанию для узла
"lists": [
{
"key": "tasks",
"node": { "$ref": "nodes.tasks" },
"slug": "user-tasks",
"title": "Задачи",
"addToSideMenu": true,
"fields": [
{ "internalName": "title", "title": "Название", "fieldType": "text", "isRequired": true, "sortOrder": 0 },
{
"internalName": "status",
"title": "Статус",
"fieldType": "choice",
"sortOrder": 1,
"settings": { "choices": ["Новая", "В работе", "Готово"] }
},
{
"internalName": "assigned_to",
"title": "Исполнитель",
"fieldType": "person",
"settings": { "allowUsers": true, "allowGroups": false }
}
]
}
]

Типы полей — как при создании списка через API. Lookup на другой список модуля:

"settings": { "lookupListId": { "$ref": "lists.departments.id" } }
"libraries": [
{
"key": "bannerImages",
"node": { "$ref": "nodes.banners" },
"slug": "banner-images",
"title": "Изображения баннеров"
}
]
"pages": [
{
"slug": "home",
"title": "Главная",
"node": { "$ref": "nodes.tasks" },
"layoutTemplate": "1",
"htmlContent": "<p></p>",
"zonesContent": [
[
{
"type": "webpart",
"instanceId": "00000000-0000-0000-0000-000000000001",
"definitionKey": "demo.tasks",
"properties": {
"tasksListId": { "$ref": "lists.tasks.id" }
},
"title": "Задачи на таймлайне"
}
]
]
}
]

definitionKey — id WebPart из manifest пакета .portalpart. В properties можно ссылаться на $ref списков и библиотек — UUID подставятся при установке.

"permissions": [
{
"resource": { "$ref": "nodes.tasks" },
"principalType": "group",
"principalName": "Читатели портала",
"permissionLevel": "view"
}
]

Уровни: view, add, edit, delete. Принципал: user или group (по имени или id).

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

"eventReceivers": [
{
"eventName": "listItem.updated",
"title": "Обработчик изменений",
"jobType": "contoso.list-change-handler",
"serviceKey": "contoso.list-change-handler",
"config": { "listId": { "$ref": "lists.tasks.id" } },
"isEnabled": true
}
],
"timerJobs": [
{
"title": "Ежедневная синхронизация",
"definitionKey": "contoso.heartbeat",
"scheduleType": "cron",
"scheduleConfig": { "expression": "0 8 * * *" },
"isEnabled": true
}
]

Когда декларативного module.json недостаточно (демо-пользователи, файлы в библиотеках, сложные связи):

"provisioner": {
"entry": "MyModule.dll",
"type": "Contoso.Modules.TasksProvisioner"
}

SDK и интерфейс: Portal.Module.Sdk.

Вместо UUID используйте символьные ключи — платформа подставит id при установке:

СсылкаЗначение
{ "$ref": "nodes.tasks" }UUID узла с key: tasks
{ "$ref": "lists.tasks.id" }UUID списка
{ "$ref": "libraries.bannerImages.id" }UUID библиотеки
{ "$ref": "modules.address-book.lists.employees.id" }Ресурс из другого установленного модуля

Зависимый модуль должен объявить requires — тогда ресурсы предшественника доступны через префикс modules.{id}..

Окно терминала
cd my-module/
zip -r ../dist/my-solution.portalmod module.json packages/
# при необходимости: seed/, provisioner/

В репозитории Portal: packages/modules/ — каждый модуль содержит module.json, build.sh, packages/*.portalpart.

Окно терминала
cd packages/webpart-examples/tasks && ./build.sh
cd packages/modules/tasks && chmod +x build.sh && ./build.sh
# → dist/demo.tasks.portalmod

Сборка .portalmod: шаблон dotnet new portal-module из Extension SDK, затем ./build.sh, scripts/pack-portalmod.sh или на Windows scripts/pack-portalmod.ps1 из SDK ZIP.

МодульКаталогЧто демонстрирует
demo.taskspackages/modules/tasksСписки, страница с WebPart, provisioner
demo.address-bookpackages/modules/address-book/Несколько списков, lookup-поля
demo.bannerspackages/modules/banners/Библиотека, загрузка файлов в provisioner
demo.birthdayspackages/modules/birthdays/Зависимость requires, два WebPart
demo.meeting-room-bookingpackages/modules/meeting-room-booking/Два связанных списка

Рекомендуемый порядок установки демо: address-book → tasks / banners / meeting-rooms → birthdays.

МетодПутьОписание
GET/api/v1/modulesУстановленные модули
POST/api/v1/modules/installmultipart, поле package
GET/api/v1/modules/{key}/resourcesКарта ресурсов (resource_map)

Подробнее: API модулей.