1476 lines
45 KiB
Go
1476 lines
45 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"`
|
||
}
|
||
|
||
type CreateBadgeRequest struct {
|
||
Name string `json:"name"`
|
||
Description string `json:"description"`
|
||
Image string `json:"image"`
|
||
Type string `json:"type"`
|
||
Multiple bool `json:"multiple"`
|
||
ChannelID string `json:"channel_id"`
|
||
}
|
||
|
||
type UpdateBadgeRequest struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
Description string `json:"description"`
|
||
Image string `json:"image"`
|
||
Type string `json:"type"`
|
||
Multiple bool `json:"multiple"`
|
||
}
|
||
|
||
type CreateTypeRequest struct {
|
||
Name string `json:"name"`
|
||
EveryoneCanCreate bool `json:"everyone_can_create"`
|
||
EveryoneCanGrant bool `json:"everyone_can_grant"`
|
||
ChannelID string `json:"channel_id"`
|
||
}
|
||
|
||
type TypeWithBadgeCount struct {
|
||
*badgesmodel.BadgeTypeDefinition
|
||
BadgeCount int `json:"badge_count"`
|
||
}
|
||
|
||
type GetTypesResponse struct {
|
||
Types []TypeWithBadgeCount `json:"types"`
|
||
CanCreateType bool `json:"can_create_type"`
|
||
}
|
||
|
||
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)
|
||
apiRouter.HandleFunc("/getTypes", p.extractUserMiddleWare(p.getTypes, ResponseTypeJSON)).Methods(http.MethodGet)
|
||
apiRouter.HandleFunc("/createBadge", p.extractUserMiddleWare(p.apiCreateBadge, ResponseTypeJSON)).Methods(http.MethodPost)
|
||
apiRouter.HandleFunc("/createType", p.extractUserMiddleWare(p.apiCreateType, ResponseTypeJSON)).Methods(http.MethodPost)
|
||
apiRouter.HandleFunc("/updateBadge", p.extractUserMiddleWare(p.apiUpdateBadge, ResponseTypeJSON)).Methods(http.MethodPut)
|
||
apiRouter.HandleFunc("/deleteBadge/{badgeID}", p.extractUserMiddleWare(p.apiDeleteBadge, ResponseTypeJSON)).Methods(http.MethodDelete)
|
||
apiRouter.HandleFunc("/deleteType/{typeID}", p.extractUserMiddleWare(p.apiDeleteType, ResponseTypeJSON)).Methods(http.MethodDelete)
|
||
|
||
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) getTypes(w http.ResponseWriter, r *http.Request, userID string) {
|
||
u, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_user", Message: "Cannot get user", StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
types, err := p.filterCreateBadgeTypes(u)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_types", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
badges, err := p.store.GetRawBadges()
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_badges", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
badgeCountByType := map[badgesmodel.BadgeType]int{}
|
||
for _, badge := range badges {
|
||
badgeCountByType[badge.Type]++
|
||
}
|
||
|
||
result := make([]TypeWithBadgeCount, len(types))
|
||
for i, t := range types {
|
||
result[i] = TypeWithBadgeCount{
|
||
BadgeTypeDefinition: t,
|
||
BadgeCount: badgeCountByType[t.ID],
|
||
}
|
||
}
|
||
|
||
resp := GetTypesResponse{
|
||
Types: result,
|
||
CanCreateType: canCreateType(u, p.badgeAdminUserID, false),
|
||
}
|
||
|
||
b, _ := json.Marshal(resp)
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) apiCreateBadge(w http.ResponseWriter, r *http.Request, userID string) {
|
||
var req CreateBadgeRequest
|
||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_request", Message: "Invalid request body", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
user, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_user", Message: "Cannot get user", StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
req.Name = strings.TrimSpace(req.Name)
|
||
req.Description = strings.TrimSpace(req.Description)
|
||
req.Image = strings.TrimSpace(req.Image)
|
||
|
||
if req.Name == "" {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_name", Message: "Name is required", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
if length := len(req.Image); length > 1 && req.Image[0] == ':' && req.Image[length-1] == ':' {
|
||
req.Image = req.Image[1 : length-1]
|
||
}
|
||
if req.Image == "" {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_image", Message: "Emoji is required", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
t, err := p.store.GetType(badgesmodel.BadgeType(req.Type))
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "type_not_found", Message: "Badge type not found", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
if !canCreateBadge(user, p.badgeAdminUserID, t) {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "no_permission", Message: "No permission to create badge of this type", StatusCode: http.StatusForbidden,
|
||
})
|
||
return
|
||
}
|
||
|
||
toCreate := &badgesmodel.Badge{
|
||
Name: req.Name,
|
||
Description: req.Description,
|
||
Image: req.Image,
|
||
ImageType: badgesmodel.ImageTypeEmoji,
|
||
Multiple: req.Multiple,
|
||
Type: badgesmodel.BadgeType(req.Type),
|
||
CreatedBy: userID,
|
||
}
|
||
|
||
created, err := p.store.AddBadge(toCreate)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_create_badge", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
if req.ChannelID != "" {
|
||
T := p.getT(user.Locale)
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelID,
|
||
Message: T("badges.api.badge_created", "Значок `%s` создан.", toCreate.Name),
|
||
})
|
||
}
|
||
|
||
b, _ := json.Marshal(created)
|
||
w.WriteHeader(http.StatusCreated)
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) apiCreateType(w http.ResponseWriter, r *http.Request, userID string) {
|
||
var req CreateTypeRequest
|
||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_request", Message: "Invalid request body", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
user, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_user", Message: "Cannot get user", StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
if !canCreateType(user, p.badgeAdminUserID, false) {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "no_permission", Message: "No permission to create type", StatusCode: http.StatusForbidden,
|
||
})
|
||
return
|
||
}
|
||
|
||
req.Name = strings.TrimSpace(req.Name)
|
||
if req.Name == "" {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_name", Message: "Type name is required", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
toCreate := &badgesmodel.BadgeTypeDefinition{
|
||
Name: req.Name,
|
||
CreatedBy: userID,
|
||
}
|
||
toCreate.CanCreate.Everyone = req.EveryoneCanCreate
|
||
toCreate.CanGrant.Everyone = req.EveryoneCanGrant
|
||
|
||
created, err := p.store.AddType(toCreate)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_create_type", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
if req.ChannelID != "" {
|
||
T := p.getT(user.Locale)
|
||
p.mm.Post.SendEphemeralPost(userID, &model.Post{
|
||
UserId: p.BotUserID,
|
||
ChannelId: req.ChannelID,
|
||
Message: T("badges.api.type_created", "Тип `%s` создан.", toCreate.Name),
|
||
})
|
||
}
|
||
|
||
b, _ := json.Marshal(created)
|
||
w.WriteHeader(http.StatusCreated)
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) apiUpdateBadge(w http.ResponseWriter, r *http.Request, userID string) {
|
||
var req UpdateBadgeRequest
|
||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_request", Message: "Invalid request body", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
user, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_user", Message: "Cannot get user", StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
badge, err := p.store.GetBadge(badgesmodel.BadgeID(req.ID))
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "badge_not_found", Message: "Badge not found", StatusCode: http.StatusNotFound,
|
||
})
|
||
return
|
||
}
|
||
|
||
if !canEditBadge(user, p.badgeAdminUserID, badge) {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "no_permission", Message: "No permission to edit this badge", StatusCode: http.StatusForbidden,
|
||
})
|
||
return
|
||
}
|
||
|
||
req.Name = strings.TrimSpace(req.Name)
|
||
req.Description = strings.TrimSpace(req.Description)
|
||
req.Image = strings.TrimSpace(req.Image)
|
||
|
||
if req.Name == "" {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_name", Message: "Name is required", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
if length := len(req.Image); length > 1 && req.Image[0] == ':' && req.Image[length-1] == ':' {
|
||
req.Image = req.Image[1 : length-1]
|
||
}
|
||
if req.Image == "" {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "invalid_image", Message: "Emoji is required", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
badge.Name = req.Name
|
||
badge.Description = req.Description
|
||
badge.Image = req.Image
|
||
badge.Type = badgesmodel.BadgeType(req.Type)
|
||
badge.Multiple = req.Multiple
|
||
|
||
if err := p.store.UpdateBadge(badge); err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_update_badge", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
b, _ := json.Marshal(badge)
|
||
_, _ = w.Write(b)
|
||
}
|
||
|
||
func (p *Plugin) apiDeleteBadge(w http.ResponseWriter, r *http.Request, userID string) {
|
||
badgeID, ok := mux.Vars(r)["badgeID"]
|
||
if !ok {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "missing_badge_id", Message: "Missing badge ID", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
user, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_user", Message: "Cannot get user", StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
badge, err := p.store.GetBadge(badgesmodel.BadgeID(badgeID))
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "badge_not_found", Message: "Badge not found", StatusCode: http.StatusNotFound,
|
||
})
|
||
return
|
||
}
|
||
|
||
if !canEditBadge(user, p.badgeAdminUserID, badge) {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "no_permission", Message: "No permission to delete this badge", StatusCode: http.StatusForbidden,
|
||
})
|
||
return
|
||
}
|
||
|
||
if err := p.store.DeleteBadge(badgesmodel.BadgeID(badgeID)); err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_delete_badge", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
_, _ = w.Write([]byte(`{"success": true}`))
|
||
}
|
||
|
||
func (p *Plugin) apiDeleteType(w http.ResponseWriter, r *http.Request, userID string) {
|
||
typeID, ok := mux.Vars(r)["typeID"]
|
||
if !ok {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "missing_type_id", Message: "Missing type ID", StatusCode: http.StatusBadRequest,
|
||
})
|
||
return
|
||
}
|
||
|
||
user, err := p.mm.User.Get(userID)
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_get_user", Message: "Cannot get user", StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
t, err := p.store.GetType(badgesmodel.BadgeType(typeID))
|
||
if err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "type_not_found", Message: "Type not found", StatusCode: http.StatusNotFound,
|
||
})
|
||
return
|
||
}
|
||
|
||
if t.IsDefault {
|
||
T := p.getT("ru")
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_delete_default_type", Message: T("badges.api.cannot_delete_default_type", "Нельзя удалить тип по умолчанию"), StatusCode: http.StatusForbidden,
|
||
})
|
||
return
|
||
}
|
||
|
||
if !canEditType(user, p.badgeAdminUserID, t) {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "no_permission", Message: "No permission to delete this type", StatusCode: http.StatusForbidden,
|
||
})
|
||
return
|
||
}
|
||
|
||
if err := p.store.DeleteType(badgesmodel.BadgeType(typeID)); err != nil {
|
||
p.writeAPIError(w, &APIErrorResponse{
|
||
ID: "cannot_delete_type", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||
})
|
||
return
|
||
}
|
||
|
||
_, _ = w.Write([]byte(`{"success": true}`))
|
||
}
|
||
|
||
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) {
|
||
if originalType.IsDefault {
|
||
dialogError(w, T("badges.api.cannot_delete_default_type", "Нельзя удалить тип по умолчанию"), nil)
|
||
return
|
||
}
|
||
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
|
||
}
|