From 0cdad1f41d8cc2002e7c265702099dcd4e418547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 1 Feb 2023 15:30:14 +0100 Subject: [PATCH 1/9] MM-48246 - ab test open RHS with linked board; add telemetry info --- .../webapp/src/components/rhsChannelBoardItem.tsx | 13 ++++++++++++- webapp/src/telemetry/telemetryClient.ts | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx index 5f60e71a8..43ee7841d 100644 --- a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx +++ b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx @@ -6,6 +6,7 @@ import {FormattedMessage, useIntl} from 'react-intl' import mutator from '../../../../webapp/src/mutator' import {Utils} from '../../../../webapp/src/utils' import {getCurrentTeam} from '../../../../webapp/src/store/teams' +import {getCurrentChannel} from '../../../../webapp/src/store/channels' import {createBoard, Board} from '../../../../webapp/src/blocks/board' import {useAppSelector} from '../../../../webapp/src/store/hooks' import IconButton from '../../../../webapp/src/widgets/buttons/iconButton' @@ -17,8 +18,10 @@ import CompassIcon from '../../../../webapp/src/widgets/icons/compassIcon' import {Permission} from '../../../../webapp/src/constants' -import './rhsChannelBoardItem.scss' import BoardPermissionGate from '../../../../webapp/src/components/permissions/boardPermissionGate' +import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../../../../webapp/src/telemetry/telemetryClient' + +import './rhsChannelBoardItem.scss' const windowAny = (window as SuiteWindow) @@ -35,7 +38,15 @@ const RHSChannelBoardItem = (props: Props) => { return null } + const currentChannel = useAppSelector(getCurrentChannel) + if (!currentChannel) { + return null + } + const handleBoardClicked = (boardID: string) => { + // send the telemetry information for the clicked board + TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, {teamID: team.id, channelID: currentChannel.id}) + window.open(`${windowAny.frontendBaseURL}/team/${team.id}/${boardID}`, '_blank', 'noopener') } diff --git a/webapp/src/telemetry/telemetryClient.ts b/webapp/src/telemetry/telemetryClient.ts index 460e9d247..1787315da 100644 --- a/webapp/src/telemetry/telemetryClient.ts +++ b/webapp/src/telemetry/telemetryClient.ts @@ -50,6 +50,7 @@ export const TelemetryActions = { LimitCardLimitReached: 'limit_cardLimitReached', LimitCardLimitLinkOpen: 'limit_cardLimitLinkOpen', VersionMoreInfo: 'version_more_info', + ClickChannelsRHSBoard: 'click_channels_RHS_board', } interface IEventProps { From b4ea125a63ab10133ad3f97849c7687dd7bf5397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 1 Feb 2023 18:53:02 +0100 Subject: [PATCH 2/9] add board info --- .../webapp/src/components/rhsChannelBoardItem.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx index 43ee7841d..e796af1e5 100644 --- a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx +++ b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx @@ -45,7 +45,8 @@ const RHSChannelBoardItem = (props: Props) => { const handleBoardClicked = (boardID: string) => { // send the telemetry information for the clicked board - TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, {teamID: team.id, channelID: currentChannel.id}) + const extraData = {teamID: team.id, channelID: currentChannel.id, board: boardID} + TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, extraData) window.open(`${windowAny.frontendBaseURL}/team/${team.id}/${boardID}`, '_blank', 'noopener') } From 4030866a286c77ee14de1961f215a3336a9c498a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Mon, 6 Feb 2023 14:31:25 +0100 Subject: [PATCH 3/9] remove innecessary telemetry channel information --- .../webapp/src/components/rhsChannelBoardItem.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx index e796af1e5..1f03dc003 100644 --- a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx +++ b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx @@ -6,7 +6,6 @@ import {FormattedMessage, useIntl} from 'react-intl' import mutator from '../../../../webapp/src/mutator' import {Utils} from '../../../../webapp/src/utils' import {getCurrentTeam} from '../../../../webapp/src/store/teams' -import {getCurrentChannel} from '../../../../webapp/src/store/channels' import {createBoard, Board} from '../../../../webapp/src/blocks/board' import {useAppSelector} from '../../../../webapp/src/store/hooks' import IconButton from '../../../../webapp/src/widgets/buttons/iconButton' @@ -38,14 +37,9 @@ const RHSChannelBoardItem = (props: Props) => { return null } - const currentChannel = useAppSelector(getCurrentChannel) - if (!currentChannel) { - return null - } - const handleBoardClicked = (boardID: string) => { // send the telemetry information for the clicked board - const extraData = {teamID: team.id, channelID: currentChannel.id, board: boardID} + const extraData = {teamID: team.id, board: boardID} TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, extraData) window.open(`${windowAny.frontendBaseURL}/team/${team.id}/${boardID}`, '_blank', 'noopener') From 5b1c53dffa7bb70fbaeb2fe6040a23df12706037 Mon Sep 17 00:00:00 2001 From: Benjamin Cooke Date: Wed, 22 Feb 2023 16:21:12 -0500 Subject: [PATCH 4/9] updating date when prop has changed --- webapp/src/properties/date/date.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webapp/src/properties/date/date.tsx b/webapp/src/properties/date/date.tsx index 23d764a1b..e2c285b5e 100644 --- a/webapp/src/properties/date/date.tsx +++ b/webapp/src/properties/date/date.tsx @@ -1,6 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useMemo, useState, useCallback} from 'react' +import React, {useMemo, useState, useCallback, useEffect} from 'react' import {useIntl} from 'react-intl' import {DateUtils} from 'react-day-picker' import MomentLocaleUtils from 'react-day-picker/moment' @@ -58,6 +58,12 @@ function DateRange(props: PropertyProps): JSX.Element { const [value, setValue] = useState(propertyValue) const intl = useIntl() + useEffect(() => { + if (value !== propertyValue) { + setValue(propertyValue) + } + }, [propertyValue, setValue]) + const onChange = useCallback((newValue) => { if (value !== newValue) { setValue(newValue) From ae44d6f2bfc78d36e9060a9c52ac8945695822c9 Mon Sep 17 00:00:00 2001 From: Benjamin Cooke Date: Thu, 23 Feb 2023 14:27:52 -0500 Subject: [PATCH 5/9] unit test --- .../date/__snapshots__/date.test.tsx.snap | 17 ++++++++++ webapp/src/properties/date/date.test.tsx | 32 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/webapp/src/properties/date/__snapshots__/date.test.tsx.snap b/webapp/src/properties/date/__snapshots__/date.test.tsx.snap index 861e203c5..231f47e8a 100644 --- a/webapp/src/properties/date/__snapshots__/date.test.tsx.snap +++ b/webapp/src/properties/date/__snapshots__/date.test.tsx.snap @@ -34,6 +34,23 @@ exports[`properties/dateRange handle clear 1`] = ` `; +exports[`properties/dateRange returns component with new date after prop change 1`] = ` +
+
+ +
+
+`; + exports[`properties/dateRange returns default correctly 1`] = `
{ expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, JSON.stringify({from: today})) }) + + test('returns component with new date after prop change', () => { + const component = wrapIntl( + , + ) + + const {container, rerender} = render(component) + + rerender( + wrapIntl( + + ) + ) + + expect(container).toMatchSnapshot() + }) }) From 30c9da952efa2aff4c2c1e817cdddcb2ba1bbc09 Mon Sep 17 00:00:00 2001 From: Benjamin Cooke Date: Thu, 23 Feb 2023 15:35:04 -0500 Subject: [PATCH 6/9] lint --- webapp/src/properties/date/date.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/src/properties/date/date.test.tsx b/webapp/src/properties/date/date.test.tsx index d1989425b..dfabdf706 100644 --- a/webapp/src/properties/date/date.test.tsx +++ b/webapp/src/properties/date/date.test.tsx @@ -315,7 +315,7 @@ describe('properties/dateRange', () => { expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, JSON.stringify({from: today})) }) - + test('returns component with new date after prop change', () => { const component = wrapIntl( { board={{...board}} card={{...card}} propertyTemplate={propertyTemplate} - /> - ) + />, + ), ) expect(container).toMatchSnapshot() From 8f3407291d7eee1864169d3b792f653b9b9bb3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 1 Mar 2023 00:34:43 +0100 Subject: [PATCH 7/9] update telemetry id Co-authored-by: Maria A Nunez --- webapp/src/telemetry/telemetryClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/telemetry/telemetryClient.ts b/webapp/src/telemetry/telemetryClient.ts index 1787315da..c722929ff 100644 --- a/webapp/src/telemetry/telemetryClient.ts +++ b/webapp/src/telemetry/telemetryClient.ts @@ -50,7 +50,7 @@ export const TelemetryActions = { LimitCardLimitReached: 'limit_cardLimitReached', LimitCardLimitLinkOpen: 'limit_cardLimitLinkOpen', VersionMoreInfo: 'version_more_info', - ClickChannelsRHSBoard: 'click_channels_RHS_board', + ClickChannelsRHSBoard: 'click_board_in_channels_RHS', } interface IEventProps { From d16bbcbe10fba7d525c836c7c84d3cacbe4eb3c1 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 6 Mar 2023 08:48:30 -0700 Subject: [PATCH 8/9] Fix table multiperson (#4613) * clear overflow on multiperson * clear overflow on multiperson --- webapp/src/components/table/table.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/src/components/table/table.scss b/webapp/src/components/table/table.scss index bbc9ef118..6d36a7215 100644 --- a/webapp/src/components/table/table.scss +++ b/webapp/src/components/table/table.scss @@ -203,6 +203,7 @@ width: inherit; } + .MultiPerson.octo-propertyvalue, .Person.octo-propertyvalue, .DateRange.octo-propertyvalue { overflow: unset; From fa72286427d0d11bc10638625ad581795cfbc46f Mon Sep 17 00:00:00 2001 From: Harshil Sharma <18575143+harshilsharma63@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:21:53 +0530 Subject: [PATCH 9/9] Board teamless file path (#4577) * Updated upload and donwload code * Removed archived file handling as we no longer have cloud limits * Fixed server lint * Restored unused * CI * Added new tests * Fixed integration tests * Added Path column for personal server's FileInfo table * Removed sophesticated, elegant debug logs --------- Co-authored-by: Mattermost Build --- server/api/api.go | 2 +- server/api/files.go | 27 +----- server/app/files.go | 41 ++++++++- server/app/files_test.go | 90 +++++++++++++++++-- server/client/client.go | 9 ++ server/services/store/sqlstore/file.go | 4 + .../000039_add_path_to_file_info.down.sql | 1 + .../000039_add_path_to_file_info.up.sql | 1 + server/utils/utils.go | 5 ++ 9 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql create mode 100644 server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql diff --git a/server/api/api.go b/server/api/api.go index ec376a9c0..be0287257 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -227,7 +227,7 @@ func jsonStringResponse(w http.ResponseWriter, code int, message string) { //nol fmt.Fprint(w, message) } -func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { +func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { //nolint:unparam setResponseHeader(w, "Content-Type", "application/json") w.WriteHeader(code) _, _ = w.Write(json) diff --git a/server/api/files.go b/server/api/files.go index d9f6aa109..bb0ddcfc7 100644 --- a/server/api/files.go +++ b/server/api/files.go @@ -123,37 +123,12 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { auditRec.AddMeta("teamID", board.TeamID) auditRec.AddMeta("filename", filename) - fileInfo, err := a.app.GetFileInfo(filename) + fileInfo, fileReader, err := a.app.GetFile(board.TeamID, boardID, filename) if err != nil && !model.IsErrNotFound(err) { a.errorResponse(w, r, err) return } - if fileInfo != nil && fileInfo.Archived { - fileMetadata := map[string]interface{}{ - "archived": true, - "name": fileInfo.Name, - "size": fileInfo.Size, - "extension": fileInfo.Extension, - } - - data, jsonErr := json.Marshal(fileMetadata) - if jsonErr != nil { - a.logger.Error("failed to marshal archived file metadata", mlog.String("filename", filename), mlog.Err(jsonErr)) - a.errorResponse(w, r, jsonErr) - return - } - - jsonBytesResponse(w, http.StatusBadRequest, data) - return - } - - fileReader, err := a.app.GetFileReader(board.TeamID, boardID, filename) - if err != nil && !errors.Is(err, app.ErrFileNotFound) { - a.errorResponse(w, r, err) - return - } - if errors.Is(err, app.ErrFileNotFound) && board.ChannelID != "" { // prior to moving from workspaces to teams, the filepath was constructed from // workspaceID, which is the channel ID in plugin mode. diff --git a/server/app/files.go b/server/app/files.go index 081b78e14..1474a291a 100644 --- a/server/app/files.go +++ b/server/app/files.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/mattermost/focalboard/server/model" mmModel "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/focalboard/server/utils" @@ -28,7 +29,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin createdFilename := utils.NewID(utils.IDTypeNone) fullFilename := fmt.Sprintf(`%s%s`, createdFilename, fileExtension) - filePath := filepath.Join(teamID, rootID, fullFilename) + filePath := filepath.Join(utils.GetBaseFilePath(), fullFilename) fileSize, appErr := a.filesBackend.WriteFile(reader, filePath) if appErr != nil { @@ -45,7 +46,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin CreateAt: now, UpdateAt: now, DeleteAt: 0, - Path: emptyString, + Path: filePath, ThumbnailPath: emptyString, PreviewPath: emptyString, Name: filename, @@ -59,6 +60,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin Content: "", RemoteId: nil, } + err := a.store.SaveFileInfo(fileInfo) if err != nil { return "", err @@ -77,6 +79,7 @@ func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) { // will be the fileinfo id. parts := strings.Split(filename, ".") fileInfoID := parts[0][1:] + fileInfo, err := a.store.GetFileInfo(fileInfoID) if err != nil { return nil, err @@ -85,6 +88,40 @@ func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) { return fileInfo, nil } +func (a *App) GetFile(teamID, rootID, fileName string) (*mmModel.FileInfo, filestore.ReadCloseSeeker, error) { + fileInfo, err := a.GetFileInfo(fileName) + if err != nil && !model.IsErrNotFound(err) { + a.logger.Error("111") + return nil, nil, err + } + + var filePath string + + if fileInfo != nil && fileInfo.Path != "" { + filePath = fileInfo.Path + } else { + filePath = filepath.Join(teamID, rootID, fileName) + } + + exists, err := a.filesBackend.FileExists(filePath) + if err != nil { + a.logger.Error(fmt.Sprintf("GetFile: Failed to check if file exists as path. Path: %s, error: %e", filePath, err)) + return nil, nil, err + } + + if !exists { + return nil, nil, ErrFileNotFound + } + + reader, err := a.filesBackend.Reader(filePath) + if err != nil { + a.logger.Error(fmt.Sprintf("GetFile: Failed to get file reader of existing file at path: %s, error: %e", filePath, err)) + return nil, nil, err + } + + return fileInfo, reader, nil +} + func (a *App) GetFileReader(teamID, rootID, filename string) (filestore.ReadCloseSeeker, error) { filePath := filepath.Join(teamID, rootID, filename) exists, err := a.filesBackend.FileExists(filePath) diff --git a/server/app/files_test.go b/server/app/files_test.go index 11e4991b8..b39327f7b 100644 --- a/server/app/files_test.go +++ b/server/app/files_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" @@ -195,8 +196,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, testBoardID, paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) fileName = paths[2] return int64(10) } @@ -219,8 +220,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, "test-board-id", paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) assert.Equal(t, "jpg", strings.Split(paths[2], ".")[1]) return int64(10) } @@ -243,8 +244,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, "test-board-id", paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) assert.Equal(t, "jpg", strings.Split(paths[2], ".")[1]) return int64(10) } @@ -304,3 +305,80 @@ func TestGetFileInfo(t *testing.T) { assert.Nil(t, fetchedFileInfo) }) } + +func TestGetFile(t *testing.T) { + th, _ := SetupTestHelper(t) + + t.Run("when FileInfo exists", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(&mmModel.FileInfo{ + Id: "fileInfoID", + Path: "/path/to/file/fileName.txt", + }, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + mockedFileBackend.On("Reader", "/path/to/file/fileName.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "/path/to/file/fileName.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.NotNil(t, fileInfo) + assert.NotNil(t, seeker) + }) + + t.Run("when FileInfo doesn't exist", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(nil, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + + mockedFileBackend.On("Reader", "teamID/boardID/7fileInfoID.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "teamID/boardID/7fileInfoID.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.Nil(t, fileInfo) + assert.NotNil(t, seeker) + }) + + t.Run("when FileInfo exists but FileInfo.Path is not set", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(&mmModel.FileInfo{ + Id: "fileInfoID", + Path: "", + }, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + mockedFileBackend.On("Reader", "teamID/boardID/7fileInfoID.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "teamID/boardID/7fileInfoID.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.NotNil(t, fileInfo) + assert.NotNil(t, seeker) + }) +} diff --git a/server/client/client.go b/server/client/client.go index 6be594f40..8c794ccfe 100644 --- a/server/client/client.go +++ b/server/client/client.go @@ -380,6 +380,8 @@ func (c *Client) GetCards(boardID string, page int, perPage int) ([]*model.Card, return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var cards []*model.Card if err := json.NewDecoder(r.Body).Decode(&cards); err != nil { return nil, BuildErrorResponse(r, err) @@ -398,6 +400,8 @@ func (c *Client) PatchCard(cardID string, cardPatch *model.CardPatch, disableNot return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var cardNew *model.Card if err := json.NewDecoder(r.Body).Decode(&cardNew); err != nil { return nil, BuildErrorResponse(r, err) @@ -412,6 +416,8 @@ func (c *Client) GetCard(cardID string) (*model.Card, *Response) { return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var card *model.Card if err := json.NewDecoder(r.Body).Decode(&card); err != nil { return nil, BuildErrorResponse(r, err) @@ -450,6 +456,7 @@ func (c *Client) DeleteCategory(teamID, categoryID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } @@ -1049,6 +1056,7 @@ func (c *Client) HideBoard(teamID, categoryID, boardID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } @@ -1058,5 +1066,6 @@ func (c *Client) UnhideBoard(teamID, categoryID, boardID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } diff --git a/server/services/store/sqlstore/file.go b/server/services/store/sqlstore/file.go index c1e189f3d..5825244c9 100644 --- a/server/services/store/sqlstore/file.go +++ b/server/services/store/sqlstore/file.go @@ -22,6 +22,7 @@ func (s *SQLStore) saveFileInfo(db sq.BaseRunner, fileInfo *mmModel.FileInfo) er "extension", "size", "delete_at", + "path", "archived", ). Values( @@ -31,6 +32,7 @@ func (s *SQLStore) saveFileInfo(db sq.BaseRunner, fileInfo *mmModel.FileInfo) er fileInfo.Extension, fileInfo.Size, fileInfo.DeleteAt, + fileInfo.Path, false, ) @@ -57,6 +59,7 @@ func (s *SQLStore) getFileInfo(db sq.BaseRunner, id string) (*mmModel.FileInfo, "extension", "size", "archived", + "path", ). From(s.tablePrefix + "file_info"). Where(sq.Eq{"Id": id}) @@ -73,6 +76,7 @@ func (s *SQLStore) getFileInfo(db sq.BaseRunner, id string) (*mmModel.FileInfo, &fileInfo.Extension, &fileInfo.Size, &fileInfo.Archived, + &fileInfo.Path, ) if err != nil { diff --git a/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql new file mode 100644 index 000000000..027b7d63f --- /dev/null +++ b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql new file mode 100644 index 000000000..af7d5e8fe --- /dev/null +++ b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql @@ -0,0 +1 @@ +{{ addColumnIfNeeded "file_info" "path" "varchar(512)" "" }} \ No newline at end of file diff --git a/server/utils/utils.go b/server/utils/utils.go index 7a1ad9763..46326dd66 100644 --- a/server/utils/utils.go +++ b/server/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "encoding/json" + "path" "reflect" "time" @@ -120,3 +121,7 @@ func DedupeStringArr(arr []string) []string { return dedupedArr } + +func GetBaseFilePath() string { + return path.Join("boards", time.Now().Format("20060102")) +}