1076 lines
33 KiB
Go
1076 lines
33 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"net/http"
|
||
"runtime/debug"
|
||
"strings"
|
||
|
||
"github.com/gorilla/mux"
|
||
"github.com/larkox/mattermost-plugin-badges/badgesmodel"
|
||
"github.com/mattermost/mattermost-server/v5/model"
|
||
)
|
||
|
||
// HTTPHandlerFuncWithUser is http.HandleFunc but userID is already exported
|
||
type HTTPHandlerFuncWithUser func(w http.ResponseWriter, r *http.Request, userID string)
|
||
|
||
// ResponseType indicates type of response returned by api
|
||
type ResponseType string
|
||
|
||
const (
|
||
// ResponseTypeJSON indicates that response type is json
|
||
ResponseTypeJSON ResponseType = "JSON_RESPONSE"
|
||
// ResponseTypePlain indicates that response type is text plain
|
||
ResponseTypePlain ResponseType = "TEXT_RESPONSE"
|
||
// ResponseTypeDialog indicates that response type is a dialog response
|
||
ResponseTypeDialog ResponseType = "DIALOG"
|
||
)
|
||
|
||
type APIErrorResponse struct {
|
||
ID string `json:"id"`
|
||
Message string `json:"message"`
|
||
StatusCode int `json:"status_code"`
|
||
}
|
||
|
||
func (p *Plugin) initializeAPI() {
|
||
p.router = mux.NewRouter()
|
||
p.router.Use(p.withRecovery)
|
||
|
||
apiRouter := p.router.PathPrefix("/api/v1").Subrouter()
|
||
pluginAPIRouter := p.router.PathPrefix(badgesmodel.PluginAPIPath).Subrouter()
|
||
autocompleteRouter := p.router.PathPrefix(AutocompletePath).Subrouter()
|
||
dialogRouter := p.router.PathPrefix(DialogPath).Subrouter()
|
||
|
||
apiRouter.HandleFunc("/getUserBadges/{userID}", p.extractUserMiddleWare(p.getUserBadges, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
apiRouter.HandleFunc("/getBadgeDetails/{badgeID}", p.extractUserMiddleWare(p.getBadgeDetails, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
apiRouter.HandleFunc("/getAllBadges", p.extractUserMiddleWare(p.getAllBadges, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
|
||
pluginAPIRouter.HandleFunc(badgesmodel.PluginAPIPathEnsure, checkPluginRequest(p.ensureBadges)).Methods(http.MethodPost)
|
||
pluginAPIRouter.HandleFunc(badgesmodel.PluginAPIPathGrant, checkPluginRequest(p.grantBadge)).Methods(http.MethodPost)
|
||
|
||
autocompleteRouter.HandleFunc(AutocompletePathBadgeSuggestions, p.extractUserMiddleWare(p.getBadgeSuggestions, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
autocompleteRouter.HandleFunc(AutocompletePathEditBadgeSuggestions, p.extractUserMiddleWare(p.getEditBadgeSuggestions, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
autocompleteRouter.HandleFunc(AutocompletePathTypeSuggestions, p.extractUserMiddleWare(p.getBadgeTypeSuggestions, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
autocompleteRouter.HandleFunc(AutocompletePathEditTypeSuggestions, p.extractUserMiddleWare(p.getEditBadgeTypeSuggestions, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
|
||
dialogRouter.HandleFunc(DialogPathCreateBadge, p.extractUserMiddleWare(p.dialogCreateBadge, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathCreateType, p.extractUserMiddleWare(p.dialogCreateType, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathGrant, p.extractUserMiddleWare(p.dialogGrant, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathSelectBadge, p.extractUserMiddleWare(p.dialogSelectBadge, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathSelectType, p.extractUserMiddleWare(p.dialogSelectType, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathEditBadge, p.extractUserMiddleWare(p.dialogEditBadge, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathEditType, p.extractUserMiddleWare(p.dialogEditType, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathCreateSubscription, p.extractUserMiddleWare(p.dialogCreateSubscription, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
dialogRouter.HandleFunc(DialogPathDeleteSubscription, p.extractUserMiddleWare(p.dialogDeleteSubscription, ResponseTypeDialog)).Methods(http.MethodPost)
|
||
|
||
p.router.PathPrefix("/").HandlerFunc(p.defaultHandler)
|
||
}
|
||
|
||
func (p *Plugin) defaultHandler(w http.ResponseWriter, r *http.Request) {
|
||
p.mm.Log.Debug("Unexpected call", "url", r.URL)
|
||
w.WriteHeader(http.StatusNotFound)
|
||
}
|
||
|
||
func dialogError(w http.ResponseWriter, text string, errors map[string]string) {
|
||
resp := &model.SubmitDialogResponse{
|
||
Error: "Error: " + text,
|
||
Errors: errors,
|
||
}
|
||
_, _ = w.Write(resp.ToJson())
|
||
}
|
||
|
||
func dialogOK(w http.ResponseWriter) {
|
||
resp := &model.SubmitDialogResponse{}
|
||
_, _ = w.Write(resp.ToJson())
|
||
}
|
||
|
||
func dialogKeepOpen(w http.ResponseWriter) {
|
||
resp := &model.SubmitDialogResponse{
|
||
Error: "_",
|
||
}
|
||
_, _ = w.Write(resp.ToJson())
|
||
}
|
||
|
||
func (p *Plugin) dialogCreateBadge(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
user, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_get_user", "Не удалось найти пользователя"), nil)
|
||
return
|
||
}
|
||
T := p.getT(user.Locale)
|
||
|
||
toCreate := &badgesmodel.Badge{}
|
||
toCreate.CreatedBy = userID
|
||
toCreate.ImageType = badgesmodel.ImageTypeEmoji
|
||
name, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeName)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
toCreate.Name = name
|
||
|
||
description, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeDescription)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
toCreate.Description = description
|
||
|
||
image, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeImage)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
if length := len(image); length > 1 && image[0] == ':' && image[length-1] == ':' {
|
||
image = image[1 : len(image)-1]
|
||
}
|
||
if image == "" {
|
||
dialogError(w, T("badges.api.invalid_field", "Некорректное поле"), map[string]string{"image": T("badges.api.empty_emoji", "Пустой эмодзи")})
|
||
return
|
||
}
|
||
toCreate.Image = image
|
||
|
||
badgeTypeStr, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeType)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
toCreate.Type = badgesmodel.BadgeType(badgeTypeStr)
|
||
toCreate.Multiple = getDialogSubmissionBoolField(req, DialogFieldBadgeMultiple)
|
||
|
||
t, err := p.store.GetType(badgesmodel.BadgeType(badgeTypeStr))
|
||
if err != nil {
|
||
dialogError(w, T("badges.api.type_not_exist", "Этот тип не существует"), nil)
|
||
return
|
||
}
|
||
|
||
if !canCreateBadge(user, p.badgeAdminUserID, t) {
|
||
dialogError(w, T("badges.api.no_permissions_create_badge", "У вас нет прав на создание этого значка"), nil)
|
||
return
|
||
}
|
||
|
||
_, err = p.store.AddBadge(toCreate)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelId,
|
||
Message: T("badges.api.badge_created", "Значок `%s` создан.", toCreate.Name),
|
||
})
|
||
|
||
dialogOK(w)
|
||
}
|
||
|
||
func (p *Plugin) dialogCreateType(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_get_user", "Не удалось найти пользователя"), nil)
|
||
return
|
||
}
|
||
T := p.getT(u.Locale)
|
||
|
||
if !canCreateType(u, p.badgeAdminUserID, false) {
|
||
dialogError(w, T("badges.api.no_permissions_create_type", "У вас нет прав на создание типа"), nil)
|
||
return
|
||
}
|
||
|
||
toCreate := &badgesmodel.BadgeTypeDefinition{}
|
||
toCreate.CreatedBy = userID
|
||
toCreate.CanCreate.Everyone = getDialogSubmissionBoolField(req, DialogFieldTypeEveryoneCanCreate)
|
||
toCreate.CanGrant.Everyone = getDialogSubmissionBoolField(req, DialogFieldTypeEveryoneCanGrant)
|
||
name, errText, errors := getDialogSubmissionTextField(req, DialogFieldTypeName)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
toCreate.Name = name
|
||
|
||
createAllowList, _ := req.Submission[DialogFieldTypeAllowlistCanCreate].(string)
|
||
grantAllowList, _ := req.Submission[DialogFieldTypeAllowlistCanGrant].(string)
|
||
|
||
if createAllowList != "" {
|
||
toCreate.CanCreate.AllowList = map[string]bool{}
|
||
usernames := strings.Split(createAllowList, ",")
|
||
for _, username := range usernames {
|
||
username = strings.TrimSpace(username)
|
||
if username == "" {
|
||
continue
|
||
}
|
||
foundUser, userErr := p.mm.User.GetByUsername(username)
|
||
if userErr != nil {
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), map[string]string{DialogFieldTypeAllowlistCanCreate: T("badges.api.error_getting_user", "Ошибка получения пользователя %s: %v", username, userErr)})
|
||
return
|
||
}
|
||
toCreate.CanCreate.AllowList[foundUser.Id] = true
|
||
}
|
||
}
|
||
|
||
if grantAllowList != "" {
|
||
toCreate.CanGrant.AllowList = map[string]bool{}
|
||
usernames := strings.Split(grantAllowList, ",")
|
||
for _, username := range usernames {
|
||
username = strings.TrimSpace(username)
|
||
if username == "" {
|
||
continue
|
||
}
|
||
foundUser, userErr := p.mm.User.GetByUsername(username)
|
||
if userErr != nil {
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), map[string]string{DialogFieldTypeAllowlistCanGrant: T("badges.api.error_getting_user", "Ошибка получения пользователя %s: %v", username, userErr)})
|
||
return
|
||
}
|
||
toCreate.CanGrant.AllowList[foundUser.Id] = true
|
||
}
|
||
}
|
||
|
||
_, err = p.store.AddType(toCreate)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelId,
|
||
Message: T("badges.api.type_created", "Тип `%s` создан.", toCreate.Name),
|
||
})
|
||
|
||
dialogOK(w)
|
||
}
|
||
|
||
// This is not working on the current webapp architecture. A similar approach should be handled using Apps.
|
||
func (p *Plugin) dialogSelectType(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
badgeTypeStr, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeType)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
t, err := p.store.GetType(badgesmodel.BadgeType(badgeTypeStr))
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_get_type", "Не удалось получить тип"), map[string]string{DialogFieldBadgeType: T("badges.api.cannot_get_type", "Не удалось получить тип")})
|
||
return
|
||
}
|
||
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), nil)
|
||
return
|
||
}
|
||
T := p.getT(u.Locale)
|
||
|
||
if !canEditType(u, p.badgeAdminUserID, t) {
|
||
dialogError(w, T("badges.api.cannot_edit_type", "Вы не можете редактировать этот тип"), nil)
|
||
return
|
||
}
|
||
|
||
_, _ = p.mm.SlashCommand.Execute(&model.CommandArgs{
|
||
UserId: userID,
|
||
ChannelId: req.ChannelId,
|
||
TeamId: req.TeamId,
|
||
Command: "/badges edit type --type " + badgeTypeStr,
|
||
})
|
||
|
||
dialogKeepOpen(w)
|
||
}
|
||
|
||
func (p *Plugin) dialogEditType(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), nil)
|
||
return
|
||
}
|
||
T := p.getT(u.Locale)
|
||
|
||
originalTypeID := req.State
|
||
|
||
originalType, err := p.store.GetType(badgesmodel.BadgeType(originalTypeID))
|
||
if err != nil {
|
||
dialogError(w, T("badges.api.could_not_get_type", "Не удалось получить тип"), nil)
|
||
return
|
||
}
|
||
|
||
if !canEditType(u, p.badgeAdminUserID, originalType) {
|
||
dialogError(w, T("badges.api.no_permissions_edit_type", "У вас нет прав на редактирование этого типа"), nil)
|
||
return
|
||
}
|
||
|
||
if getDialogSubmissionBoolField(req, DialogFieldTypeDelete) {
|
||
err = p.store.DeleteType(badgesmodel.BadgeType(originalTypeID))
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
}
|
||
return
|
||
}
|
||
originalType.CanCreate.Everyone = getDialogSubmissionBoolField(req, DialogFieldTypeEveryoneCanCreate)
|
||
originalType.CanGrant.Everyone = getDialogSubmissionBoolField(req, DialogFieldTypeEveryoneCanGrant)
|
||
name, errText, errors := getDialogSubmissionTextField(req, DialogFieldTypeName)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
originalType.Name = name
|
||
|
||
createAllowList, _ := req.Submission[DialogFieldTypeAllowlistCanCreate].(string)
|
||
grantAllowList, _ := req.Submission[DialogFieldTypeAllowlistCanGrant].(string)
|
||
|
||
originalType.CanCreate.AllowList = map[string]bool{}
|
||
usernames := strings.Split(createAllowList, ",")
|
||
for _, username := range usernames {
|
||
username = strings.TrimSpace(username)
|
||
if username == "" {
|
||
continue
|
||
}
|
||
var allowedUser *model.User
|
||
allowedUser, err = p.mm.User.GetByUsername(username)
|
||
if err != nil {
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), map[string]string{DialogFieldTypeAllowlistCanCreate: T("badges.api.error_getting_user", "Ошибка получения пользователя %s: %v", username, err)})
|
||
return
|
||
}
|
||
originalType.CanCreate.AllowList[allowedUser.Id] = true
|
||
}
|
||
|
||
originalType.CanGrant.AllowList = map[string]bool{}
|
||
usernames = strings.Split(grantAllowList, ",")
|
||
for _, username := range usernames {
|
||
username = strings.TrimSpace(username)
|
||
if username == "" {
|
||
continue
|
||
}
|
||
var allowedUser *model.User
|
||
allowedUser, err = p.mm.User.GetByUsername(username)
|
||
if err != nil {
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), map[string]string{DialogFieldTypeAllowlistCanGrant: T("badges.api.error_getting_user", "Ошибка получения пользователя %s: %v", username, err)})
|
||
return
|
||
}
|
||
originalType.CanGrant.AllowList[allowedUser.Id] = true
|
||
}
|
||
|
||
err = p.store.UpdateType(originalType)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelId,
|
||
Message: T("badges.api.type_updated", "Тип `%s` обновлён.", originalType.Name),
|
||
})
|
||
|
||
dialogOK(w)
|
||
}
|
||
|
||
// This is not working on the current webapp architecture. A similar approach should be handled using Apps.
|
||
func (p *Plugin) dialogSelectBadge(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
badgeIDStr, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadge)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
b, err := p.store.GetBadge(badgesmodel.BadgeID(badgeIDStr))
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_get_badge", "Не удалось получить значок"), map[string]string{DialogFieldBadge: T("badges.api.cannot_get_badge", "Не удалось получить значок")})
|
||
return
|
||
}
|
||
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), nil)
|
||
return
|
||
}
|
||
T := p.getT(u.Locale)
|
||
|
||
if !canEditBadge(u, p.badgeAdminUserID, b) {
|
||
dialogError(w, T("badges.api.cannot_edit_badge", "Вы не можете редактировать этот значок"), nil)
|
||
return
|
||
}
|
||
|
||
_, _ = p.mm.SlashCommand.Execute(&model.CommandArgs{
|
||
UserId: userID,
|
||
ChannelId: req.ChannelId,
|
||
TeamId: req.TeamId,
|
||
Command: "/badges edit badge --id " + badgeIDStr,
|
||
})
|
||
|
||
dialogKeepOpen(w)
|
||
}
|
||
|
||
func (p *Plugin) dialogEditBadge(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.cannot_find_user", "Не удалось найти пользователя"), nil)
|
||
return
|
||
}
|
||
T := p.getT(u.Locale)
|
||
|
||
originalBadgeID := req.State
|
||
|
||
originalBadge, err := p.store.GetBadge(badgesmodel.BadgeID(originalBadgeID))
|
||
if err != nil {
|
||
dialogError(w, T("badges.api.could_not_get_badge", "Не удалось получить значок"), nil)
|
||
return
|
||
}
|
||
|
||
if !canEditBadge(u, p.badgeAdminUserID, originalBadge) {
|
||
dialogError(w, T("badges.api.no_permissions_edit_badge", "У вас нет прав на редактирование этого значка"), nil)
|
||
return
|
||
}
|
||
|
||
if getDialogSubmissionBoolField(req, DialogFieldBadgeDelete) {
|
||
err = p.store.DeleteBadge(badgesmodel.BadgeID(originalBadgeID))
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
return
|
||
}
|
||
name, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeName)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
originalBadge.Name = name
|
||
|
||
description, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeDescription)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
originalBadge.Description = description
|
||
|
||
image, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeImage)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
if length := len(image); length > 1 && image[0] == ':' && image[length-1] == ':' {
|
||
image = image[1 : len(image)-1]
|
||
}
|
||
if image == "" {
|
||
dialogError(w, T("badges.api.invalid_field", "Некорректное поле"), map[string]string{"image": T("badges.api.empty_emoji", "Пустой эмодзи")})
|
||
return
|
||
}
|
||
originalBadge.Image = image
|
||
|
||
badgeTypeStr, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeType)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
originalBadge.Type = badgesmodel.BadgeType(badgeTypeStr)
|
||
|
||
originalBadge.Multiple = getDialogSubmissionBoolField(req, DialogFieldBadgeMultiple)
|
||
|
||
err = p.store.UpdateBadge(originalBadge)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelId,
|
||
Message: T("badges.api.badge_updated", "Значок `%s` обновлён.", originalBadge.Name),
|
||
})
|
||
|
||
dialogOK(w)
|
||
}
|
||
|
||
func (p *Plugin) dialogGrant(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
badgeIDStr, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadge)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
notifyHere := getDialogSubmissionBoolField(req, DialogFieldNotifyHere)
|
||
|
||
badge, err := p.store.GetBadge(badgesmodel.BadgeID(badgeIDStr))
|
||
if err != nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.badge_not_found", "Значок не найден"), nil)
|
||
return
|
||
}
|
||
|
||
granter, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
T := p.getT(granter.Locale)
|
||
|
||
badgeType, err := p.store.GetType(badge.Type)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
|
||
if !canGrantBadge(granter, p.badgeAdminUserID, badge, badgeType) {
|
||
dialogError(w, T("badges.api.no_permissions_grant", "У вас нет прав на выдачу этого значка"), nil)
|
||
return
|
||
}
|
||
|
||
grantToID := req.State
|
||
if grantToID == "" {
|
||
grantToID, errText, errors = getDialogSubmissionTextField(req, DialogFieldUser)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
}
|
||
|
||
grantToUser, err := p.mm.User.Get(grantToID)
|
||
if err != nil {
|
||
dialogError(w, T("badges.api.user_not_found", "Пользователь не найден"), nil)
|
||
return
|
||
}
|
||
|
||
reason, _ := req.Submission[DialogFieldGrantReason].(string)
|
||
|
||
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(badgeIDStr), grantToID, userID, reason)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot grant badge",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
if shouldNotify {
|
||
p.notifyGrant(badgesmodel.BadgeID(badgeIDStr), userID, grantToUser, notifyHere, req.ChannelId, reason)
|
||
}
|
||
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelId,
|
||
Message: T("badges.api.badge_granted", "Значок `%s` выдан @%s.", badge.Name, grantToUser.Username),
|
||
})
|
||
|
||
dialogOK(w)
|
||
}
|
||
|
||
func (p *Plugin) dialogCreateSubscription(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
T := p.getT(u.Locale)
|
||
|
||
if !canCreateSubscription(u, p.badgeAdminUserID, req.ChannelId) {
|
||
dialogError(w, T("badges.api.cannot_create_subscription", "Вы не можете создать подписку"), nil)
|
||
return
|
||
}
|
||
|
||
typeIDStr, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeType)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
err = p.store.AddSubscription(badgesmodel.BadgeType(typeIDStr), req.ChannelId)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
}
|
||
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelId,
|
||
Message: T("badges.api.subscription_added", "Подписка добавлена"),
|
||
})
|
||
|
||
dialogOK(w)
|
||
}
|
||
|
||
func (p *Plugin) dialogDeleteSubscription(w http.ResponseWriter, r *http.Request, userID string) {
|
||
req := model.SubmitDialogRequestFromJson(r.Body)
|
||
if req == nil {
|
||
T := p.getT("ru")
|
||
dialogError(w, T("badges.api.dialog_parse_error", "Не удалось получить данные диалога"), nil)
|
||
return
|
||
}
|
||
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
return
|
||
}
|
||
T := p.getT(u.Locale)
|
||
|
||
if !canCreateSubscription(u, p.badgeAdminUserID, req.ChannelId) {
|
||
dialogError(w, T("badges.api.cannot_delete_subscription", "Вы не можете удалить подписку"), nil)
|
||
return
|
||
}
|
||
|
||
typeIDStr, errText, errors := getDialogSubmissionTextField(req, DialogFieldBadgeType)
|
||
if errors != nil {
|
||
dialogError(w, errText, errors)
|
||
return
|
||
}
|
||
|
||
err = p.store.RemoveSubscriptions(badgesmodel.BadgeType(typeIDStr), req.ChannelId)
|
||
if err != nil {
|
||
dialogError(w, err.Error(), nil)
|
||
}
|
||
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelId,
|
||
Message: T("badges.api.subscription_removed", "Подписка удалена"),
|
||
})
|
||
|
||
dialogOK(w)
|
||
}
|
||
|
||
func getDialogSubmissionTextField(req *model.SubmitDialogRequest, fieldName string) (value string, errText string, errors map[string]string) {
|
||
value, ok := req.Submission[fieldName].(string)
|
||
value = strings.TrimSpace(value)
|
||
if !ok || value == "" {
|
||
return "", "Invalid argument", map[string]string{fieldName: "Field empty or not recognized."}
|
||
}
|
||
|
||
return value, "", nil
|
||
}
|
||
|
||
func getDialogSubmissionBoolField(req *model.SubmitDialogRequest, fieldName string) bool {
|
||
value, _ := req.Submission[fieldName].(bool)
|
||
return value
|
||
}
|
||
|
||
func (p *Plugin) grantBadge(w http.ResponseWriter, r *http.Request, pluginID string) {
|
||
var req *badgesmodel.GrantBadgeRequest
|
||
err := json.NewDecoder(r.Body).Decode(&req)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot unmarshal request",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
p.mm.Log.Debug("Granting badge", "req", req)
|
||
|
||
if req == nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "missing request",
|
||
Message: "Missing grant request on request body",
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
granter, err := p.mm.User.Get(req.BotID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot get user",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
badge, err := p.store.GetBadge(badgesmodel.BadgeID(req.BadgeID))
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot get badge",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
badgeType, err := p.store.GetType(badge.Type)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot get type",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
if !canGrantBadge(granter, p.badgeAdminUserID, badge, badgeType) {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot grant badge",
|
||
Message: "you have no permissions to grant this badge",
|
||
StatusCode: http.StatusUnauthorized,
|
||
})
|
||
return
|
||
}
|
||
|
||
shouldNotify, err := p.store.GrantBadge(req.BadgeID, req.UserID, req.BotID, req.Reason)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot grant badge",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
if shouldNotify {
|
||
u, err := p.mm.User.Get(req.UserID)
|
||
if err == nil {
|
||
p.notifyGrant(req.BadgeID, req.BotID, u, false, "", req.Reason)
|
||
}
|
||
}
|
||
|
||
_, _ = w.Write([]byte(`{"sucess": true}`))
|
||
}
|
||
|
||
func (p *Plugin) ensureBadges(w http.ResponseWriter, r *http.Request, pluginID string) {
|
||
var req *badgesmodel.EnsureBadgesRequest
|
||
err := json.NewDecoder(r.Body).Decode(&req)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot unmarshal request",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
if req == nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "missing request",
|
||
Message: "Missing ensure request on request body",
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
badges, err := p.store.EnsureBadges(req.Badges, pluginID, req.BotID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot ensure",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
b, err := json.Marshal(badges)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot marshal",
|
||
Message: err.Error(),
|
||
StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) getBadgeSuggestions(w http.ResponseWriter, r *http.Request, actingUserID string) {
|
||
out := []model.AutocompleteListItem{}
|
||
u, err := p.mm.User.Get(actingUserID)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting user", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
bb, err := p.filterGrantBadges(u)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting suggestions", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
for _, b := range bb {
|
||
s := model.AutocompleteListItem{
|
||
Item: string(b.ID),
|
||
Hint: b.Name,
|
||
HelpText: b.Description,
|
||
}
|
||
|
||
out = append(out, s)
|
||
}
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
}
|
||
|
||
func (p *Plugin) getEditBadgeSuggestions(w http.ResponseWriter, r *http.Request, actingUserID string) {
|
||
out := []model.AutocompleteListItem{}
|
||
u, err := p.mm.User.Get(actingUserID)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting user", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
bb, err := p.filterEditBadges(u)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting suggestions", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
for _, b := range bb {
|
||
s := model.AutocompleteListItem{
|
||
Item: string(b.ID),
|
||
Hint: b.Name,
|
||
HelpText: b.Description,
|
||
}
|
||
|
||
out = append(out, s)
|
||
}
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
}
|
||
|
||
func (p *Plugin) getBadgeTypeSuggestions(w http.ResponseWriter, r *http.Request, actingUserID string) {
|
||
out := []model.AutocompleteListItem{}
|
||
u, err := p.mm.User.Get(actingUserID)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting user", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
types, err := p.filterCreateBadgeTypes(u)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting suggestions", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
for _, t := range types {
|
||
s := model.AutocompleteListItem{
|
||
Item: string(t.ID),
|
||
Hint: t.Name,
|
||
}
|
||
|
||
out = append(out, s)
|
||
}
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
}
|
||
|
||
func (p *Plugin) getEditBadgeTypeSuggestions(w http.ResponseWriter, r *http.Request, actingUserID string) {
|
||
out := []model.AutocompleteListItem{}
|
||
u, err := p.mm.User.Get(actingUserID)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting user", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
types, err := p.filterEditTypes(u)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting suggestions", "error", err)
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
return
|
||
}
|
||
|
||
for _, t := range types {
|
||
s := model.AutocompleteListItem{
|
||
Item: string(t.ID),
|
||
Hint: t.Name,
|
||
}
|
||
|
||
out = append(out, s)
|
||
}
|
||
_, _ = w.Write(model.AutocompleteStaticListItemsToJSON(out))
|
||
}
|
||
|
||
func (p *Plugin) getUserBadges(w http.ResponseWriter, r *http.Request, actingUserID string) {
|
||
userID, ok := mux.Vars(r)["userID"]
|
||
if !ok {
|
||
userID = actingUserID
|
||
}
|
||
|
||
badges, err := p.store.GetUserBadges(userID)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Error getting the badges for user", "error", err, "user", userID)
|
||
}
|
||
|
||
b, _ := json.Marshal(badges)
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) getBadgeDetails(w http.ResponseWriter, r *http.Request, actingUserID string) {
|
||
badgeIDString, ok := mux.Vars(r)["badgeID"]
|
||
if !ok {
|
||
errMessage := "Missing badge id"
|
||
p.mm.Log.Debug(errMessage)
|
||
w.WriteHeader(http.StatusBadRequest)
|
||
_, _ = w.Write([]byte(errMessage))
|
||
return
|
||
}
|
||
|
||
badgeID := badgesmodel.BadgeID(badgeIDString)
|
||
|
||
badge, err := p.store.GetBadgeDetails(badgeID)
|
||
if err != nil {
|
||
p.mm.Log.Debug("Cannot get badge details", "badgeID", badgeID, "error", err)
|
||
}
|
||
|
||
b, _ := json.Marshal(badge)
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) getAllBadges(w http.ResponseWriter, r *http.Request, actingUserID string) {
|
||
badge, err := p.store.GetAllBadges()
|
||
if err != nil {
|
||
p.mm.Log.Debug("Cannot get all badges", "error", err)
|
||
}
|
||
|
||
b, _ := json.Marshal(badge)
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) extractUserMiddleWare(handler HTTPHandlerFuncWithUser, responseType ResponseType) http.HandlerFunc {
|
||
return func(w http.ResponseWriter, r *http.Request) {
|
||
userID := r.Header.Get("Mattermost-User-ID")
|
||
if userID == "" {
|
||
T := p.getT("ru")
|
||
msg := T("badges.api.not_authorized", "Не авторизован")
|
||
switch responseType {
|
||
case ResponseTypeJSON:
|
||
p.writeAPIError(w, &APIErrorResponse{ID: "", Message: msg, StatusCode: http.StatusUnauthorized})
|
||
case ResponseTypePlain:
|
||
http.Error(w, msg, http.StatusUnauthorized)
|
||
case ResponseTypeDialog:
|
||
dialogError(w, msg, nil)
|
||
default:
|
||
p.mm.Log.Error("Unknown ResponseType detected")
|
||
}
|
||
return
|
||
}
|
||
|
||
handler(w, r, userID)
|
||
}
|
||
}
|
||
|
||
func (p *Plugin) withRecovery(next http.Handler) http.Handler {
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
defer func() {
|
||
if x := recover(); x != nil {
|
||
p.mm.Log.Error("Recovered from a panic",
|
||
"url", r.URL.String(),
|
||
"error", x,
|
||
"stack", string(debug.Stack()))
|
||
}
|
||
}()
|
||
|
||
next.ServeHTTP(w, r)
|
||
})
|
||
}
|
||
|
||
func checkPluginRequest(next HTTPHandlerFuncWithUser) http.HandlerFunc {
|
||
return func(w http.ResponseWriter, r *http.Request) {
|
||
// All other plugins are allowed
|
||
pluginID := r.Header.Get("Mattermost-Plugin-ID")
|
||
if pluginID == "" {
|
||
http.Error(w, "Not authorized", http.StatusUnauthorized)
|
||
return
|
||
}
|
||
|
||
next(w, r, pluginID)
|
||
}
|
||
}
|
||
|
||
func (p *Plugin) writeAPIError(w http.ResponseWriter, apiErr *APIErrorResponse) {
|
||
b, err := json.Marshal(apiErr)
|
||
if err != nil {
|
||
p.mm.Log.Warn("Failed to marshal API error", "error", err.Error())
|
||
w.WriteHeader(http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
w.WriteHeader(apiErr.StatusCode)
|
||
|
||
_, err = w.Write(b)
|
||
if err != nil {
|
||
p.mm.Log.Warn("Failed to write JSON response", "error", err.Error())
|
||
w.WriteHeader(http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
func (p *Plugin) getPluginURL() string {
|
||
urlP := p.mm.Configuration.GetConfig().ServiceSettings.SiteURL
|
||
url := "/"
|
||
if urlP != nil {
|
||
url = *urlP
|
||
}
|
||
if url[len(url)-1] == '/' {
|
||
url = url[0 : len(url)-1]
|
||
}
|
||
return url + "/plugins/" + manifest.Id
|
||
}
|
||
|
||
func (p *Plugin) getDialogURL() string {
|
||
return p.getPluginURL() + DialogPath
|
||
}
|