1076 lines
33 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}