Създаване на приложения (плъгини)
Приложенията добавят функционалности в Antimatter от вътре: сървърна част на Go, която се хука в сървъра, и уеб част на React, която добавя менюта, панели, типове съобщения и страници. Тази страница показва как е структурирано приложение, като пример се използва вграденото приложение Polls, и какви промени има в интерфейса Fusion на Antimatter.
Как работи едно приложение
- Сървърната част е програма на Go. Сървърът я стартира като отделен процес и комуникира с нея чрез RPC: той извиква хуковете на приложението (съобщение е на път да бъде публикувано, команда е била изпълнена…), а приложението извиква API-то на плъгините на сървъра (създаване на публикация, четене на потребител, съхранение на данни…). То може също да обслужва HTTP маршрути под
/plugins/<app id>/. - Уеб частта е JavaScript пакет, зареден в браузъра заедно с уеб приложението. То регистрира React компоненти и обратни извиквания в регистъра: бутон в меню, панел, начин за изобразяване на тип съобщение.
- И двете части са опционални. Приложението се инсталира като
.tar.gzпакет, а системните администратори го активират, деактивират и конфигурират.
Antimatter изпълнява приложения, написани за Mattermost, и обратно, стига приложението да използва само хукове, които и двете имат. Вижте Хукове, които има само Antimatter.
Структура на пакета
Няма отделен стартов шаблон: започнете от малко вградено приложение като antimatter-plugin-polls или antimatter-plugin-todo и променете ID-то и кода му. Polls е структуриран по следния начин:
antimatter-plugin-polls/
├── plugin.json the manifest
├── Makefile build, test, bundle, deploy
├── build/ the manifest and pluginctl helper tools (Go), setup.mk
├── assets/icon.svg files shipped with the bundle
├── go.mod
├── server/
│ ├── main.go plugin.ClientMain(&Plugin{})
│ ├── plugin.go the Plugin type, OnActivate, ServeHTTP
│ ├── manifest.go generated from plugin.json
│ ├── api.go, command.go, hooks.go, store.go …
│ └── dist/ compiled binaries, one per platform
└── webapp/
├── package.json, webpack.config.js, tsconfig.json
├── i18n/ en.json and one file per language
├── src/index.tsx registers the app with the web app
├── src/manifest.ts generated from plugin.json
├── src/translations.ts
└── dist/main.js the bundle
Опционално: папка public/, обслужвана такава каквато е на /plugins/<app id>/public/.
plugin.json
{
"id": "com.example.weather",
"name": "Weather",
"description": "Forecasts in the chat.",
"homepage_url": "https://github.com/example/weather",
"support_url": "https://github.com/example/weather/issues",
"icon_path": "assets/icon.svg",
"version": "1.0.0",
"min_server_version": "12.0.0",
"server": {
"executables": {
"linux-amd64": "server/dist/plugin-linux-amd64",
"linux-arm64": "server/dist/plugin-linux-arm64",
"darwin-amd64": "server/dist/plugin-darwin-amd64",
"darwin-arm64": "server/dist/plugin-darwin-arm64",
"windows-amd64": "server/dist/plugin-windows-amd64.exe"
}
},
"webapp": {"bundle_path": "webapp/dist/main.js"},
"settings_schema": {"settings": []}
}
id: уникално и постоянно; обичайно е обратното име на домейн. То определя маршрутите, съхранените настройки и WebSocket събитията.version: семантична версия. Вградените приложения не го включват вplugin.json; инструментът за изграждане го взима от последнияv*git таг.min_server_version: Antimatter докладва12.0.0.settings_schema: страницата с настройки на приложението в Системната конзола (вижте Настройки).
make apply копира манифеста в server/manifest.go и webapp/src/manifest.ts; make manifest-check го валидира.
Сървърната част
Сървърната част импортира същия Go модул като приложението Mattermost, github.com/mattermost/mattermost/server/public, от Go модулния прокси. Неговият main само стартира приложението:
package main
import "github.com/mattermost/mattermost/server/public/plugin"
func main() {
plugin.ClientMain(&Plugin{})
}
Типът Plugin вгражда plugin.MattermostPlugin, което му дава p.API, и реализира хуковете, от които има нужда:
type Plugin struct {
plugin.MattermostPlugin
router *mux.Router
}
func (p *Plugin) OnActivate() error {
p.router = p.newRouter()
return p.API.RegisterCommand(&model.Command{
Trigger: "weather",
AutoComplete: true,
AutoCompleteDesc: "Show the forecast",
AutoCompleteHint: "[city]",
})
}
func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
city := strings.TrimSpace(strings.TrimPrefix(args.Command, "/weather"))
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "Forecast for " + city + ": sunny",
}, nil
}
func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) {
p.router.ServeHTTP(w, r)
}
Общи хукове
| Хук | Кога се изпълнява |
|---|---|
OnActivate, OnDeactivate | Когато приложението се активира или деактивира, както и при стартиране и спиране на сървъра. Регистрирайте команди и ботове в OnActivate. |
OnConfigurationChange | Когато настройките му се променят; заредете ги с p.API.LoadPluginConfiguration(&cfg). |
ServeHTTP | Заявки към /plugins/<app id>/…. Сървърът задава заглавието Mattermost-User-Id, когато заявката идва от влязъл потребител; отхвърляйте заявки без него, освен ако не са предназначени да са публични. |
ExecuteCommand | Команда, регистрирана от приложението, е била изпълнена. |
MessageWillBePosted, MessageWillBeUpdated | Преди съобщение да бъде записано: върнете променено съобщение или причина за отхвърляне. |
MessageHasBeenPosted, MessageHasBeenUpdated, MessageHasBeenDeleted | След събитието, за странични ефекти. |
ReactionHasBeenAdded, ReactionHasBeenRemoved | Реакции. |
UserHasJoinedChannel, UserHasLeftChannel, UserHasJoinedTeam, UserHasLeftTeam, ChannelHasBeenCreated | Промени в членството и каналите. Екипът е общност. |
FileWillBeUploaded | Преди файл да бъде съхранен: променете го или го отхвърлете. |
OnPluginClusterEvent | Съобщение от същото приложение на друг сървър в кластера. |
Пълният списък, с точните сигнатури, е в server/public/plugin/hooks.go; API-то е в server/public/plugin/api.go. Пакетът pluginapi (server/public/pluginapi) обвива API-то в по-удобни помощни функции.
Полезни API извиквания
CreatePost,UpdatePost,SendEphemeralPost: публикуване от името на бота на приложението или от името на потребител.EnsureBotUser(&model.Bot{Username, DisplayName}): създава бота на приложението веднъж и връща неговия ID.PublishWebSocketEvent("poll_updated", payload, &model.WebsocketBroadcast{ChannelId: id}): изпраща събитие към уеб приложението. То пристига катоcustom_<app id>_poll_updated.OpenInteractiveDialog: отваря диалог.LogInfo,LogErrorи подобни: записва в сървърния журнал, маркиран с ID-то на приложението.GetUser,GetChannel,HasPermissionToChannel: проверява кой може да направи какво, преди да действате от името на потребител.
Приложението се изпълнява на всеки сървър в кластера. Запазвайте състоянието в хранилището за ключ-стойност или в базата данни, а не само в паметта.
AI услугата
Сървърът на Antimatter извиква AI модели за приложенията чрез моделните връзки, които администраторът е конфигурирал (вижте AI в ръководството за администратори). Приложението никога не съхранява ключ на доставчик: то посочва за кого и къде действа, а сървърът проверява дали този човек може да използва връзката там, прилага лимитите и бюджетите ѝ, отчита токените спрямо човека и приложението и води одит на извикването без неговото съдържание.
| Метод | Какво прави |
|---|---|
AIChat(actingUserID, channelID, teamID, *ai.ChatRequest) | Chat завършване: съобщения, инструменти, структуриран изход (JSONSchema). |
AIChatStream(…) | Същото, но в поток: четете Events от потока, докато каналът се затвори, или го Close-нете, за да спрете извикването. |
AIEmbed(…, *ai.EmbedRequest) | Embeddings на текстове. |
AITranscribe(…, *ai.TranscribeRequest) | Говор в текст. |
AIDecide(…, *ai.DecideRequest) | Типизирани въпроси към модел за вземане на решения (да или не, избор, оценка) относно текст. |
AIConnectionsForUser(userID, channelID, teamID) | Връзките, които човек може да използва там, с техните модели и възможности, но никога с техните ключове. |
ConnectionID на всяко запитване избира връзката, а Model – един от моделите ѝ (празно за модел по подразбиране на връзката). Operation е кратка етикета за записите за използване и одит, никога не е съдържание. Минималната версия на сървъра е 11.11.
actingUserID само от източник, който сървърът е верифицирал: заглавието Mattermost-User-Id на запитване към вашия ServeHTTP, или потребителя от контекста на хук (автор на публикация, потребителят, който е изпълнил команда). Никога не го вземайте от съобщение, webhook payload, URL или запитване на друго приложение: приложението ви би разхарчило достъпа на някой друг.Тези методи и пакетът ai (server/public/ai) съществуват само в копото на Antimatter от модула, а не в версията, публикувана от Mattermost. За да ги използвате, компилирайте спрямо server/public на Antimatter с ред replace във вашия go.mod, например replace github.com/mattermost/mattermost/server/public => ../antimatter/server/public. Такова приложение работи само на Antimatter.
Осигуряване на инструменти за агенти
Приложението предлага собствените си инструменти на агентите чрез RegisterAITools([]*model.AIToolDefinition): за всеки инструмент се задават име, заглавие, описание, входна JSON схема и тип (read, write или external_write), който определя дали политиката на агента може да го изпълни самостоятелно (вижте Инструменти в ръководството за администратори). Правата се проверяват спрямо аргумента за канал или общност при извикването, или спрямо сървъра, в зависимост от нуждите на инструмента. Когато агент използва инструмента, сървърът извиква хука ExecuteAITool(c, *model.AIToolExecution) *model.AIToolResult, като в UserId е посочена проверената личност, която е отправила заявката: действайте само с правата на тази личност, никога с правата на агента. Инструментите се наименуват като <app id>.<name>; минималната версия на сървъра е 11.11; тези методи и хукът съществуват само в server/public на Antimatter, подобно на описаната по-горе AI услуга. Приложение с ID antimatter или такова, което започва с mcp., не може да регистрира инструменти.
Запазване на данни
Всяко приложение има собствено хранилище за ключ-стойност в базата данни на сървъра:
KVSet(key, value),KVGet(key),KVDelete(key): стойностите са байтове, обикновено JSON. Ключовете са най-много 150 символа.KVSetWithExpiry(key, value, seconds): стойността изчезва след това време.KVCompareAndSet(key, old, new)иKVSetWithOptionsсAtomic: безопасни актуализации, когато няколко заявки или сървъра записват един и същ ключ. Polls запазва всеки глас по този начин и повтаря при конфликт.KVList(page, perPage): ключовете, страница по страница.
Частта за уеб приложението
Бандлът регистрира клас чрез window.registerPlugin. Неговият initialize получава регистъра и Redux магазина:
import manifest from './manifest';
class Plugin {
initialize(registry, store) {
registry.registerTranslations((locale) => translationsFor(locale));
registry.registerPostTypeComponent('custom_poll', PollPost);
registry.registerWebSocketEventHandler(`custom_${manifest.id}_poll_updated`, (msg) => {
store.dispatch(pollUpdated(JSON.parse(msg.data.poll)));
});
registry.registerReconnectHandler(() => store.dispatch(reloadPolls()));
}
}
window.registerPlugin(manifest.id, new Plugin());
- Споделени библиотеки. Уеб приложението осигурява React, ReactDOM, Redux, React Redux и React Intl: изреждайте ги като webpack
externals(React,ReactDOM,Redux,ReactRedux,ReactIntl), вместо да опаковате собствени копия. Вижтеwebpack.config.jsна Polls. - Извикване на сървърната ви част. Използвайте Fetch за
${window.basename}/plugins/<app id>/…с опциите отClient4.getOptions()(отmattermost-redux/client): те носят заглавията, от които сървърът се нуждае, за да приеме заявка от браузъра. - Собствено състояние.
registerReducerдобавя редюсер, запазен подstate['plugins-<app id>']. - Типове. Допълнителните методи на регистъра на Antimatter не са в публикуваните типове на Mattermost: Polls декларира регистъра, който използва, в
webapp/src/types/host.ts.
Методите на регистъра и техните параметри са документирани в кода, в webapp/fusion/src/plugins/registry.ts (Fusion) и webapp/channels/src/plugins/registry.ts (класически).
Поддръжка на интерфейса Fusion
Antimatter има два уеб интерфейса: Fusion, който е по подразбиране, и класическия. И двата зареждат одни и същи бандлове на приложенията и имат един и същ регистър, но Fusion чертае много неща сам и разполага компонентите на плъгините по свой начин. Тествайте приложението си и в двата.
Откриване на интерфейса
Преди зареждането на всяко приложение, и двата интерфейса задават window.antimatterWebUI на 'fusion' или 'classic', и същата стойност на коренния елемент на страницата като data-am-web-ui. В Mattermost нито едно от двете не е зададено.
export function isFusionUI(): boolean {
return window.antimatterWebUI === 'fusion' || document.documentElement.dataset.amWebUi === 'fusion';
}
Вградените приложения използват това, за да се изобразяват по различен начин. Приложението за анкети изрисува картите си в стил Fusion, като в Fusion анкетата се пише в полето за въвеждане (един от режимите на полето за въвеждане), вместо в диалогов прозорец. Обажданията и гласовите канали пропускат страничната лента и компонентите за известия (toast), които Fusion замества със собствен интерфейс за обаждания. Приложението за чат с AI поставя своите AI действия в менюто на съобщението, като група под Още действия, вместо в лентата с инструменти при посочване с мишката. Собствените CSS класове на Fusion (с префикс am-) и неговият слой за диалогови прозорци (елементът am-layer) са това, което тези приложения използват, за да съответстват на интерфейса; те не са документиран, стабилен API.
Къде Fusion показва компонентите на плъгините
| Метод на регистъра | Къде се появява в Fusion |
|---|---|
registerAppBarComponent, registerChannelHeaderButtonAction | Лентата с приложения, колоната с икони на дясната страна, между вашия аватар и бутона За Antimatter. Там се поставят и бутоните в заглавието на канала, а не в самото заглавие. Скрива се, когато системният администратор активира Забрани лентата с приложения (ExperimentalSettings.DisableAppBar, изключено по подразбиране). |
registerRightHandSidebarComponent | Десният панел. Неговата заглавка е на Fusion и е еднаква за всяко приложение: иконата и името на приложението (регистрираното заглавие, когато се изобразява само текст, в противен случай етикетът от релсата на приложението или името от манифеста), след това Възстановяване на размера на панела (след промяна на размера), Отваряне в нов прозорец и Затвори. Заглавие, което изобразява нещо друго (контрола, връзка, изображение), се игнорира, затова изрисувате собствените си контроли в самия панел, например в тънка редица в горната му част. Потребителите могат да променят размера на панела, като влачат края му. Вижте Панелът в нов прозорец. |
registerChannelHeaderIcon | Икони в заглавието на канала, след бутоните за обаждане; на телефони, в менюто Още на заглавието. |
registerCallButtonAction | Бутонът за обаждане в заглавието на канала; няколко доставчика споделят меню Други начини за обаждане. |
registerPostDropdownMenuAction | Менюто Действия върху съобщение (⋯) на съобщението, в подменюто му Още действия; също и в менюто Действия с нишката на панела за нишки, под Още действия. Fusion изобразява иконата на приложението преди всяко действие, в сиво: собствената икона за вградените приложения, в противен случай иконата, която приложението показва в лентата за приложения, в противен случай неутрална. Елемент без текст в началото на текста на действието (икона, която приложението изобразява само) се скрива. |
registerPostDropdownSubMenuAction | Група от елементи в едно и също подменю Още действия, след единичните действия, отделени с линия, без заглавие. Никое приложение няма собствен елемент в менюто на съобщението. |
registerPostActionComponent | Лентата за действия при посочване на съобщението, преди бутона ⋯. |
registerPostTypeComponent, registerPostMessageAttachmentComponent, registerPostHeaderComponent, registerPostWillRenderEmbedComponent | Самото съобщение: неговото тяло, под текста му, след часа му и на мястото на превюто на линкове. |
registerChannelHeaderMenuAction | Менюто Опции за канала (⋯ върху канал в страничната лента, или десен клик), като елементи с икона на щепсел. |
registerMainMenuAction | Менюто на общността (името на общността в горната част на списъка с канали), под Още действия, групирани по приложение. |
registerSidebarBrowseOrAddChannelMenuAction | Менюто за общността, след Създаване на канал и Създаване на категория. |
registerFileUploadMethod | Менюто + на редактора (Добавяне към съобщението), след Качване на файл. |
registerPostEditorActionComponent | Лентата за форматиране на редактора, когато форматирането е показано. |
registerAIActionMenuItemComponent | Менюто AI на редактора. |
registerFileDropdownMenuAction, registerFilePreviewComponent | Менюто с опции за файл и превюто на файла. |
registerUserSettings | Профил и настройки, с по един раздел за всяко приложение под заглавието Приставка. |
registerChannelSettingsTab | Диалоговият прозорец за настройки на канала, под заглавието Приложения. |
registerPopoverUserAttributesComponent, registerPopoverUserActionsComponent | Карта за профил, показана, когато щракнете върху име или аватар. |
registerLeftSidebarHeaderComponent, registerSidebarChannelLinkLabelComponent, registerChannelIconOverride, registerBottomTeamSidebarComponent | Горната част на списъка с канали на общността, до името на канала, иконата на канала и долната част на лентата на общността вляво. |
registerChannelIntro, registerChannelIntroButtonAction, registerChannelToastComponent, registerNewMessagesSeparatorActionComponent, registerChannelComposerBannerComponent, registerComposerPlaceholder | Началото на историята на канала, горната част на списъка със съобщения, линията за непрочетено, над редактора и заглавният текст на редактора. |
registerChannelTypeOption | Типове канали в диалоговия прозорец Създаване на канал. |
registerSearchComponents | Поле за търсене в заглавието на канала: обхватите на приложенията се показват, когато е празно. |
registerUserGuideDropdownMenuAction | За Antimatter (в долната част на лентата на приложението), под Помощ. |
registerProduct | Пълни страници на URL адреса на продукта, в класическата рамка на страницата, без меню на продукта: Fusion няма такова и не изброява продукти никъде, затова създайте връзка към страницата сами. Никое вградено приложение не регистрира продукт: Kanban, Наръчници и чатът с AI се отварят в страничните си панели. |
registerRootComponent, registerGlobalComponent, хукове като registerMessageWillBePostedHook, registerSlashCommandWillBePostedHook, registerMessageWillFormatHook, registerFilesWillUploadHook, registerDesktopNotificationHook, registerWebSocketEventHandler, registerReducer, registerTranslations | Работят както в класическия интерфейс. |
registerAdminConsolePlugin, registerAdminConsoleCustomSetting | Системната конзола, която е класическа и в двата интерфейса. |
Какво Fusion не показва или не изпълнява
registerPostDropdownMenuComponent(компонент в менюто на съобщението) иregisterPostCardTypeComponent(картата на публикация в десния панел) не се показват в основните изгледи на Fusion.registerProductSwitcherMenuItemне прави нищо във Fusion, тъй като няма меню на продукта (записва предупреждение). ИзползвайтеregisterPostDropdownMenuActionвместо първия.registerMessageWillBeUpdatedHookне се изпълнява, когато съобщение се редактира в Fusion. Проверете промените на сървъра с хукаMessageWillBeUpdatedвместо това.registerActionAfterChannelCreationсе изпълнява само от класическия диалог за създаване на канал, който Fusion отваря от Още опции… в диалога си за Създаване на канал.- Компонентите на лентата на приложенията се показват независимо от
supportedProductIds. - Маршрутите от
registerNeedsTeamRouteиregisterCustomRouteзапазват класическия рамка на страницата, както продуктите.
Хукове, които има само Antimatter
Тези методи на регистъра не съществуват в Mattermost. Проверете дали методът съществува, преди да го извикате, за да работи приложението и там:
if (typeof registry.registerComposerMenuItem === 'function') {
registry.registerComposerMenuItem({
text: 'Create a poll',
icon: <i className='icon icon-chart-bar'/>,
fusionIcon: 'poll',
action: ({channelId, rootId}) => openPollCreation(channelId, rootId),
});
} else {
registry.registerChannelHeaderButtonAction(icon, (channel) => openPollCreation(channel.id), 'Create a poll', 'Create a poll');
}
| Метод | Какво добавя |
|---|---|
registerComposerMenuItem({text, icon, fusionIcon, action, shouldRender}) | Елемент в менюто + на кутията за съобщения, и в двата интерфейса. action получава {channelId, rootId}. |
registerEmojiPickerTab({id, label, icon, fusionIcon, order, component, shouldRender, replacesGifPicker}) | Вкладка на изборника за емоджи в кутията за съобщения, например GIF или стикери. В Fusion, вкладките с order под 10 идват преди вкладката Емоджи. Компонентът получава channelId, rootId, filter, onFilterChange, onSelectDone, insertText, isFusion и, в Fusion, pickerKit (вижте по-долу). |
registerChannelViewPanel({matcher, component}) | Панел между заглавието на канала и съобщенията, предназначен за каналите, които matcher(state, channel) избира. Може да скрие съобщенията и редактора (setMessagesVisible), както правят гласовите канали. |
registerSidebarChannelFooterComponent(component) | Ред под канал в страничната лента, например участниците в текущото обаждане. Получава channel. |
registerIntegrationsOption({title, description, fusionIcon, action, shouldRender}) | Само Fusion: елемент на страниците Интеграции, до персонализираните емоджи. Приложението GIF добавя там своите персонализирани GIF и стикери. |
Добавяне и управление на изгледи в изборника за емоджи
В Fusion таблоидът за избор на емоджи получава свойството pickerKit: компонентите, които таблоидът Емоджи използва за собствените си изгледи за добавяне и управление, така че изгледите на таблоида да изглеждат еднакво и да остават в рамките на изборника. Приложенията GIFs и Memes го използват за добавяне и управление на GIF-ове, стикери и шаблони за меми. То съдържа version (1), хуковете usePickerView и useOnEscape, както и компонентите PickerViewButtons (бутоните + и за управление до полето за търсене), PickerViewFrame (изглед със стрелка за връщане), PickerNotice, PickerField, PickerSearch, PickerDropZone и PickerManageList. Клавишът Escape връща от изгледа към мрежата на таблоида. Проверете дали свойството присъства: други хостове го остават извън, и тогава таблоидът запазва собствените си диалогови прозорци. Типовете са в webapp/fusion/src/types/store/plugins.ts (EmojiPickerTabProps), а компонентите — в webapp/fusion/src/fusion/popovers/picker_views.tsx.
Режими на полето за въвеждане
В Fusion registerComposerMode({id, label, fusionIcon, placeholder, sendLabel, component, shouldRender}) добавя нов начин за изписване на съобщение в полето за въвеждане, подобно на това как приложението Анкети добавя възможност за създаване на анкета. Режимът се предлага в менюто + на полето за въвеждане под неговия label. След като бъде избран, полето за въвеждане запазва текстовото си поле (с placeholder на режима: въпроса на анкетата), показва вашия component под него и лента с името на режима и бутон Откажи; бутонът Изпращане приема sendLabel и изпълнява вашата функция за изпращане вместо да публикува текста.
- Компонентът получава
channelId,rootId(нишката, в полето за отговор),message(текста в полето за въвеждане),setSubmitиonCancel(връща се към обикновено съобщение, запазвайки текста). - Передайте на
setSubmitфункция, която се извиква с текста от полето за въвеждане и връща нищо (или празен низ) след изпращане: тогава полето за въвеждане се изчиства и се връща към обикновено съобщение. Върнете низ, за да го покажете като грешка на място. placeholderиsendLabelса низове или функции наintlот react-intl.shouldRender(state, {channelId, rootId})определя дали дадено поле за въвеждане предлага режима.- За да отворите режима от друго място, например чрез команда с наклонена черта без аргументи:
window.dispatchEvent(new CustomEvent('am-composer-mode', {detail: {id, channelId, rootId}})).
Само Fusion поддържа режими за полето за въвеждане: проверете дали регистърът съдържа функцията и в противен случай използвайте собствен диалог. Методът връща идентификатора на режима, а режимът се премахва, когато приложението бъде премахнато.
Панелът в нов прозорец
Fusion предоставя на компонента на registerRightHandSidebarComponent два допълнителни пропса, така че Отваряне в нов прозорец да отваря панела там, където е бил, а не в началната страница на приложението:
setPanelState(state): извиквайте го, когато панелът смени местоположението си, с кратка низа, дефинирана от вашето приложение, например'note/<id>'или'board/<id>/card/<id>', илиnullна началната страница. Тази стойност се записва в адреса на прозореца, така че никога не поставяйте в нея поверителна информация. Състояние, по-дълго от 512 символа или съдържащо управляващи символи, се отхвърля.initialPanelState: в новия прозорец това е състоянието, което панелът е имал, илиundefined. Отнасяйте се към него като към ненадеен вход: заредете елемента отново като влязлия потребител и покажете началната страница на приложението или изглед „не е намерено“, ако потребителят не може да го отвори.
Състоянието се забравя, когато панелът се затвори или панел на друго приложение заеме неговото място. За да отворите прозореца на панел сам, popoutRhsPlugin (намира се и в window.WebappUtils.popouts) приема състоянието като опционален пети аргумент. Бележки, Бяла дъска, Задачи, Kanban и Наръчници използват това.
fusionIcon задава име на икона от набора икони на Fusion (напр. poll, sticker, calendar или image; вижте webapp/fusion/src/fusion/components/icon). Fusion я показва вместо icon, за да съвпада с останалите елементи.
Настройки
settings_schema в plugin.json описва страницата на приложението в Системна конзола › Приставка:
"settings_schema": {
"header": "Forecasts come from the weather service below.",
"settings": [
{"key": "ServiceURL", "display_name": "Service URL", "type": "text", "default": "https://api.example.com"},
{"key": "Units", "display_name": "Units", "type": "dropdown", "default": "metric",
"options": [{"display_name": "Celsius", "value": "metric"}, {"display_name": "Fahrenheit", "value": "imperial"}]},
{"key": "EnableAlerts", "display_name": "Severe weather alerts", "type": "bool", "default": false,
"help_text": "Post alerts in the channels that follow a city."}
]
}
Типове настройки: bool, text, longtext, number, dropdown, radio, generated (случайна тайна), username и custom (рисува се от компонент, регистриран от веб частта на приложението чрез registerAdminConsoleCustomSetting). Настройките могат да се групират в sections.
Сървърната част ги прочита в структура, чиито полета съответстват на ключовете:
type configuration struct {
ServiceURL string
Units string
EnableAlerts bool
}
func (p *Plugin) OnConfigurationChange() error {
var cfg configuration
if err := p.API.LoadPluginConfiguration(&cfg); err != nil {
return err
}
p.setConfiguration(&cfg)
return nil
}
Администраторите могат да ги задават и в конфигурацията, под PluginSettings.Plugins.<app id>, с ключове в малки букви (вижте ръководството за администратори).
Преводи
Antimatter е преведен на 37 езика, както и вградените приложения. Веб частта на приложението го прави по следния начин:
- Записвайте всяка низ с React Intl, с ID и английския ѝ текст:
<FormattedMessage id='polls.create' defaultMessage='Create a poll'/>илиdefineMessages. Предоставете ID-тата с името на вашето приложение. - Извличайте английските низове в
webapp/i18n/en.json(вградените приложения използватformatjs extract). - Добавете по един файл за всеки език до него, със същите ID-та:
fr.json,de.json,pt-BR.json,zh-CN.json… - Предоставяте ги на уеб приложението, което пита за езика на потребителя:
import fr from '../i18n/fr.json'; import ptBR from '../i18n/pt-BR.json'; const catalogs = {fr, 'pt-BR': ptBR}; export function translationsFor(locale) { return catalogs[locale] || catalogs[locale.split('-')[0]] || {}; } registry.registerTranslations(translationsFor);
Липсващият текст се връща към английския defaultMessage. За текст, изпратен от сървърната част (отговори на команди, съобщения от ботове), пакетът pluginapi/i18n зарежда файловете за превод, доставени в пакета (напр. assets/i18n) и предоставя локализатор на езика на всеки потребител.
Създаване, инсталиране и дебъгване
Нужни са Go и Node.js (версията от .nvmrc на приложението). В папката на приложението:
| Команда | Какво прави |
|---|---|
make dist | Създава сървърната част за всяка платформа и уеб приложението, и записва пакета в dist/<app id>-<version>.tar.gz. |
make test | Изпълнява тестовете за Go и JavaScript. |
make check-style | Проверява кода (golangci-lint, ESLint) и типове на TypeScript. |
make deploy | Създава, след което инсталира и активира пакета на сървъра. |
make watch, make deploy-from-watch | Първото създава уеб приложението всеки път, когато запазите промени; второто опакова създаденото и го инсталира на сървъра. |
make enable, make disable, make reset | Активира, деактивира или рестартира приложението на сървъра. |
make logs, make logs-watch | Показва редовете на приложението в сървърния журнал. |
make attach | Прикрепва дебъгъра Delve към работещата сървърна част (преди това създайте с AM_DEBUG=1). |
make deploy и другите сървърни команди използват локален режим на сокета, ако го намерят (/var/tmp/antimatter_local.socket, или AM_LOCALSOCKETPATH): изпълнявайте ги на машината на сървъра с локален режим включен. В противен случай те се влизат на сървъра, зададен от MM_SERVICESETTINGS_SITEURL, с AM_ADMIN_TOKEN, или AM_ADMIN_USERNAME и AM_ADMIN_PASSWORD. Инструментите за създаване все още не четат AM_ име за адреса на сайта.
Ръчно инсталиране на пакет
- Системен администратор разрешава качвания с
AM_PLUGINSETTINGS_ENABLEUPLOADS=true(изключено по подразбиране; няма превключвател в Системната конзола). - След това Системна конзола › Приставка › Управление на приставките › Качване на приставка, или
amctl plugin add weather.tar.gz, последвано отamctl plugin enable com.example.weather. - Ако на сървъра е активирано Изисква се подпис на приставката, той приема само пакети, подписани с ключ, на който има доверие; вижте Инсталиране на други приложения.
AM_SERVICESETTINGS_ENABLEDEVELOPER=true на сървъра: тогава грешките ще носят своите детайли. Makefile-овете на приложенията в пакета създават сървърната част само за вашата платформа, когато MM_SERVICESETTINGS_ENABLEDEVELOPER е зададен в вашата shell.