MM-32882: Add an unread badge/marker to the Main Menu icon and the ‘Plugin Marketplace’ Menu Item (#16992)

This commit is contained in:
catalintomai
2021-03-29 22:55:44 -07:00
committed by GitHub
parent 16e6296c54
commit ca9f4c9ed8
6 changed files with 86 additions and 0 deletions

View File

@@ -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()))
}

View File

@@ -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."

View 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"

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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"}