Функциональные модули (.portalmod)
Функциональный модуль — ZIP-архив .portalmod, который разворачивает на портале заказчика готовое решение одним файлом:
- вложенные
.portalpart/.portalevent/.portaltimer; - узлы навигации, списки с полями, библиотеки, страницы с WebPart;
- права доступа, привязки Event Receivers и экземпляры Timer Jobs;
- опционально C# provisioner — демо-данные, пользователи, файлы в библиотеках.
Установка: Админка → Модули или POST /api/v1/modules/install.
Отладка provisioner с брейкпоинтами: Отладка расширений.
Готовые демо-модули с исходниками: korport.ru/modules.
Зачем нужен .portalmod
Заголовок раздела «Зачем нужен .portalmod»| Подход | Когда использовать |
|---|---|
Отдельный пакет (.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.dllJSON Schema: module.schema.json.
Ограничения: размер архива до 50 МБ; корень ZIP — module.json (не вложенная папка).
Порядок установки на портале
Заголовок раздела «Порядок установки на портале»При загрузке .portalmod платформа выполняет шаги в фиксированном порядке:
- Проверка зависимостей (
requires) - Установка пакетов из
packages/(WebPart, Event Receiver, Timer Job) - Создание узлов (
nodes) - Создание списков (
lists) и библиотек (libraries) - Создание страниц (
pages) и назначение страниц по умолчанию (defaultPage) - Назначение прав (
permissions) - Регистрация Event Receivers и Timer Jobs из манифеста
- Запуск C# provisioner (если указан)
Повторная установка той же версии — пропуск (skipped: true). Новая версия — обновление с вызовом UpgradeAsync у provisioner.
module.json — обязательные поля
Заголовок раздела «module.json — обязательные поля»| Поле | Описание |
|---|---|
id | Уникальный ключ модуля, напр. contoso.tasks или demo.address-book |
version | Semver, напр. 1.0.0 |
title | Название в админке |
description | Краткое описание (опционально) |
requires | Зависимости от других модулей, напр. ["demo.address-book@>=1.0.0"] |
packages — вложенные расширения
Заголовок раздела «packages — вложенные расширения»"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 — узлы навигации
Заголовок раздела «nodes — узлы навигации»"nodes": [ { "key": "tasks", "slug": "tasks", "title": "Задачи", "description": "Модуль управления задачами", "parent": { "slug": "portal" }, "sortOrder": 0, "addToSideMenu": true, "defaultPage": "home" }]| Поле | Описание |
|---|---|
key | Символьный ключ для $ref внутри модуля |
slug | URL-сегмент: /portal/tasks/... |
parent | { "slug": "portal" } или { "$ref": "nodes.demo" } |
defaultPage | slug страницы по умолчанию для узла |
lists — списки и поля
Заголовок раздела «lists — списки и поля»"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 — библиотеки документов
Заголовок раздела «libraries — библиотеки документов»"libraries": [ { "key": "bannerImages", "node": { "$ref": "nodes.banners" }, "slug": "banner-images", "title": "Изображения баннеров" }]pages — страницы с WebPart
Заголовок раздела «pages — страницы с WebPart»"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 — права доступа
Заголовок раздела «permissions — права доступа»"permissions": [ { "resource": { "$ref": "nodes.tasks" }, "principalType": "group", "principalName": "Читатели портала", "permissionLevel": "view" }]Уровни: view, add, edit, delete. Принципал: user или group (по имени или id).
eventReceivers и timerJobs
Заголовок раздела «eventReceivers и timerJobs»Регистрация обработчиков и заданий, привязанных к установленным пакетам:
"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 }]provisioner — C# для контента и сложной логики
Заголовок раздела «provisioner — C# для контента и сложной логики»Когда декларативного module.json недостаточно (демо-пользователи, файлы в библиотеках, сложные связи):
"provisioner": { "entry": "MyModule.dll", "type": "Contoso.Modules.TasksProvisioner"}SDK и интерфейс: Portal.Module.Sdk.
Ссылки ($ref)
Заголовок раздела «Ссылки ($ref)»Вместо 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/По образцу demo-модулей
Заголовок раздела «По образцу demo-модулей»В репозитории Portal: packages/modules/ — каждый модуль содержит module.json, build.sh, packages/*.portalpart.
cd packages/webpart-examples/tasks && ./build.shcd 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.tasks | packages/modules/tasks | Списки, страница с WebPart, provisioner |
| demo.address-book | packages/modules/address-book/ | Несколько списков, lookup-поля |
| demo.banners | packages/modules/banners/ | Библиотека, загрузка файлов в provisioner |
| demo.birthdays | packages/modules/birthdays/ | Зависимость requires, два WebPart |
| demo.meeting-room-booking | packages/modules/meeting-room-booking/ | Два связанных списка |
Рекомендуемый порядок установки демо: address-book → tasks / banners / meeting-rooms → birthdays.
| Метод | Путь | Описание |
|---|---|---|
| GET | /api/v1/modules | Установленные модули |
| POST | /api/v1/modules/install | multipart, поле package |
| GET | /api/v1/modules/{key}/resources | Карта ресурсов (resource_map) |
Подробнее: API модулей.
См. также
Заголовок раздела «См. также»- Кейсы работы с сущностями — реальные примеры кода (списки, lookup, библиотеки, WebPart, provisioner)
- WebPart — сборка
.portalpart - Event Receivers —
.portalevent - Timer Jobs —
.portaltimer - Hot deploy — обновление расширений без перезапуска
- Portal.Module.Sdk — C# provisioner