snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
This commit is contained in:
383
server/channels/app/channel_category.go
Normal file
383
server/channels/app/channel_category.go
Normal file
@@ -0,0 +1,383 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||
)
|
||||
|
||||
func (a *App) createInitialSidebarCategories(rctx request.CTX, userID string, teamID string) (*model.OrderedSidebarCategories, *model.AppError) {
|
||||
categories, nErr := a.Srv().Store().Channel().CreateInitialSidebarCategories(rctx, userID, teamID)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("createInitialSidebarCategories", "app.channel.create_initial_sidebar_categories.internal_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
func (a *App) GetSidebarCategoriesForTeamForUser(rctx request.CTX, userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) {
|
||||
var appErr *model.AppError
|
||||
categories, err := a.Srv().Store().Channel().GetSidebarCategoriesForTeamForUser(userID, teamID)
|
||||
if err == nil && len(categories.Categories) == 0 {
|
||||
// A user must always have categories, so migration must not have happened yet, and we should run it ourselves
|
||||
categories, appErr = a.createInitialSidebarCategories(rctx, userID, teamID)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("GetSidebarCategoriesForTeamForUser", "app.channel.sidebar_categories.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
default:
|
||||
return nil, model.NewAppError("GetSidebarCategoriesForTeamForUser", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
func (a *App) GetSidebarCategories(rctx request.CTX, userID string, teamID string) (*model.OrderedSidebarCategories, *model.AppError) {
|
||||
var appErr *model.AppError
|
||||
categories, err := a.Srv().Store().Channel().GetSidebarCategories(userID, teamID)
|
||||
if err == nil && len(categories.Categories) == 0 {
|
||||
// A user must always have categories, so migration must not have happened yet, and we should run it ourselves
|
||||
categories, appErr = a.createInitialSidebarCategories(rctx, userID, teamID)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("GetSidebarCategories", "app.channel.sidebar_categories.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
default:
|
||||
return nil, model.NewAppError("GetSidebarCategories", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
func (a *App) GetSidebarCategoryOrder(rctx request.CTX, userID, teamID string) ([]string, *model.AppError) {
|
||||
categories, err := a.Srv().Store().Channel().GetSidebarCategoryOrder(userID, teamID)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("GetSidebarCategoryOrder", "app.channel.sidebar_categories.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
default:
|
||||
return nil, model.NewAppError("GetSidebarCategoryOrder", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
func (a *App) GetSidebarCategory(rctx request.CTX, categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
|
||||
category, err := a.Srv().Store().Channel().GetSidebarCategory(categoryId)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("GetSidebarCategory", "app.channel.sidebar_categories.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
default:
|
||||
return nil, model.NewAppError("GetSidebarCategory", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return category, nil
|
||||
}
|
||||
|
||||
func (a *App) CreateSidebarCategory(rctx request.CTX, userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
|
||||
category, err := a.Srv().Store().Channel().CreateSidebarCategory(userID, teamID, newCategory)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("CreateSidebarCategory", "app.channel.sidebar_categories.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
default:
|
||||
return nil, model.NewAppError("CreateSidebarCategory", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryCreated, teamID, "", userID, nil, "")
|
||||
message.Add("category_id", category.Id)
|
||||
a.Publish(message)
|
||||
return category, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateSidebarCategoryOrder(rctx request.CTX, userID, teamID string, categoryOrder []string) *model.AppError {
|
||||
err := a.Srv().Store().Channel().UpdateSidebarCategoryOrder(userID, teamID, categoryOrder)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
var invErr *store.ErrInvalidInput
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return model.NewAppError("UpdateSidebarCategoryOrder", "app.channel.sidebar_categories.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
case errors.As(err, &invErr):
|
||||
return model.NewAppError("UpdateSidebarCategoryOrder", "app.channel.sidebar_categories.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
default:
|
||||
return model.NewAppError("UpdateSidebarCategoryOrder", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryOrderUpdated, teamID, "", userID, nil, "")
|
||||
message.Add("order", categoryOrder)
|
||||
a.Publish(message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateSidebarCategories(rctx request.CTX, userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
|
||||
updatedCategories, originalCategories, err := a.Srv().Store().Channel().UpdateSidebarCategories(userID, teamID, categories)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("UpdateSidebarCategories", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryUpdated, teamID, "", userID, nil, "")
|
||||
|
||||
updatedCategoriesJSON, jsonErr := json.Marshal(updatedCategories)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("UpdateSidebarCategories", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
}
|
||||
|
||||
message.Add("updatedCategories", string(updatedCategoriesJSON))
|
||||
|
||||
a.Publish(message)
|
||||
|
||||
a.muteChannelsForUpdatedCategories(rctx, userID, updatedCategories, originalCategories)
|
||||
|
||||
return updatedCategories, nil
|
||||
}
|
||||
|
||||
func (a *App) muteChannelsForUpdatedCategories(rctx request.CTX, userID string, updatedCategories []*model.SidebarCategoryWithChannels, originalCategories []*model.SidebarCategoryWithChannels) {
|
||||
var channelsToMute []string
|
||||
var channelsToUnmute []string
|
||||
|
||||
// Mute or unmute all channels in categories that were muted or unmuted
|
||||
for i, updatedCategory := range updatedCategories {
|
||||
if i > len(originalCategories)-1 {
|
||||
// The two slices should be the same length, but double check that to be safe
|
||||
continue
|
||||
}
|
||||
|
||||
originalCategory := originalCategories[i]
|
||||
|
||||
if updatedCategory.Muted && !originalCategory.Muted {
|
||||
channelsToMute = append(channelsToMute, updatedCategory.Channels...)
|
||||
} else if !updatedCategory.Muted && originalCategory.Muted {
|
||||
channelsToUnmute = append(channelsToUnmute, updatedCategory.Channels...)
|
||||
}
|
||||
}
|
||||
|
||||
// Mute any channels moved from an unmuted category into a muted one and vice versa
|
||||
channelsDiff := diffChannelsBetweenCategories(updatedCategories, originalCategories)
|
||||
if len(channelsDiff) != 0 {
|
||||
makeCategoryMap := func(categories []*model.SidebarCategoryWithChannels) map[string]*model.SidebarCategoryWithChannels {
|
||||
result := make(map[string]*model.SidebarCategoryWithChannels)
|
||||
for _, category := range categories {
|
||||
result[category.Id] = category
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
updatedCategoriesById := makeCategoryMap(updatedCategories)
|
||||
originalCategoriesById := makeCategoryMap(originalCategories)
|
||||
|
||||
for channelID, diff := range channelsDiff {
|
||||
fromCategory := originalCategoriesById[diff.fromCategoryId]
|
||||
toCategory := updatedCategoriesById[diff.toCategoryId]
|
||||
|
||||
if toCategory.Muted && !fromCategory.Muted {
|
||||
channelsToMute = append(channelsToMute, channelID)
|
||||
} else if !toCategory.Muted && fromCategory.Muted {
|
||||
channelsToUnmute = append(channelsToUnmute, channelID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(channelsToMute) > 0 {
|
||||
_, err := a.setChannelsMuted(rctx, channelsToMute, userID, true)
|
||||
if err != nil {
|
||||
rctx.Logger().Error(
|
||||
"Failed to mute channels to match category",
|
||||
mlog.String("user_id", userID),
|
||||
mlog.Err(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if len(channelsToUnmute) > 0 {
|
||||
_, err := a.setChannelsMuted(rctx, channelsToUnmute, userID, false)
|
||||
if err != nil {
|
||||
rctx.Logger().Error(
|
||||
"Failed to unmute channels to match category",
|
||||
mlog.String("user_id", userID),
|
||||
mlog.Err(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type categoryChannelDiff struct {
|
||||
fromCategoryId string
|
||||
toCategoryId string
|
||||
}
|
||||
|
||||
func diffChannelsBetweenCategories(updatedCategories []*model.SidebarCategoryWithChannels, originalCategories []*model.SidebarCategoryWithChannels) map[string]*categoryChannelDiff {
|
||||
// mapChannelIdsToCategories returns a map of channel IDs to the IDs of the categories that they're a member of.
|
||||
mapChannelIdsToCategories := func(categories []*model.SidebarCategoryWithChannels) map[string]string {
|
||||
result := make(map[string]string)
|
||||
for _, category := range categories {
|
||||
for _, channelID := range category.Channels {
|
||||
result[channelID] = category.Id
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
updatedChannelIdsMap := mapChannelIdsToCategories(updatedCategories)
|
||||
originalChannelIdsMap := mapChannelIdsToCategories(originalCategories)
|
||||
|
||||
// Check for any channels that have changed categories. Note that we don't worry about any channels that have moved
|
||||
// outside of these categories since that heavily complicates things and doesn't currently happen in our apps.
|
||||
channelsDiff := make(map[string]*categoryChannelDiff)
|
||||
for channelID, originalCategoryId := range originalChannelIdsMap {
|
||||
updatedCategoryId := updatedChannelIdsMap[channelID]
|
||||
|
||||
if originalCategoryId != updatedCategoryId && updatedCategoryId != "" {
|
||||
channelsDiff[channelID] = &categoryChannelDiff{originalCategoryId, updatedCategoryId}
|
||||
}
|
||||
}
|
||||
|
||||
return channelsDiff
|
||||
}
|
||||
|
||||
func (a *App) DeleteSidebarCategory(rctx request.CTX, userID, teamID, categoryId string) *model.AppError {
|
||||
err := a.Srv().Store().Channel().DeleteSidebarCategory(categoryId)
|
||||
if err != nil {
|
||||
var invErr *store.ErrInvalidInput
|
||||
switch {
|
||||
case errors.As(err, &invErr):
|
||||
return model.NewAppError("DeleteSidebarCategory", "app.channel.sidebar_categories.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
default:
|
||||
return model.NewAppError("DeleteSidebarCategory", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryDeleted, teamID, "", userID, nil, "")
|
||||
message.Add("category_id", categoryId)
|
||||
a.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) SetChannelManagedCategory(rctx request.CTX, channelID, categoryName string) *model.AppError {
|
||||
categoryName = strings.TrimSpace(categoryName)
|
||||
if categoryName == "" {
|
||||
return model.NewAppError("SetChannelManagedCategory", "app.managed_category.empty_name.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
channel, appErr := a.GetChannel(rctx, channelID)
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
|
||||
return model.NewAppError("SetChannelManagedCategory", "app.managed_category.dm_gm_not_allowed.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
nameJSON, _ := json.Marshal(categoryName)
|
||||
|
||||
value := &model.PropertyValue{
|
||||
GroupID: a.Channels().managedCategoryGroupID,
|
||||
FieldID: a.Channels().managedCategoryFieldID,
|
||||
TargetID: channelID,
|
||||
TargetType: model.PropertyValueTargetTypeChannel,
|
||||
Value: nameJSON,
|
||||
}
|
||||
|
||||
if _, appErr := a.UpsertPropertyValues(rctx, []*model.PropertyValue{value}, model.PropertyFieldObjectTypeChannel, channelID, ""); appErr != nil {
|
||||
return model.NewAppError("SetChannelManagedCategory", "app.managed_category.set.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) ClearChannelManagedCategory(rctx request.CTX, channelID string) *model.AppError {
|
||||
values, appErr := a.SearchPropertyValues(rctx, a.Channels().managedCategoryGroupID, model.PropertyValueSearchOpts{
|
||||
FieldID: a.Channels().managedCategoryFieldID,
|
||||
TargetIDs: []string{channelID},
|
||||
PerPage: 1,
|
||||
})
|
||||
if appErr != nil {
|
||||
return model.NewAppError("ClearChannelManagedCategory", "app.managed_category.search.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if appErr := a.DeletePropertyValue(rctx, a.Channels().managedCategoryGroupID, values[0].ID); appErr != nil {
|
||||
return model.NewAppError("ClearChannelManagedCategory", "app.managed_category.clear.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetVisibleManagedCategoryMappings returns a map of channelID -> categoryName for all channels
|
||||
// the user is a member of (within the given team) that have a managed category assigned.
|
||||
func (a *App) GetVisibleManagedCategoryMappings(rctx request.CTX, teamID string) (map[string]string, *model.AppError) {
|
||||
channels, appErr := a.GetChannelsForTeamForUser(rctx, teamID, rctx.Session().UserId, &model.ChannelSearchOpts{})
|
||||
if appErr != nil {
|
||||
if appErr.StatusCode == http.StatusNotFound {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
if len(channels) == 0 {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
|
||||
channelIDs := make([]string, 0, len(channels))
|
||||
for _, ch := range channels {
|
||||
channelIDs = append(channelIDs, ch.Id)
|
||||
}
|
||||
|
||||
values, appErr := a.SearchPropertyValues(rctx, a.Channels().managedCategoryGroupID, model.PropertyValueSearchOpts{
|
||||
FieldID: a.Channels().managedCategoryFieldID,
|
||||
TargetIDs: channelIDs,
|
||||
PerPage: len(channelIDs),
|
||||
})
|
||||
if appErr != nil {
|
||||
return nil, model.NewAppError("GetVisibleManagedCategoryMappings", "app.managed_category.get_mappings.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
}
|
||||
|
||||
result := make(map[string]string, len(values))
|
||||
for _, v := range values {
|
||||
var name string
|
||||
if err := json.Unmarshal(v.Value, &name); err != nil {
|
||||
rctx.Logger().Warn("Failed to unmarshal managed category name",
|
||||
mlog.String("channel_id", v.TargetID),
|
||||
mlog.Err(err),
|
||||
)
|
||||
continue
|
||||
}
|
||||
result[v.TargetID] = name
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user