Merge pull request #21883 from mattermost/mpa-playbooks
Add services and functions for playbooks to work in mpa mode
Этот коммит содержится в:
24
Makefile
24
Makefile
@@ -93,6 +93,22 @@ else
|
||||
BUILD_BOARDS = false
|
||||
endif
|
||||
|
||||
# Playbooks
|
||||
BUILD_PLAYBOOKS_DIR ?= ../mattermost-plugin-playbooks
|
||||
BUILD_PLAYBOOKS ?= false
|
||||
BUILD_HASH_PLAYBOOKS = none
|
||||
|
||||
ifneq ($(wildcard $(BUILD_PLAYBOOKS_DIR)/.),)
|
||||
ifeq ($(BUILD_PLAYBOOKS),true)
|
||||
BUILD_PLAYBOOKS = true
|
||||
BUILD_HASH_PLAYBOOKS = $(shell cd $(BUILD_PLAYBOOKS_DIR) && git rev-parse HEAD)
|
||||
else
|
||||
BUILD_PLAYBOOKS = false
|
||||
endif
|
||||
else
|
||||
BUILD_PLAYBOOKS = false
|
||||
endif
|
||||
|
||||
# We need current user's UID for `run-haserver` so docker compose does not run server
|
||||
# as root and mess up file permissions for devs. When running like this HOME will be blank
|
||||
# and docker will add '/', so we need to set the go-build cache location or we'll get
|
||||
@@ -116,6 +132,7 @@ LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashEnterpr
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashBoards=$(BUILD_HASH_BOARDS)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildBoards=$(BUILD_BOARDS)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashPlaybooks=$(BUILD_HASH_PLAYBOOKS)"
|
||||
|
||||
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
|
||||
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
@@ -198,6 +215,12 @@ else
|
||||
IGNORE:=$(shell rm -f imports/boards_imports.go)
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_PLAYBOOKS),true)
|
||||
IGNORE:=$(shell cp $(BUILD_PLAYBOOKS_DIR)/product/imports/playbooks_imports.go imports/)
|
||||
else
|
||||
IGNORE:=$(shell rm -f imports/playbooks_imports.go)
|
||||
endif
|
||||
|
||||
all: run ## Alias for 'run'.
|
||||
|
||||
-include config.override.mk
|
||||
@@ -421,6 +444,7 @@ endif
|
||||
|
||||
setup-go-work: export BUILD_ENTERPRISE_READY := $(BUILD_ENTERPRISE_READY)
|
||||
setup-go-work: export BUILD_BOARDS := $(BUILD_BOARDS)
|
||||
setup-go-work: export BUILD_PLAYBOOKS := $(BUILD_PLAYBOOKS)
|
||||
setup-go-work: ## Sets up your go.work file
|
||||
./scripts/setup_go_work.sh $(IGNORE_GO_WORK_IF_EXISTS)
|
||||
|
||||
|
||||
@@ -960,6 +960,7 @@ type AppIface interface {
|
||||
RegenOutgoingWebhookToken(hook *model.OutgoingWebhook) (*model.OutgoingWebhook, *model.AppError)
|
||||
RegenerateOAuthAppSecret(app *model.OAuthApp) (*model.OAuthApp, *model.AppError)
|
||||
RegenerateTeamInviteId(teamID string) (*model.Team, *model.AppError)
|
||||
RegisterCollectionAndTopic(pluginID, collectionType, topicType string) error
|
||||
RegisterPluginCommand(pluginID string, command *model.Command) error
|
||||
ReloadConfig() error
|
||||
RemoveAllDeactivatedMembersFromChannel(c request.CTX, channel *model.Channel) *model.AppError
|
||||
|
||||
@@ -27,30 +27,82 @@ import (
|
||||
|
||||
// channelsWrapper provides an implementation of `product.ChannelService` to be used by products.
|
||||
type channelsWrapper struct {
|
||||
srv *Server
|
||||
app *App
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) GetDirectChannel(userID1, userID2 string) (*model.Channel, *model.AppError) {
|
||||
return s.srv.getDirectChannel(request.EmptyContext(s.srv.Log()), userID1, userID2)
|
||||
return s.app.getDirectChannel(request.EmptyContext(s.app.Log()), userID1, userID2)
|
||||
}
|
||||
|
||||
// GetChannelByID gets a Channel by its ID.
|
||||
func (s *channelsWrapper) GetChannelByID(channelID string) (*model.Channel, *model.AppError) {
|
||||
return s.srv.getChannel(request.EmptyContext(s.srv.Log()), channelID)
|
||||
return s.app.GetChannel(request.EmptyContext(s.app.Log()), channelID)
|
||||
}
|
||||
|
||||
// GetChannelMember gets a channel member by userID.
|
||||
func (s *channelsWrapper) GetChannelMember(channelID string, userID string) (*model.ChannelMember, *model.AppError) {
|
||||
return s.srv.getChannelMember(request.EmptyContext(s.srv.Log()), channelID, userID)
|
||||
return s.app.GetChannelMember(request.EmptyContext(s.app.Log()), channelID, userID)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) GetChannelsForTeamForUser(teamID string, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, *model.AppError) {
|
||||
return s.srv.getChannelsForTeamForUser(request.EmptyContext(s.srv.Log()), teamID, userID, opts)
|
||||
return s.app.GetChannelsForTeamForUser(request.EmptyContext(s.app.Log()), teamID, userID, opts)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) GetChannelSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) {
|
||||
return s.app.GetSidebarCategoriesForTeamForUser(request.EmptyContext(s.app.Log()), userID, teamID)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) GetChannelMembers(channelID string, page, perPage int) (model.ChannelMembers, *model.AppError) {
|
||||
return s.app.GetChannelMembersPage(request.EmptyContext(s.app.Log()), channelID, page, perPage)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) CreateChannelSidebarCategory(userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
|
||||
return s.app.CreateSidebarCategory(request.EmptyContext(s.app.Log()), userID, teamID, newCategory)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) UpdateChannelSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
|
||||
return s.app.UpdateSidebarCategories(request.EmptyContext(s.app.Log()), userID, teamID, categories)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) {
|
||||
return s.app.CreateChannel(request.EmptyContext(s.app.Log()), channel, false)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) AddUserToChannel(channelID, userID, asUserID string) (*model.ChannelMember, *model.AppError) {
|
||||
ctx := request.EmptyContext(s.app.Log())
|
||||
channel, err := s.app.GetChannel(ctx, channelID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.app.AddChannelMember(ctx, userID, channel, ChannelMemberOpts{
|
||||
UserRequestorID: asUserID,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) UpdateChannelMemberRoles(channelID, userID, newRoles string) (*model.ChannelMember, *model.AppError) {
|
||||
return s.app.UpdateChannelMemberRoles(request.EmptyContext(s.app.Log()), channelID, userID, newRoles)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) DeleteChannelMember(channelID, userID string) *model.AppError {
|
||||
return s.app.LeaveChannel(request.EmptyContext(s.app.Log()), channelID, userID)
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) AddChannelMember(channelID, userID string) (*model.ChannelMember, *model.AppError) {
|
||||
channel, err := s.GetChannelByID(channelID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.app.AddChannelMember(request.EmptyContext(s.app.Log()), userID, channel, ChannelMemberOpts{
|
||||
// For now, don't allow overriding these via the plugin API.
|
||||
UserRequestorID: "",
|
||||
PostRootID: "",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *channelsWrapper) GetDirectChannelOrCreate(userID1, userID2 string) (*model.Channel, *model.AppError) {
|
||||
return s.app.GetOrCreateDirectChannel(request.EmptyContext(s.srv.Log()), userID1, userID2)
|
||||
return s.app.GetOrCreateDirectChannel(request.EmptyContext(s.app.Log()), userID1, userID2)
|
||||
}
|
||||
|
||||
// Ensure the wrapper implements the product service.
|
||||
|
||||
@@ -214,7 +214,6 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
|
||||
pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest)
|
||||
|
||||
services[product.ChannelKey] = &channelsWrapper{
|
||||
srv: s,
|
||||
app: &App{ch: ch},
|
||||
}
|
||||
|
||||
@@ -244,6 +243,10 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
|
||||
app: &App{ch: ch},
|
||||
}
|
||||
|
||||
services[product.CommandKey] = &App{ch: ch}
|
||||
|
||||
services[product.ThreadsKey] = &App{ch: ch}
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
func (a *App) registerCollectionAndTopic(pluginID, collectionType, topicType string) error {
|
||||
func (a *App) RegisterCollectionAndTopic(pluginID, collectionType, topicType string) error {
|
||||
// we have a race condition due to multiple plugins calling this method
|
||||
a.ch.collectionAndTopicTypesMut.Lock()
|
||||
defer a.ch.collectionAndTopicTypesMut.Unlock()
|
||||
|
||||
@@ -13682,6 +13682,28 @@ func (a *OpenTracingAppLayer) RegenerateTeamInviteId(teamID string) (*model.Team
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) RegisterCollectionAndTopic(pluginID string, collectionType string, topicType string) error {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RegisterCollectionAndTopic")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.RegisterCollectionAndTopic(pluginID, collectionType, topicType)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) RegisterPluginCommand(pluginID string, command *model.Command) error {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RegisterPluginCommand")
|
||||
|
||||
@@ -41,6 +41,10 @@ func (s *permissionsServiceWrapper) HasPermissionToChannel(askingUserID string,
|
||||
return s.app.HasPermissionToChannel(request.EmptyContext(s.app.Log()), askingUserID, channelID, permission)
|
||||
}
|
||||
|
||||
func (s *permissionsServiceWrapper) RolesGrantPermission(roleNames []string, permissionId string) bool {
|
||||
return s.app.RolesGrantPermission(roleNames, permissionId)
|
||||
}
|
||||
|
||||
func (a *App) ResetPermissionsSystem() *model.AppError {
|
||||
// Reset all Teams to not have a scheme.
|
||||
if err := a.Srv().Store().Team().ResetAllTeamSchemes(); err != nil {
|
||||
|
||||
@@ -1235,7 +1235,7 @@ func (api *PluginAPI) GetCloudLimits() (*model.ProductLimits, error) {
|
||||
// RegisterCollectionAndTopic informs the server that this plugin handles
|
||||
// the given collection and topic types.
|
||||
func (api *PluginAPI) RegisterCollectionAndTopic(collectionType, topicType string) error {
|
||||
return api.app.registerCollectionAndTopic(api.id, collectionType, topicType)
|
||||
return api.app.RegisterCollectionAndTopic(api.id, collectionType, topicType)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) CreateUploadSession(us *model.UploadSession) (*model.UploadSession, error) {
|
||||
|
||||
20
app/post.go
20
app/post.go
@@ -46,6 +46,26 @@ func (s *postServiceWrapper) CreatePost(ctx *request.Context, post *model.Post)
|
||||
return s.app.CreatePostMissingChannel(ctx, post, true)
|
||||
}
|
||||
|
||||
func (s *postServiceWrapper) GetPostsByIds(postIDs []string) ([]*model.Post, int64, *model.AppError) {
|
||||
return s.app.GetPostsByIds(postIDs)
|
||||
}
|
||||
|
||||
func (s *postServiceWrapper) SendEphemeralPost(ctx *request.Context, userID string, post *model.Post) *model.Post {
|
||||
return s.app.SendEphemeralPost(ctx, userID, post)
|
||||
}
|
||||
|
||||
func (s *postServiceWrapper) GetPost(postID string) (*model.Post, *model.AppError) {
|
||||
return s.app.GetSinglePost(postID, false)
|
||||
}
|
||||
|
||||
func (s *postServiceWrapper) DeletePost(ctx *request.Context, postID, productID string) (*model.Post, *model.AppError) {
|
||||
return s.app.DeletePost(ctx, postID, productID)
|
||||
}
|
||||
|
||||
func (s *postServiceWrapper) UpdatePost(ctx *request.Context, post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) {
|
||||
return s.app.UpdatePost(ctx, post, false)
|
||||
}
|
||||
|
||||
func (a *App) CreatePostAsUser(c request.CTX, post *model.Post, currentSessionId string, setOnline bool) (*model.Post, *model.AppError) {
|
||||
// Check that channel has not been deleted
|
||||
channel, errCh := a.Srv().Store().Channel().Get(post.ChannelId, true)
|
||||
|
||||
@@ -241,7 +241,6 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
app := New(ServerConnector(s.Channels()))
|
||||
serviceMap := map[product.ServiceKey]any{
|
||||
ServerKey: s,
|
||||
product.ChannelKey: &channelsWrapper{srv: s, app: app},
|
||||
product.ConfigKey: s.platform,
|
||||
product.LicenseKey: s.licenseWrapper,
|
||||
product.FilestoreKey: s.platform.FileBackend(),
|
||||
@@ -253,6 +252,8 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
product.KVStoreKey: s.platform,
|
||||
product.StoreKey: store.NewStoreServiceAdapter(s.Store()),
|
||||
product.SystemKey: &systemServiceAdapter{server: s},
|
||||
product.SessionKey: app,
|
||||
product.FrontendKey: app,
|
||||
}
|
||||
|
||||
// Step 4: Initialize products.
|
||||
@@ -262,6 +263,13 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
return nil, errors.Wrap(err, "failed to initialize products")
|
||||
}
|
||||
|
||||
// After channel is initialized set it to the App object
|
||||
channelsWrapper, ok := serviceMap[product.ChannelKey].(*channelsWrapper)
|
||||
if !ok {
|
||||
return nil, errors.Wrap(err, "channels product is not initialized")
|
||||
}
|
||||
app.ch = channelsWrapper.app.ch
|
||||
|
||||
// It is important to initialize the hub only after the global logger is set
|
||||
// to avoid race conditions while logging from inside the hub.
|
||||
// Step 5: Start hub in platform which the hub depends on s.Channels() (step 4)
|
||||
|
||||
13
app/team.go
13
app/team.go
@@ -44,6 +44,19 @@ func (w *teamServiceWrapper) CreateMember(ctx *request.Context, teamID, userID s
|
||||
return w.app.AddTeamMember(ctx, teamID, userID)
|
||||
}
|
||||
|
||||
func (w *teamServiceWrapper) GetGroup(groupID string) (*model.Group, *model.AppError) {
|
||||
return w.app.GetGroup(groupID, nil, nil)
|
||||
}
|
||||
|
||||
func (w *teamServiceWrapper) GetTeam(teamID string) (*model.Team, *model.AppError) {
|
||||
return w.app.GetTeam(teamID)
|
||||
}
|
||||
|
||||
func (w *teamServiceWrapper) GetGroupMemberUsers(groupID string, page, perPage int) ([]*model.User, *model.AppError) {
|
||||
users, _, err := w.app.GetGroupMemberUsersPage(groupID, page, perPage, nil)
|
||||
return users, err
|
||||
}
|
||||
|
||||
// Ensure the wrapper implements the product service.
|
||||
var _ product.TeamService = (*teamServiceWrapper)(nil)
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
|
||||
props["BuildEnterpriseReady"] = model.BuildEnterpriseReady
|
||||
props["BuildHashBoards"] = model.BuildHashBoards
|
||||
props["BuildBoards"] = model.BuildBoards
|
||||
props["BuildHashPlaybooks"] = model.BuildHashPlaybooks
|
||||
|
||||
props["EnableBotAccountCreation"] = strconv.FormatBool(*c.ServiceSettings.EnableBotAccountCreation)
|
||||
props["EnableFile"] = strconv.FormatBool(*c.LogSettings.EnableFile)
|
||||
|
||||
@@ -118,6 +118,7 @@ var BuildHashEnterprise string
|
||||
var BuildEnterpriseReady string
|
||||
var BuildHashBoards string
|
||||
var BuildBoards string
|
||||
var BuildHashPlaybooks string
|
||||
var versionsWithoutHotFixes []string
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -33,6 +33,11 @@ type RouterService interface {
|
||||
// The service shall be registered via app.PostKey service key.
|
||||
type PostService interface {
|
||||
CreatePost(context *request.Context, post *model.Post) (*model.Post, *model.AppError)
|
||||
GetPostsByIds(postIDs []string) ([]*model.Post, int64, *model.AppError)
|
||||
SendEphemeralPost(ctx *request.Context, userID string, post *model.Post) *model.Post
|
||||
GetPost(postID string) (*model.Post, *model.AppError)
|
||||
DeletePost(ctx *request.Context, postID, productID string) (*model.Post, *model.AppError)
|
||||
UpdatePost(c *request.Context, post *model.Post, safeUpdate bool) (*model.Post, *model.AppError)
|
||||
}
|
||||
|
||||
// PermissionService provides permissions related utilities. For now, the service implementation
|
||||
@@ -44,6 +49,7 @@ type PermissionService interface {
|
||||
HasPermissionTo(userID string, permission *model.Permission) bool
|
||||
HasPermissionToTeam(userID, teamID string, permission *model.Permission) bool
|
||||
HasPermissionToChannel(askingUserID string, channelID string, permission *model.Permission) bool
|
||||
RolesGrantPermission(roleNames []string, permissionID string) bool
|
||||
}
|
||||
|
||||
// ClusterService enables to publish cluster events. In addition to that, It's being used for
|
||||
@@ -66,6 +72,15 @@ type ChannelService interface {
|
||||
GetChannelByID(channelID string) (*model.Channel, *model.AppError)
|
||||
GetChannelMember(channelID string, userID string) (*model.ChannelMember, *model.AppError)
|
||||
GetChannelsForTeamForUser(teamID string, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, *model.AppError)
|
||||
GetChannelSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError)
|
||||
GetChannelMembers(channelID string, page, perPage int) (model.ChannelMembers, *model.AppError)
|
||||
CreateChannelSidebarCategory(userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError)
|
||||
UpdateChannelSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError)
|
||||
CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError)
|
||||
AddUserToChannel(channelID, userID, asUserID string) (*model.ChannelMember, *model.AppError)
|
||||
UpdateChannelMemberRoles(channelID, userID, newRoles string) (*model.ChannelMember, *model.AppError)
|
||||
DeleteChannelMember(channelID, userID string) *model.AppError
|
||||
AddChannelMember(channelID, userID string) (*model.ChannelMember, *model.AppError)
|
||||
}
|
||||
|
||||
// LicenseService provides license related utilities.
|
||||
@@ -96,6 +111,9 @@ type UserService interface {
|
||||
type TeamService interface {
|
||||
GetMember(teamID, userID string) (*model.TeamMember, *model.AppError)
|
||||
CreateMember(ctx *request.Context, teamID, userID string) (*model.TeamMember, *model.AppError)
|
||||
GetGroup(groupId string) (*model.Group, *model.AppError)
|
||||
GetTeam(teamID string) (*model.Team, *model.AppError)
|
||||
GetGroupMemberUsers(groupID string, page, perPage int) ([]*model.User, *model.AppError)
|
||||
}
|
||||
|
||||
// BotService is just a copy implementation of mattermost-plugin-api EnsureBot method.
|
||||
@@ -161,6 +179,9 @@ type CloudService interface {
|
||||
// The service shall be registered via app.KVStoreKey service key.
|
||||
type KVStoreService interface {
|
||||
SetPluginKeyWithOptions(pluginID string, key string, value []byte, options model.PluginKVSetOptions) (bool, *model.AppError)
|
||||
KVGet(productID, key string) ([]byte, *model.AppError)
|
||||
KVDelete(productID, key string) *model.AppError
|
||||
KVList(productID string, page, perPage int) ([]string, *model.AppError)
|
||||
}
|
||||
|
||||
// LogService is the API for accessing the log service APIs.
|
||||
@@ -212,3 +233,32 @@ type BoardsService interface {
|
||||
HasPermissionToBoard(userID, boardID string, permission *model.Permission) bool
|
||||
DuplicateBoard(boardID string, userID string, toTeam string, asTemplate bool) (*fb_model.BoardsAndBlocks, []*fb_model.BoardMember, error)
|
||||
}
|
||||
|
||||
// SessionService is the API for accessing the session.
|
||||
//
|
||||
// The service shall be registered via app.SessionKey service key.
|
||||
type SessionService interface {
|
||||
GetSessionById(sessionID string) (*model.Session, *model.AppError)
|
||||
}
|
||||
|
||||
// FrontendService is the API for interacting with front end.
|
||||
//
|
||||
// The service shall be registered via app.FrontendKey service key.
|
||||
type FrontendService interface {
|
||||
OpenInteractiveDialog(dialog model.OpenDialogRequest) *model.AppError
|
||||
}
|
||||
|
||||
// CommandService is the API for interacting with front end.
|
||||
//
|
||||
// The service shall be registered via app.CommandKey service key.
|
||||
type CommandService interface {
|
||||
ExecuteCommand(c request.CTX, args *model.CommandArgs) (*model.CommandResponse, *model.AppError)
|
||||
RegisterPluginCommand(pluginID string, command *model.Command) error
|
||||
}
|
||||
|
||||
// ThreadsService is the API for interacting with threads anywhere.
|
||||
//
|
||||
// The service shall be registered via app.ThreadsKey service key.
|
||||
type ThreadsService interface {
|
||||
RegisterCollectionAndTopic(productID string, collectionType, topicType string) error
|
||||
}
|
||||
|
||||
@@ -26,4 +26,8 @@ const (
|
||||
SystemKey ServiceKey = "systemkey"
|
||||
PreferencesKey ServiceKey = "preferenceskey"
|
||||
BoardsKey ServiceKey = "boards"
|
||||
SessionKey ServiceKey = "sessionkey"
|
||||
FrontendKey ServiceKey = "frontendkey"
|
||||
CommandKey ServiceKey = "commandkey"
|
||||
ThreadsKey ServiceKey = "threadskey"
|
||||
)
|
||||
|
||||
@@ -16,5 +16,10 @@ then
|
||||
txt="${txt}use ../focalboard/server\nuse ../focalboard/mattermost-plugin\n"
|
||||
fi
|
||||
|
||||
if [ "$BUILD_PLAYBOOKS" == "true" ]
|
||||
then
|
||||
txt="${txt}use ../mattermost-plugin-playbooks\n"
|
||||
fi
|
||||
|
||||
printf "$txt" > "go.work"
|
||||
fi
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/go-i18n/i18n"
|
||||
"github.com/mattermost/go-i18n/i18n/bundle"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
@@ -22,6 +23,9 @@ const defaultLocale = "en"
|
||||
// TranslateFunc is the type of the translate functions
|
||||
type TranslateFunc func(translationID string, args ...any) string
|
||||
|
||||
// TranslationFuncByLocal is the type of function that takes local as a string and returns the translation function
|
||||
type TranslationFuncByLocal func(locale string) TranslateFunc
|
||||
|
||||
// T is the translate function using the default server language as fallback language
|
||||
var T TranslateFunc
|
||||
|
||||
@@ -74,6 +78,41 @@ func initTranslationsWithDir(dir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTranslationFuncForDir loads translations from the filesystem into a new instance of the bundle.
|
||||
// It returns a function to access loaded translations.
|
||||
func GetTranslationFuncForDir(dir string) (TranslationFuncByLocal, error) {
|
||||
var availableLocals map[string]string = make(map[string]string)
|
||||
bundle := bundle.New()
|
||||
files, _ := os.ReadDir(dir)
|
||||
for _, f := range files {
|
||||
if filepath.Ext(f.Name()) != ".json" {
|
||||
continue
|
||||
}
|
||||
|
||||
filename := f.Name()
|
||||
availableLocals[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename)
|
||||
if err := bundle.LoadTranslationFile(filepath.Join(dir, filename)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return func(locale string) TranslateFunc {
|
||||
if _, ok := availableLocals[locale]; !ok {
|
||||
locale = defaultLocale
|
||||
}
|
||||
|
||||
t, _ := bundle.Tfunc(locale)
|
||||
return func(translationID string, args ...any) string {
|
||||
if translated := t(translationID, args...); translated != translationID {
|
||||
return translated
|
||||
}
|
||||
|
||||
t, _ := bundle.Tfunc(defaultLocale)
|
||||
return t(translationID, args...)
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getTranslationsBySystemLocale() (TranslateFunc, error) {
|
||||
locale := defaultServerLocale
|
||||
if _, ok := locales[locale]; !ok {
|
||||
|
||||
Ссылка в новой задаче
Block a user