1476 lines
45 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"`
}
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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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.badgeAdminUserIDs, 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
}