mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
MM-32882: Add an unread badge/marker to the Main Menu icon and the ‘Plugin Marketplace’ Menu Item (#16992)
This commit is contained in:
@@ -16,6 +16,8 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/audit"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/store"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -38,6 +40,9 @@ func (api *API) InitPlugin() {
|
||||
api.BaseRoutes.Plugins.Handle("/webapp", api.ApiHandler(getWebappPlugins)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.Plugins.Handle("/marketplace", api.ApiSessionRequired(getMarketplacePlugins)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.Plugins.Handle("/marketplace/first_admin_visit", api.ApiHandler(setFirstAdminVisitMarketplaceStatus)).Methods("POST")
|
||||
api.BaseRoutes.Plugins.Handle("/marketplace/first_admin_visit", api.ApiSessionRequired(getFirstAdminVisitMarketplaceStatus)).Methods("GET")
|
||||
}
|
||||
|
||||
func uploadPlugin(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -383,3 +388,61 @@ func installPlugin(c *Context, w http.ResponseWriter, plugin io.ReadSeeker, forc
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte(manifest.ToJson()))
|
||||
}
|
||||
|
||||
func setFirstAdminVisitMarketplaceStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec := c.MakeAuditRecord("setFirstAdminVisitMarketplaceStatus", audit.Fail)
|
||||
defer c.LogAuditRec(auditRec)
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
firstAdminVisitMarketplaceObj := model.System{
|
||||
Name: model.SYSTEM_FIRST_ADMIN_VISIT_MARKETPLACE,
|
||||
Value: "true",
|
||||
}
|
||||
|
||||
if err := c.App.Srv().Store.System().SaveOrUpdate(&firstAdminVisitMarketplaceObj); err != nil {
|
||||
c.Err = model.NewAppError("setFirstAdminVisitMarketplaceStatus", "api.error_set_first_admin_visit_marketplace_status", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, "", "", "", nil)
|
||||
message.Add("firstAdminVisitMarketplaceStatus", firstAdminVisitMarketplaceObj.Value)
|
||||
c.App.Publish(message)
|
||||
|
||||
auditRec.Success()
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func getFirstAdminVisitMarketplaceStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec := c.MakeAuditRecord("getFirstAdminVisitMarketplaceStatus", audit.Fail)
|
||||
defer c.LogAuditRec(auditRec)
|
||||
c.LogAudit("attempt")
|
||||
|
||||
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
firstAdminVisitMarketplaceObj, err := c.App.Srv().Store.System().GetByName(model.SYSTEM_FIRST_ADMIN_VISIT_MARKETPLACE)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
firstAdminVisitMarketplaceObj = &model.System{
|
||||
Name: model.SYSTEM_FIRST_ADMIN_VISIT_MARKETPLACE,
|
||||
Value: "false",
|
||||
}
|
||||
default:
|
||||
c.Err = model.NewAppError("getFirstAdminVisitMarketplaceStatus", "api.error_get_first_admin_visit_marketplace_status", nil, err.Error(), http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
auditRec.Success()
|
||||
w.Write([]byte(firstAdminVisitMarketplaceObj.ToJson()))
|
||||
}
|
||||
|
||||
@@ -1376,6 +1376,14 @@
|
||||
"id": "api.emoji.upload.open.app_error",
|
||||
"translation": "Unable to create the emoji. An error occurred when trying to open the attached image."
|
||||
},
|
||||
{
|
||||
"id": "api.error_get_first_admin_visit_marketplace_status",
|
||||
"translation": "Error trying to retrieve the first admin visit marketplace status from the store."
|
||||
},
|
||||
{
|
||||
"id": "api.error_set_first_admin_visit_marketplace_status",
|
||||
"translation": "Error trying to save the first admin visit marketplace status in the store."
|
||||
},
|
||||
{
|
||||
"id": "api.export.export_not_found.app_error",
|
||||
"translation": "Unable to find export file."
|
||||
|
||||
@@ -32,6 +32,7 @@ const (
|
||||
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500 = "warn_metric_number_of_active_users_500"
|
||||
SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M = "warn_metric_number_of_posts_2M"
|
||||
SYSTEM_WARN_METRIC_LAST_RUN_TIMESTAMP_KEY = "LastWarnMetricRunTimestamp"
|
||||
SYSTEM_FIRST_ADMIN_VISIT_MARKETPLACE = "FirstAdminVisitMarketplace"
|
||||
AWS_METERING_REPORT_INTERVAL = 1
|
||||
AWS_METERING_DIMENSION_USAGE_HRS = "UsageHrs"
|
||||
USER_LIMIT_OVERAGE_CYCLE_END_DATE = "UserLimitOverageCycleEndDate"
|
||||
|
||||
@@ -73,6 +73,7 @@ const (
|
||||
WEBSOCKET_EVENT_THREAD_UPDATED = "thread_updated"
|
||||
WEBSOCKET_EVENT_THREAD_FOLLOW_CHANGED = "thread_follow_changed"
|
||||
WEBSOCKET_EVENT_THREAD_READ_CHANGED = "thread_read_changed"
|
||||
WEBSOCKET_FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED = "first_admin_visit_marketplace_status_received"
|
||||
)
|
||||
|
||||
type WebSocketMessage interface {
|
||||
|
||||
@@ -6,6 +6,7 @@ package sqlstore
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -99,6 +100,9 @@ func (s SqlSystemStore) Get() (model.StringMap, error) {
|
||||
func (s SqlSystemStore) GetByName(name string) (*model.System, error) {
|
||||
var system model.System
|
||||
if err := s.GetMaster().SelectOne(&system, "SELECT * FROM Systems WHERE Name = :Name", map[string]interface{}{"Name": name}); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, store.NewErrNotFound("System", fmt.Sprintf("name=%s", system.Name))
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed to get system property with name=%s", system.Name)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ func TestSystemStore(t *testing.T, ss store.Store) {
|
||||
testInsertIfExists(t, ss)
|
||||
})
|
||||
t.Run("SaveOrUpdateWithWarnMetricHandling", func(t *testing.T) { testSystemStoreSaveOrUpdateWithWarnMetricHandling(t, ss) })
|
||||
t.Run("GetByNameNoEntries", func(t *testing.T) { testSystemStoreGetByNameNoEntries(t, ss) })
|
||||
}
|
||||
|
||||
func testSystemStore(t *testing.T, ss store.Store) {
|
||||
@@ -83,6 +84,14 @@ func testSystemStoreSaveOrUpdateWithWarnMetricHandling(t *testing.T, ss store.St
|
||||
assert.Equal(t, val1, val2)
|
||||
}
|
||||
|
||||
func testSystemStoreGetByNameNoEntries(t *testing.T, ss store.Store) {
|
||||
res, nErr := ss.System().GetByName(model.SYSTEM_FIRST_ADMIN_VISIT_MARKETPLACE)
|
||||
_, ok := nErr.(*store.ErrNotFound)
|
||||
require.Error(t, nErr)
|
||||
assert.True(t, ok)
|
||||
assert.Nil(t, res)
|
||||
}
|
||||
|
||||
func testSystemStorePermanentDeleteByName(t *testing.T, ss store.Store) {
|
||||
s1 := &model.System{Name: model.NewId(), Value: "value"}
|
||||
s2 := &model.System{Name: model.NewId(), Value: "value"}
|
||||
|
||||
Reference in New Issue
Block a user