Merge branch 'master' into post-metadata

Этот коммит содержится в:
Harrison Healey
2018-11-16 09:34:35 -05:00
родитель 2113ad0205 7a8d7e53cc
Коммит 7aef759fd8
10 изменённых файлов: 294 добавлений и 41 удалений

Просмотреть файл

@@ -492,7 +492,7 @@ func (a *App) ImportUser(data *UserImportData, dryRun bool) *model.AppError {
if err != nil {
mlog.Error("Unable to open the profile image.", mlog.Any("err", err))
}
if err := a.SetProfileImageFromFile(savedUser.Id, file); err != nil {
if err := a.SetProfileImageFromMultiPartFile(savedUser.Id, file); err != nil {
mlog.Error("Unable to set the profile image from a file.", mlog.Any("err", err))
}
}

Просмотреть файл

@@ -4,6 +4,7 @@
package app
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
@@ -389,6 +390,20 @@ func (api *PluginAPI) GetProfileImage(userId string) ([]byte, *model.AppError) {
return data, err
}
func (api *PluginAPI) SetProfileImage(userId string, data []byte) *model.AppError {
_, err := api.app.GetUser(userId)
if err != nil {
return err
}
fileReader := bytes.NewReader(data)
err = api.app.SetProfileImageFromFile(userId, fileReader)
if err != nil {
return err
}
return nil
}
func (api *PluginAPI) GetEmojiList(sortBy string, page, perPage int) ([]*model.Emoji, *model.AppError) {
return api.app.GetEmojiList(page, perPage, sortBy)
}
@@ -438,6 +453,19 @@ func (api *PluginAPI) GetEmojiImage(emojiId string) ([]byte, string, *model.AppE
return api.app.GetEmojiImage(emojiId)
}
func (api *PluginAPI) GetTeamIcon(teamId string) ([]byte, *model.AppError) {
team, err := api.app.GetTeam(teamId)
if err != nil {
return nil, err
}
data, err := api.app.GetTeamIcon(team)
if err != nil {
return nil, err
}
return data, nil
}
// Plugin Section
func (api *PluginAPI) GetPlugins() ([]*model.Manifest, *model.AppError) {

Просмотреть файл

@@ -4,8 +4,12 @@
package app
import (
"bytes"
"encoding/json"
"fmt"
"image"
"image/color"
"image/png"
"io/ioutil"
"os"
"path/filepath"
@@ -220,6 +224,36 @@ func TestPluginAPIGetProfileImage(t *testing.T) {
require.Nil(t, data)
}
func TestPluginAPISetProfileImage(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
api := th.SetupPluginAPI()
// Create an 128 x 128 image
img := image.NewRGBA(image.Rect(0, 0, 128, 128))
// Draw a red dot at (2, 3)
img.Set(2, 3, color.RGBA{255, 0, 0, 255})
buf := new(bytes.Buffer)
err := png.Encode(buf, img)
require.Nil(t, err)
dataBytes := buf.Bytes()
// Set the user profile image
err = api.SetProfileImage(th.BasicUser.Id, dataBytes)
require.Nil(t, err)
// Get the user profile image to check
imageProfile, err := api.GetProfileImage(th.BasicUser.Id)
require.Nil(t, err)
require.NotEmpty(t, imageProfile)
colorful := color.NRGBA{255, 0, 0, 255}
byteReader := bytes.NewReader(imageProfile)
img2, _, err2 := image.Decode(byteReader)
require.Nil(t, err2)
require.Equal(t, img2.At(2, 3), colorful)
}
func TestPluginAPIGetPlugins(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
@@ -277,3 +311,34 @@ func TestPluginAPIGetPlugins(t *testing.T) {
assert.NotEmpty(t, plugins)
assert.Equal(t, pluginManifests, plugins)
}
func TestPluginAPIGetTeamIcon(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
api := th.SetupPluginAPI()
// Create an 128 x 128 image
img := image.NewRGBA(image.Rect(0, 0, 128, 128))
// Draw a red dot at (2, 3)
img.Set(2, 3, color.RGBA{255, 0, 0, 255})
buf := new(bytes.Buffer)
err := png.Encode(buf, img)
require.Nil(t, err)
dataBytes := buf.Bytes()
fileReader := bytes.NewReader(dataBytes)
// Set the Team Icon
err = th.App.SetTeamIconFromFile(th.BasicTeam, fileReader)
require.Nil(t, err)
// Get the team icon to check
imageProfile, err := api.GetTeamIcon(th.BasicTeam.Id)
require.Nil(t, err)
require.NotEmpty(t, imageProfile)
colorful := color.NRGBA{255, 0, 0, 255}
byteReader := bytes.NewReader(imageProfile)
img2, _, err2 := image.Decode(byteReader)
require.Nil(t, err2)
require.Equal(t, img2.At(2, 3), colorful)
}

Просмотреть файл

@@ -605,7 +605,7 @@ func (a *App) DeleteFlaggedPosts(postId string) {
}
func (a *App) DeletePostFiles(post *model.Post) {
if len(post.FileIds) != 0 {
if len(post.FileIds) == 0 {
return
}

Просмотреть файл

@@ -537,3 +537,48 @@ func TestMaxPostSize(t *testing.T) {
})
}
}
func TestDeletePostWithFileAttachments(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
// Create a post with a file attachment.
teamId := th.BasicTeam.Id
channelId := th.BasicChannel.Id
userId := th.BasicUser.Id
filename := "test"
data := []byte("abcd")
info1, err := th.App.DoUploadFile(time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamId, channelId, userId, filename, data)
if err != nil {
t.Fatal(err)
} else {
defer func() {
<-th.App.Srv.Store.FileInfo().PermanentDelete(info1.Id)
th.App.RemoveFile(info1.Path)
}()
}
post := &model.Post{
Message: "asd",
ChannelId: channelId,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: userId,
CreateAt: 0,
FileIds: []string{info1.Id},
}
post, err = th.App.CreatePost(post, th.BasicChannel, false)
assert.Nil(t, err)
// Delete the post.
post, err = th.App.DeletePost(post.Id, userId)
assert.Nil(t, err)
// Wait for the cleanup routine to finish.
time.Sleep(time.Millisecond * 100)
// Check that the file can no longer be reached.
_, err = th.App.GetFileInfo(info1.Id)
assert.NotNil(t, err)
}

Просмотреть файл

@@ -8,6 +8,7 @@ import (
"fmt"
"image"
"image/png"
"io"
"mime/multipart"
"net/http"
"net/url"
@@ -1093,10 +1094,10 @@ func (a *App) SetTeamIcon(teamId string, imageData *multipart.FileHeader) *model
return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.open.app_error", nil, err.Error(), http.StatusBadRequest)
}
defer file.Close()
return a.SetTeamIconFromFile(teamId, file)
return a.SetTeamIconFromMultiPartFile(teamId, file)
}
func (a *App) SetTeamIconFromFile(teamId string, file multipart.File) *model.AppError {
func (a *App) SetTeamIconFromMultiPartFile(teamId string, file multipart.File) *model.AppError {
team, getTeamErr := a.GetTeam(teamId)
if getTeamErr != nil {
@@ -1118,14 +1119,16 @@ func (a *App) SetTeamIconFromFile(teamId string, file multipart.File) *model.App
file.Seek(0, 0)
return a.SetTeamIconFromFile(team, file)
}
func (a *App) SetTeamIconFromFile(team *model.Team, file io.Reader) *model.AppError {
// Decode image into Image object
img, _, err := image.Decode(file)
if err != nil {
return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.decode.app_error", nil, err.Error(), http.StatusBadRequest)
}
file.Seek(0, 0)
orientation, _ := getImageOrientation(file)
img = makeImageUpright(img, orientation)
@@ -1139,7 +1142,7 @@ func (a *App) SetTeamIconFromFile(teamId string, file multipart.File) *model.App
return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.encode.app_error", nil, err.Error(), http.StatusInternalServerError)
}
path := "teams/" + teamId + "/teamIcon.png"
path := "teams/" + team.Id + "/teamIcon.png"
if _, err := a.WriteFile(buf, path); err != nil {
return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.write_file.app_error", nil, "", http.StatusInternalServerError)
@@ -1147,7 +1150,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 {
if result := <-a.Srv.Store.Team().UpdateLastTeamIconUpdate(team.Id, curTime); result.Err != nil {
return model.NewAppError("SetTeamIcon", "api.team.team_icon.update.app_error", nil, result.Err.Error(), http.StatusBadRequest)
}

Просмотреть файл

@@ -801,10 +801,10 @@ func (a *App) SetProfileImage(userId string, imageData *multipart.FileHeader) *m
return model.NewAppError("SetProfileImage", "api.user.upload_profile_user.open.app_error", nil, err.Error(), http.StatusBadRequest)
}
defer file.Close()
return a.SetProfileImageFromFile(userId, file)
return a.SetProfileImageFromMultiPartFile(userId, file)
}
func (a *App) SetProfileImageFromFile(userId string, file multipart.File) *model.AppError {
func (a *App) SetProfileImageFromMultiPartFile(userId string, file multipart.File) *model.AppError {
// Decode image config first to check dimensions before loading the whole thing into memory later on
config, _, err := image.DecodeConfig(file)
if err != nil {
@@ -816,14 +816,17 @@ func (a *App) SetProfileImageFromFile(userId string, file multipart.File) *model
file.Seek(0, 0)
return a.SetProfileImageFromFile(userId, file)
}
func (a *App) SetProfileImageFromFile(userId string, file io.Reader) *model.AppError {
// Decode image into Image object
img, _, err := image.Decode(file)
if err != nil {
return model.NewAppError("SetProfileImage", "api.user.upload_profile_user.decode.app_error", nil, err.Error(), http.StatusBadRequest)
}
file.Seek(0, 0)
orientation, _ := getImageOrientation(file)
img = makeImageUpright(img, orientation)

Просмотреть файл

@@ -64,6 +64,11 @@ type API interface {
// Minimum server version: 5.6
GetUsersInTeam(teamId string, page int, perPage int) ([]*model.User, *model.AppError)
// GetTeamIcon gets the Team Icon.
//
// Minimum server version: 5.6
GetTeamIcon(teamId string) ([]byte, *model.AppError)
// UpdateUser updates a user.
UpdateUser(user *model.User) (*model.User, *model.AppError)
@@ -264,6 +269,11 @@ type API interface {
// Minimum server version: 5.6
GetProfileImage(userId string) ([]byte, *model.AppError)
// SetProfileImage sets a user's profile image.
//
// Minimum server version: 5.6
SetProfileImage(userId string, data []byte) *model.AppError
// GetEmojiList returns a page of custom emoji on the system.
//
// The sortBy parameter can be: "name".

Просмотреть файл

@@ -887,6 +887,35 @@ func (s *apiRPCServer) GetUsersInTeam(args *Z_GetUsersInTeamArgs, returns *Z_Get
return nil
}
type Z_GetTeamIconArgs struct {
A string
}
type Z_GetTeamIconReturns struct {
A []byte
B *model.AppError
}
func (g *apiRPCClient) GetTeamIcon(teamId string) ([]byte, *model.AppError) {
_args := &Z_GetTeamIconArgs{teamId}
_returns := &Z_GetTeamIconReturns{}
if err := g.client.Call("Plugin.GetTeamIcon", _args, _returns); err != nil {
log.Printf("RPC call to GetTeamIcon API failed: %s", err.Error())
}
return _returns.A, _returns.B
}
func (s *apiRPCServer) GetTeamIcon(args *Z_GetTeamIconArgs, returns *Z_GetTeamIconReturns) error {
if hook, ok := s.impl.(interface {
GetTeamIcon(teamId string) ([]byte, *model.AppError)
}); ok {
returns.A, returns.B = hook.GetTeamIcon(args.A)
} else {
return encodableError(fmt.Errorf("API GetTeamIcon called but not implemented."))
}
return nil
}
type Z_UpdateUserArgs struct {
A *model.User
}
@@ -2461,6 +2490,35 @@ func (s *apiRPCServer) GetProfileImage(args *Z_GetProfileImageArgs, returns *Z_G
return nil
}
type Z_SetProfileImageArgs struct {
A string
B []byte
}
type Z_SetProfileImageReturns struct {
A *model.AppError
}
func (g *apiRPCClient) SetProfileImage(userId string, data []byte) *model.AppError {
_args := &Z_SetProfileImageArgs{userId, data}
_returns := &Z_SetProfileImageReturns{}
if err := g.client.Call("Plugin.SetProfileImage", _args, _returns); err != nil {
log.Printf("RPC call to SetProfileImage API failed: %s", err.Error())
}
return _returns.A
}
func (s *apiRPCServer) SetProfileImage(args *Z_SetProfileImageArgs, returns *Z_SetProfileImageReturns) error {
if hook, ok := s.impl.(interface {
SetProfileImage(userId string, data []byte) *model.AppError
}); ok {
returns.A = hook.SetProfileImage(args.A, args.B)
} else {
return encodableError(fmt.Errorf("API SetProfileImage called but not implemented."))
}
return nil
}
type Z_GetEmojiListArgs struct {
A string
B int
@@ -2756,35 +2814,6 @@ func (s *apiRPCServer) GetPlugins(args *Z_GetPluginsArgs, returns *Z_GetPluginsR
return nil
}
type Z_GetPluginStatusArgs struct {
A string
}
type Z_GetPluginStatusReturns struct {
A *model.PluginStatus
B *model.AppError
}
func (g *apiRPCClient) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
_args := &Z_GetPluginStatusArgs{id}
_returns := &Z_GetPluginStatusReturns{}
if err := g.client.Call("Plugin.GetPluginStatus", _args, _returns); err != nil {
log.Printf("RPC call to GetPluginStatus API failed: %s", err.Error())
}
return _returns.A, _returns.B
}
func (s *apiRPCServer) GetPluginStatus(args *Z_GetPluginStatusArgs, returns *Z_GetPluginStatusReturns) error {
if hook, ok := s.impl.(interface {
GetPluginStatus(id string) (*model.PluginStatus, *model.AppError)
}); ok {
returns.A, returns.B = hook.GetPluginStatus(args.A)
} else {
return encodableError(fmt.Errorf("API GetPluginStatus called but not implemented."))
}
return nil
}
type Z_EnablePluginArgs struct {
A string
}
@@ -2869,6 +2898,35 @@ func (s *apiRPCServer) RemovePlugin(args *Z_RemovePluginArgs, returns *Z_RemoveP
return nil
}
type Z_GetPluginStatusArgs struct {
A string
}
type Z_GetPluginStatusReturns struct {
A *model.PluginStatus
B *model.AppError
}
func (g *apiRPCClient) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
_args := &Z_GetPluginStatusArgs{id}
_returns := &Z_GetPluginStatusReturns{}
if err := g.client.Call("Plugin.GetPluginStatus", _args, _returns); err != nil {
log.Printf("RPC call to GetPluginStatus API failed: %s", err.Error())
}
return _returns.A, _returns.B
}
func (s *apiRPCServer) GetPluginStatus(args *Z_GetPluginStatusArgs, returns *Z_GetPluginStatusReturns) error {
if hook, ok := s.impl.(interface {
GetPluginStatus(id string) (*model.PluginStatus, *model.AppError)
}); ok {
returns.A, returns.B = hook.GetPluginStatus(args.A)
} else {
return encodableError(fmt.Errorf("API GetPluginStatus called but not implemented."))
}
return nil
}
type Z_KVSetArgs struct {
A string
B []byte

Просмотреть файл

@@ -1175,6 +1175,31 @@ func (_m *API) GetTeamByName(name string) (*model.Team, *model.AppError) {
return r0, r1
}
// GetTeamIcon provides a mock function with given fields: teamId
func (_m *API) GetTeamIcon(teamId string) ([]byte, *model.AppError) {
ret := _m.Called(teamId)
var r0 []byte
if rf, ok := ret.Get(0).(func(string) []byte); ok {
r0 = rf(teamId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string) *model.AppError); ok {
r1 = rf(teamId)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// GetTeamMember provides a mock function with given fields: teamId, userId
func (_m *API) GetTeamMember(teamId string, userId string) (*model.TeamMember, *model.AppError) {
ret := _m.Called(teamId, userId)
@@ -1835,6 +1860,22 @@ func (_m *API) SendEphemeralPost(userId string, post *model.Post) *model.Post {
return r0
}
// SetProfileImage provides a mock function with given fields: userId, data
func (_m *API) SetProfileImage(userId string, data []byte) *model.AppError {
ret := _m.Called(userId, data)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(string, []byte) *model.AppError); ok {
r0 = rf(userId, data)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// UnregisterCommand provides a mock function with given fields: teamId, trigger
func (_m *API) UnregisterCommand(teamId string, trigger string) error {
ret := _m.Called(teamId, trigger)