From 30011f67e88935f750bced6530e8ee92b352b7a3 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Mon, 30 Apr 2018 17:57:57 +0800 Subject: [MM-10354] Add feature to remove team icon (#8684) * set team.LastTeamIconUpdate to 0 when removing team icon * add APIv4 for removing team icon * removed comment and updated typo on AppError --- api4/team.go | 21 ++++++++ api4/team_test.go | 37 ++++++++++++++ app/team.go | 20 +++++++- i18n/en.json | 8 +++ model/client4.go | 116 +++++++++++++++++++++++-------------------- store/sqlstore/team_store.go | 1 - 6 files changed, 148 insertions(+), 55 deletions(-) diff --git a/api4/team.go b/api4/team.go index 94035a770..023289579 100644 --- a/api4/team.go +++ b/api4/team.go @@ -32,6 +32,7 @@ func (api *API) InitTeam() { api.BaseRoutes.Team.Handle("/image", api.ApiSessionRequiredTrustRequester(getTeamIcon)).Methods("GET") api.BaseRoutes.Team.Handle("/image", api.ApiSessionRequired(setTeamIcon)).Methods("POST") + api.BaseRoutes.Team.Handle("/image", api.ApiSessionRequired(removeTeamIcon)).Methods("DELETE") api.BaseRoutes.TeamMembers.Handle("", api.ApiSessionRequired(getTeamMembers)).Methods("GET") api.BaseRoutes.TeamMembers.Handle("/ids", api.ApiSessionRequired(getTeamMembersByIds)).Methods("POST") @@ -812,3 +813,23 @@ func setTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("") ReturnStatusOK(w) } + +func removeTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireTeamId() + if c.Err != nil { + return + } + + if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) { + c.SetPermissionError(model.PERMISSION_MANAGE_TEAM) + return + } + + if err := c.App.RemoveTeamIcon(c.Params.TeamId); err != nil { + c.Err = err + return + } + + c.LogAudit("") + ReturnStatusOK(w) +} diff --git a/api4/team_test.go b/api4/team_test.go index cdf201771..705ff603b 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -2015,3 +2015,40 @@ func TestGetTeamIcon(t *testing.T) { _, resp = Client.GetTeamIcon(team.Id, "") CheckUnauthorizedStatus(t, resp) } + +func TestRemoveTeamIcon(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + Client := th.Client + team := th.BasicTeam + + th.LoginTeamAdmin() + data, _ := readTestFile("test.png") + Client.SetTeamIcon(team.Id, data) + + _, resp := Client.RemoveTeamIcon(team.Id) + CheckNoError(t, resp) + teamAfter, _ := th.App.GetTeam(team.Id) + if teamAfter.LastTeamIconUpdate != 0 { + t.Fatal("should update LastTeamIconUpdate to 0") + } + + Client.SetTeamIcon(team.Id, data) + + _, resp = th.SystemAdminClient.RemoveTeamIcon(team.Id) + CheckNoError(t, resp) + teamAfter, _ = th.App.GetTeam(team.Id) + if teamAfter.LastTeamIconUpdate != 0 { + t.Fatal("should update LastTeamIconUpdate to 0") + } + + Client.SetTeamIcon(team.Id, data) + Client.Logout() + + _, resp = Client.RemoveTeamIcon(team.Id) + CheckUnauthorizedStatus(t, resp) + + th.LoginBasic() + _, resp = Client.RemoveTeamIcon(team.Id) + CheckForbiddenStatus(t, resp) +} diff --git a/app/team.go b/app/team.go index 78d932870..f5235792f 100644 --- a/app/team.go +++ b/app/team.go @@ -103,6 +103,7 @@ func (a *App) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { oldTeam.AllowOpenInvite = team.AllowOpenInvite oldTeam.CompanyName = team.CompanyName oldTeam.AllowedDomains = team.AllowedDomains + oldTeam.LastTeamIconUpdate = team.LastTeamIconUpdate if result := <-a.Srv.Store.Team().Update(oldTeam); result.Err != nil { return nil, result.Err @@ -1007,7 +1008,7 @@ func (a *App) SetTeamIconFromFile(teamId string, file multipart.File) *model.App curTime := model.GetMillis() if result := <-a.Srv.Store.Team().UpdateLastTeamIconUpdate(teamId, curTime); result.Err != nil { - return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.update.app_error", nil, result.Err.Error(), http.StatusBadRequest) + return model.NewAppError("SetTeamIcon", "api.team.team_icon.update.app_error", nil, result.Err.Error(), http.StatusBadRequest) } // manually set time to avoid possible cluster inconsistencies @@ -1017,3 +1018,20 @@ func (a *App) SetTeamIconFromFile(teamId string, file multipart.File) *model.App return nil } + +func (a *App) RemoveTeamIcon(teamId string) *model.AppError { + team, err := a.GetTeam(teamId) + if err != nil { + return model.NewAppError("RemoveTeamIcon", "api.team.remove_team_icon.get_team.app_error", nil, err.Error(), http.StatusBadRequest) + } + + if result := <-a.Srv.Store.Team().UpdateLastTeamIconUpdate(teamId, 0); result.Err != nil { + return model.NewAppError("RemoveTeamIcon", "api.team.team_icon.update.app_error", nil, result.Err.Error(), http.StatusBadRequest) + } + + team.LastTeamIconUpdate = 0 + + a.sendTeamEvent(team, model.WEBSOCKET_EVENT_UPDATE_TEAM) + + return nil +} diff --git a/i18n/en.json b/i18n/en.json index 172ea47b1..3825ad3e2 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2458,6 +2458,10 @@ "id": "api.team.set_team_icon.encode.app_error", "translation": "Could not encode team icon" }, + { + "id": "api.team.remove_team_icon.get_team.app_error", + "translation": "An error occurred getting the team" + }, { "id": "api.team.set_team_icon.get_team.app_error", "translation": "An error occurred getting the team" @@ -2482,6 +2486,10 @@ "id": "api.team.set_team_icon.too_large.app_error", "translation": "Unable to upload team icon. File is too large." }, + { + "id": "api.team.team_icon.update.app_error", + "translation": "An error occurred updating the team icon" + }, { "id": "api.team.set_team_icon.write_file.app_error", "translation": "Could not save team icon" diff --git a/model/client4.go b/model/client4.go index 387ca038f..cf34c9fd7 100644 --- a/model/client4.go +++ b/model/client4.go @@ -1459,6 +1459,69 @@ func (c *Client4) GetTeamInviteInfo(inviteId string) (*Team, *Response) { } } +// SetTeamIcon sets team icon of the team +func (c *Client4) SetTeamIcon(teamId string, data []byte) (bool, *Response) { + + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("image", "teamIcon.png"); err != nil { + return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if err := writer.Close(); err != nil { + return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.writer.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetTeamRoute(teamId)+"/image", bytes.NewReader(body.Bytes())) + rq.Header.Set("Content-Type", writer.FormDataContentType()) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { + // set to http.StatusForbidden(403) + return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetTeamRoute(teamId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)} + } else { + defer closeBody(rp) + + if rp.StatusCode >= 300 { + return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else { + return CheckStatusOK(rp), BuildResponse(rp) + } + } +} + +// GetTeamIcon gets the team icon of the team +func (c *Client4) GetTeamIcon(teamId, etag string) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetTeamRoute(teamId)+"/image", etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("GetTeamIcon", "model.client.get_team_icon.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// RemoveTeamIcon updates LastTeamIconUpdate to 0 which indicates team icon is removed. +func (c *Client4) RemoveTeamIcon(teamId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "/image"); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // Channel Section // CreateChannel creates a channel based on the provided channel struct. @@ -3442,56 +3505,3 @@ func (c *Client4) DeactivatePlugin(id string) (bool, *Response) { return CheckStatusOK(r), BuildResponse(r) } } - -// SetTeamIcon sets team icon of the team -func (c *Client4) SetTeamIcon(teamId string, data []byte) (bool, *Response) { - - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - if part, err := writer.CreateFormFile("image", "teamIcon.png"); err != nil { - return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if err := writer.Close(); err != nil { - return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetTeamRoute(teamId)+"/image", bytes.NewReader(body.Bytes())) - rq.Header.Set("Content-Type", writer.FormDataContentType()) - rq.Close = true - - if len(c.AuthToken) > 0 { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { - // set to http.StatusForbidden(403) - return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetTeamRoute(teamId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)} - } else { - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } else { - return CheckStatusOK(rp), BuildResponse(rp) - } - } -} - -// GetTeamIcon gets the team icon of the team -func (c *Client4) GetTeamIcon(teamId, etag string) ([]byte, *Response) { - if r, err := c.DoApiGet(c.GetTeamRoute(teamId)+"/image", etag); err != nil { - return nil, BuildErrorResponse(r, err) - } else { - defer closeBody(r) - - if data, err := ioutil.ReadAll(r.Body); err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetTeamIcon", "model.client.get_team_icon.app_error", nil, err.Error(), r.StatusCode)) - } else { - return data, BuildResponse(r) - } - } -} diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index 6528b8e4c..ad27675ce 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -99,7 +99,6 @@ func (s SqlTeamStore) Update(team *model.Team) store.StoreChannel { team.CreateAt = oldTeam.CreateAt team.UpdateAt = model.GetMillis() team.Name = oldTeam.Name - team.LastTeamIconUpdate = oldTeam.LastTeamIconUpdate if count, err := s.GetMaster().Update(team); err != nil { result.Err = model.NewAppError("SqlTeamStore.Update", "store.sql_team.update.updating.app_error", nil, "id="+team.Id+", "+err.Error(), http.StatusInternalServerError) -- cgit v1.2.3-1-g7c22