From ca9f4c9ed8bc879283545ed5a5e197d6aede2c5b Mon Sep 17 00:00:00 2001 From: catalintomai <56169943+catalintomai@users.noreply.github.com> Date: Mon, 29 Mar 2021 22:55:44 -0700 Subject: [PATCH] =?UTF-8?q?MM-32882:=20Add=20an=20unread=20badge/marker=20?= =?UTF-8?q?to=20the=20Main=20Menu=20icon=20and=20the=20=E2=80=98Plugin=20M?= =?UTF-8?q?arketplace=E2=80=99=20Menu=20Item=20(#16992)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api4/plugin.go | 63 +++++++++++++++++++++++++++++++++ i18n/en.json | 8 +++++ model/system.go | 1 + model/websocket_message.go | 1 + store/sqlstore/system_store.go | 4 +++ store/storetest/system_store.go | 9 +++++ 6 files changed, 86 insertions(+) diff --git a/api4/plugin.go b/api4/plugin.go index 521376755c..8ab71803e6 100644 --- a/api4/plugin.go +++ b/api4/plugin.go @@ -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())) +} diff --git a/i18n/en.json b/i18n/en.json index 8a4aa12c31..9ce2c51a43 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -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." diff --git a/model/system.go b/model/system.go index 7f7742404c..088990b3eb 100644 --- a/model/system.go +++ b/model/system.go @@ -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" diff --git a/model/websocket_message.go b/model/websocket_message.go index 3d667725c5..9a845d36bd 100644 --- a/model/websocket_message.go +++ b/model/websocket_message.go @@ -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 { diff --git a/store/sqlstore/system_store.go b/store/sqlstore/system_store.go index f1fca00f9b..b14c0c0549 100644 --- a/store/sqlstore/system_store.go +++ b/store/sqlstore/system_store.go @@ -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) } diff --git a/store/storetest/system_store.go b/store/storetest/system_store.go index 8d5482e888..c3731c2ddf 100644 --- a/store/storetest/system_store.go +++ b/store/storetest/system_store.go @@ -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"}