From d1b7e601c58c9b3250f9c639d6b8f9b9ea33bbce Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Wed, 23 Mar 2022 16:21:48 +0530 Subject: [PATCH 01/51] Added server changes to support private templates --- server/api/api.go | 59 ++++++++++++++++++- server/app/boards.go | 8 ++- server/app/onboarding.go | 2 +- server/app/templates.go | 2 +- server/services/store/mockstore/mockstore.go | 23 ++++++-- .../000017_add_teams_and_boards.up.sql | 2 - .../services/store/sqlstore/public_methods.go | 9 ++- server/services/store/sqlstore/templates.go | 28 ++++++++- server/services/store/store.go | 3 +- webapp/src/octoClient.ts | 9 +++ webapp/src/store/globalTemplates.ts | 2 +- 11 files changed, 130 insertions(+), 17 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index b833f2429..7b33c509a 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -76,6 +76,7 @@ func (a *API) RegisterRoutes(r *mux.Router) { apiv1.HandleFunc("/teams/{teamID}/boards", a.sessionRequired(a.handleGetBoards)).Methods("GET") apiv1.HandleFunc("/teams/{teamID}/boards/search", a.sessionRequired(a.handleSearchBoards)).Methods("GET") apiv1.HandleFunc("/teams/{teamID}/templates", a.sessionRequired(a.handleGetTemplates)).Methods("GET") + apiv1.HandleFunc("/templates", a.sessionRequired(a.handleGetDefaultTemplates)).Methods("GET") apiv1.HandleFunc("/boards", a.sessionRequired(a.handleCreateBoard)).Methods("POST") apiv1.HandleFunc("/boards/{boardID}", a.attachSession(a.handleGetBoard, false)).Methods("GET") apiv1.HandleFunc("/boards/{boardID}", a.sessionRequired(a.handlePatchBoard)).Methods("PATCH") @@ -2184,7 +2185,7 @@ func (a *API) handleGetTemplates(w http.ResponseWriter, r *http.Request) { auditRec.AddMeta("teamID", teamID) // retrieve boards list - boards, err := a.app.GetTemplateBoards(teamID) + boards, err := a.app.GetTemplateBoards(teamID, userID) if err != nil { a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) return @@ -2208,6 +2209,62 @@ func (a *API) handleGetTemplates(w http.ResponseWriter, r *http.Request) { auditRec.Success() } +func (a *API) handleGetDefaultTemplates(w http.ResponseWriter, r *http.Request) { + // swagger:operation GET /api/v1/templates getDefaultTemplates + // + // Returns default templates + // + // --- + // produces: + // - application/json + // parameters: + // - name: teamID + // in: path + // description: Team ID + // required: true + // type: string + // security: + // - BearerAuth: [] + // responses: + // '200': + // description: success + // schema: + // type: array + // items: + // items: + // "$ref": "#/definitions/Board" + // default: + // description: internal error + // schema: + // "$ref": "#/definitions/ErrorResponse" + + auditRec := a.makeAuditRecord(r, "getDefaultTemplates", audit.Fail) + defer a.audit.LogRecord(audit.LevelRead, auditRec) + + // retrieve boards list + boards, err := a.app.GetDefaultTemplates() + if err != nil { + a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) + return + } + + a.logger.Debug("GetDefaultTemplates", + mlog.Int("boardsCount", len(boards)), + ) + + data, err := json.Marshal(boards) + if err != nil { + a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) + return + } + + // response + jsonBytesResponse(w, http.StatusOK, data) + + auditRec.AddMeta("templatesCount", len(boards)) + auditRec.Success() +} + // subscriptions func (a *API) handleCreateSubscription(w http.ResponseWriter, r *http.Request) { diff --git a/server/app/boards.go b/server/app/boards.go index 6fc0b76bb..202c4298a 100644 --- a/server/app/boards.go +++ b/server/app/boards.go @@ -49,8 +49,12 @@ func (a *App) GetBoardsForUserAndTeam(userID, teamID string) ([]*model.Board, er return a.store.GetBoardsForUserAndTeam(userID, teamID) } -func (a *App) GetTemplateBoards(teamID string) ([]*model.Board, error) { - return a.store.GetTemplateBoards(teamID) +func (a *App) GetTemplateBoards(teamID, userID string) ([]*model.Board, error) { + return a.store.GetTemplateBoards(teamID, userID) +} + +func (a *App) GetDefaultTemplates() ([]*model.Board, error) { + return a.store.GetDefaultTemplates() } func (a *App) CreateBoard(board *model.Board, userID string, addMember bool) (*model.Board, error) { diff --git a/server/app/onboarding.go b/server/app/onboarding.go index 369e32227..6f517432f 100644 --- a/server/app/onboarding.go +++ b/server/app/onboarding.go @@ -46,7 +46,7 @@ func (a *App) PrepareOnboardingTour(userID string, teamID string) (string, strin } func (a *App) getOnboardingBoardID() (string, error) { - boards, err := a.store.GetTemplateBoards(globalTeamID) + boards, err := a.store.GetDefaultTemplates() if err != nil { return "", err } diff --git a/server/app/templates.go b/server/app/templates.go index da28e7567..7d23b7ce5 100644 --- a/server/app/templates.go +++ b/server/app/templates.go @@ -26,7 +26,7 @@ func (a *App) InitTemplates() error { // initializeTemplates imports default templates if the boards table is empty. func (a *App) initializeTemplates() error { - boards, err := a.store.GetTemplateBoards(globalTeamID) + boards, err := a.store.GetDefaultTemplates() if err != nil { return fmt.Errorf("cannot initialize templates: %w", err) } diff --git a/server/services/store/mockstore/mockstore.go b/server/services/store/mockstore/mockstore.go index 68dc50af3..ded8dbed7 100644 --- a/server/services/store/mockstore/mockstore.go +++ b/server/services/store/mockstore/mockstore.go @@ -535,6 +535,21 @@ func (mr *MockStoreMockRecorder) GetCategory(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCategory", reflect.TypeOf((*MockStore)(nil).GetCategory), arg0) } +// GetDefaultTemplates mocks base method. +func (m *MockStore) GetDefaultTemplates() ([]*model.Board, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDefaultTemplates") + ret0, _ := ret[0].([]*model.Board) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDefaultTemplates indicates an expected call of GetDefaultTemplates. +func (mr *MockStoreMockRecorder) GetDefaultTemplates() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultTemplates", reflect.TypeOf((*MockStore)(nil).GetDefaultTemplates)) +} + // GetMemberForBoard mocks base method. func (m *MockStore) GetMemberForBoard(arg0, arg1 string) (*model.BoardMember, error) { m.ctrl.T.Helper() @@ -821,18 +836,18 @@ func (mr *MockStoreMockRecorder) GetTeamsForUser(arg0 interface{}) *gomock.Call } // GetTemplateBoards mocks base method. -func (m *MockStore) GetTemplateBoards(arg0 string) ([]*model.Board, error) { +func (m *MockStore) GetTemplateBoards(arg0, arg1 string) ([]*model.Board, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTemplateBoards", arg0) + ret := m.ctrl.Call(m, "GetTemplateBoards", arg0, arg1) ret0, _ := ret[0].([]*model.Board) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTemplateBoards indicates an expected call of GetTemplateBoards. -func (mr *MockStoreMockRecorder) GetTemplateBoards(arg0 interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetTemplateBoards(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplateBoards", reflect.TypeOf((*MockStore)(nil).GetTemplateBoards), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplateBoards", reflect.TypeOf((*MockStore)(nil).GetTemplateBoards), arg0, arg1) } // GetUserByEmail mocks base method. diff --git a/server/services/store/sqlstore/migrations/000017_add_teams_and_boards.up.sql b/server/services/store/sqlstore/migrations/000017_add_teams_and_boards.up.sql index 2ed63ba57..74286beb4 100644 --- a/server/services/store/sqlstore/migrations/000017_add_teams_and_boards.up.sql +++ b/server/services/store/sqlstore/migrations/000017_add_teams_and_boards.up.sql @@ -367,12 +367,10 @@ INSERT INTO {{.prefix}}board_members ( SELECT B.Id, CM.UserId, CM.Roles, (CM.UserId=B.created_by) OR CM.SchemeAdmin, CM.SchemeUser, FALSE, CM.SchemeGuest FROM {{.prefix}}boards AS B INNER JOIN ChannelMembers as CM ON CM.ChannelId=B.channel_id - WHERE NOT B.is_template ); {{else}} {{- /* if we're in personal server or desktop, create memberships for everyone */ -}} INSERT INTO {{.prefix}}board_members SELECT B.id, U.id, '', B.created_by=U.id, TRUE, FALSE, FALSE FROM {{.prefix}}boards AS B, {{.prefix}}users AS U - WHERE NOT B.is_template; {{end}} diff --git a/server/services/store/sqlstore/public_methods.go b/server/services/store/sqlstore/public_methods.go index 8861bbbf3..c3231dc27 100644 --- a/server/services/store/sqlstore/public_methods.go +++ b/server/services/store/sqlstore/public_methods.go @@ -319,6 +319,11 @@ func (s *SQLStore) GetCategory(id string) (*model.Category, error) { } +func (s *SQLStore) GetDefaultTemplates() ([]*model.Board, error) { + return s.getDefaultTemplates(s.db) + +} + func (s *SQLStore) GetMemberForBoard(boardID string, userID string) (*model.BoardMember, error) { return s.getMemberForBoard(s.db, boardID, userID) @@ -414,8 +419,8 @@ func (s *SQLStore) GetTeamsForUser(userID string) ([]*model.Team, error) { } -func (s *SQLStore) GetTemplateBoards(teamID string) ([]*model.Board, error) { - return s.getTemplateBoards(s.db, teamID) +func (s *SQLStore) GetTemplateBoards(teamID string, userID string) ([]*model.Board, error) { + return s.getTemplateBoards(s.db, teamID, userID) } diff --git a/server/services/store/sqlstore/templates.go b/server/services/store/sqlstore/templates.go index a58ba15b4..f8354ce68 100644 --- a/server/services/store/sqlstore/templates.go +++ b/server/services/store/sqlstore/templates.go @@ -55,11 +55,11 @@ func (s *SQLStore) removeDefaultTemplates(db sq.BaseRunner, boards []*model.Boar } // getDefaultTemplateBoards fetches all template blocks . -func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID string) ([]*model.Board, error) { +func (s *SQLStore) getDefaultTemplates(db sq.BaseRunner) ([]*model.Board, error) { query := s.getQueryBuilder(db). Select(boardFields("")...). From(s.tablePrefix + "boards"). - Where(sq.Eq{"coalesce(team_id, '0')": teamID}). + Where(sq.Eq{"team_id": "0"}). Where(sq.Eq{"is_template": true}) rows, err := query.Query() @@ -71,3 +71,27 @@ func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID string) ([]*model. return s.boardsFromRows(rows) } + +// getDefaultTemplateBoards fetches all template blocks . +func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID, userID string) ([]*model.Board, error) { + query := s.getQueryBuilder(db). + Select(boardFields("")...). + From(s.tablePrefix+"boards as b"). + Join(s.tablePrefix+"board_members as bm on b.id = bm.board_id and b.team_id = ? and bm.user_id = ?", teamID, userID). + Where(sq.Eq{"is_template": true}) + + rows, err := query.Query() + if err != nil { + s.logger.Error(`getTemplateBoards ERROR`, mlog.Err(err)) + return nil, err + } + defer s.CloseRows(rows) + + userTemplates, err := s.boardsFromRows(rows) + if err != nil { + return nil, err + } + + return userTemplates, nil + +} diff --git a/server/services/store/store.go b/server/services/store/store.go index 28b63eb03..7bb82c25b 100644 --- a/server/services/store/store.go +++ b/server/services/store/store.go @@ -124,8 +124,9 @@ type Store interface { GetNotificationHint(blockID string) (*model.NotificationHint, error) GetNextNotificationHint(remove bool) (*model.NotificationHint, error) + GetDefaultTemplates() ([]*model.Board, error) RemoveDefaultTemplates(boards []*model.Board) error - GetTemplateBoards(teamID string) ([]*model.Board, error) + GetTemplateBoards(teamID, userID string) ([]*model.Board, error) DBType() string diff --git a/webapp/src/octoClient.ts b/webapp/src/octoClient.ts index 6b67fc7e9..aa3e5196b 100644 --- a/webapp/src/octoClient.ts +++ b/webapp/src/octoClient.ts @@ -596,6 +596,15 @@ class OctoClient { return this.getBoardsWithPath(path) } + async getDefaultTemplates(): Promise { + const path = '/api/v1/templates' + const response = await fetch(this.getBaseURL() + path, {headers: this.headers()}) + if (response.status !== 200) { + return [] + } + return (await this.getJson(response, [])) as Board[] + } + // Boards // ToDo: . // - goal? make the interface show boards & blocks for boards diff --git a/webapp/src/store/globalTemplates.ts b/webapp/src/store/globalTemplates.ts index b7b0ae2ec..3dc5ead82 100644 --- a/webapp/src/store/globalTemplates.ts +++ b/webapp/src/store/globalTemplates.ts @@ -13,7 +13,7 @@ import {RootState} from './index' export const fetchGlobalTemplates = createAsyncThunk( 'globalTemplates/fetch', async () => { - const templates = await client.getTeamTemplates('0') + const templates = await client.getDefaultTemplates() return templates.sort((a, b) => a.title.localeCompare(b.title)) }, ) From 018c829132465383813d47dfa55a31a4a1c4d879 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Thu, 24 Mar 2022 13:16:19 +0530 Subject: [PATCH 02/51] WIP --- server/services/store/sqlstore/templates.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/server/services/store/sqlstore/templates.go b/server/services/store/sqlstore/templates.go index f8354ce68..1bf44cd96 100644 --- a/server/services/store/sqlstore/templates.go +++ b/server/services/store/sqlstore/templates.go @@ -77,8 +77,22 @@ func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID, userID string) ([ query := s.getQueryBuilder(db). Select(boardFields("")...). From(s.tablePrefix+"boards as b"). - Join(s.tablePrefix+"board_members as bm on b.id = bm.board_id and b.team_id = ? and bm.user_id = ?", teamID, userID). - Where(sq.Eq{"is_template": true}) + LeftJoin(s.tablePrefix+"board_members as bm on b.id = bm.board_id and and bm.user_id = ?", userID). + Where(sq.Eq{"is_template": true}). + Where(sq.Eq{"b.team_id": teamID}). + Where(sq.Or{ + sq.And{ + sq.Eq{"bm.board_id": nil}, + sq.Eq{"b.type": model.BoardTypeOpen}, + }, + sq.And{ + sq.NotEq{"bm.board_id": nil}, + }, + }) + + ss, pp, _ := query.ToSql() + s.logger.Error(ss) + s.logger.Error(fmt.Sprintf("%v", pp)) rows, err := query.Query() if err != nil { From f26a854d3cdce87e387c1252de298a24e654a816 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Fri, 25 Mar 2022 11:54:27 +0530 Subject: [PATCH 03/51] Updated share board modal to support templates --- server/services/store/sqlstore/templates.go | 3 ++- webapp/src/components/shareBoard/shareBoard.tsx | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/server/services/store/sqlstore/templates.go b/server/services/store/sqlstore/templates.go index 1bf44cd96..c593cd987 100644 --- a/server/services/store/sqlstore/templates.go +++ b/server/services/store/sqlstore/templates.go @@ -77,10 +77,11 @@ func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID, userID string) ([ query := s.getQueryBuilder(db). Select(boardFields("")...). From(s.tablePrefix+"boards as b"). - LeftJoin(s.tablePrefix+"board_members as bm on b.id = bm.board_id and and bm.user_id = ?", userID). + LeftJoin(s.tablePrefix+"board_members as bm on b.id = bm.board_id and bm.user_id = ?", userID). Where(sq.Eq{"is_template": true}). Where(sq.Eq{"b.team_id": teamID}). Where(sq.Or{ + // this is to include public templates even if there is not board_member entry sq.And{ sq.Eq{"bm.board_id": nil}, sq.Eq{"b.type": model.BoardTypeOpen}, diff --git a/webapp/src/components/shareBoard/shareBoard.tsx b/webapp/src/components/shareBoard/shareBoard.tsx index 21860df1d..e13395543 100644 --- a/webapp/src/components/shareBoard/shareBoard.tsx +++ b/webapp/src/components/shareBoard/shareBoard.tsx @@ -9,7 +9,7 @@ import Select from 'react-select/async' import {CSSObject} from '@emotion/serialize' import {useAppSelector} from '../../store/hooks' -import {getCurrentBoardId, getCurrentBoardMembers} from '../../store/boards' +import {getCurrentBoard, getCurrentBoardMembers} from '../../store/boards' import {getMe, getBoardUsersList} from '../../store/users' import {Utils, IDType} from '../../utils' @@ -95,7 +95,8 @@ export default function ShareBoardDialog(props: Props): JSX.Element { // members of the current board const members = useAppSelector<{[key: string]: BoardMember}>(getCurrentBoardMembers) - const boardId = useAppSelector(getCurrentBoardId) + const board = useAppSelector(getCurrentBoard) + const boardId = board.id const boardUsers = useAppSelector(getBoardUsersList) const me = useAppSelector(getMe) @@ -291,7 +292,7 @@ export default function ShareBoardDialog(props: Props): JSX.Element { })} - {props.enableSharedBoards && ( + {props.enableSharedBoards && !board.isTemplate && (
)} - {(props.enableSharedBoards && publish) && + {(props.enableSharedBoards && publish && !board.isTemplate) && (
@@ -393,7 +394,7 @@ export default function ShareBoardDialog(props: Props): JSX.Element { )} - {!publish && ( + {!publish && !board.isTemplate && (
From c4c5692144a4ddf13a8bdf3ae39e918d37976c85 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Fri, 25 Mar 2022 12:18:15 +0530 Subject: [PATCH 04/51] Fixed tests and lint --- server/app/onboarding_test.go | 14 +++++++------- server/auth/auth_test.go | 2 +- server/services/store/sqlstore/templates.go | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/server/app/onboarding_test.go b/server/app/onboarding_test.go index 1388add44..fd176159b 100644 --- a/server/app/onboarding_test.go +++ b/server/app/onboarding_test.go @@ -25,7 +25,7 @@ func TestPrepareOnboardingTour(t *testing.T) { IsTemplate: true, } - th.Store.EXPECT().GetTemplateBoards("0").Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) th.Store.EXPECT().DuplicateBoard(welcomeBoard.ID, userID, teamID, false).Return(&model.BoardsAndBlocks{Boards: []*model.Board{&welcomeBoard}}, nil, nil) th.Store.EXPECT().GetMembersForBoard(welcomeBoard.ID).Return([]*model.BoardMember{}, nil) @@ -60,7 +60,7 @@ func TestCreateWelcomeBoard(t *testing.T) { TeamID: "0", IsTemplate: true, } - th.Store.EXPECT().GetTemplateBoards("0").Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) th.Store.EXPECT().DuplicateBoard(welcomeBoard.ID, userID, teamID, false). Return(&model.BoardsAndBlocks{Boards: []*model.Board{&welcomeBoard}}, nil, nil) th.Store.EXPECT().GetMembersForBoard(welcomeBoard.ID).Return([]*model.BoardMember{}, nil) @@ -72,7 +72,7 @@ func TestCreateWelcomeBoard(t *testing.T) { t.Run("template doesn't contain a board", func(t *testing.T) { teamID := testTeamID - th.Store.EXPECT().GetTemplateBoards("0").Return([]*model.Board{}, nil) + th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{}, nil) boardID, err := th.App.createWelcomeBoard("user_id_1", teamID) assert.Error(t, err) assert.Empty(t, boardID) @@ -86,7 +86,7 @@ func TestCreateWelcomeBoard(t *testing.T) { TeamID: teamID, IsTemplate: true, } - th.Store.EXPECT().GetTemplateBoards("0").Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) boardID, err := th.App.createWelcomeBoard("user_id_1", "workspace_id_1") assert.Error(t, err) assert.Empty(t, boardID) @@ -104,7 +104,7 @@ func TestGetOnboardingBoardID(t *testing.T) { TeamID: "0", IsTemplate: true, } - th.Store.EXPECT().GetTemplateBoards("0").Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) onboardingBoardID, err := th.App.getOnboardingBoardID() assert.NoError(t, err) @@ -112,7 +112,7 @@ func TestGetOnboardingBoardID(t *testing.T) { }) t.Run("no blocks found", func(t *testing.T) { - th.Store.EXPECT().GetTemplateBoards("0").Return([]*model.Board{}, nil) + th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{}, nil) onboardingBoardID, err := th.App.getOnboardingBoardID() assert.Error(t, err) @@ -126,7 +126,7 @@ func TestGetOnboardingBoardID(t *testing.T) { TeamID: "0", IsTemplate: true, } - th.Store.EXPECT().GetTemplateBoards("0").Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) onboardingBoardID, err := th.App.getOnboardingBoardID() assert.Error(t, err) diff --git a/server/auth/auth_test.go b/server/auth/auth_test.go index 04b6b0218..954f01eb9 100644 --- a/server/auth/auth_test.go +++ b/server/auth/auth_test.go @@ -42,7 +42,7 @@ func setupTestHelper(t *testing.T) *TestHelper { newAuth := New(&cfg, mockStore, localpermissions.New(mockPermissions, logger)) // called during default template setup for every test - mockStore.EXPECT().GetTemplateBoards(gomock.Any()).AnyTimes() + mockStore.EXPECT().GetDefaultTemplates().AnyTimes() mockStore.EXPECT().RemoveDefaultTemplates(gomock.Any()).AnyTimes() mockStore.EXPECT().InsertBlock(gomock.Any(), gomock.Any()).AnyTimes() diff --git a/server/services/store/sqlstore/templates.go b/server/services/store/sqlstore/templates.go index c593cd987..849e6b0cb 100644 --- a/server/services/store/sqlstore/templates.go +++ b/server/services/store/sqlstore/templates.go @@ -108,5 +108,4 @@ func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID, userID string) ([ } return userTemplates, nil - } From e51edd4df506762b2530088c20253eacc402fe7a Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Mon, 28 Mar 2022 11:58:26 +0530 Subject: [PATCH 05/51] Fixed bug where migration failed if no teamless board existed --- server/services/store/sqlstore/migrate.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/services/store/sqlstore/migrate.go b/server/services/store/sqlstore/migrate.go index 63b681403..c59a914ff 100644 --- a/server/services/store/sqlstore/migrate.go +++ b/server/services/store/sqlstore/migrate.go @@ -557,7 +557,12 @@ func (s *SQLStore) getDMBoards(tx sq.BaseRunner) ([]*model.Board, error) { }, } - return s.getBoardsByCondition(tx, conditions) + boards, err := s.getBoardsByCondition(tx, conditions) + if err != nil && errors.Is(err, sql.ErrNoRows) { + return []*model.Board{}, nil + } + + return boards, err } // The destination is selected as the first team where all members From 3679e04d44d23354bee00a0b5b663674d891f696 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Mon, 28 Mar 2022 13:41:31 +0530 Subject: [PATCH 06/51] Removed the extra getDefaultTemplate API as its not needed --- server/api/api.go | 57 ----------------------------- webapp/src/octoClient.ts | 9 ----- webapp/src/store/globalTemplates.ts | 2 +- 3 files changed, 1 insertion(+), 67 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index 86c14c60d..2707b8b9d 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -76,7 +76,6 @@ func (a *API) RegisterRoutes(r *mux.Router) { apiv1.HandleFunc("/teams/{teamID}/boards", a.sessionRequired(a.handleGetBoards)).Methods("GET") apiv1.HandleFunc("/teams/{teamID}/boards/search", a.sessionRequired(a.handleSearchBoards)).Methods("GET") apiv1.HandleFunc("/teams/{teamID}/templates", a.sessionRequired(a.handleGetTemplates)).Methods("GET") - apiv1.HandleFunc("/templates", a.sessionRequired(a.handleGetDefaultTemplates)).Methods("GET") apiv1.HandleFunc("/boards", a.sessionRequired(a.handleCreateBoard)).Methods("POST") apiv1.HandleFunc("/boards/{boardID}", a.attachSession(a.handleGetBoard, false)).Methods("GET") apiv1.HandleFunc("/boards/{boardID}", a.sessionRequired(a.handlePatchBoard)).Methods("PATCH") @@ -2211,62 +2210,6 @@ func (a *API) handleGetTemplates(w http.ResponseWriter, r *http.Request) { auditRec.Success() } -func (a *API) handleGetDefaultTemplates(w http.ResponseWriter, r *http.Request) { - // swagger:operation GET /api/v1/templates getDefaultTemplates - // - // Returns default templates - // - // --- - // produces: - // - application/json - // parameters: - // - name: teamID - // in: path - // description: Team ID - // required: true - // type: string - // security: - // - BearerAuth: [] - // responses: - // '200': - // description: success - // schema: - // type: array - // items: - // items: - // "$ref": "#/definitions/Board" - // default: - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - - auditRec := a.makeAuditRecord(r, "getDefaultTemplates", audit.Fail) - defer a.audit.LogRecord(audit.LevelRead, auditRec) - - // retrieve boards list - boards, err := a.app.GetDefaultTemplates() - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - a.logger.Debug("GetDefaultTemplates", - mlog.Int("boardsCount", len(boards)), - ) - - data, err := json.Marshal(boards) - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - // response - jsonBytesResponse(w, http.StatusOK, data) - - auditRec.AddMeta("templatesCount", len(boards)) - auditRec.Success() -} - // subscriptions func (a *API) handleCreateSubscription(w http.ResponseWriter, r *http.Request) { diff --git a/webapp/src/octoClient.ts b/webapp/src/octoClient.ts index 5bdaa27e5..f9e47191c 100644 --- a/webapp/src/octoClient.ts +++ b/webapp/src/octoClient.ts @@ -600,15 +600,6 @@ class OctoClient { return this.getBoardsWithPath(path) } - async getDefaultTemplates(): Promise { - const path = '/api/v1/templates' - const response = await fetch(this.getBaseURL() + path, {headers: this.headers()}) - if (response.status !== 200) { - return [] - } - return (await this.getJson(response, [])) as Board[] - } - // Boards // ToDo: . // - goal? make the interface show boards & blocks for boards diff --git a/webapp/src/store/globalTemplates.ts b/webapp/src/store/globalTemplates.ts index 3dc5ead82..b7b0ae2ec 100644 --- a/webapp/src/store/globalTemplates.ts +++ b/webapp/src/store/globalTemplates.ts @@ -13,7 +13,7 @@ import {RootState} from './index' export const fetchGlobalTemplates = createAsyncThunk( 'globalTemplates/fetch', async () => { - const templates = await client.getDefaultTemplates() + const templates = await client.getTeamTemplates('0') return templates.sort((a, b) => a.title.localeCompare(b.title)) }, ) From acd0ca71cda1039b928305a7ccbbff511878bbdb Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Mon, 28 Mar 2022 14:06:41 +0530 Subject: [PATCH 07/51] Updated share modal's title for templates --- webapp/i18n/en.json | 1 + webapp/src/components/shareBoard/shareBoard.tsx | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index 844f6858b..62fd262ff 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -181,6 +181,7 @@ "ShareBoard.tokenRegenrated": "Token regenerated", "ShareBoard.userPermissionsRemoveMemberText": "Remove member", "ShareBoard.userPermissionsYouText": "(You)", + "ShareTemplate.Title": "Share Template", "Sidebar.about": "About Focalboard", "Sidebar.add-board": "+ Add board", "Sidebar.changePassword": "Change password", diff --git a/webapp/src/components/shareBoard/shareBoard.tsx b/webapp/src/components/shareBoard/shareBoard.tsx index f75036357..e2ade5a51 100644 --- a/webapp/src/components/shareBoard/shareBoard.tsx +++ b/webapp/src/components/shareBoard/shareBoard.tsx @@ -240,7 +240,7 @@ export default function ShareBoardDialog(props: Props): JSX.Element { )) } - const toolbar = ( + const shareBoardTitle = ( ) + const shareTemplateTitle = ( + + + + ) + + const toolbar = board.isTemplate ? shareTemplateTitle : shareBoardTitle + return ( Date: Mon, 28 Mar 2022 15:27:53 +0530 Subject: [PATCH 08/51] Fixed user role dropdown CSS issue --- webapp/src/components/shareBoard/shareBoard.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/webapp/src/components/shareBoard/shareBoard.scss b/webapp/src/components/shareBoard/shareBoard.scss index 325752e87..692864107 100644 --- a/webapp/src/components/shareBoard/shareBoard.scss +++ b/webapp/src/components/shareBoard/shareBoard.scss @@ -212,5 +212,16 @@ text-decoration: underline; } } + + .Menu { + position: fixed; + left: 55%; + right: calc(45% - 240px); + + .menu-contents { + min-width: 240px; + max-width: 240px; + } + } } } From 761e7cd463a50af93570fcccc1450f82b1c7f1f3 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Tue, 29 Mar 2022 11:16:16 +0530 Subject: [PATCH 09/51] Removed getDefaultTemplates functin as no longer needed --- server/app/boards.go | 4 ---- server/app/onboarding.go | 4 ++-- server/app/onboarding_test.go | 14 +++++------ server/app/templates.go | 2 +- server/auth/auth_test.go | 2 +- server/services/store/mockstore/mockstore.go | 15 ------------ .../services/store/sqlstore/public_methods.go | 5 ---- server/services/store/sqlstore/templates.go | 24 +------------------ server/services/store/store.go | 1 - 9 files changed, 12 insertions(+), 59 deletions(-) diff --git a/server/app/boards.go b/server/app/boards.go index 202c4298a..36bdf961d 100644 --- a/server/app/boards.go +++ b/server/app/boards.go @@ -53,10 +53,6 @@ func (a *App) GetTemplateBoards(teamID, userID string) ([]*model.Board, error) { return a.store.GetTemplateBoards(teamID, userID) } -func (a *App) GetDefaultTemplates() ([]*model.Board, error) { - return a.store.GetDefaultTemplates() -} - func (a *App) CreateBoard(board *model.Board, userID string, addMember bool) (*model.Board, error) { if board.ID != "" { return nil, ErrNewBoardCannotHaveID diff --git a/server/app/onboarding.go b/server/app/onboarding.go index 6f517432f..3e6dbb9ea 100644 --- a/server/app/onboarding.go +++ b/server/app/onboarding.go @@ -46,14 +46,14 @@ func (a *App) PrepareOnboardingTour(userID string, teamID string) (string, strin } func (a *App) getOnboardingBoardID() (string, error) { - boards, err := a.store.GetDefaultTemplates() + boards, err := a.store.GetTemplateBoards("0", "") if err != nil { return "", err } var onboardingBoardID string for _, block := range boards { - if block.Title == WelcomeBoardTitle { + if block.Title == WelcomeBoardTitle && block.TeamID == "0" { onboardingBoardID = block.ID break } diff --git a/server/app/onboarding_test.go b/server/app/onboarding_test.go index fd176159b..ca57855c0 100644 --- a/server/app/onboarding_test.go +++ b/server/app/onboarding_test.go @@ -25,7 +25,7 @@ func TestPrepareOnboardingTour(t *testing.T) { IsTemplate: true, } - th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetTemplateBoards("0", "").Return([]*model.Board{&welcomeBoard}, nil) th.Store.EXPECT().DuplicateBoard(welcomeBoard.ID, userID, teamID, false).Return(&model.BoardsAndBlocks{Boards: []*model.Board{&welcomeBoard}}, nil, nil) th.Store.EXPECT().GetMembersForBoard(welcomeBoard.ID).Return([]*model.BoardMember{}, nil) @@ -60,7 +60,7 @@ func TestCreateWelcomeBoard(t *testing.T) { TeamID: "0", IsTemplate: true, } - th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetTemplateBoards("0", "").Return([]*model.Board{&welcomeBoard}, nil) th.Store.EXPECT().DuplicateBoard(welcomeBoard.ID, userID, teamID, false). Return(&model.BoardsAndBlocks{Boards: []*model.Board{&welcomeBoard}}, nil, nil) th.Store.EXPECT().GetMembersForBoard(welcomeBoard.ID).Return([]*model.BoardMember{}, nil) @@ -72,7 +72,7 @@ func TestCreateWelcomeBoard(t *testing.T) { t.Run("template doesn't contain a board", func(t *testing.T) { teamID := testTeamID - th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{}, nil) + th.Store.EXPECT().GetTemplateBoards("0", "").Return([]*model.Board{}, nil) boardID, err := th.App.createWelcomeBoard("user_id_1", teamID) assert.Error(t, err) assert.Empty(t, boardID) @@ -86,7 +86,7 @@ func TestCreateWelcomeBoard(t *testing.T) { TeamID: teamID, IsTemplate: true, } - th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetTemplateBoards("0", "").Return([]*model.Board{&welcomeBoard}, nil) boardID, err := th.App.createWelcomeBoard("user_id_1", "workspace_id_1") assert.Error(t, err) assert.Empty(t, boardID) @@ -104,7 +104,7 @@ func TestGetOnboardingBoardID(t *testing.T) { TeamID: "0", IsTemplate: true, } - th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetTemplateBoards("0", "").Return([]*model.Board{&welcomeBoard}, nil) onboardingBoardID, err := th.App.getOnboardingBoardID() assert.NoError(t, err) @@ -112,7 +112,7 @@ func TestGetOnboardingBoardID(t *testing.T) { }) t.Run("no blocks found", func(t *testing.T) { - th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{}, nil) + th.Store.EXPECT().GetTemplateBoards("0", "").Return([]*model.Board{}, nil) onboardingBoardID, err := th.App.getOnboardingBoardID() assert.Error(t, err) @@ -126,7 +126,7 @@ func TestGetOnboardingBoardID(t *testing.T) { TeamID: "0", IsTemplate: true, } - th.Store.EXPECT().GetDefaultTemplates().Return([]*model.Board{&welcomeBoard}, nil) + th.Store.EXPECT().GetTemplateBoards("0", "").Return([]*model.Board{&welcomeBoard}, nil) onboardingBoardID, err := th.App.getOnboardingBoardID() assert.Error(t, err) diff --git a/server/app/templates.go b/server/app/templates.go index 7d23b7ce5..e1f5d8de4 100644 --- a/server/app/templates.go +++ b/server/app/templates.go @@ -26,7 +26,7 @@ func (a *App) InitTemplates() error { // initializeTemplates imports default templates if the boards table is empty. func (a *App) initializeTemplates() error { - boards, err := a.store.GetDefaultTemplates() + boards, err := a.store.GetTemplateBoards(globalTeamID, "") if err != nil { return fmt.Errorf("cannot initialize templates: %w", err) } diff --git a/server/auth/auth_test.go b/server/auth/auth_test.go index 954f01eb9..4103edba5 100644 --- a/server/auth/auth_test.go +++ b/server/auth/auth_test.go @@ -42,7 +42,7 @@ func setupTestHelper(t *testing.T) *TestHelper { newAuth := New(&cfg, mockStore, localpermissions.New(mockPermissions, logger)) // called during default template setup for every test - mockStore.EXPECT().GetDefaultTemplates().AnyTimes() + mockStore.EXPECT().GetTemplateBoards("0", "").AnyTimes() mockStore.EXPECT().RemoveDefaultTemplates(gomock.Any()).AnyTimes() mockStore.EXPECT().InsertBlock(gomock.Any(), gomock.Any()).AnyTimes() diff --git a/server/services/store/mockstore/mockstore.go b/server/services/store/mockstore/mockstore.go index ded8dbed7..3af9c1a16 100644 --- a/server/services/store/mockstore/mockstore.go +++ b/server/services/store/mockstore/mockstore.go @@ -535,21 +535,6 @@ func (mr *MockStoreMockRecorder) GetCategory(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCategory", reflect.TypeOf((*MockStore)(nil).GetCategory), arg0) } -// GetDefaultTemplates mocks base method. -func (m *MockStore) GetDefaultTemplates() ([]*model.Board, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDefaultTemplates") - ret0, _ := ret[0].([]*model.Board) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetDefaultTemplates indicates an expected call of GetDefaultTemplates. -func (mr *MockStoreMockRecorder) GetDefaultTemplates() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultTemplates", reflect.TypeOf((*MockStore)(nil).GetDefaultTemplates)) -} - // GetMemberForBoard mocks base method. func (m *MockStore) GetMemberForBoard(arg0, arg1 string) (*model.BoardMember, error) { m.ctrl.T.Helper() diff --git a/server/services/store/sqlstore/public_methods.go b/server/services/store/sqlstore/public_methods.go index c3231dc27..e23562140 100644 --- a/server/services/store/sqlstore/public_methods.go +++ b/server/services/store/sqlstore/public_methods.go @@ -319,11 +319,6 @@ func (s *SQLStore) GetCategory(id string) (*model.Category, error) { } -func (s *SQLStore) GetDefaultTemplates() ([]*model.Board, error) { - return s.getDefaultTemplates(s.db) - -} - func (s *SQLStore) GetMemberForBoard(boardID string, userID string) (*model.BoardMember, error) { return s.getMemberForBoard(s.db, boardID, userID) diff --git a/server/services/store/sqlstore/templates.go b/server/services/store/sqlstore/templates.go index 849e6b0cb..f23b0fa4c 100644 --- a/server/services/store/sqlstore/templates.go +++ b/server/services/store/sqlstore/templates.go @@ -54,25 +54,7 @@ func (s *SQLStore) removeDefaultTemplates(db sq.BaseRunner, boards []*model.Boar return nil } -// getDefaultTemplateBoards fetches all template blocks . -func (s *SQLStore) getDefaultTemplates(db sq.BaseRunner) ([]*model.Board, error) { - query := s.getQueryBuilder(db). - Select(boardFields("")...). - From(s.tablePrefix + "boards"). - Where(sq.Eq{"team_id": "0"}). - Where(sq.Eq{"is_template": true}) - - rows, err := query.Query() - if err != nil { - s.logger.Error(`getTemplateBoards ERROR`, mlog.Err(err)) - return nil, err - } - defer s.CloseRows(rows) - - return s.boardsFromRows(rows) -} - -// getDefaultTemplateBoards fetches all template blocks . +// getTemplateBoards fetches all template boards . func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID, userID string) ([]*model.Board, error) { query := s.getQueryBuilder(db). Select(boardFields("")...). @@ -91,10 +73,6 @@ func (s *SQLStore) getTemplateBoards(db sq.BaseRunner, teamID, userID string) ([ }, }) - ss, pp, _ := query.ToSql() - s.logger.Error(ss) - s.logger.Error(fmt.Sprintf("%v", pp)) - rows, err := query.Query() if err != nil { s.logger.Error(`getTemplateBoards ERROR`, mlog.Err(err)) diff --git a/server/services/store/store.go b/server/services/store/store.go index 7bb82c25b..cc6bd7499 100644 --- a/server/services/store/store.go +++ b/server/services/store/store.go @@ -124,7 +124,6 @@ type Store interface { GetNotificationHint(blockID string) (*model.NotificationHint, error) GetNextNotificationHint(remove bool) (*model.NotificationHint, error) - GetDefaultTemplates() ([]*model.Board, error) RemoveDefaultTemplates(boards []*model.Board) error GetTemplateBoards(teamID, userID string) ([]*model.Board, error) From e864acb8e34173066815afc3233ec492d2086626 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Wed, 30 Mar 2022 15:40:20 +0530 Subject: [PATCH 10/51] WIP --- webapp/i18n/en.json | 1 + .../components/sidebar/sidebarBoardItem.tsx | 29 ++++++++++++++++++- .../components/sidebar/sidebarCategory.tsx | 19 ++++++------ webapp/src/utils.ts | 20 +++++++++++++ 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index 844f6858b..f2547846b 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -185,6 +185,7 @@ "Sidebar.add-board": "+ Add board", "Sidebar.changePassword": "Change password", "Sidebar.delete-board": "Delete board", + "Sidebar.duplicate-board": "Duplicate board", "Sidebar.export-archive": "Export archive", "Sidebar.import": "Import", "Sidebar.import-archive": "Import archive", diff --git a/webapp/src/components/sidebar/sidebarBoardItem.tsx b/webapp/src/components/sidebar/sidebarBoardItem.tsx index 3ef59d9ea..d2ad07792 100644 --- a/webapp/src/components/sidebar/sidebarBoardItem.tsx +++ b/webapp/src/components/sidebar/sidebarBoardItem.tsx @@ -1,6 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useState} from 'react' +import React, {useCallback, useMemo, useState} from 'react' import {useIntl} from 'react-intl' import {Board} from '../../blocks/board' @@ -27,6 +27,10 @@ import CalendarIcon from '../../widgets/icons/calendar' import {getCurrentTeam} from '../../store/teams' import {Permission} from '../../constants' +import DuplicateIcon from "../../widgets/icons/duplicate" +import {Utils} from "../../utils" + +import {useHistory, useRouteMatch} from "react-router-dom" const iconForViewType = (viewType: IViewType): JSX.Element => { switch (viewType) { @@ -58,6 +62,9 @@ const SidebarBoardItem = (props: Props) => { const currentViewId = useAppSelector(getCurrentViewId) const teamID = team?.id || '' + const match = useRouteMatch<{boardId: string, viewId?: string, cardId?: string, teamId?: string}>() + const history = useHistory() + const generateMoveToCategoryOptions = (blockID: string) => { return props.allCategories.map((category) => ( { } const board = props.board + + const handleDuplicateBoard = useCallback(() => { + return mutator.duplicateBoard( + board.id, + undefined, + board.isTemplate, + undefined, + () => { + Utils.showBoard(board.id, match, history) + return Promise.resolve() + } + ) + }, [board.id]) + const title = board.title || intl.formatMessage({id: 'Sidebar.untitled-board', defaultMessage: '(Untitled Board)'}) return ( <> @@ -126,6 +147,12 @@ const SidebarBoardItem = (props: Props) => { > {generateMoveToCategoryOptions(board.id)} + } + onClick={handleDuplicateBoard} + />
diff --git a/webapp/src/components/sidebar/sidebarCategory.tsx b/webapp/src/components/sidebar/sidebarCategory.tsx index cb068f361..2c3fd3d73 100644 --- a/webapp/src/components/sidebar/sidebarCategory.tsx +++ b/webapp/src/components/sidebar/sidebarCategory.tsx @@ -60,15 +60,16 @@ const SidebarCategory = (props: Props) => { const teamID = team?.id || '' const showBoard = useCallback((boardId) => { - // if the same board, reuse the match params - // otherwise remove viewId and cardId, results in first view being selected - const params = {...match.params, boardId: boardId || ''} - if (boardId !== match.params.boardId) { - params.viewId = undefined - params.cardId = undefined - } - const newPath = generatePath(match.path, params) - history.push(newPath) + // // if the same board, reuse the match params + // // otherwise remove viewId and cardId, results in first view being selected + // const params = {...match.params, boardId: boardId || ''} + // if (boardId !== match.params.boardId) { + // params.viewId = undefined + // params.cardId = undefined + // } + // const newPath = generatePath(match.path, params) + // history.push(newPath) + Utils.showBoard(boardId, match, history) props.hideSidebar() }, [match, history]) diff --git a/webapp/src/utils.ts b/webapp/src/utils.ts index 015c47ee7..3830d5987 100644 --- a/webapp/src/utils.ts +++ b/webapp/src/utils.ts @@ -4,6 +4,10 @@ import {marked} from 'marked' import {IntlShape} from 'react-intl' import moment from 'moment' +import {generatePath, match as routerMatch} from "react-router-dom" + +import {History} from "history" + import {Block} from './blocks/block' import {Board as BoardType, BoardMember, createBoard} from './blocks/board' import {createBoardView} from './blocks/boardView' @@ -703,6 +707,22 @@ class Utils { } return (Utils.isMac() && e.metaKey) || (!Utils.isMac() && e.ctrlKey && !e.altKey) } + + static showBoard( + boardId: string, + match: routerMatch<{boardId: string, viewId?: string, cardId?: string, teamId?: string}>, + history: History, + ) { + // if the same board, reuse the match params + // otherwise remove viewId and cardId, results in first view being selected + const params = {...match.params, boardId: boardId || ''} + if (boardId !== match.params.boardId) { + params.viewId = undefined + params.cardId = undefined + } + const newPath = generatePath(match.path, params) + history.push(newPath) + } } export {Utils, IDType} From 59ed51686f78c144d9c1f3ecc4de40564eb8a998 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Wed, 30 Mar 2022 17:50:25 +0530 Subject: [PATCH 11/51] WIP --- webapp/i18n/en.json | 1 + .../boardTemplateSelector.tsx | 9 +++---- .../boardTemplateSelectorItem.tsx | 2 ++ .../components/sidebar/sidebarBoardItem.tsx | 27 ++++++++++++++----- .../components/sidebar/sidebarCategory.tsx | 9 ------- webapp/src/components/workspace.scss | 2 ++ webapp/src/mutator.ts | 2 +- 7 files changed, 31 insertions(+), 21 deletions(-) diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index f2547846b..32f39a4f2 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -186,6 +186,7 @@ "Sidebar.changePassword": "Change password", "Sidebar.delete-board": "Delete board", "Sidebar.duplicate-board": "Duplicate board", + "Sidebar.template-from-board": "New template from board", "Sidebar.export-archive": "Export archive", "Sidebar.import": "Import", "Sidebar.import-archive": "Import archive", diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx index ec7186c9e..a39d0bd5b 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React, {useEffect, useState, useCallback, useMemo} from 'react' import {FormattedMessage, useIntl} from 'react-intl' -import {generatePath, useHistory, useRouteMatch} from 'react-router-dom' +import {useHistory, useRouteMatch} from 'react-router-dom' import {Board} from '../../blocks/board' import IconButton from '../../widgets/buttons/iconButton' @@ -23,6 +23,8 @@ import {IUser, UserConfigPatch, UserPropPrefix} from '../../user' import {getMe, patchProps} from '../../store/users' import {BaseTourSteps, TOUR_BASE} from '../onboardingTour' +import {Utils} from "../../utils" + import BoardTemplateSelectorPreview from './boardTemplateSelectorPreview' import BoardTemplateSelectorItem from './boardTemplateSelectorItem' @@ -44,10 +46,7 @@ const BoardTemplateSelector = (props: Props) => { const me = useAppSelector(getMe) const showBoard = useCallback(async (boardId) => { - const params = {...match.params, boardId: boardId || ''} - delete params.viewId - const newPath = generatePath(match.path, params) - history.push(newPath) + Utils.showBoard(boardId, match, history) if (onClose) { onClose() } diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx index 9c7308857..4a056899c 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx @@ -31,6 +31,8 @@ const BoardTemplateSelectorItem = (props: Props) => { onEdit(template.id) }, [onEdit, template]) + console.log(`Template Metadata: name: ${template.title} template version: ${template.templateVersion}`) + return (
{ switch (viewType) { @@ -82,17 +83,25 @@ const SidebarBoardItem = (props: Props) => { const board = props.board - const handleDuplicateBoard = useCallback(() => { - return mutator.duplicateBoard( + const handleDuplicateBoard = useCallback(async(asTemplate: boolean) => { + const blocksAndBoards = await mutator.duplicateBoard( board.id, undefined, - board.isTemplate, + asTemplate, undefined, () => { Utils.showBoard(board.id, match, history) return Promise.resolve() } ) + + if (blocksAndBoards.boards.length === 0) { + return + } + + const boardId = blocksAndBoards.boards[0].id + Utils.showBoard(boardId, match, history) + }, [board.id]) const title = board.title || intl.formatMessage({id: 'Sidebar.untitled-board', defaultMessage: '(Untitled Board)'}) @@ -151,7 +160,13 @@ const SidebarBoardItem = (props: Props) => { id='duplicateBoard' name={intl.formatMessage({id: 'Sidebar.duplicate-board', defaultMessage: 'Duplicate board'})} icon={} - onClick={handleDuplicateBoard} + onClick={() => handleDuplicateBoard(board.isTemplate)} + /> + } + onClick={() => handleDuplicateBoard(true)} /> diff --git a/webapp/src/components/sidebar/sidebarCategory.tsx b/webapp/src/components/sidebar/sidebarCategory.tsx index 2c3fd3d73..eb4567dc1 100644 --- a/webapp/src/components/sidebar/sidebarCategory.tsx +++ b/webapp/src/components/sidebar/sidebarCategory.tsx @@ -60,15 +60,6 @@ const SidebarCategory = (props: Props) => { const teamID = team?.id || '' const showBoard = useCallback((boardId) => { - // // if the same board, reuse the match params - // // otherwise remove viewId and cardId, results in first view being selected - // const params = {...match.params, boardId: boardId || ''} - // if (boardId !== match.params.boardId) { - // params.viewId = undefined - // params.cardId = undefined - // } - // const newPath = generatePath(match.path, params) - // history.push(newPath) Utils.showBoard(boardId, match, history) props.hideSidebar() }, [match, history]) diff --git a/webapp/src/components/workspace.scss b/webapp/src/components/workspace.scss index 14357dca5..adcf0d246 100644 --- a/webapp/src/components/workspace.scss +++ b/webapp/src/components/workspace.scss @@ -17,6 +17,8 @@ background-color: rgba(230, 220, 192, 0.9); text-align: center; padding: 10px; + color: rgb(63, 67, 80); + font-weight: bold; } } } diff --git a/webapp/src/mutator.ts b/webapp/src/mutator.ts index f77762ceb..c8e617b17 100644 --- a/webapp/src/mutator.ts +++ b/webapp/src/mutator.ts @@ -1014,7 +1014,7 @@ class Mutator { afterRedo?: (newBoardId: string) => Promise, beforeUndo?: () => Promise, toTeam?: string, - ): Promise<[Block[], string]> { + ): Promise { return undoManager.perform( async () => { const boardsAndBlocks = await octoClient.duplicateBoard(boardId, asTemplate, toTeam) From 9aa29bc2bf961301c8a64891e2d0576a2f493149 Mon Sep 17 00:00:00 2001 From: master7 Date: Wed, 30 Mar 2022 12:47:31 +0200 Subject: [PATCH 12/51] Translated using Weblate (Polish) Currently translated at 100.0% (288 of 288 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/pl/ --- webapp/i18n/pl.json | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/pl.json b/webapp/i18n/pl.json index 5dc4de0de..064811138 100644 --- a/webapp/i18n/pl.json +++ b/webapp/i18n/pl.json @@ -126,6 +126,8 @@ "GalleryCard.delete": "Usuń", "GalleryCard.duplicate": "Duplikuj", "General.BoardCount": "{count, plural, one {# Tablica} other {# Tablice}}", + "GroupBy.hideEmptyGroups": "Ukryj {count} pustych grup", + "GroupBy.showHiddenGroups": "Pokaż {count} ukrytych grup", "GroupBy.ungroup": "Rozgrupuj", "KanbanCard.copiedLink": "Skopiowane!", "KanbanCard.copyLink": "Kopiuj odnośnik", @@ -136,6 +138,20 @@ "Mutator.new-card-from-template": "nowa karta z szablonu", "Mutator.new-template-from-board": "nowy szablon z tablicy", "Mutator.new-template-from-card": "nowy szablon z karty", + "OnboardingTour.AddComments.Body": "Możesz komentować wydarzenia, a nawet @mention innych użytkowników Mattermost, aby zwrócić na siebie ich uwagę.", + "OnboardingTour.AddComments.Title": "Dodaj komentarze", + "OnboardingTour.AddDescription.Body": "Dodaj opis do swojej karty, aby koledzy z zespołu wiedzieli, czego ona dotyczy .", + "OnboardingTour.AddDescription.Title": "Dodaj opis", + "OnboardingTour.AddProperties.Body": "Dodawaj różne właściwości do kart, aby zwiększyć ich moc!", + "OnboardingTour.AddProperties.Title": "Dodaj właściwości", + "OnboardingTour.AddView.Body": "Przejdź tutaj, aby utworzyć nowy widok w celu uporządkowania tablicy przy użyciu różnych układów.", + "OnboardingTour.AddView.Title": "Dodaj nowy widok", + "OnboardingTour.CopyLink.Body": "Karty można udostępniać kolegom z zespołu, kopiując łącze i wklejając je w kanale, Wiadomości Bezpośredniej lub Wiadomości Grupowej.", + "OnboardingTour.CopyLink.Title": "Kopiuj odnośnik", + "OnboardingTour.OpenACard.Body": "Otwórz kartę, aby poznać różne sposoby, w których Tablice mogą pomóc Ci w organizacji pracy.", + "OnboardingTour.OpenACard.Title": "Otwórz kartę", + "OnboardingTour.ShareBoard.Body": "Możesz udostępniać swoją tablicę wewnętrznie, w ramach zespołu, lub publikować ją publicznie, aby była widoczna poza organizacją.", + "OnboardingTour.ShareBoard.Title": "Udostępnij tablicę", "PropertyMenu.Delete": "Usuń", "PropertyMenu.changeType": "Zmień typ właściwości", "PropertyMenu.selectType": "Wybierz typ właściwości", @@ -176,6 +192,7 @@ "Sidebar.delete-board": "Usuń tablicę", "Sidebar.duplicate-board": "Duplikuj tablicę", "Sidebar.export-archive": "Eksportuj archiwum", + "Sidebar.import": "Importuj", "Sidebar.import-archive": "Importuj archiwum", "Sidebar.invite-users": "Zaproś użytkowników", "Sidebar.logout": "Wyloguj się", @@ -242,18 +259,32 @@ "ViewTitle.show-description": "pokaż opis", "ViewTitle.untitled-board": "Tablica bez tytułu", "WelcomePage.Description": "Tablice to narzędzie do zarządzania projektami, które pomaga definiować, organizować, śledzić i zarządzać pracą w zespołach, wykorzystując widok znanych tablic kanban", - "WelcomePage.Explore.Button": "Poznaj", + "WelcomePage.Explore.Button": "Wybierz się na wycieczkę", "WelcomePage.Heading": "Witamy w Tablicach", + "WelcomePage.NoThanks.Text": "Nie, dzięki, sam sobie z tym poradzę", "Workspace.editing-board-template": "Edytujesz szablon tablicy.", "calendar.month": "Miesiąc", "calendar.today": "DZIŚ", "calendar.week": "Tydzień", "default-properties.badges": "Uwagi i opis", "default-properties.title": "Tytuł", + "error.back-to-boards": "Powrót do tablic", + "error.back-to-home": "Powrót na stronę główną", + "error.go-login": "Zaloguj się", + "error.not-logged-in": "Być może Twoja sesja wygasła lub nie jesteś zalogowany.", + "error.page.title": "Przepraszam, coś poszło nie tak", "error.relogin": "Zaloguj się ponownie", + "error.unknown": "Wystąpił błąd.", + "error.workspace-undefined": "Nie jest to prawidłowy obszar roboczy.", + "generic.previous": "Wstecz", "login.log-in-button": "Zaloguj się", "login.log-in-title": "Zaloguj się", "login.register-button": "lub załóż konto, jeśli jeszcze go nie masz", "register.login-button": "lub zaloguj się, jeśli masz już konto", - "register.signup-title": "Zarejestruj się na swoim koncie" + "register.signup-title": "Zarejestruj się na swoim koncie", + "tutorial_tip.finish_tour": "Gotowe", + "tutorial_tip.got_it": "Jasne", + "tutorial_tip.ok": "Dalej", + "tutorial_tip.out": "Zrezygnuj z tych porad", + "tutorial_tip.seen": "Widziałeś to wcześniej?" } From baf5a5e509b36fc70710ed27b195faa15408d2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=B3th=20Csaba=20//=20Online=20ERP=20Hungary=20Kft?= Date: Wed, 30 Mar 2022 12:47:32 +0200 Subject: [PATCH 13/51] Translated using Weblate (Hungarian) Currently translated at 100.0% (288 of 288 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/hu/ --- webapp/i18n/hu.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webapp/i18n/hu.json b/webapp/i18n/hu.json index 8f442ca64..24edf001d 100644 --- a/webapp/i18n/hu.json +++ b/webapp/i18n/hu.json @@ -268,6 +268,8 @@ "calendar.week": "Hét", "default-properties.badges": "Megjegyzések és leírás", "default-properties.title": "Cím", + "error.back-to-boards": "Vissza a táblákba", + "error.back-to-home": "Vissza a kezdőlapra", "error.go-login": "Bejelentkezés", "error.not-logged-in": "Lehet, hogy lejárt a munkamenete, vagy nincs bejelentkezve.", "error.page.title": "Sajnálom, valami rosszul sikerült", From ab72eba26b13496b4f01b9dcee3bbef6cd2e0e49 Mon Sep 17 00:00:00 2001 From: Milo Ivir Date: Wed, 30 Mar 2022 12:47:32 +0200 Subject: [PATCH 14/51] Translated using Weblate (Croatian) Currently translated at 95.0% (285 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/hr/ Translated using Weblate (Croatian) Currently translated at 99.3% (286 of 288 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/hr/ --- webapp/i18n/hr.json | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/hr.json b/webapp/i18n/hr.json index 2c1d77ef0..7c64070c4 100644 --- a/webapp/i18n/hr.json +++ b/webapp/i18n/hr.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "Bez svojstva {property}", "BoardComponent.no-property-title": "Elementi s praznim svojstvom {property} smjestit će se ovdje. Ovaj se stupac ne može ukloniti.", "BoardComponent.show": "Prikaži", + "BoardMember.schemeAdmin": "Administrator", + "BoardMember.schemeEditor": "Urednik", + "BoardMember.schemeNone": "Ništa", "BoardPage.newVersion": "Dostupna je nova verzija za „Ploče”. Pritisni ovdje za ponovno učitavanje.", "BoardPage.syncFailed": "Ploča se može izbrisati ili pristup opozvati.", "BoardTemplateSelector.add-template": "Novi predložak", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "Izbriši", "BoardTemplateSelector.description": "Za početak odaberi predložak. Prilagodi predložak kako bi odgovarao tvojim potrebama ili stvori praznu ploču.", "BoardTemplateSelector.edit-template": "Uredi", - "BoardTemplateSelector.plugin.no-content-description": "Dodaj ploču u bočnu traku koristeći bilo koji od niže dolje definiranih predložaka ili počni ispočetka.{lineBreak} „{workspaceName}” članovi imat će pristup ovdje stvorenim pločama.", - "BoardTemplateSelector.plugin.no-content-title": "Stvori ploču u {workspaceName}", + "BoardTemplateSelector.plugin.no-content-description": "Dodaj ploču u bočnu traku koristeći bilo koji od niže dolje definiranih predložaka ili počni ispočetka.{lineBreak} Članovi tima „{teamName}” imat će pristup ovdje stvorenim pločama.", + "BoardTemplateSelector.plugin.no-content-title": "Stvori ploču u timu {teamName}", "BoardTemplateSelector.title": "Stvori ploču", "BoardTemplateSelector.use-this-template": "Koristi ovaj predložak", + "BoardsSwitcher.Title": "Pronađi ploče", "BoardsUnfurl.Remainder": "+ još {remainder}", "BoardsUnfurl.Updated": "Aktulaizirano {time}", "Calculations.Options.average.displayName": "Prosjek", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "Potvrdi brisanje karte!", "CardDialog.editing-template": "Uređuješ predložak.", "CardDialog.nocard": "Ova kartica ne postoji ili nije dostupna.", + "Categories.CreateCategoryDialog.CancelText": "Odustani", + "Categories.CreateCategoryDialog.CreateText": "Stvori", + "Categories.CreateCategoryDialog.Placeholder": "Zadaj ime kategoriji", + "Categories.CreateCategoryDialog.UpdateText": "Aktualiziraj", "CenterPanel.Share": "Dijeli", "ColorOption.selectColor": "Odaberi boju {color}", "Comment.delete": "Izbriši", @@ -121,6 +129,9 @@ "Filter.not-includes": "ne uključuje", "FilterComponent.add-filter": "+ Dodaj filtar", "FilterComponent.delete": "Izbriši", + "FindBoFindBoardsDialog.IntroText": "Traži ploče", + "FindBoardsDialog.NoResultsFor": "Nema rezultata za „{searchQuery}”", + "FindBoardsDialog.NoResultsSubtext": "Provjeri pravopis ili pretraži s jednim drugim pojmom.", "GalleryCard.copiedLink": "Kopirano!", "GalleryCard.copyLink": "Kopiraj poveznicu", "GalleryCard.delete": "Izbriši", @@ -266,6 +277,8 @@ "calendar.week": "Tjedan", "default-properties.badges": "Komentari i opis", "default-properties.title": "Naslov", + "error.back-to-boards": "Natrag na ploče", + "error.back-to-home": "Natrag na početnu stranicu", "error.go-login": "Prijava", "error.not-logged-in": "Tvoja sesija je istekla ili nisi prijavljen/prijavljena.", "error.page.title": "Oprosti, dogodila se greška", From de68861bf3e8ff638a21858113c109ac8b11438c Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 30 Mar 2022 12:47:32 +0200 Subject: [PATCH 15/51] Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ --- webapp/i18n/ar.json | 1 - webapp/i18n/ca.json | 6 ------ webapp/i18n/de.json | 12 ------------ webapp/i18n/el.json | 1 - webapp/i18n/en_AU.json | 12 ------------ webapp/i18n/es.json | 12 ------------ webapp/i18n/et.json | 2 -- webapp/i18n/fr.json | 11 ----------- webapp/i18n/hr.json | 12 ------------ webapp/i18n/hu.json | 12 ------------ webapp/i18n/id.json | 12 ------------ webapp/i18n/it.json | 12 ------------ webapp/i18n/ja.json | 12 ------------ webapp/i18n/kk.json | 12 ------------ webapp/i18n/ko.json | 12 ------------ webapp/i18n/ml.json | 12 ------------ webapp/i18n/nl.json | 12 ------------ webapp/i18n/oc.json | 12 ------------ webapp/i18n/pl.json | 12 ------------ webapp/i18n/pt_BR.json | 7 ------- webapp/i18n/ru.json | 12 ------------ webapp/i18n/sk.json | 12 ------------ webapp/i18n/sv.json | 12 ------------ webapp/i18n/tr.json | 12 ------------ webapp/i18n/zh_Hans.json | 7 ------- webapp/i18n/zh_Hant.json | 10 ---------- 26 files changed, 261 deletions(-) diff --git a/webapp/i18n/ar.json b/webapp/i18n/ar.json index 07d832efb..5f6845d7f 100644 --- a/webapp/i18n/ar.json +++ b/webapp/i18n/ar.json @@ -24,7 +24,6 @@ "ContentBlock.DeleteAction": "حذف", "ContentBlock.addElement": "إضافة {type}", "ContentBlock.text": "نص", - "DashboardPage.title": "أهلًا بك إلى Focalboard (تجريبي)!", "EditableDayPicker.today": "اليوم", "Filter.is-empty": "فارغ", "Filter.is-not-empty": "ليس فارغًا", diff --git a/webapp/i18n/ca.json b/webapp/i18n/ca.json index e3022b2eb..95e13fa3a 100644 --- a/webapp/i18n/ca.json +++ b/webapp/i18n/ca.json @@ -45,9 +45,7 @@ "KanbanCard.delete": "Eliminar", "KanbanCard.duplicate": "Duplicar", "KanbanCard.untitled": "Sense títol", - "Mutator.duplicate-board": "duplicar tauler", "Mutator.new-card-from-template": "nova targeta des de plantilla", - "Mutator.new-template-from-board": "nova plantilla des de tauler", "Mutator.new-template-from-card": "nova plantilla des de targeta", "PropertyMenu.Delete": "Eliminar", "PropertyMenu.changeType": "Canviar el tipus de propietat", @@ -81,19 +79,15 @@ "Sidebar.add-board": "+ Afegir tauler", "Sidebar.changePassword": "Canvi de contrasenya", "Sidebar.delete-board": "Eliminar el tauler", - "Sidebar.duplicate-board": "Duplicar el tauler", "Sidebar.export-archive": "Arxiu d'exportació", "Sidebar.import-archive": "Arxiu d'importació", "Sidebar.invite-users": "Convida usuaris", "Sidebar.logout": "Tanca sessió", - "Sidebar.no-views-in-board": "Cap pàgina a l'interior", "Sidebar.random-icons": "Icones aleatòries", "Sidebar.set-language": "Seleccionar idioma", "Sidebar.set-theme": "Definir un tema", "Sidebar.settings": "Paràmetres", - "Sidebar.template-from-board": "Nova plantilla desde tauler", "Sidebar.untitled-board": "(Tauler sense títol)", - "Sidebar.untitled-view": "(Vista sense títol)", "TableComponent.add-icon": "Afegeix icona", "TableComponent.name": "Nom", "TableComponent.plus-new": "+ Nou", diff --git a/webapp/i18n/de.json b/webapp/i18n/de.json index df8ad2295..e204de441 100644 --- a/webapp/i18n/de.json +++ b/webapp/i18n/de.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Nach unten bewegen", "ContentBlock.moveUp": "Nach oben bewegen", "ContentBlock.text": "Text", - "DashboardPage.CenterPanel.ChangeChannels": "Verwende den Umschalter zum einfachen Wechseln der Kanäle", - "DashboardPage.CenterPanel.NoWorkspaces": "Leider konnten wir keine Kanäle finden, die diesem Begriff entsprechen", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Bitte versuche nach einem anderen Begriff zu suchen", - "DashboardPage.showEmpty": "Kanäle ohne Boards anzeigen", - "DashboardPage.title": "Dashboard", "DeleteBoardDialog.confirm-cancel": "Abbrechen", "DeleteBoardDialog.confirm-delete": "Löschen", "DeleteBoardDialog.confirm-info": "Bist du sicher, dass du das Board \"{boardTitle}\" löschen möchtest? Wenn du es löschen, werden allen Karten auf diesem Board gelöscht.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "Löschen", "KanbanCard.duplicate": "Duplizieren", "KanbanCard.untitled": "Unbenannt", - "Mutator.duplicate-board": "Board duplizieren", "Mutator.new-card-from-template": "neue Karte aus Vorlage", - "Mutator.new-template-from-board": "neue Vorlage aus Board", "Mutator.new-template-from-card": "neue Vorlage aus Karte", "OnboardingTour.AddComments.Body": "Du kannst Themen kommentieren und sogar deine Mattermost-Kollegen @erwähnen, um deren Aufmerksamkeit zu erhalten.", "OnboardingTour.AddComments.Title": "Kommentare hinzufügen", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ Board hinzufügen", "Sidebar.changePassword": "Passwort ändern", "Sidebar.delete-board": "Board löschen", - "Sidebar.duplicate-board": "Board duplizieren", "Sidebar.export-archive": "Archiv exportieren", "Sidebar.import": "Importieren", "Sidebar.import-archive": "Archiv importieren", "Sidebar.invite-users": "Nutzer einladen", "Sidebar.logout": "Ausloggen", - "Sidebar.no-more-workspaces": "Keine Arbeitsbereiche mehr", - "Sidebar.no-views-in-board": "Keine Seiten enthalten", "Sidebar.random-icons": "Zufällige Icons", "Sidebar.set-language": "Sprache übernehmen", "Sidebar.set-theme": "Theme übernehmen", "Sidebar.settings": "Einstellungen", - "Sidebar.template-from-board": "Neue Vorlage aus Board", "Sidebar.untitled-board": "(Unbenanntes Board)", - "Sidebar.untitled-view": "(Unbenannte Ansicht)", "TableComponent.add-icon": "Symbol hinzufügen", "TableComponent.name": "Name", "TableComponent.plus-new": "+ Neu", diff --git a/webapp/i18n/el.json b/webapp/i18n/el.json index 79ab096e5..a68d98269 100644 --- a/webapp/i18n/el.json +++ b/webapp/i18n/el.json @@ -20,7 +20,6 @@ "ContentBlock.moveDown": "Μετακίνηση κάτω", "ContentBlock.moveUp": "Μετακίνηση επάνω", "ContentBlock.text": "κείμενο", - "DashboardPage.showEmpty": "Προβολή άδειου", "EditableDayPicker.today": "Σήμερα", "Filter.includes": "περιέχει", "Filter.is-empty": "είναι άδειο", diff --git a/webapp/i18n/en_AU.json b/webapp/i18n/en_AU.json index 74be32f81..2f3473bac 100644 --- a/webapp/i18n/en_AU.json +++ b/webapp/i18n/en_AU.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Move down", "ContentBlock.moveUp": "Move up", "ContentBlock.text": "text", - "DashboardPage.CenterPanel.ChangeChannels": "Use the switcher to easily change channels", - "DashboardPage.CenterPanel.NoWorkspaces": "No channels matching that term could be found", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Please try searching for another term", - "DashboardPage.showEmpty": "Show empty", - "DashboardPage.title": "Dashboard", "DeleteBoardDialog.confirm-cancel": "Cancel", "DeleteBoardDialog.confirm-delete": "Delete", "DeleteBoardDialog.confirm-info": "Are you sure you want to delete the board '{boardTitle}'? This will remove all cards in the board.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "Delete", "KanbanCard.duplicate": "Duplicate", "KanbanCard.untitled": "Untitled", - "Mutator.duplicate-board": "duplicate board", "Mutator.new-card-from-template": "new card from template", - "Mutator.new-template-from-board": "new template from board", "Mutator.new-template-from-card": "new template from card", "OnboardingTour.AddComments.Body": "You can comment on issues, and even @mention your fellow Mattermost users to get their attention.", "OnboardingTour.AddComments.Title": "Add comments", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ Add board", "Sidebar.changePassword": "Change password", "Sidebar.delete-board": "Delete board", - "Sidebar.duplicate-board": "Duplicate board", "Sidebar.export-archive": "Export archive", "Sidebar.import": "Import", "Sidebar.import-archive": "Import archive", "Sidebar.invite-users": "Invite users", "Sidebar.logout": "Log out", - "Sidebar.no-more-workspaces": "No more workspaces", - "Sidebar.no-views-in-board": "No pages contained within", "Sidebar.random-icons": "Random icons", "Sidebar.set-language": "Set language", "Sidebar.set-theme": "Set theme", "Sidebar.settings": "Settings", - "Sidebar.template-from-board": "New template from board", "Sidebar.untitled-board": "(Untitled Board)", - "Sidebar.untitled-view": "(Untitled View)", "TableComponent.add-icon": "Add icon", "TableComponent.name": "Name", "TableComponent.plus-new": "+ New", diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json index 487d9d5e5..169a05325 100644 --- a/webapp/i18n/es.json +++ b/webapp/i18n/es.json @@ -41,11 +41,6 @@ "ContentBlock.moveDown": "Mover hacia abajo", "ContentBlock.moveUp": "Mover hacia arriba", "ContentBlock.text": "texto", - "DashboardPage.CenterPanel.ChangeChannels": "Usa el selector para cambiar fácilmente de canal", - "DashboardPage.CenterPanel.NoWorkspaces": "Lo sentimos, no pudimos encontrar ningún canal que coincida con ese término", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Por favor, intenta buscar otro término", - "DashboardPage.showEmpty": "Mostrar vacío", - "DashboardPage.title": "Tablero", "Dialog.closeDialog": "Cerrar diálogo", "EditableDayPicker.today": "Hoy", "Error.websocket-closed": "Conexión de Websocket cerrada, conexión interrumpida. Si esto persiste, verifique la configuración de su servidor o proxy web.", @@ -66,9 +61,7 @@ "KanbanCard.delete": "Eliminar", "KanbanCard.duplicate": "Duplicar", "KanbanCard.untitled": "Sin título", - "Mutator.duplicate-board": "duplicar panel", "Mutator.new-card-from-template": "nueva tarjeta desde una plantilla", - "Mutator.new-template-from-board": "nueva plantilla a partir de un panel", "Mutator.new-template-from-card": "nueva plantilla desde una tarjeta", "PropertyMenu.Delete": "Borrar", "PropertyMenu.changeType": "Cambiar el tipo de propiedad", @@ -102,20 +95,15 @@ "Sidebar.add-board": "+ Añadir panel", "Sidebar.changePassword": "Cambiar contraseña", "Sidebar.delete-board": "Borrar Panel", - "Sidebar.duplicate-board": "Duplicar panel", "Sidebar.export-archive": "Exportar Archivo", "Sidebar.import-archive": "Importar Archivo", "Sidebar.invite-users": "Invitar usuarios", "Sidebar.logout": "Cerrar sesión", - "Sidebar.no-more-workspaces": "No hay más espacios de trabajo", - "Sidebar.no-views-in-board": "No hay páginas dentro", "Sidebar.random-icons": "Íconos random", "Sidebar.set-language": "Establecer idioma", "Sidebar.set-theme": "Establecer apariencia", "Sidebar.settings": "Configuración", - "Sidebar.template-from-board": "Nueva plantilla desde un panel", "Sidebar.untitled-board": "(Panel sin titulo)", - "Sidebar.untitled-view": "(Vista sin titulo)", "TableComponent.add-icon": "Añadir Icono", "TableComponent.name": "Nombre", "TableComponent.plus-new": "+ Nueva", diff --git a/webapp/i18n/et.json b/webapp/i18n/et.json index 237bdd750..9a2f89a7a 100644 --- a/webapp/i18n/et.json +++ b/webapp/i18n/et.json @@ -60,8 +60,6 @@ "ContentBlock.moveDown": "Liiguta alla", "ContentBlock.moveUp": "Liiguta üles", "ContentBlock.text": "tekst", - "DashboardPage.showEmpty": "Näita tühja", - "DashboardPage.title": "Töölaud", "DeleteBoardDialog.confirm-cancel": "Loobu", "DeleteBoardDialog.confirm-delete": "Kustuta", "EditableDayPicker.today": "Täna", diff --git a/webapp/i18n/fr.json b/webapp/i18n/fr.json index 79aaf1ead..def2753aa 100644 --- a/webapp/i18n/fr.json +++ b/webapp/i18n/fr.json @@ -66,10 +66,6 @@ "ContentBlock.moveDown": "Déplacer vers le bas", "ContentBlock.moveUp": "Déplacer vers le haut", "ContentBlock.text": "texte", - "DashboardPage.CenterPanel.ChangeChannels": "Utiliser le sélecteur pour changer de canal", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Essayez de rechercher un autre terme", - "DashboardPage.showEmpty": "Tout afficher", - "DashboardPage.title": "Bienvenue dans Focalboard !", "DeleteBoardDialog.confirm-cancel": "Annuler", "DeleteBoardDialog.confirm-delete": "Supprimer", "DeleteBoardDialog.confirm-info": "Êtes-vous sûr de vouloir supprimer le tableau «{boardTitle}» ? Cela supprimera toutes les cartes dans ce tableau.", @@ -96,9 +92,7 @@ "KanbanCard.delete": "Supprimer", "KanbanCard.duplicate": "Dupliquer", "KanbanCard.untitled": "Sans titre", - "Mutator.duplicate-board": "dupliquer le tableau", "Mutator.new-card-from-template": "nouvelle carte depuis un modèle", - "Mutator.new-template-from-board": "nouveau modèle depuis un tableau", "Mutator.new-template-from-card": "nouveau modèle depuis une carte", "OnboardingTour.AddComments.Body": "Vous pouvez commenter les bugs et même @mentionner les utilisateurs de Mattermost pour attirer leur attention.", "OnboardingTour.AddComments.Title": "Ajouter des commentaires", @@ -152,21 +146,16 @@ "Sidebar.add-board": "+ Ajouter un tableau", "Sidebar.changePassword": "Modifier le mot de passe", "Sidebar.delete-board": "Supprimer le tableau", - "Sidebar.duplicate-board": "Dupliquer le tableau", "Sidebar.export-archive": "Exporter une archive", "Sidebar.import": "Importer", "Sidebar.import-archive": "Importer une archive", "Sidebar.invite-users": "Inviter des utilisateurs", "Sidebar.logout": "Se déconnecter", - "Sidebar.no-more-workspaces": "Plus d'espace de travail", - "Sidebar.no-views-in-board": "Pas de page incluse", "Sidebar.random-icons": "Icônes aléatoires", "Sidebar.set-language": "Choisir la langue", "Sidebar.set-theme": "Choisir le thème", "Sidebar.settings": "Réglages", - "Sidebar.template-from-board": "Nouveau modèle depuis le tableau", "Sidebar.untitled-board": "(Tableau sans titre)", - "Sidebar.untitled-view": "(Vue sans titre)", "TableComponent.add-icon": "Ajouter une icône", "TableComponent.name": "Nom", "TableComponent.plus-new": "+ Nouveau", diff --git a/webapp/i18n/hr.json b/webapp/i18n/hr.json index 7c64070c4..79ca3958e 100644 --- a/webapp/i18n/hr.json +++ b/webapp/i18n/hr.json @@ -109,11 +109,6 @@ "ContentBlock.moveDown": "Pomakni dolje", "ContentBlock.moveUp": "Pomakni gore", "ContentBlock.text": "tekst", - "DashboardPage.CenterPanel.ChangeChannels": "Koristi prekidač za jednostavno mijenjanja kanala", - "DashboardPage.CenterPanel.NoWorkspaces": "Nažalost, nismo mogli pronaći nijedan kanal koji odgovara tom pojmu", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Pokušaj tražiti jedan drugi pojam", - "DashboardPage.showEmpty": "Prikaži prazne", - "DashboardPage.title": "Pregledna ploča", "DeleteBoardDialog.confirm-cancel": "Odustani", "DeleteBoardDialog.confirm-delete": "Izbriši", "DeleteBoardDialog.confirm-info": "Stvarno želiš izbrisati ploču „{boardTitle}”? Brisanjem će se izbrisati sve karte na ploči.", @@ -143,9 +138,7 @@ "KanbanCard.delete": "Izbriši", "KanbanCard.duplicate": "Dupliciraj", "KanbanCard.untitled": "Bez naslova", - "Mutator.duplicate-board": "dupliciraj ploču", "Mutator.new-card-from-template": "nova kartica iz predloška", - "Mutator.new-template-from-board": "novi predložak iz ploče", "Mutator.new-template-from-card": "novi predložak iz kartice", "OnboardingTour.AddComments.Body": "Probleme možeš komentirati. Možeš čak i @spomenuti svoje Mattermost kolege za privlačenje njihove pozornosti.", "OnboardingTour.AddComments.Title": "Dodaj komentare", @@ -199,21 +192,16 @@ "Sidebar.add-board": "+ Dodaj ploču", "Sidebar.changePassword": "Promijeni lozinku", "Sidebar.delete-board": "Izbriši ploču", - "Sidebar.duplicate-board": "Dupliciraj ploču", "Sidebar.export-archive": "Izvezi arhivu", "Sidebar.import": "Uvezi", "Sidebar.import-archive": "Uvezi arhivu", "Sidebar.invite-users": "Pozovi korisnika", "Sidebar.logout": "Odjavi se", - "Sidebar.no-more-workspaces": "Nema daljnjih radnih prostora", - "Sidebar.no-views-in-board": "Unutra nema stranica", "Sidebar.random-icons": "Slučajne ikone", "Sidebar.set-language": "Postavi jezik", "Sidebar.set-theme": "Postavi temu", "Sidebar.settings": "Postavke", - "Sidebar.template-from-board": "Novi predložak iz ploče", "Sidebar.untitled-board": "(Ploča bez naslova)", - "Sidebar.untitled-view": "(Prikaz bez naslova)", "TableComponent.add-icon": "Dodaj ikonu", "TableComponent.name": "Ime", "TableComponent.plus-new": "+ Novo", diff --git a/webapp/i18n/hu.json b/webapp/i18n/hu.json index 24edf001d..1a1dd1277 100644 --- a/webapp/i18n/hu.json +++ b/webapp/i18n/hu.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Mozgatás le", "ContentBlock.moveUp": "Mozgatás fel", "ContentBlock.text": "szöveg", - "DashboardPage.CenterPanel.ChangeChannels": "Használja a váltót a beszélgetések könnyű váltásához", - "DashboardPage.CenterPanel.NoWorkspaces": "Sajnáljuk, de nem találtunk egy beszélgetést sem ami ennek a kifejezésnek megfelelne", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Kérjük próbáljon keresni egy másik kifejezésre", - "DashboardPage.showEmpty": "Mutassa az üreseket", - "DashboardPage.title": "Vezérlőpult", "DeleteBoardDialog.confirm-cancel": "Mégsem", "DeleteBoardDialog.confirm-delete": "Törlés", "DeleteBoardDialog.confirm-info": "Biztos benne, hogy törölni szeretné a “{boardTitle}” táblát? A törlésével az összes benne lévő kártya is törlődni fog.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "Törlés", "KanbanCard.duplicate": "Duplikálás", "KanbanCard.untitled": "Névtelen", - "Mutator.duplicate-board": "tábla duplikálása", "Mutator.new-card-from-template": "új kártya sablonból", - "Mutator.new-template-from-board": "új sablon táblából", "Mutator.new-template-from-card": "új sablon kártyából", "OnboardingTour.AddComments.Body": "Hozzászólhat a témákhoz, sőt, más Mattermost felhasználó társát is @megemlítheti, hogy felhívja a figyelmüket.", "OnboardingTour.AddComments.Title": "Megjegyzés hozzáadása", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ Tábla hozzáadása", "Sidebar.changePassword": "Jelszó módosítása", "Sidebar.delete-board": "Tábla törlése", - "Sidebar.duplicate-board": "Tábla duplikálása", "Sidebar.export-archive": "Archiváltak exportálása", "Sidebar.import": "Importálás", "Sidebar.import-archive": "Archiváltak importálása", "Sidebar.invite-users": "Felhasználók meghívása", "Sidebar.logout": "Kijelentkezés", - "Sidebar.no-more-workspaces": "Nincs több munkaterület", - "Sidebar.no-views-in-board": "Nincsennek benne lapok", "Sidebar.random-icons": "Véletlen ikonok", "Sidebar.set-language": "Nyelv megadása", "Sidebar.set-theme": "Téma megadása", "Sidebar.settings": "Beállítások", - "Sidebar.template-from-board": "Új sablon táblából", "Sidebar.untitled-board": "(Névtelen tábla)", - "Sidebar.untitled-view": "(Névtelen nézet)", "TableComponent.add-icon": "Ikon hozzáadása", "TableComponent.name": "Név", "TableComponent.plus-new": "+ Új", diff --git a/webapp/i18n/id.json b/webapp/i18n/id.json index cc636d79a..0a29ec205 100644 --- a/webapp/i18n/id.json +++ b/webapp/i18n/id.json @@ -44,11 +44,6 @@ "ContentBlock.moveDown": "Turunkan", "ContentBlock.moveUp": "Naikkan", "ContentBlock.text": "teks", - "DashboardPage.CenterPanel.ChangeChannels": "Gunakan tombol saklar untuk berpindah kanal dengan mudah", - "DashboardPage.CenterPanel.NoWorkspaces": "Maaf, kami tidak dapat menemukan kanal yang sesuai dengan kata tersebut", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Mohon lalukan pencarian untuk kata yang lain", - "DashboardPage.showEmpty": "Tampilan kosong", - "DashboardPage.title": "Dasbor", "Dialog.closeDialog": "Tutup dialog", "EditableDayPicker.today": "Hari Ini", "Error.websocket-closed": "Koneksi ke soket web tertutup, koneksi terganggu. Jika hal ini terus berlanjut, periksa konfigurasi server atau proxy web Anda.", @@ -69,9 +64,7 @@ "KanbanCard.delete": "Hapus", "KanbanCard.duplicate": "Duplikasikan", "KanbanCard.untitled": "Tidak berjudul", - "Mutator.duplicate-board": "duplikasikan papan", "Mutator.new-card-from-template": "kartu baru dari template", - "Mutator.new-template-from-board": "template baru dari papan", "Mutator.new-template-from-card": "template baru dari kartu", "PropertyMenu.Delete": "Hapus", "PropertyMenu.changeType": "Ubah jenis properti", @@ -105,20 +98,15 @@ "Sidebar.add-board": "+ Tambahkan papan", "Sidebar.changePassword": "Ubah kata sandi", "Sidebar.delete-board": "Hapus papan", - "Sidebar.duplicate-board": "Duplikasikan papan", "Sidebar.export-archive": "Ekpor arsip", "Sidebar.import-archive": "Impor arsip", "Sidebar.invite-users": "Undang pengguna", "Sidebar.logout": "Keluar", - "Sidebar.no-more-workspaces": "Tidak ada ruang kerja", - "Sidebar.no-views-in-board": "Tidak ada halaman di dalam", "Sidebar.random-icons": "Ikon acak", "Sidebar.set-language": "Tetapkan bahasa", "Sidebar.set-theme": "Tetapkan tema", "Sidebar.settings": "Pengaturan", - "Sidebar.template-from-board": "Template baru dari papan", "Sidebar.untitled-board": "(Papan Tak Berjudul)", - "Sidebar.untitled-view": "(Tampilan Tak Berjudul)", "TableComponent.add-icon": "Tambahkan ikon", "TableComponent.name": "Nama", "TableComponent.plus-new": "+ Buat", diff --git a/webapp/i18n/it.json b/webapp/i18n/it.json index 689678806..a2fcb08ad 100644 --- a/webapp/i18n/it.json +++ b/webapp/i18n/it.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Sposta giù", "ContentBlock.moveUp": "Sposta su", "ContentBlock.text": "testo", - "DashboardPage.CenterPanel.ChangeChannels": "Usa lo switch per cambiare facilmente canali", - "DashboardPage.CenterPanel.NoWorkspaces": "Spiacente, non siamo riusciti a trovare nessuno canale corrispondente a quel termine", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Per favore prova a cercare un altro termine", - "DashboardPage.showEmpty": "Mostra vuoto", - "DashboardPage.title": "Dashboard", "DeleteBoardDialog.confirm-cancel": "Annulla", "DeleteBoardDialog.confirm-delete": "Elimina", "DeleteBoardDialog.confirm-info": "Sei sicuro di voler eliminare la bacheca \"{boardTitle}\"? Eliminandola, rimuoverai tutte le schede in bacheca.", @@ -132,9 +127,7 @@ "KanbanCard.delete": "Elimina", "KanbanCard.duplicate": "Duplica", "KanbanCard.untitled": "Senza titolo", - "Mutator.duplicate-board": "duplica contenitore", "Mutator.new-card-from-template": "nuova scheda da modello", - "Mutator.new-template-from-board": "nuovo modello da contenitore", "Mutator.new-template-from-card": "nuovo modello da scheda", "PropertyMenu.Delete": "Elimina", "PropertyMenu.changeType": "Cambia il tipo di proprietà", @@ -174,20 +167,15 @@ "Sidebar.add-board": "+ Aggiungi Contenitore", "Sidebar.changePassword": "Cambia password", "Sidebar.delete-board": "Elimina contenitore", - "Sidebar.duplicate-board": "Duplica contenitore", "Sidebar.export-archive": "Esporta archivio", "Sidebar.import-archive": "Importa archivio", "Sidebar.invite-users": "Invita utenti", "Sidebar.logout": "Logout", - "Sidebar.no-more-workspaces": "Non ci sono spazi di lavoro", - "Sidebar.no-views-in-board": "Nessuna pagina all'interno", "Sidebar.random-icons": "Icone casuali", "Sidebar.set-language": "Imposta la lingua", "Sidebar.set-theme": "Imposta il tema", "Sidebar.settings": "Impostazioni", - "Sidebar.template-from-board": "Nuovo modello da contenitore", "Sidebar.untitled-board": "(Contenitore senza titolo)", - "Sidebar.untitled-view": "(Vista senza titolo)", "TableComponent.add-icon": "Aggiungi icona", "TableComponent.name": "Nome", "TableComponent.plus-new": "+ Nuovo", diff --git a/webapp/i18n/ja.json b/webapp/i18n/ja.json index 3e6e0a7b5..1555f3b17 100644 --- a/webapp/i18n/ja.json +++ b/webapp/i18n/ja.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "下へ移動する", "ContentBlock.moveUp": "上へ移動する", "ContentBlock.text": "テキスト", - "DashboardPage.CenterPanel.ChangeChannels": "スイッチャーで簡単にチャンネルを変更できます", - "DashboardPage.CenterPanel.NoWorkspaces": "その言葉に一致するチャンネルは見つかりませんでした", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "別の言葉で検索してみてください", - "DashboardPage.showEmpty": "空のボードを表示", - "DashboardPage.title": "ダッシュボード", "DeleteBoardDialog.confirm-cancel": "キャンセル", "DeleteBoardDialog.confirm-delete": "削除", "DeleteBoardDialog.confirm-info": "本当にボード \"{boardTitle}\" を削除しますか? 削除すると、このボードのすべてのカードが削除されます。", @@ -134,9 +129,7 @@ "KanbanCard.delete": "削除", "KanbanCard.duplicate": "複製", "KanbanCard.untitled": "無題", - "Mutator.duplicate-board": "ボードを複製する", "Mutator.new-card-from-template": "テンプレートから新しいカードを作成", - "Mutator.new-template-from-board": "ボードから新しいテンプレートを作成", "Mutator.new-template-from-card": "カードから新しいテンプレートを作成", "OnboardingTour.AddComments.Body": "問題にコメントしたり、仲間のMattermostユーザーの注意を引くために@メンションすることもできます。", "OnboardingTour.AddComments.Title": "コメントを追加する", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ ボードを追加する", "Sidebar.changePassword": "パスワードを変更する", "Sidebar.delete-board": "ボードを削除", - "Sidebar.duplicate-board": "ボードを複製する", "Sidebar.export-archive": "エクスポート", "Sidebar.import": "インポート", "Sidebar.import-archive": "インポート", "Sidebar.invite-users": "ユーザーを招待する", "Sidebar.logout": "ログアウト", - "Sidebar.no-more-workspaces": "他のワークスペースがありません", - "Sidebar.no-views-in-board": "ページがありません", "Sidebar.random-icons": "ランダムアイコン", "Sidebar.set-language": "言語設定", "Sidebar.set-theme": "テーマ設定", "Sidebar.settings": "設定", - "Sidebar.template-from-board": "ボードから新しいテンプレートを作成", "Sidebar.untitled-board": "(無題のボード)", - "Sidebar.untitled-view": "(無題のビュー)", "TableComponent.add-icon": "アイコンを追加する", "TableComponent.name": "名前", "TableComponent.plus-new": "+ 新規", diff --git a/webapp/i18n/kk.json b/webapp/i18n/kk.json index 273016d1a..9de49584a 100644 --- a/webapp/i18n/kk.json +++ b/webapp/i18n/kk.json @@ -86,11 +86,6 @@ "ContentBlock.moveDown": "Түсіру", "ContentBlock.moveUp": "Көтеру", "ContentBlock.text": "мәтін", - "DashboardPage.CenterPanel.ChangeChannels": "Арналарды оңай өзгерту үшін ауыстырғышты қолданыныз", - "DashboardPage.CenterPanel.NoWorkspaces": "Кешіріңіз, біз бұл термінге сәйкес ешбір арнаны таба алмадық", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Басқа термінді іздеп көрініз", - "DashboardPage.showEmpty": "Босты көрсету", - "DashboardPage.title": "Дашборт", "DeleteBoardDialog.confirm-cancel": "Болдырмау", "DeleteBoardDialog.confirm-delete": "Жою", "DeleteBoardDialog.confirm-info": "\"{boardTitle}\" тақтасын шынымен жойғыңыз келе ме? Оны жою тақтадағы барлық кәртелерді жояды.", @@ -116,9 +111,7 @@ "KanbanCard.delete": "Жою", "KanbanCard.duplicate": "Көшірмесін жасау", "KanbanCard.untitled": "Атаусыз", - "Mutator.duplicate-board": "тақта көшірмесін жасау", "Mutator.new-card-from-template": "үлгіден жаңа кәрте жасау", - "Mutator.new-template-from-board": "тақтадан жаңа үлгі", "Mutator.new-template-from-card": "кәртеден жаңа үлгі", "PropertyMenu.Delete": "Жою", "PropertyMenu.changeType": "Property түрін өзгерту", @@ -154,20 +147,15 @@ "Sidebar.add-board": "+ Тақта қосу", "Sidebar.changePassword": "Кілтсөзді өзгерту", "Sidebar.delete-board": "Тақтаны жою", - "Sidebar.duplicate-board": "Тақтанын көшірмесін жасау", "Sidebar.export-archive": "Мұрағатты экспорттау", "Sidebar.import-archive": "Мұрағатты импорттау", "Sidebar.invite-users": "Қолданушыларды шақыру", "Sidebar.logout": "Шығу", - "Sidebar.no-more-workspaces": "Басқа workspace жоқ", - "Sidebar.no-views-in-board": "Ішінде беттер жоқ", "Sidebar.random-icons": "Рандом икондар", "Sidebar.set-language": "Тілді таңдау", "Sidebar.set-theme": "Теміні орнату", "Sidebar.settings": "Баптаулар", - "Sidebar.template-from-board": "Тақтадан жаңа үлгі жасау", "Sidebar.untitled-board": "(Атаусыз Тақта)", - "Sidebar.untitled-view": "(Атаусыз Көрініс)", "TableComponent.add-icon": "Иконды қосу", "TableComponent.name": "Атауы", "TableComponent.plus-new": "+ Қосу", diff --git a/webapp/i18n/ko.json b/webapp/i18n/ko.json index ca20b7999..04fbdf9cd 100644 --- a/webapp/i18n/ko.json +++ b/webapp/i18n/ko.json @@ -100,11 +100,6 @@ "ContentBlock.moveDown": "아래로 이동하기", "ContentBlock.moveUp": "위로 이동하기", "ContentBlock.text": "텍스트", - "DashboardPage.CenterPanel.ChangeChannels": "채널을 쉽게 바꾸기 위해 전환기를 사용하세요", - "DashboardPage.CenterPanel.NoWorkspaces": "죄송합니다. 해당 단어와 일치하는 채널을 찾을 수 없습니다", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "다른 단어로 검색해보세요", - "DashboardPage.showEmpty": "빈칸 표시", - "DashboardPage.title": "대시보드", "DeleteBoardDialog.confirm-cancel": "취소", "DeleteBoardDialog.confirm-delete": "삭제", "DeleteBoardDialog.confirm-info": "“{boardTitle}” 보드를 삭제하시겠습니까? 이 보드에 있는 모든 카드들이 삭제됩니다.", @@ -131,9 +126,7 @@ "KanbanCard.delete": "삭제", "KanbanCard.duplicate": "복제", "KanbanCard.untitled": "제목 없음", - "Mutator.duplicate-board": "보드 복제", "Mutator.new-card-from-template": "템플릿에서 새 카드 만들기", - "Mutator.new-template-from-board": "보드에서 새 템플릿 만들기", "Mutator.new-template-from-card": "카드에서 새 템플릿 만들기", "PropertyMenu.Delete": "삭제", "PropertyMenu.changeType": "속성 유형 변경", @@ -169,20 +162,15 @@ "Sidebar.add-board": "+ 보드 추가", "Sidebar.changePassword": "패스워드 변경", "Sidebar.delete-board": "보드 삭제", - "Sidebar.duplicate-board": "보드 복제", "Sidebar.export-archive": "아카이브 내보내기", "Sidebar.import-archive": "아카이브 들여오기", "Sidebar.invite-users": "사용자 초대", "Sidebar.logout": "로그아웃", - "Sidebar.no-more-workspaces": "더 이상 작업 공간이 없습니다", - "Sidebar.no-views-in-board": "페이지가 없습니다", "Sidebar.random-icons": "임의 아이콘", "Sidebar.set-language": "언어 설정", "Sidebar.set-theme": "테마 설정", "Sidebar.settings": "설정", - "Sidebar.template-from-board": "보드에서 새 템플릿 만들기", "Sidebar.untitled-board": "(제목 없는 보드)", - "Sidebar.untitled-view": "(제목 없는 뷰)", "TableComponent.add-icon": "아이콘 추가", "TableComponent.name": "이름", "TableComponent.plus-new": "+ 생성", diff --git a/webapp/i18n/ml.json b/webapp/i18n/ml.json index fe81c61e6..3fe0a3c43 100644 --- a/webapp/i18n/ml.json +++ b/webapp/i18n/ml.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "താഴേക്ക് നീക്കുക", "ContentBlock.moveUp": "മുകളിലേക്കു നീക്കുക", "ContentBlock.text": "വാചകം", - "DashboardPage.CenterPanel.ChangeChannels": "ചാനലുകൾ എളുപ്പത്തിൽ മാറ്റാൻ സ്വിച്ചർ ഉപയോഗിക്കുക", - "DashboardPage.CenterPanel.NoWorkspaces": "ക്ഷമിക്കണം, ആ പദവുമായി പൊരുത്തപ്പെടുന്ന ചാനലുകളൊന്നും കണ്ടെത്താൻ ഞങ്ങൾക്ക് കഴിഞ്ഞില്ല", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "ദയവായി മറ്റൊരു പദത്തിനായി തിരയാൻ ശ്രമിക്കുക", - "DashboardPage.showEmpty": "ശൂന്യമായി കാണിക്കുക", - "DashboardPage.title": "ഡാഷ്ബോർഡ്", "DeleteBoardDialog.confirm-cancel": "നിര്‍ത്തലാക്കുക", "DeleteBoardDialog.confirm-delete": "നീക്കം ചെയ്യുക", "DeleteBoardDialog.confirm-info": "\"{boardTitle}\" എന്ന ബോർഡ് ഇല്ലാതാക്കണമെന്ന് തീർച്ചയാണോ? ഇത് ഇല്ലാതാക്കുന്നത് ബോർഡിലെ എല്ലാ കാർഡുകളും ഇല്ലാതാക്കും.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "ഇല്ലാതാക്കുക", "KanbanCard.duplicate": "തനിപ്പകർപ്പ്", "KanbanCard.untitled": "ശീർഷകമില്ലാത്തത്", - "Mutator.duplicate-board": "ബോർഡിൻറെ തനിപ്പകർപ്പ്", "Mutator.new-card-from-template": "ടെംപ്ലേറ്റിൽ നിന്നുള്ള പുതിയ കാർഡ്", - "Mutator.new-template-from-board": "ബോർഡിൽ നിന്നുള്ള പുതിയ ടെംപ്ലേറ്റ്", "Mutator.new-template-from-card": "കാർഡിൽ നിന്നുള്ള പുതിയ ടെംപ്ലേറ്റ്", "OnboardingTour.AddComments.Body": "നിങ്ങൾക്ക് പ്രശ്‌നങ്ങളിൽ അഭിപ്രായമിടാം, ഒപ്പം നിങ്ങളുടെ സഹ മാറ്റർമോസ് ഉപയോക്താക്കളെ അവരുടെ ശ്രദ്ധ ആകർഷിക്കാൻ അവരെ @പരാമർശിക്കുകയും ചെയ്യാം.", "OnboardingTour.AddComments.Title": "അഭിപ്രായങ്ങൾ ചേർക്കുക", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ ബോർഡ് ചേർക്കുക", "Sidebar.changePassword": "പാസ്സ്‌വേഡ്‌ മാറ്റുക", "Sidebar.delete-board": "ബോർഡ് നീക്കം ചെയ്യുക", - "Sidebar.duplicate-board": "ബോർഡിൻറെ തനിപ്പകർപ്പ്", "Sidebar.export-archive": "ആർക്കൈവ് എക്സ്പോർട്ട് ചെയ്യുക", "Sidebar.import": "ഇറക്കുമതി ചെയ്യുക", "Sidebar.import-archive": "ആർക്കൈവ് ഇമ്പോർട്ട് ചെയ്യുക", "Sidebar.invite-users": "ഉപയോക്താക്കളെ ക്ഷണിക്കുക", "Sidebar.logout": "ലോഗ്ഔട്ട്", - "Sidebar.no-more-workspaces": "കൂടുതൽ വർക്ക്‌സ്‌പെയ്‌സുകളൊന്നുമില്ല", - "Sidebar.no-views-in-board": "അകത്ത് പേജുകളൊന്നുമില്ല", "Sidebar.random-icons": "ക്രമരഹിതമായ ഐക്കണുകൾ", "Sidebar.set-language": "ഭാഷ സജ്ജമാക്കുക", "Sidebar.set-theme": "തീം സജ്ജമാക്കുക", "Sidebar.settings": "ക്രമീകരണങ്ങൾ", - "Sidebar.template-from-board": "ബോർഡിൽ നിന്നുള്ള പുതിയ ടെംപ്ലേറ്റ്", "Sidebar.untitled-board": "(പേരില്ലാത്ത ബോർഡ്)", - "Sidebar.untitled-view": "(ശീർഷകമില്ലാത്ത കാഴ്ച)", "TableComponent.add-icon": "ഐക്കൺ ചേർക്കുക", "TableComponent.name": "പേര്", "TableComponent.plus-new": "+ പുതിയത്", diff --git a/webapp/i18n/nl.json b/webapp/i18n/nl.json index 9e2fe40c8..cef264536 100644 --- a/webapp/i18n/nl.json +++ b/webapp/i18n/nl.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Naar beneden verplaatsen", "ContentBlock.moveUp": "Naar boven verplaatsen", "ContentBlock.text": "tekst", - "DashboardPage.CenterPanel.ChangeChannels": "Gebruik de wisselaar om gemakkelijk van kanaal te veranderen", - "DashboardPage.CenterPanel.NoWorkspaces": "Sorry, we konden geen kanalen vinden die aan deze term voldoen", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Probeer op een andere term te zoeken", - "DashboardPage.showEmpty": "Leeg tonen", - "DashboardPage.title": "Dashboard", "DeleteBoardDialog.confirm-cancel": "Annuleren", "DeleteBoardDialog.confirm-delete": "Verwijderen", "DeleteBoardDialog.confirm-info": "Weet je zeker dat u het bord \"{boardTitle}\" wil verwijderen? Het verwijderen van het bord zal alle kaarten in het bord verwijderen.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "Verwijderen", "KanbanCard.duplicate": "Kopiëren", "KanbanCard.untitled": "Titelloos", - "Mutator.duplicate-board": "bord kopiëren", "Mutator.new-card-from-template": "nieuwe kaart van sjabloon", - "Mutator.new-template-from-board": "nieuw sjabloon van bord", "Mutator.new-template-from-card": "nieuw sjabloon van kaart", "OnboardingTour.AddComments.Body": "Je kunt commentaar geven op onderwerpen, en zelfs je medeMattermostgebruikers @vermelden om hun aandacht te trekken.", "OnboardingTour.AddComments.Title": "Opmerkingen toevoegen", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ Bord toevoegen", "Sidebar.changePassword": "Wachtwoord wijzigen", "Sidebar.delete-board": "Verwijder bord", - "Sidebar.duplicate-board": "Bord kopiëren", "Sidebar.export-archive": "Archief exporteren", "Sidebar.import": "Importeren", "Sidebar.import-archive": "Archief importeren", "Sidebar.invite-users": "Gebruikers uitnodigen", "Sidebar.logout": "Afmelden", - "Sidebar.no-more-workspaces": "Geen werkruimtes meer", - "Sidebar.no-views-in-board": "Geen pagina's binnenin", "Sidebar.random-icons": "Willekeurige iconen", "Sidebar.set-language": "Taal instellen", "Sidebar.set-theme": "Thema instellen", "Sidebar.settings": "Instellingen", - "Sidebar.template-from-board": "Nieuw sjabloon van bord", "Sidebar.untitled-board": "(Titelloze bord )", - "Sidebar.untitled-view": "(Titelloze weergave)", "TableComponent.add-icon": "Pictogram toevoegen", "TableComponent.name": "Naam", "TableComponent.plus-new": "+ Nieuw", diff --git a/webapp/i18n/oc.json b/webapp/i18n/oc.json index 13b50c83a..de4e1fbe2 100644 --- a/webapp/i18n/oc.json +++ b/webapp/i18n/oc.json @@ -40,11 +40,6 @@ "ContentBlock.moveDown": "Desplaçar al dejós", "ContentBlock.moveUp": "Desplaçar al dessús", "ContentBlock.text": "tèxt", - "DashboardPage.CenterPanel.ChangeChannels": "Utilizatz l’alternator per cambiar facilament de cadenas", - "DashboardPage.CenterPanel.NoWorkspaces": "O planhèm, avèm pas trobat cap de cadena correspondenta al tèrme", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Mercés d’ensajar d’autres tèrmes", - "DashboardPage.showEmpty": "Mostrar void", - "DashboardPage.title": "Tablèu de bòrd", "Dialog.closeDialog": "Tampar la fenèstra de dialòg", "EditableDayPicker.today": "Uèi", "Error.websocket-closed": "Connexion al websocket tampada, connexion interrompuda. S’aquò ten de se produire, verificatz la configuracion del servidor o del servidor mandatari.", @@ -65,9 +60,7 @@ "KanbanCard.delete": "Suprimir", "KanbanCard.duplicate": "Duplicar", "KanbanCard.untitled": "Sens títol", - "Mutator.duplicate-board": "duplicar lo tablèu", "Mutator.new-card-from-template": "zòna novèla a partir d’un modèl", - "Mutator.new-template-from-board": "modèl novèl a parti d’un tablèu", "Mutator.new-template-from-card": "modèl novèl a partir d’una zòna", "PropertyMenu.Delete": "Suprimir", "PropertyMenu.changeType": "Modificar lo tipe de proprietat", @@ -103,20 +96,15 @@ "Sidebar.add-board": "+ Apondre un tablèu", "Sidebar.changePassword": "Modificar lo senhal", "Sidebar.delete-board": "Suprimir lo tablèu", - "Sidebar.duplicate-board": "Duplicar lo tablèu", "Sidebar.export-archive": "Exportar un archiu", "Sidebar.import-archive": "Importar un archiu", "Sidebar.invite-users": "Convidar utilizaires", "Sidebar.logout": "Se desconnectar", - "Sidebar.no-more-workspaces": "Pas mai d’espaci de trabalh", - "Sidebar.no-views-in-board": "Cap de pagina pas inclusa", "Sidebar.random-icons": "Icònas aleatòrias", "Sidebar.set-language": "Definir la lenga", "Sidebar.set-theme": "Causir lo tèma", "Sidebar.settings": "Paramètres", - "Sidebar.template-from-board": "Modèl novèl a partir del tablèu", "Sidebar.untitled-board": "(Tablèu sens títol)", - "Sidebar.untitled-view": "(Vista sens títol)", "TableComponent.add-icon": "Apondre una icòna", "TableComponent.name": "Nom", "TableComponent.plus-new": "+ Novèl", diff --git a/webapp/i18n/pl.json b/webapp/i18n/pl.json index 064811138..4bf8ccd02 100644 --- a/webapp/i18n/pl.json +++ b/webapp/i18n/pl.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Przesuń w dół", "ContentBlock.moveUp": "Przesuń do góry", "ContentBlock.text": "tekst", - "DashboardPage.CenterPanel.ChangeChannels": "Użyj przełącznika, aby łatwo zmieniać kanały", - "DashboardPage.CenterPanel.NoWorkspaces": "Niestety, nie znaleźliśmy żadnych kanałów pasujących do tej frazy", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Proszę spróbować wyszukać inną frazę", - "DashboardPage.showEmpty": "Pokaż puste", - "DashboardPage.title": "Tablica", "DeleteBoardDialog.confirm-cancel": "Anuluj", "DeleteBoardDialog.confirm-delete": "Usuń", "DeleteBoardDialog.confirm-info": "Czy na pewno chcesz usunąć tablicę \"{boardTitle}\"? Usunięcie jej spowoduje usunięcie wszystkich kart na tablicy.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "Usuń", "KanbanCard.duplicate": "Duplikuj", "KanbanCard.untitled": "Bez tytułu", - "Mutator.duplicate-board": "duplikuj tablicę", "Mutator.new-card-from-template": "nowa karta z szablonu", - "Mutator.new-template-from-board": "nowy szablon z tablicy", "Mutator.new-template-from-card": "nowy szablon z karty", "OnboardingTour.AddComments.Body": "Możesz komentować wydarzenia, a nawet @mention innych użytkowników Mattermost, aby zwrócić na siebie ich uwagę.", "OnboardingTour.AddComments.Title": "Dodaj komentarze", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ Dodaj tablicę", "Sidebar.changePassword": "Zmień hasło", "Sidebar.delete-board": "Usuń tablicę", - "Sidebar.duplicate-board": "Duplikuj tablicę", "Sidebar.export-archive": "Eksportuj archiwum", "Sidebar.import": "Importuj", "Sidebar.import-archive": "Importuj archiwum", "Sidebar.invite-users": "Zaproś użytkowników", "Sidebar.logout": "Wyloguj się", - "Sidebar.no-more-workspaces": "Nie ma więcej obszaru roboczego", - "Sidebar.no-views-in-board": "Brak stron wewnątrz", "Sidebar.random-icons": "Losowe ikony", "Sidebar.set-language": "Ustaw język", "Sidebar.set-theme": "Ustaw motyw", "Sidebar.settings": "Ustawienia", - "Sidebar.template-from-board": "Nowy szablon z tablicy", "Sidebar.untitled-board": "(Tablica bez tytułu)", - "Sidebar.untitled-view": "(Widok bez Tytułu)", "TableComponent.add-icon": "Dodaj Ikonę", "TableComponent.name": "Nazwa", "TableComponent.plus-new": "+ Nowy", diff --git a/webapp/i18n/pt_BR.json b/webapp/i18n/pt_BR.json index 71cee3efe..23eb78fb4 100644 --- a/webapp/i18n/pt_BR.json +++ b/webapp/i18n/pt_BR.json @@ -32,7 +32,6 @@ "ContentBlock.moveDown": "Mover para baixo", "ContentBlock.moveUp": "Mover para cima", "ContentBlock.text": "texto", - "DashboardPage.title": "Welcome to Focalboard!", "Dialog.closeDialog": "Fechar diálogo", "EditableDayPicker.today": "Hoje", "Error.websocket-closed": "Conexão Websocket fechada, conexão interrompida. Se isso persistir, verifique a configuração do seu servidor ou proxy da web.", @@ -48,9 +47,7 @@ "KanbanCard.delete": "Deletar", "KanbanCard.duplicate": "Duplicar", "KanbanCard.untitled": "Sem nome", - "Mutator.duplicate-board": "quadro duplicado", "Mutator.new-card-from-template": "novo card à partir de um template", - "Mutator.new-template-from-board": "novo template à partir de um quadro", "Mutator.new-template-from-card": "novo template à partir de um card", "PropertyMenu.Delete": "Deletar", "PropertyMenu.changeType": "Alterar tipo da propriedade", @@ -84,19 +81,15 @@ "Sidebar.add-board": "+ Adicionar Quadro", "Sidebar.changePassword": "Mudar senha", "Sidebar.delete-board": "Deletar quadro", - "Sidebar.duplicate-board": "Duplicar quadro", "Sidebar.export-archive": "Exportar arquivo", "Sidebar.import-archive": "Importar arquivo", "Sidebar.invite-users": "Convidar Usuários", "Sidebar.logout": "Sair", - "Sidebar.no-views-in-board": "Sem páginas dentro", "Sidebar.random-icons": "Ícones aleatórios", "Sidebar.set-language": "Definir linguagem", "Sidebar.set-theme": "Definir tema", "Sidebar.settings": "Configurações", - "Sidebar.template-from-board": "Novo modelo à partir do quadro", "Sidebar.untitled-board": "(Quadro sem nome)", - "Sidebar.untitled-view": "(Vista sem nome)", "TableComponent.add-icon": "Adicionar Ícone", "TableComponent.name": "Nome", "TableComponent.plus-new": "+ Novo", diff --git a/webapp/i18n/ru.json b/webapp/i18n/ru.json index 36e98d633..22b17dac2 100644 --- a/webapp/i18n/ru.json +++ b/webapp/i18n/ru.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Опустить", "ContentBlock.moveUp": "Поднять", "ContentBlock.text": "текст", - "DashboardPage.CenterPanel.ChangeChannels": "Используйте переключатель, чтобы легко сменить каналы", - "DashboardPage.CenterPanel.NoWorkspaces": "К сожалению, мы не смогли найти ни одного канала, соответствующего этому термину", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Пожалуйста, попробуйте найти другой термин", - "DashboardPage.showEmpty": "Показать пустой", - "DashboardPage.title": "Панель управления", "DeleteBoardDialog.confirm-cancel": "Отмена", "DeleteBoardDialog.confirm-delete": "Удалить", "DeleteBoardDialog.confirm-info": "Вы уверены, что хотите удалить доску \"{boardTitle}\"? Его удаление приведет к удалению всех карточек на доске.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "Удалить", "KanbanCard.duplicate": "Создать дубликат", "KanbanCard.untitled": "Без названия", - "Mutator.duplicate-board": "сделать дубликат доски", "Mutator.new-card-from-template": "новая карточка из шаблона", - "Mutator.new-template-from-board": "новый шаблон из доски", "Mutator.new-template-from-card": "новый шаблон из карточки", "OnboardingTour.AddComments.Body": "Вы можете комментировать проблемы и даже @упоминать своих коллег-пользователей Mattermost, чтобы привлечь их внимание.", "OnboardingTour.AddComments.Title": "Добавить комментарии", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ Добавить доску", "Sidebar.changePassword": "Изменить пароль", "Sidebar.delete-board": "Удалить доску", - "Sidebar.duplicate-board": "Сделать дубликат доски", "Sidebar.export-archive": "Экспорт архива", "Sidebar.import": "Импорт", "Sidebar.import-archive": "Импорт архива", "Sidebar.invite-users": "Пригласить пользователей", "Sidebar.logout": "Выйти", - "Sidebar.no-more-workspaces": "Рабочих пространств больше нет", - "Sidebar.no-views-in-board": "Внутри нет страниц", "Sidebar.random-icons": "Случайные иконки", "Sidebar.set-language": "Язык", "Sidebar.set-theme": "Тема", "Sidebar.settings": "Настройки", - "Sidebar.template-from-board": "Новый шаблон из доски", "Sidebar.untitled-board": "(Доска без названия)", - "Sidebar.untitled-view": "(Вид без названия)", "TableComponent.add-icon": "Добавить иконку", "TableComponent.name": "Название", "TableComponent.plus-new": "+ Создать", diff --git a/webapp/i18n/sk.json b/webapp/i18n/sk.json index dee1a7357..aa06e8fa0 100644 --- a/webapp/i18n/sk.json +++ b/webapp/i18n/sk.json @@ -86,11 +86,6 @@ "ContentBlock.moveDown": "Presuň dole", "ContentBlock.moveUp": "Presuň hore", "ContentBlock.text": "text", - "DashboardPage.CenterPanel.ChangeChannels": "Na zmenu kanálov použite prepínač", - "DashboardPage.CenterPanel.NoWorkspaces": "Prepáčte, nenašli sa kanály s daným výrazom", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Skúste vyhľadať iný výraz", - "DashboardPage.showEmpty": "Zobraziť prázdne", - "DashboardPage.title": "Dashboard", "DeleteBoardDialog.confirm-cancel": "Zrušiť", "DeleteBoardDialog.confirm-delete": "Odstrániť", "DeleteBoardDialog.confirm-info": "Naozaj chcete odstrániť nástenku “{boardTitle}”? Odstránením vymažete všetky karty na tabuli.", @@ -116,9 +111,7 @@ "KanbanCard.delete": "Odstrániť", "KanbanCard.duplicate": "Duplikuj", "KanbanCard.untitled": "Nepomenované", - "Mutator.duplicate-board": "duplikuj nástenku", "Mutator.new-card-from-template": "nová karta z template-u", - "Mutator.new-template-from-board": "nový template z nástenky", "Mutator.new-template-from-card": "nový template z karty", "PropertyMenu.Delete": "Odstrániť", "PropertyMenu.changeType": "Zmeniť vlastnosť", @@ -154,20 +147,15 @@ "Sidebar.add-board": "+ Pridať nástenku", "Sidebar.changePassword": "Zmeniť heslo", "Sidebar.delete-board": "Odstrániť nástenku", - "Sidebar.duplicate-board": "Duplikuj nástenku", "Sidebar.export-archive": "Export archívu", "Sidebar.import-archive": "Import archívu", "Sidebar.invite-users": "Pozvať užívateľa", "Sidebar.logout": "Odhlásiť sa", - "Sidebar.no-more-workspaces": "Žiadne miesta", - "Sidebar.no-views-in-board": "Bez obsahu", "Sidebar.random-icons": "Náhodné ikony", "Sidebar.set-language": "Nastaviť jazyk", "Sidebar.set-theme": "Nastaviť tému", "Sidebar.settings": "nastavenia", - "Sidebar.template-from-board": "Nový template z nástenky", "Sidebar.untitled-board": "(nástenka bez názvu)", - "Sidebar.untitled-view": "(náhľad bez názvu)", "TableComponent.add-icon": "Pridať ikonu", "TableComponent.name": "názov", "TableComponent.plus-new": "+ Nový", diff --git a/webapp/i18n/sv.json b/webapp/i18n/sv.json index 8ab1066e8..1d552c64c 100644 --- a/webapp/i18n/sv.json +++ b/webapp/i18n/sv.json @@ -80,11 +80,6 @@ "ContentBlock.moveDown": "Flytta ned", "ContentBlock.moveUp": "Flytta upp", "ContentBlock.text": "text", - "DashboardPage.CenterPanel.ChangeChannels": "Använd kanalväljaren för att smidigt växla mellan kanaler", - "DashboardPage.CenterPanel.NoWorkspaces": "Tyvärr hittade vi inga kanaler som matchar den termen", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Försök att söka efter en annan term", - "DashboardPage.showEmpty": "Visa tomma", - "DashboardPage.title": "Dashboard", "DeleteBoardDialog.confirm-cancel": "Avbryt", "DeleteBoardDialog.confirm-delete": "Radera", "DeleteBoardDialog.confirm-info": "Är du säker på att du vill ta bort tavlan “{boardTitle}”? När du tar bort den kommer du radera alla kort på tavlan.", @@ -110,9 +105,7 @@ "KanbanCard.delete": "Radera", "KanbanCard.duplicate": "Radera", "KanbanCard.untitled": "Saknar titel", - "Mutator.duplicate-board": "duplicera tavla", "Mutator.new-card-from-template": "nytt kort från mall", - "Mutator.new-template-from-board": "ny mall från tavla", "Mutator.new-template-from-card": "ny mall från kort", "PropertyMenu.Delete": "Radera", "PropertyMenu.changeType": "Ändra egenskapstyp", @@ -148,20 +141,15 @@ "Sidebar.add-board": "+ Lägg till tavla", "Sidebar.changePassword": "Ändra lösenord", "Sidebar.delete-board": "Radera tavla", - "Sidebar.duplicate-board": "Duplicera tavla", "Sidebar.export-archive": "Exportera arkiv", "Sidebar.import-archive": "Importera arkiv", "Sidebar.invite-users": "Bjud in användare", "Sidebar.logout": "Logga ut", - "Sidebar.no-more-workspaces": "Inga fler arbetsytor", - "Sidebar.no-views-in-board": "Inga vyer på tavla", "Sidebar.random-icons": "Slumpmässiga ikoner", "Sidebar.set-language": "Välj språk", "Sidebar.set-theme": "Välj tema", "Sidebar.settings": "Inställningar", - "Sidebar.template-from-board": "Ny mall från tavla", "Sidebar.untitled-board": "(Tavla saknar titel)", - "Sidebar.untitled-view": "(Vy saknar titel)", "TableComponent.add-icon": "Lägg till ikon", "TableComponent.name": "Namn", "TableComponent.plus-new": "+ Ny", diff --git a/webapp/i18n/tr.json b/webapp/i18n/tr.json index a23adabf6..c299f4da9 100644 --- a/webapp/i18n/tr.json +++ b/webapp/i18n/tr.json @@ -101,11 +101,6 @@ "ContentBlock.moveDown": "Alta taşı", "ContentBlock.moveUp": "Üste taşı", "ContentBlock.text": "metin", - "DashboardPage.CenterPanel.ChangeChannels": "Kanalları kolayca değiştirmek için değiştiriciyi kullanın", - "DashboardPage.CenterPanel.NoWorkspaces": "Maalesef, bu arama ifadesine uyan bir kanal bulunamadı", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "Lütfen başka bir ifade ile aramayı deneyin", - "DashboardPage.showEmpty": "Boş olanları görüntüle", - "DashboardPage.title": "Pano", "DeleteBoardDialog.confirm-cancel": "İptal", "DeleteBoardDialog.confirm-delete": "Sil", "DeleteBoardDialog.confirm-info": "“{boardTitle}” panosunu silmek istediğinize emin misiniz? Silme işlemi bu panodaki tüm kartları siler.", @@ -134,9 +129,7 @@ "KanbanCard.delete": "Sil", "KanbanCard.duplicate": "Kopyala", "KanbanCard.untitled": "Başlıksız", - "Mutator.duplicate-board": "panoyu kopyala", "Mutator.new-card-from-template": "kalıptan yeni kart oluştur", - "Mutator.new-template-from-board": "panodan yeni kalıp oluştur", "Mutator.new-template-from-card": "karttan yeni kalıp oluştur", "OnboardingTour.AddComments.Body": "Sorunlar hakkında yorum yapabilir ve Mattermost kullanıcılarının dikkatini çekmek için @anabilirsiniz.", "OnboardingTour.AddComments.Title": "Yorum yap", @@ -190,21 +183,16 @@ "Sidebar.add-board": "+ Pano ekle", "Sidebar.changePassword": "Parola değiştir", "Sidebar.delete-board": "Panoyu sil", - "Sidebar.duplicate-board": "Panoyu kopyala", "Sidebar.export-archive": "Arşivi dışa aktar", "Sidebar.import": "İçe aktar", "Sidebar.import-archive": "Arşivi içe aktar", "Sidebar.invite-users": "Kullanıcıları çağır", "Sidebar.logout": "Oturumu kapat", - "Sidebar.no-more-workspaces": "Başka bir çalışma alanı yok", - "Sidebar.no-views-in-board": "İçeride bir sayfa yok", "Sidebar.random-icons": "Rastgele simgeler", "Sidebar.set-language": "Dil ayarla", "Sidebar.set-theme": "Tema ayarla", "Sidebar.settings": "Ayarlar", - "Sidebar.template-from-board": "Panodan yeni kalıp", "Sidebar.untitled-board": "(Başlıksız pano)", - "Sidebar.untitled-view": "(Başlıksız görünüm)", "TableComponent.add-icon": "Simge ekle", "TableComponent.name": "Ad", "TableComponent.plus-new": "+ Yeni", diff --git a/webapp/i18n/zh_Hans.json b/webapp/i18n/zh_Hans.json index 4834c41eb..935d5c813 100644 --- a/webapp/i18n/zh_Hans.json +++ b/webapp/i18n/zh_Hans.json @@ -43,7 +43,6 @@ "ContentBlock.moveDown": "下移", "ContentBlock.moveUp": "上移", "ContentBlock.text": "文字", - "DashboardPage.title": "仪表板", "Dialog.closeDialog": "关闭对话框", "EditableDayPicker.today": "今天", "Error.websocket-closed": "Websocket 连接关闭,连接中断。如果这种情况仍然存在,请检查您的服务器或网页代理配置。", @@ -59,9 +58,7 @@ "KanbanCard.delete": "删除", "KanbanCard.duplicate": "复制", "KanbanCard.untitled": "无标题", - "Mutator.duplicate-board": "复制版面", "Mutator.new-card-from-template": "使用模板新增卡片", - "Mutator.new-template-from-board": "从版面新增模板", "Mutator.new-template-from-card": "从卡片新增模板", "PropertyMenu.Delete": "删除", "PropertyMenu.changeType": "修改属性类型", @@ -95,19 +92,15 @@ "Sidebar.add-board": "+ 新增版面", "Sidebar.changePassword": "变更密码", "Sidebar.delete-board": "删除版面", - "Sidebar.duplicate-board": "复制版面", "Sidebar.export-archive": "导出档案", "Sidebar.import-archive": "导入档案", "Sidebar.invite-users": "邀请使用者", "Sidebar.logout": "登出", - "Sidebar.no-views-in-board": "里面没有页面", "Sidebar.random-icons": "随机图标", "Sidebar.set-language": "设定语言", "Sidebar.set-theme": "设置主题", "Sidebar.settings": "设定", - "Sidebar.template-from-board": "从版面创建模板", "Sidebar.untitled-board": "(无标题版面)", - "Sidebar.untitled-view": "(无标题视图)", "TableComponent.add-icon": "加入图标", "TableComponent.name": "姓名", "TableComponent.plus-new": "+ 新增", diff --git a/webapp/i18n/zh_Hant.json b/webapp/i18n/zh_Hant.json index 070da1ff4..243866cd3 100644 --- a/webapp/i18n/zh_Hant.json +++ b/webapp/i18n/zh_Hant.json @@ -44,9 +44,6 @@ "ContentBlock.moveDown": "下移", "ContentBlock.moveUp": "上移", "ContentBlock.text": "文字", - "DashboardPage.CenterPanel.NoWorkspaces": "很抱歉,我們找不到與符合該字詞的任何頻道", - "DashboardPage.CenterPanel.NoWorkspacesDescription": "請嘗試以其他字詞進行搜尋", - "DashboardPage.title": "資訊面板", "DeleteBoardDialog.confirm-cancel": "取消", "DeleteBoardDialog.confirm-delete": "刪除", "Dialog.closeDialog": "關閉對話框", @@ -67,9 +64,7 @@ "KanbanCard.delete": "刪除", "KanbanCard.duplicate": "建立副本", "KanbanCard.untitled": "無標題", - "Mutator.duplicate-board": "建立版面副本", "Mutator.new-card-from-template": "使用範本新增卡片", - "Mutator.new-template-from-board": "從版面新增範本", "Mutator.new-template-from-card": "從卡片新增範本", "PropertyMenu.Delete": "刪除", "PropertyMenu.changeType": "修改屬性類型", @@ -104,20 +99,15 @@ "Sidebar.add-board": "+ 新增版面", "Sidebar.changePassword": "變更密碼", "Sidebar.delete-board": "刪除版面", - "Sidebar.duplicate-board": "建立版面副本", "Sidebar.export-archive": "匯入打包檔", "Sidebar.import-archive": "匯出打包檔", "Sidebar.invite-users": "邀請使用者", "Sidebar.logout": "登出", - "Sidebar.no-more-workspaces": "沒有更多工作區", - "Sidebar.no-views-in-board": "裡面沒有頁面", "Sidebar.random-icons": "隨機圖示", "Sidebar.set-language": "設定語言", "Sidebar.set-theme": "設定佈景主題", "Sidebar.settings": "設定", - "Sidebar.template-from-board": "從版面新增範本", "Sidebar.untitled-board": "(無標題版面)", - "Sidebar.untitled-view": "(無標題視圖)", "TableComponent.add-icon": "加入圖示", "TableComponent.name": "姓名", "TableComponent.plus-new": "+ 新增", From 0d1d05f8eb28d7f6156ca02ad7172946dbcc9d68 Mon Sep 17 00:00:00 2001 From: jprusch Date: Wed, 30 Mar 2022 12:47:33 +0200 Subject: [PATCH 16/51] Translated using Weblate (German) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/de/ --- webapp/i18n/de.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/de.json b/webapp/i18n/de.json index e204de441..6ff28356f 100644 --- a/webapp/i18n/de.json +++ b/webapp/i18n/de.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "Keine {property}", "BoardComponent.no-property-title": "Elemente mit einer leeren {property} Eigenschaft erscheinen hier. Diese Spalte kann nicht entfernt werden.", "BoardComponent.show": "Anzeigen", + "BoardMember.schemeAdmin": "Administrator", + "BoardMember.schemeEditor": "Bearbeiter", + "BoardMember.schemeNone": "Keine", "BoardPage.newVersion": "Eine neue Version von Boards ist verfügbar, klicke hier, um neu zu laden.", "BoardPage.syncFailed": "Das Board kann gelöscht oder der Zugang entzogen werden.", "BoardTemplateSelector.add-template": "Neue Vorlage", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "Löschen", "BoardTemplateSelector.description": "Wähle eine Vorlage um zu starten. Passe die Vorlage einfach an deine Anforderungen an oder erstelle ein leeres Board.", "BoardTemplateSelector.edit-template": "Bearbeiten", - "BoardTemplateSelector.plugin.no-content-description": "Füge ein Board zur Seitenleiste hinzu, indem du eine der Vorlagen unten verwendest oder starte mit einem leeren Board.{lineBreak} Mitglieder von \"{workspaceName}\" werden Zugriff auf die Boards haben, die hier erstellt werden.", - "BoardTemplateSelector.plugin.no-content-title": "Erstelle ein Board in {workspaceName}", + "BoardTemplateSelector.plugin.no-content-description": "Füge ein Board zur Seitenleiste hinzu, indem du eine der Vorlagen unten verwendest oder starte mit einem leeren Board.{lineBreak} Mitglieder von \"{teamName}\" werden Zugriff auf die Boards haben, die hier erstellt werden.", + "BoardTemplateSelector.plugin.no-content-title": "Erstelle ein Board in {teamName}", "BoardTemplateSelector.title": "Erstelle ein Board", "BoardTemplateSelector.use-this-template": "Verwende diese Vorlage", + "BoardsSwitcher.Title": "Finde Boards", "BoardsUnfurl.Remainder": "+{remainder} mehr", "BoardsUnfurl.Updated": "Aktualisiert {time}", "Calculations.Options.average.displayName": "Durchschnitt", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "Karte wirklich löschen!", "CardDialog.editing-template": "Du bearbeitest eine Vorlage.", "CardDialog.nocard": "Diese Karte existiert nicht oder ist nicht verfügbar.", + "Categories.CreateCategoryDialog.CancelText": "Abbrechen", + "Categories.CreateCategoryDialog.CreateText": "Erstellen", + "Categories.CreateCategoryDialog.Placeholder": "Benenne deine Kategorie", + "Categories.CreateCategoryDialog.UpdateText": "Aktualisieren", "CenterPanel.Share": "Teilen", "ColorOption.selectColor": "Wähle Farbe {color}", "Comment.delete": "Löschen", @@ -116,6 +124,11 @@ "Filter.not-includes": "beinhaltet nicht", "FilterComponent.add-filter": "+ Filter hinzufügen", "FilterComponent.delete": "Löschen", + "FindBoFindBoardsDialog.IntroText": "Suche nach Boards", + "FindBoardsDialog.NoResultsFor": "Keine Ergebnisse für \"{searchQuery}\"", + "FindBoardsDialog.NoResultsSubtext": "Prüfe die Schreibweise oder versuche eine weitere Suche.", + "FindBoardsDialog.SubTitle": "Tippe um ein Board zu finden. Benutze HOCH/RUNTER zum Browsen. ENTER zur Auswahl, ESC zum Schließen", + "FindBoardsDialog.Title": "Finde Boards", "GalleryCard.copiedLink": "Kopiert!", "GalleryCard.copyLink": "Link kopieren", "GalleryCard.delete": "Löschen", @@ -178,7 +191,10 @@ "ShareBoard.copiedLink": "Kopiert!", "ShareBoard.copyLink": "Link kopieren", "ShareBoard.regenerate": "Token neu erstellen", + "ShareBoard.teamPermissionsText": "Jeder im {teamName} Team", "ShareBoard.tokenRegenrated": "Token neu generiert", + "ShareBoard.userPermissionsRemoveMemberText": "Mitglied entfernen", + "ShareBoard.userPermissionsYouText": "(Du)", "Sidebar.about": "Über Focalboard", "Sidebar.add-board": "+ Board hinzufügen", "Sidebar.changePassword": "Passwort ändern", @@ -188,11 +204,18 @@ "Sidebar.import-archive": "Archiv importieren", "Sidebar.invite-users": "Nutzer einladen", "Sidebar.logout": "Ausloggen", + "Sidebar.no-boards-in-category": "Keine Boards vorhanden", "Sidebar.random-icons": "Zufällige Icons", "Sidebar.set-language": "Sprache übernehmen", "Sidebar.set-theme": "Theme übernehmen", "Sidebar.settings": "Einstellungen", "Sidebar.untitled-board": "(Unbenanntes Board)", + "SidebarCategories.BlocksMenu.Move": "Bewege nach...", + "SidebarCategories.CategoryMenu.CreateNew": "Erstelle neue Kategorie", + "SidebarCategories.CategoryMenu.Delete": "Lösche Kategorie", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "Boards in {categoryName} werden zurück zu den Board-Kategorien bewegt. Du wirst von keinen Boards entfernt.", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "Diese Kategorie löschen?", + "SidebarCategories.CategoryMenu.Update": "Kategorie umbenennen", "TableComponent.add-icon": "Symbol hinzufügen", "TableComponent.name": "Name", "TableComponent.plus-new": "+ Neu", @@ -270,6 +293,7 @@ "login.register-button": "oder erstelle einen Account wenn du noch keines hast", "register.login-button": "oder melde dich an, wenn du bereits ein Konto hast", "register.signup-title": "Registriere dich für deinen Account", + "shareBoard.lastAdmin": "Boards müssen mindestens eine Administrator haben", "tutorial_tip.finish_tour": "Erledigt", "tutorial_tip.got_it": "Alles klar", "tutorial_tip.ok": "Weiter", From fb36d496849411f26eece9e50e763e909411d3d5 Mon Sep 17 00:00:00 2001 From: Milo Ivir Date: Wed, 30 Mar 2022 12:47:33 +0200 Subject: [PATCH 17/51] Translated using Weblate (Croatian) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/hr/ Translated using Weblate (Croatian) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/hr/ Translated using Weblate (Croatian) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/hr/ --- webapp/i18n/hr.json | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/webapp/i18n/hr.json b/webapp/i18n/hr.json index 79ca3958e..17bd79841 100644 --- a/webapp/i18n/hr.json +++ b/webapp/i18n/hr.json @@ -71,14 +71,14 @@ "CardDetail.new-comment-placeholder": "Dodaj komentar …", "CardDetailProperty.confirm-delete-heading": "Potvrdi brisanje svojstva", "CardDetailProperty.confirm-delete-subtext": "Stvarno želiš izbrisati svojstvo „{propertyName}”? Brisanjem će se izbrisati svojstvo sa svih kartica na ovoj ploči.", - "CardDetailProperty.confirm-property-name-change-subtext": "Stvarno želiš promijeniti svojstvo „{propertyName}” {customText}? To će utjecati na vrijednosti u {numOfcards} kartica na ovoj ploči, a može rezultirati gubitkom podataka.", + "CardDetailProperty.confirm-property-name-change-subtext": "Stvarno želiš promijeniti svojstvo „{propertyName}” {customText}? To će utjecati na vrijednosti na {numOfCards} kartica na ovoj ploči i može prouzročiti gubitak podataka.", "CardDetailProperty.confirm-property-type-change": "Potvrdi promjenu vrste svojstva!", "CardDetailProperty.delete-action-button": "Izbriši", "CardDetailProperty.property-change-action-button": "Promijeni svojstvo", "CardDetailProperty.property-changed": "Promjena svojstva uspjela!", "CardDetailProperty.property-deleted": "Svojstvo {propertyName} uspješno izbrisano!", - "CardDetailProperty.property-name-change-subtext": "vrsta iz „{oldPropType}” u „{newPropType}”", - "CardDetailProperty.property-type-change-subtext": "Ime u „{newPropName}”", + "CardDetailProperty.property-name-change-subtext": "vrste „{oldPropType}” u „{newPropType}”", + "CardDetailProperty.property-type-change-subtext": "ime u „{newPropName}”", "CardDialog.copiedLink": "Kopirano!", "CardDialog.copyLink": "Kopiraj poveznicu", "CardDialog.delete-confirmation-dialog-button-text": "Izbriši", @@ -127,11 +127,15 @@ "FindBoFindBoardsDialog.IntroText": "Traži ploče", "FindBoardsDialog.NoResultsFor": "Nema rezultata za „{searchQuery}”", "FindBoardsDialog.NoResultsSubtext": "Provjeri pravopis ili pretraži s jednim drugim pojmom.", + "FindBoardsDialog.SubTitle": "Utipkaj ime za pronalaženje ploče. Koristi GORE/DOLJE za pretraživanje. ENTER za odabiranje, ESC za prekid", + "FindBoardsDialog.Title": "Pronađi ploče", "GalleryCard.copiedLink": "Kopirano!", "GalleryCard.copyLink": "Kopiraj poveznicu", "GalleryCard.delete": "Izbriši", "GalleryCard.duplicate": "Dupliciraj", "General.BoardCount": "{count, plural, one {# ploča} few {# ploče} other {# ploča}}", + "GroupBy.hideEmptyGroups": "Sakrij {count} prazne grupe", + "GroupBy.showHiddenGroups": "Prikaži {count} skrivene grupe", "GroupBy.ungroup": "Razgrupiraj", "KanbanCard.copiedLink": "Kopirano!", "KanbanCard.copyLink": "Kopiraj poveznicu", @@ -187,7 +191,10 @@ "ShareBoard.copiedLink": "Kopirano!", "ShareBoard.copyLink": "Kopiraj poveznicu", "ShareBoard.regenerate": "Ponovo generiraj token", + "ShareBoard.teamPermissionsText": "Svatko u timu {teamName}", "ShareBoard.tokenRegenrated": "Token je ponovo generiran", + "ShareBoard.userPermissionsRemoveMemberText": "Ukloni člana", + "ShareBoard.userPermissionsYouText": "(Ti)", "Sidebar.about": "O programu Focalboard", "Sidebar.add-board": "+ Dodaj ploču", "Sidebar.changePassword": "Promijeni lozinku", @@ -197,11 +204,18 @@ "Sidebar.import-archive": "Uvezi arhivu", "Sidebar.invite-users": "Pozovi korisnika", "Sidebar.logout": "Odjavi se", + "Sidebar.no-boards-in-category": "Nema ploča u kategoriji", "Sidebar.random-icons": "Slučajne ikone", "Sidebar.set-language": "Postavi jezik", "Sidebar.set-theme": "Postavi temu", "Sidebar.settings": "Postavke", "Sidebar.untitled-board": "(Ploča bez naslova)", + "SidebarCategories.BlocksMenu.Move": "Premjesti u …", + "SidebarCategories.CategoryMenu.CreateNew": "Stvori novu kategoriju", + "SidebarCategories.CategoryMenu.Delete": "Izbriši kategoriju", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "Ploče u kategoriji {categoryName} vratit će se u kategorije ploča. Nećeš biti uklonjen/a s nijedne ploče.", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "Izbrisati ovu kategoriju?", + "SidebarCategories.CategoryMenu.Update": "Preimenuj kategoriju", "TableComponent.add-icon": "Dodaj ikonu", "TableComponent.name": "Ime", "TableComponent.plus-new": "+ Novo", @@ -279,6 +293,7 @@ "login.register-button": "ili stvori račun, ako ga još nemaš", "register.login-button": "ili se prijavi ako već imaš račun", "register.signup-title": "Prijavi se na svoj račun", + "shareBoard.lastAdmin": "Ploče moraju imati barem jednog administratora", "tutorial_tip.finish_tour": "Gotovo", "tutorial_tip.got_it": "Razumijem", "tutorial_tip.ok": "Dalje", From 90b78f3c3ef3d3f1cfde4fb9da0b760f69665e9e Mon Sep 17 00:00:00 2001 From: Kaya Zeren Date: Wed, 30 Mar 2022 12:47:33 +0200 Subject: [PATCH 18/51] Translated using Weblate (Turkish) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/tr/ --- webapp/i18n/tr.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/tr.json b/webapp/i18n/tr.json index c299f4da9..4c4420484 100644 --- a/webapp/i18n/tr.json +++ b/webapp/i18n/tr.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "{property} yok", "BoardComponent.no-property-title": "{property} alanı boş olan ögeler buraya atanır. Bu sütun silinemez.", "BoardComponent.show": "Görüntüle", + "BoardMember.schemeAdmin": "Yönetici", + "BoardMember.schemeEditor": "Düzenleyici", + "BoardMember.schemeNone": "Yok", "BoardPage.newVersion": "Yeni bir pano sürümü yayınlanmış. Yeniden yüklemek için buraya tıklayın.", "BoardPage.syncFailed": "Pano silinmiş ya da erişim izni geri alınmış olabilir.", "BoardTemplateSelector.add-template": "Yeni kalıp", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "Sil", "BoardTemplateSelector.description": "Başlamanıza yardımcı olacak bir kalıp seçin. Kalıbı gereksinimlerinize göre kolayca özelleştirin ya da sıfırdan başlamak için boş bir pano oluşturun.", "BoardTemplateSelector.edit-template": "Düzenle", - "BoardTemplateSelector.plugin.no-content-description": "Aşağıda tanımlanan kalıplardan herhangi birini kullanarak yan çubuğa bir pano ekleyin ya da sıfırdan başlayın.{lineBreak} \"{workspaceName}\" üyeleri burada oluşturulan panolara erişebilecek.", - "BoardTemplateSelector.plugin.no-content-title": "{workspaceName} çalışma alanında bir pano oluştur", + "BoardTemplateSelector.plugin.no-content-description": "Aşağıda tanımlanan kalıplardan herhangi birini kullanarak yan çubuğa bir pano ekleyin ya da sıfırdan başlayın.{lineBreak} \"{teamName}\" üyeleri burada oluşturulan panolara erişebilecek.", + "BoardTemplateSelector.plugin.no-content-title": "{teamName} çalışma alanında bir pano oluştur", "BoardTemplateSelector.title": "Bir pano oluştur", "BoardTemplateSelector.use-this-template": "Bu kalıp kullanılsın", + "BoardsSwitcher.Title": "Pano arama", "BoardsUnfurl.Remainder": "+{remainder} diğer", "BoardsUnfurl.Updated": "Güncellenme: {time}", "Calculations.Options.average.displayName": "Ortalama", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "Kartı silmeyi onaylayın!", "CardDialog.editing-template": "Bir kalıbı düzenliyorsunuz.", "CardDialog.nocard": "Bu kart bulunamadı ya da erişilebilir değil.", + "Categories.CreateCategoryDialog.CancelText": "İptal", + "Categories.CreateCategoryDialog.CreateText": "Ekle", + "Categories.CreateCategoryDialog.Placeholder": "Kategorinize bir ad verin", + "Categories.CreateCategoryDialog.UpdateText": "Güncelle", "CenterPanel.Share": "Paylaş", "ColorOption.selectColor": "{color} rengi seçin", "Comment.delete": "Sil", @@ -116,6 +124,11 @@ "Filter.not-includes": "şunu içermeyen", "FilterComponent.add-filter": "+ Süzgeç ekle", "FilterComponent.delete": "Sil", + "FindBoFindBoardsDialog.IntroText": "Pano arama", + "FindBoardsDialog.NoResultsFor": "\"{searchQuery}\" için bir sonuç bulunamadı", + "FindBoardsDialog.NoResultsSubtext": "Yazımı denetleyin ya da başka bir arama yapmayı deneyin.", + "FindBoardsDialog.SubTitle": "Bulmak istediğiniz pano adını yazmaya başlayın. Gezinmek için YUKAR/AŞAĞI, seçmek için ENTER, vazgeçmek için ESC tuşlarını kullanın", + "FindBoardsDialog.Title": "Pano arama", "GalleryCard.copiedLink": "Kopyalandı!", "GalleryCard.copyLink": "Bağlantıyı kopyala", "GalleryCard.delete": "Sil", @@ -178,7 +191,10 @@ "ShareBoard.copiedLink": "Kopyalandı!", "ShareBoard.copyLink": "Bağlantıyı kopyala", "ShareBoard.regenerate": "Kodu yeniden oluştur", + "ShareBoard.teamPermissionsText": "{teamName} takımındaki herkes", "ShareBoard.tokenRegenrated": "Kod yeniden oluşturuldu", + "ShareBoard.userPermissionsRemoveMemberText": "Üyelikten çıkar", + "ShareBoard.userPermissionsYouText": "(Siz)", "Sidebar.about": "Focalboard hakkında", "Sidebar.add-board": "+ Pano ekle", "Sidebar.changePassword": "Parola değiştir", @@ -188,11 +204,18 @@ "Sidebar.import-archive": "Arşivi içe aktar", "Sidebar.invite-users": "Kullanıcıları çağır", "Sidebar.logout": "Oturumu kapat", + "Sidebar.no-boards-in-category": "İçeride bir pano yok", "Sidebar.random-icons": "Rastgele simgeler", "Sidebar.set-language": "Dil ayarla", "Sidebar.set-theme": "Tema ayarla", "Sidebar.settings": "Ayarlar", "Sidebar.untitled-board": "(Başlıksız pano)", + "SidebarCategories.BlocksMenu.Move": "Şuraya taşı...", + "SidebarCategories.CategoryMenu.CreateNew": "Yeni kategori ekle", + "SidebarCategories.CategoryMenu.Delete": "Kategoriyi sił", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "{categoryName} İçindeki panolar Panolar kategorisine taşınacak. Herhangi bir panodan çıkarılmayacaksınız.", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "Bu kategori silinsin mi?", + "SidebarCategories.CategoryMenu.Update": "Kategoriyi yeniden adlandır", "TableComponent.add-icon": "Simge ekle", "TableComponent.name": "Ad", "TableComponent.plus-new": "+ Yeni", @@ -270,6 +293,7 @@ "login.register-button": "ya da hesabınız yoksa bir hesap açın", "register.login-button": "ya da bir hesabınız varsa oturum açın", "register.signup-title": "Hesap açın", + "shareBoard.lastAdmin": "Panoların en az bir yöneticisi olmalıdır", "tutorial_tip.finish_tour": "Tamam", "tutorial_tip.got_it": "Anladım", "tutorial_tip.ok": "Sonraki", From 167e2772ed906e2649de1180e6253ff6cbd31f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=B3th=20Csaba=20//=20Online=20ERP=20Hungary=20Kft?= Date: Wed, 30 Mar 2022 12:47:33 +0200 Subject: [PATCH 19/51] Translated using Weblate (Hungarian) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/hu/ --- webapp/i18n/hu.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/hu.json b/webapp/i18n/hu.json index 1a1dd1277..5a9510e48 100644 --- a/webapp/i18n/hu.json +++ b/webapp/i18n/hu.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "Nincs {property}", "BoardComponent.no-property-title": "Elemek üres {property} tulajdonsággal kerülnek ide. Ez az oszlop nem eltávolítható.", "BoardComponent.show": "Mutat", + "BoardMember.schemeAdmin": "Admin", + "BoardMember.schemeEditor": "Szerkesztő", + "BoardMember.schemeNone": "Nincs", "BoardPage.newVersion": "Elérhető a Táblák egy új verziója, kattintson ide az újratöltéshez.", "BoardPage.syncFailed": "A tábla törölve lett vagy hozzáférés vissza lett vonva.", "BoardTemplateSelector.add-template": "Új sablon", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "Törlés", "BoardTemplateSelector.description": "Válasszon egy sablont, amely segít a kezdésben. Könnyedén testre szabhatja a sablont, hogy megfeleljen az Ön igényeinek, vagy létrehozhat egy üres táblát, hogy a nulláról kezdhesse.", "BoardTemplateSelector.edit-template": "Szerkesztés", - "BoardTemplateSelector.plugin.no-content-description": "Adjon hozzá egy táblát az oldalsávhoz az alább definiált sablonok bármelyikével, vagy kezdje elölről.{lineBreak} A \"{workspaceName}\" tagjai hozzáférhetnek az itt létrehozott táblákhoz.", - "BoardTemplateSelector.plugin.no-content-title": "Tábla létrehozása a {workspaceName} munkaterületben", + "BoardTemplateSelector.plugin.no-content-description": "Adjon hozzá egy táblát az oldalsávhoz az alább megadott sablonok bármelyikével, vagy kezdje elölről.{lineBreak} A \"{teamName}\" tagjai hozzáférhetnek az itt létrehozott táblákhoz.", + "BoardTemplateSelector.plugin.no-content-title": "Tábla létrehozása a {teamName} csapathoz", "BoardTemplateSelector.title": "Tábla létrehozása", "BoardTemplateSelector.use-this-template": "Használja ezt a sablont", + "BoardsSwitcher.Title": "Táblák keresése", "BoardsUnfurl.Remainder": "+{remainder} további", "BoardsUnfurl.Updated": "Frissítve {time}", "Calculations.Options.average.displayName": "Átlag", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "Hagyja jóvá a kártya törlését!", "CardDialog.editing-template": "Ön egy sablont szerkeszt.", "CardDialog.nocard": "Ez a kártya nem létezik vagy elérhetetlen.", + "Categories.CreateCategoryDialog.CancelText": "Mégsem", + "Categories.CreateCategoryDialog.CreateText": "Létrehozás", + "Categories.CreateCategoryDialog.Placeholder": "Nevezze el a kategóriáját", + "Categories.CreateCategoryDialog.UpdateText": "Frissítés", "CenterPanel.Share": "Megosztás", "ColorOption.selectColor": "{color} szín kiválasztása", "Comment.delete": "Törlés", @@ -116,6 +124,11 @@ "Filter.not-includes": "nem tartalmazza", "FilterComponent.add-filter": "+ Szűrő hozzáadása", "FilterComponent.delete": "Törlés", + "FindBoFindBoardsDialog.IntroText": "Táblák keresése", + "FindBoardsDialog.NoResultsFor": "Nincs találat a \"{searchQuery}\" kereséshez", + "FindBoardsDialog.NoResultsSubtext": "Ellenőrizze az elgépelést vagy próbáljon egy új keresést.", + "FindBoardsDialog.SubTitle": "Gépeljen, hogy megtalálja a táblát. Használja a FEL/LE gombokat a böngészéshez. ENTER gombot a kiválasztáshoz és ESC gombot az eldobáshoz", + "FindBoardsDialog.Title": "Táblák keresése", "GalleryCard.copiedLink": "Másolva!", "GalleryCard.copyLink": "Link másolása", "GalleryCard.delete": "Törlés", @@ -178,7 +191,10 @@ "ShareBoard.copiedLink": "Másolt!", "ShareBoard.copyLink": "Link másolása", "ShareBoard.regenerate": "Token újragenerálása", + "ShareBoard.teamPermissionsText": "Mindenki a {teamName} Csapatban", "ShareBoard.tokenRegenrated": "Token újragenerálva", + "ShareBoard.userPermissionsRemoveMemberText": "Tag eltávolítása", + "ShareBoard.userPermissionsYouText": "(Ön)", "Sidebar.about": "Focalboard névjegye", "Sidebar.add-board": "+ Tábla hozzáadása", "Sidebar.changePassword": "Jelszó módosítása", @@ -188,11 +204,18 @@ "Sidebar.import-archive": "Archiváltak importálása", "Sidebar.invite-users": "Felhasználók meghívása", "Sidebar.logout": "Kijelentkezés", + "Sidebar.no-boards-in-category": "Nincsennek bent táblák", "Sidebar.random-icons": "Véletlen ikonok", "Sidebar.set-language": "Nyelv megadása", "Sidebar.set-theme": "Téma megadása", "Sidebar.settings": "Beállítások", "Sidebar.untitled-board": "(Névtelen tábla)", + "SidebarCategories.BlocksMenu.Move": "Áthelyezés...", + "SidebarCategories.CategoryMenu.CreateNew": "Új kategória létrehozása", + "SidebarCategories.CategoryMenu.Delete": "Kategória törlése", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "A {categoryName} kategóriában lévő táblák visszakerülnek a Táblák kategóriákba. Ön egyik táblából sem lesz eltávolítva.", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "Törli ezt a kategóriát?", + "SidebarCategories.CategoryMenu.Update": "Kategória átnevezése", "TableComponent.add-icon": "Ikon hozzáadása", "TableComponent.name": "Név", "TableComponent.plus-new": "+ Új", @@ -270,6 +293,7 @@ "login.register-button": "vagy hozzon létre egy fiókot ha még nincs", "register.login-button": "vagy jelentkezzen be ha már van fiókja", "register.signup-title": "Regisztráljon fiókjáért", + "shareBoard.lastAdmin": "A tábláknak legalább egy Adminisztárorral kell rendelkezniük", "tutorial_tip.finish_tour": "Kész", "tutorial_tip.got_it": "Értettem", "tutorial_tip.ok": "Következő", From 49d4450558c9bd7369bc7ad6492ba233c0ff000a Mon Sep 17 00:00:00 2001 From: Tom De Moor Date: Wed, 30 Mar 2022 12:47:34 +0200 Subject: [PATCH 20/51] Translated using Weblate (Dutch) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/nl/ Translated using Weblate (Dutch) Currently translated at 98.0% (294 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/nl/ Translated using Weblate (Dutch) Currently translated at 95.6% (287 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/nl/ Translated using Weblate (Dutch) Currently translated at 95.0% (285 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/nl/ Translated using Weblate (Dutch) Currently translated at 94.0% (282 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/nl/ --- webapp/i18n/nl.json | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/webapp/i18n/nl.json b/webapp/i18n/nl.json index cef264536..e91934efa 100644 --- a/webapp/i18n/nl.json +++ b/webapp/i18n/nl.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "Geen {property}", "BoardComponent.no-property-title": "Items met een lege {property} eigenschap komen hier te staan. Deze kolom kan niet worden verwijderd.", "BoardComponent.show": "Toon", + "BoardMember.schemeAdmin": "Beheerder", + "BoardMember.schemeEditor": "Bewerker", + "BoardMember.schemeNone": "Geen", "BoardPage.newVersion": "Er is een nieuwe versie van Boards, klik hier om te herladen.", "BoardPage.syncFailed": "Het bord kan worden verwijderd of de toegang kan worden ingetrokken.", "BoardTemplateSelector.add-template": "Nieuw sjabloon", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "Verwijderen", "BoardTemplateSelector.description": "Kies een sjabloon om je op weg te helpen. Pas het sjabloon eenvoudig aan jouw behoeften aan, of maak een leeg bord om vanaf nul te beginnen.", "BoardTemplateSelector.edit-template": "Bewerken", - "BoardTemplateSelector.plugin.no-content-description": "Voeg een bord toe aan de zijbalk met behulp van een van de sjablonen die hieronder zijn gedefinieerd of begin vanaf nul.{lineBreak} Leden van \"{workspaceName}\" hebben toegang tot de boards die hier gemaakt zijn.", - "BoardTemplateSelector.plugin.no-content-title": "Maak een Board in {workspaceName}", + "BoardTemplateSelector.plugin.no-content-description": "Voeg een bord toe aan de zijbalk door gebruik te maken van één van de sjablonen die hieronder zijn gedefinieerd of begin vanaf nul.{lineBreak} Leden van \"{teamName}\" zullen toegang hebben tot de boards die hier zijn gemaakt.", + "BoardTemplateSelector.plugin.no-content-title": "Maak een Board in {teamName}", "BoardTemplateSelector.title": "Maak een Board", "BoardTemplateSelector.use-this-template": "Gebruik dit sjabloon", + "BoardsSwitcher.Title": "Boards vinden", "BoardsUnfurl.Remainder": "+{remainder} meer", "BoardsUnfurl.Updated": "Bijgewerkt {time}", "Calculations.Options.average.displayName": "Gemiddeld", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "Bevestig kaart verwijderen!", "CardDialog.editing-template": "Je bent een sjabloon aan het bewerken.", "CardDialog.nocard": "Deze kaart bestaat niet of is ontoegankelijk.", + "Categories.CreateCategoryDialog.CancelText": "Annuleren", + "Categories.CreateCategoryDialog.CreateText": "Aanmaken", + "Categories.CreateCategoryDialog.Placeholder": "Geef je categorie een naam", + "Categories.CreateCategoryDialog.UpdateText": "Bijwerken", "CenterPanel.Share": "Delen", "ColorOption.selectColor": "Selecteer {color} Kleur", "Comment.delete": "Verwijderen", @@ -116,6 +124,11 @@ "Filter.not-includes": "bevat niet", "FilterComponent.add-filter": "+ Filter toevoegen", "FilterComponent.delete": "Verwijderen", + "FindBoFindBoardsDialog.IntroText": "Zoeken naar borden", + "FindBoardsDialog.NoResultsFor": "Geen resultaten voor \"{searchQuery}\"", + "FindBoardsDialog.NoResultsSubtext": "Controleer de spelling of probeer een andere zoekopdracht.", + "FindBoardsDialog.SubTitle": "Typ om een bord te vinden. Gebruik UP/DOWN om te bladeren. ENTER om te selecteren, ESC om te annuleren", + "FindBoardsDialog.Title": "Boards vinden", "GalleryCard.copiedLink": "Gekopieerd!", "GalleryCard.copyLink": "Kopieer link", "GalleryCard.delete": "Verwijderen", @@ -178,7 +191,10 @@ "ShareBoard.copiedLink": "Gekopieerd!", "ShareBoard.copyLink": "Link kopiëren", "ShareBoard.regenerate": "Token opnieuw genereren", + "ShareBoard.teamPermissionsText": "Iedereen van team {teamName}", "ShareBoard.tokenRegenrated": "Token opnieuw gegenereerd", + "ShareBoard.userPermissionsRemoveMemberText": "Lid verwijderen", + "ShareBoard.userPermissionsYouText": "(jij)", "Sidebar.about": "Over Focalboard", "Sidebar.add-board": "+ Bord toevoegen", "Sidebar.changePassword": "Wachtwoord wijzigen", @@ -188,11 +204,18 @@ "Sidebar.import-archive": "Archief importeren", "Sidebar.invite-users": "Gebruikers uitnodigen", "Sidebar.logout": "Afmelden", + "Sidebar.no-boards-in-category": "Geen boards hier", "Sidebar.random-icons": "Willekeurige iconen", "Sidebar.set-language": "Taal instellen", "Sidebar.set-theme": "Thema instellen", "Sidebar.settings": "Instellingen", "Sidebar.untitled-board": "(Titelloze bord )", + "SidebarCategories.BlocksMenu.Move": "Verplaatsen naar...", + "SidebarCategories.CategoryMenu.CreateNew": "Maak een nieuwe categorie", + "SidebarCategories.CategoryMenu.Delete": "Categorie verwijderen", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "Borden in {categoryName} zullen terug verhuizen naar de Boards categorieën. Je zal niet verwijderd worden uit enig board.", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "Deze categorie verwijderen?", + "SidebarCategories.CategoryMenu.Update": "Categorie hernoemen", "TableComponent.add-icon": "Pictogram toevoegen", "TableComponent.name": "Naam", "TableComponent.plus-new": "+ Nieuw", @@ -249,7 +272,7 @@ "WelcomePage.Description": "Boards is een projectmanagementtool die helpt bij het definiëren, organiseren, volgen en beheren van werk door teams heen, met behulp van een bekende kanban-bordweergave", "WelcomePage.Explore.Button": "Start een rondleiding", "WelcomePage.Heading": "Welkom bij Boards", - "WelcomePage.NoThanks.Text": "Nee bedankt, ik zoek het zelf wel uit.", + "WelcomePage.NoThanks.Text": "Nee bedankt, ik zoek het zelf wel uit", "Workspace.editing-board-template": "Je bent een bordsjabloon aan het bewerken.", "calendar.month": "Maand", "calendar.today": "VANDAAG", @@ -270,6 +293,7 @@ "login.register-button": "of maak een account aan als je er nog geen hebt", "register.login-button": "of meldt je aan als je al een account hebt", "register.signup-title": "Maak een nieuw account", + "shareBoard.lastAdmin": "Besturen moeten ten minste één beheerder hebben", "tutorial_tip.finish_tour": "Klaar", "tutorial_tip.got_it": "Begrepen", "tutorial_tip.ok": "Volgende", From a268c9600aa828ddee330e1d9417552364b392f7 Mon Sep 17 00:00:00 2001 From: kaakaa Date: Wed, 30 Mar 2022 12:47:34 +0200 Subject: [PATCH 21/51] Translated using Weblate (Japanese) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ja/ --- webapp/i18n/ja.json | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/webapp/i18n/ja.json b/webapp/i18n/ja.json index 1555f3b17..4ad508691 100644 --- a/webapp/i18n/ja.json +++ b/webapp/i18n/ja.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "{property} 無し", "BoardComponent.no-property-title": "{property}が空のアイテムがここに表示されます。このカラムは削除できません。", "BoardComponent.show": "表示", + "BoardMember.schemeAdmin": "管理者", + "BoardMember.schemeEditor": "編集者", + "BoardMember.schemeNone": "なし", "BoardPage.newVersion": "ボードの新しいバージョンが利用可能です。ここをクリックして再読み込みしてください。", "BoardPage.syncFailed": "ボードが削除されたか、アクセスが取り消されました。", "BoardTemplateSelector.add-template": "新しいテンプレート", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "削除する", "BoardTemplateSelector.description": "手軽に始めるにはテンプレートを選択します。ニーズに合わせてテンプレートを簡単にカスタマイズしたり、空のボードを作成してゼロから始めることもできます。", "BoardTemplateSelector.edit-template": "編集", - "BoardTemplateSelector.plugin.no-content-description": "サイドバーにボードを追加するには、以下のテンプレートを利用するか、空の状態から作成します。{lineBreak} \"{workspaceName}\"のメンバーは、作成されたボードにアクセスできます。", - "BoardTemplateSelector.plugin.no-content-title": "{workspaceName} にボードを作成する", + "BoardTemplateSelector.plugin.no-content-description": "サイドバーにボードを追加するには、以下のテンプレートを利用するか、空の状態から作成します。{lineBreak} \"{teamName}\"のメンバーは、作成されたボードにアクセスできます。", + "BoardTemplateSelector.plugin.no-content-title": "{teamName} にボードを作成する", "BoardTemplateSelector.title": "ボードを作成する", "BoardTemplateSelector.use-this-template": "このテンプレートを使う", + "BoardsSwitcher.Title": "ボードを探す", "BoardsUnfurl.Remainder": "残り +{remainder}", "BoardsUnfurl.Updated": "更新日時 {time}", "Calculations.Options.average.displayName": "平均", @@ -73,7 +77,7 @@ "CardDetailProperty.property-change-action-button": "プロパティの変更", "CardDetailProperty.property-changed": "プロパティが変更されました!", "CardDetailProperty.property-deleted": "{propertyName} が正常に削除されました!", - "CardDetailProperty.property-name-change-subtext": "種別を \"{oldPropType}\" から\"{newProptype}\" に", + "CardDetailProperty.property-name-change-subtext": "種別を \"{oldPropType}\" から\"{newPropType}\" に", "CardDetailProperty.property-type-change-subtext": "名前を \"{newPropName}\" に", "CardDialog.copiedLink": "コピーしました!", "CardDialog.copyLink": "リンクをコピー", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "カード削除の確認", "CardDialog.editing-template": "テンプレートを編集しています。", "CardDialog.nocard": "このカードは存在しないか、アクセスできません。", + "Categories.CreateCategoryDialog.CancelText": "キャンセル", + "Categories.CreateCategoryDialog.CreateText": "作成", + "Categories.CreateCategoryDialog.Placeholder": "カテゴリ名を入力してください", + "Categories.CreateCategoryDialog.UpdateText": "更新", "CenterPanel.Share": "共有", "ColorOption.selectColor": "{color} 色を選択", "Comment.delete": "削除", @@ -116,6 +124,11 @@ "Filter.not-includes": "を含まない", "FilterComponent.add-filter": "+ フィルターを追加する", "FilterComponent.delete": "削除", + "FindBoFindBoardsDialog.IntroText": "ボードを検索する", + "FindBoardsDialog.NoResultsFor": "\"{searchQuery}\"に対する結果はありません", + "FindBoardsDialog.NoResultsSubtext": "スペルを確認し、再度検索してください。", + "FindBoardsDialog.SubTitle": "ボードを検索するために文字を入力してください。UP/DOWNで閲覧、ENTERで選択、ESCでキャンセル", + "FindBoardsDialog.Title": "ボードを探す", "GalleryCard.copiedLink": "コピーしました!", "GalleryCard.copyLink": "リンクをコピー", "GalleryCard.delete": "削除", @@ -178,7 +191,10 @@ "ShareBoard.copiedLink": "コピーしました!", "ShareBoard.copyLink": "リンクをコピーする", "ShareBoard.regenerate": "トークンを再生成する", + "ShareBoard.teamPermissionsText": "{teamName}チームの全員", "ShareBoard.tokenRegenrated": "トークンが再生成されました", + "ShareBoard.userPermissionsRemoveMemberText": "メンバーを削除する", + "ShareBoard.userPermissionsYouText": "(あなた)", "Sidebar.about": "Focalboardについて", "Sidebar.add-board": "+ ボードを追加する", "Sidebar.changePassword": "パスワードを変更する", @@ -188,11 +204,18 @@ "Sidebar.import-archive": "インポート", "Sidebar.invite-users": "ユーザーを招待する", "Sidebar.logout": "ログアウト", + "Sidebar.no-boards-in-category": "カテゴリ内にボードがありません", "Sidebar.random-icons": "ランダムアイコン", "Sidebar.set-language": "言語設定", "Sidebar.set-theme": "テーマ設定", "Sidebar.settings": "設定", "Sidebar.untitled-board": "(無題のボード)", + "SidebarCategories.BlocksMenu.Move": "移動...", + "SidebarCategories.CategoryMenu.CreateNew": "新しいカテゴリを作成する", + "SidebarCategories.CategoryMenu.Delete": "カテゴリを削除する", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "{categoryName} にあるボードは、Boards カテゴリに戻されます。どのボードからも削除されることはありません。", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "このカテゴリを削除しますか?", + "SidebarCategories.CategoryMenu.Update": "カテゴリ名を変更する", "TableComponent.add-icon": "アイコンを追加する", "TableComponent.name": "名前", "TableComponent.plus-new": "+ 新規", @@ -270,6 +293,7 @@ "login.register-button": "アカウントをお持ちでない方はアカウントを作成してください", "register.login-button": "または、すでにアカウントをお持ちの方はログインしてください", "register.signup-title": "アカウント登録", + "shareBoard.lastAdmin": "ボードには少なくとも1名の管理者が必要です", "tutorial_tip.finish_tour": "完了", "tutorial_tip.got_it": "了解", "tutorial_tip.ok": "次へ", From 0b38b226971a4c587972a0a5be1fd6a51cd178cd Mon Sep 17 00:00:00 2001 From: Maksim Matveev Date: Wed, 30 Mar 2022 12:47:34 +0200 Subject: [PATCH 22/51] Translated using Weblate (Russian) Currently translated at 41.0% (123 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ru/ --- webapp/i18n/ru.json | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/webapp/i18n/ru.json b/webapp/i18n/ru.json index 22b17dac2..a8c214f2c 100644 --- a/webapp/i18n/ru.json +++ b/webapp/i18n/ru.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "{property} пусто", "BoardComponent.no-property-title": "Здесь будут элементы с пустым свойством {property}. Этот столбец не может быть удален.", "BoardComponent.show": "Показать", + "BoardMember.schemeAdmin": "Администратор", + "BoardMember.schemeEditor": "Редактор", + "BoardMember.schemeNone": "Никто", "BoardPage.newVersion": "Доступна новая версия Доски. Нажмите здесь, чтобы перезагрузить.", "BoardPage.syncFailed": "Доска может быть удалена или доступ аннулирован.", "BoardTemplateSelector.add-template": "Новый шаблон", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "Удалить", "BoardTemplateSelector.description": "Выберите шаблон, который поможет Вам начать работу. Легко настройте шаблон в соответствии со своими потребностями или создайте пустую доску, чтобы начать с нуля.", "BoardTemplateSelector.edit-template": "Редактировать", - "BoardTemplateSelector.plugin.no-content-description": "Добавьте доску на боковую панель, используя любой из указанных ниже шаблонов, или начните с нуля.{lineBreak} Участники \"{workspaceName}\" будут иметь доступ к созданным здесь доскам.", - "BoardTemplateSelector.plugin.no-content-title": "Создать Доску в {workspaceName}", + "BoardTemplateSelector.plugin.no-content-description": "Добавьте доску на боковую панель, используя любой из указанных ниже шаблонов, или начните с нуля.{lineBreak} Участники \"{teamName}\" будут иметь доступ к созданным здесь доскам.", + "BoardTemplateSelector.plugin.no-content-title": "Создать доску в {teamName}", "BoardTemplateSelector.title": "Создать Доску", "BoardTemplateSelector.use-this-template": "Использовать этот шаблон", + "BoardsSwitcher.Title": "Найти доски", "BoardsUnfurl.Remainder": "+{remainder} ещё", "BoardsUnfurl.Updated": "Обновлено {time}", "Calculations.Options.average.displayName": "Среднее", @@ -72,7 +76,7 @@ "CardDetailProperty.delete-action-button": "Удалить", "CardDetailProperty.property-change-action-button": "Изменить свойство", "CardDetailProperty.property-changed": "Свойство изменено успешно!", - "CardDetailProperty.property-deleted": "{PropertyName} успешно удалено!", + "CardDetailProperty.property-deleted": "{propertyName} успешно удалено!", "CardDetailProperty.property-name-change-subtext": "тип из \"{oldPropType}\" в \"{newPropType}\"", "CardDetailProperty.property-type-change-subtext": "название для \"{newPropName}\"", "CardDialog.copiedLink": "Скопировано!", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "Подтвердите удаление карточки!", "CardDialog.editing-template": "Вы редактируете шаблон.", "CardDialog.nocard": "Эта карточка не существует или недоступна.", + "Categories.CreateCategoryDialog.CancelText": "Отмена", + "Categories.CreateCategoryDialog.CreateText": "Создать", + "Categories.CreateCategoryDialog.Placeholder": "Назовите свою категорию", + "Categories.CreateCategoryDialog.UpdateText": "Обновить", "CenterPanel.Share": "Поделится", "ColorOption.selectColor": "Выберите цвет {color}", "Comment.delete": "Удалить", @@ -116,6 +124,11 @@ "Filter.not-includes": "не содержит", "FilterComponent.add-filter": "+ Добавить фильтр", "FilterComponent.delete": "Удалить", + "FindBoFindBoardsDialog.IntroText": "Поиск досок", + "FindBoardsDialog.NoResultsFor": "Нет результатов для \"{searchQuery}\"", + "FindBoardsDialog.NoResultsSubtext": "Проверьте правильность написания или попробуйте другой запрос.", + "FindBoardsDialog.SubTitle": "Введите запрос, чтобы найти доску. Используйте ВВЕРХ/ВНИЗ для просмотра. ENTER для выбора, ESC для закрытия", + "FindBoardsDialog.Title": "Найти доски", "GalleryCard.copiedLink": "Скопировано!", "GalleryCard.copyLink": "Копировать ссылку", "GalleryCard.delete": "Удалить", @@ -178,7 +191,10 @@ "ShareBoard.copiedLink": "Скопировано!", "ShareBoard.copyLink": "Скопировать ссылку", "ShareBoard.regenerate": "Восстановить токен", + "ShareBoard.teamPermissionsText": "Все в команде {teamName}", "ShareBoard.tokenRegenrated": "Токен пересоздан", + "ShareBoard.userPermissionsRemoveMemberText": "Удалить участника", + "ShareBoard.userPermissionsYouText": "(Вы)", "Sidebar.about": "О Focalboard", "Sidebar.add-board": "+ Добавить доску", "Sidebar.changePassword": "Изменить пароль", @@ -188,11 +204,18 @@ "Sidebar.import-archive": "Импорт архива", "Sidebar.invite-users": "Пригласить пользователей", "Sidebar.logout": "Выйти", + "Sidebar.no-boards-in-category": "Без досок внутри", "Sidebar.random-icons": "Случайные иконки", "Sidebar.set-language": "Язык", "Sidebar.set-theme": "Тема", "Sidebar.settings": "Настройки", "Sidebar.untitled-board": "(Доска без названия)", + "SidebarCategories.BlocksMenu.Move": "Перейти к...", + "SidebarCategories.CategoryMenu.CreateNew": "Создать новую категорию", + "SidebarCategories.CategoryMenu.Delete": "Удалить категорию", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "Доски в {categoryName} вернутся к категориям \"Доски\". Вы не удалены ни с одной доски.", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "Удалить эту категорию?", + "SidebarCategories.CategoryMenu.Update": "Переименовать категорию", "TableComponent.add-icon": "Добавить иконку", "TableComponent.name": "Название", "TableComponent.plus-new": "+ Создать", @@ -270,6 +293,7 @@ "login.register-button": "или создать аккаунт, если у Вас его нет", "register.login-button": "или войти в систему, если у вас уже есть аккаунт", "register.signup-title": "Зарегистрируйте свой аккаунт", + "shareBoard.lastAdmin": "Доски должны иметь хотя бы одного администратора", "tutorial_tip.finish_tour": "Готово", "tutorial_tip.got_it": "Понятно", "tutorial_tip.ok": "Следующий", From b380aac8c29de160852adc4462c5c2f323815d66 Mon Sep 17 00:00:00 2001 From: Alexey Napalkov Date: Wed, 30 Mar 2022 12:47:35 +0200 Subject: [PATCH 23/51] Translated using Weblate (Russian) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ru/ --- webapp/i18n/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/ru.json b/webapp/i18n/ru.json index a8c214f2c..5bb70a824 100644 --- a/webapp/i18n/ru.json +++ b/webapp/i18n/ru.json @@ -111,7 +111,7 @@ "ContentBlock.text": "текст", "DeleteBoardDialog.confirm-cancel": "Отмена", "DeleteBoardDialog.confirm-delete": "Удалить", - "DeleteBoardDialog.confirm-info": "Вы уверены, что хотите удалить доску \"{boardTitle}\"? Его удаление приведет к удалению всех карточек на доске.", + "DeleteBoardDialog.confirm-info": "Вы уверены, что хотите удалить доску \"{boardTitle}\"? Ее удаление приведет к удалению всех карточек на доске.", "DeleteBoardDialog.confirm-tite": "Подтвердить удаление доски", "DeleteBoardDialog.confirm-tite-template": "Подтвердите удаление шаблона Доски", "Dialog.closeDialog": "Закрыть диалог", @@ -133,7 +133,7 @@ "GalleryCard.copyLink": "Копировать ссылку", "GalleryCard.delete": "Удалить", "GalleryCard.duplicate": "Создать дубликат", - "General.BoardCount": "{count, plural, one {# Доска} other {# Доски}}", + "General.BoardCount": "{count, plural, one {# Доска} few {# Доски} other {# Досок}}", "GroupBy.hideEmptyGroups": "Скрыть {count} пустых групп", "GroupBy.showHiddenGroups": "Показать {count} скрытых групп", "GroupBy.ungroup": "Разгруппировать", From c1fa2a1e147e43a6e0989e4c22237e3c1207b4b9 Mon Sep 17 00:00:00 2001 From: Matthew Williams Date: Wed, 30 Mar 2022 12:47:35 +0200 Subject: [PATCH 24/51] Translated using Weblate (English (Australia)) Currently translated at 100.0% (300 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/en_AU/ --- webapp/i18n/en_AU.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/en_AU.json b/webapp/i18n/en_AU.json index 2f3473bac..1b7bf9264 100644 --- a/webapp/i18n/en_AU.json +++ b/webapp/i18n/en_AU.json @@ -7,6 +7,9 @@ "BoardComponent.no-property": "No {property}", "BoardComponent.no-property-title": "Items with an empty {property} property will go here. This column cannot be removed.", "BoardComponent.show": "Show", + "BoardMember.schemeAdmin": "Admin", + "BoardMember.schemeEditor": "Editor", + "BoardMember.schemeNone": "None", "BoardPage.newVersion": "A new version of Boards is available, click here to reload.", "BoardPage.syncFailed": "Board may be deleted or access revoked.", "BoardTemplateSelector.add-template": "New template", @@ -14,10 +17,11 @@ "BoardTemplateSelector.delete-template": "Delete", "BoardTemplateSelector.description": "Choose a template to help you get started. Easily customise the template to fit your needs, or create an empty board to start from scratch.", "BoardTemplateSelector.edit-template": "Edit", - "BoardTemplateSelector.plugin.no-content-description": "Add a board to the sidebar using any of the templates defined below or start from scratch.{lineBreak} Members of '{workspaceName}' will have access to boards created here.", - "BoardTemplateSelector.plugin.no-content-title": "Create a Board in {workspaceName}", + "BoardTemplateSelector.plugin.no-content-description": "Add a board to the sidebar using any of the templates defined below or start from scratch.{lineBreak} Members of '\\{teamName}'\\ will have access to boards created here.", + "BoardTemplateSelector.plugin.no-content-title": "Create a Board in {teamName}", "BoardTemplateSelector.title": "Create a Board", "BoardTemplateSelector.use-this-template": "Use this template", + "BoardsSwitcher.Title": "Find Boards", "BoardsUnfurl.Remainder": "+{remainder} more", "BoardsUnfurl.Updated": "Updated {time}", "Calculations.Options.average.displayName": "Average", @@ -81,6 +85,10 @@ "CardDialog.delete-confirmation-dialog-heading": "Confirm card deletion?", "CardDialog.editing-template": "You're editing a template.", "CardDialog.nocard": "This card doesn't exist or is inaccessible.", + "Categories.CreateCategoryDialog.CancelText": "Cancel", + "Categories.CreateCategoryDialog.CreateText": "Create", + "Categories.CreateCategoryDialog.Placeholder": "Name your category", + "Categories.CreateCategoryDialog.UpdateText": "Update", "CenterPanel.Share": "Share", "ColorOption.selectColor": "Select {color} Colour", "Comment.delete": "Delete", @@ -116,6 +124,11 @@ "Filter.not-includes": "doesn't include", "FilterComponent.add-filter": "+ Add filter", "FilterComponent.delete": "Delete", + "FindBoFindBoardsDialog.IntroText": "Search for boards", + "FindBoardsDialog.NoResultsFor": "No results for '\\{searchQuery}'\\", + "FindBoardsDialog.NoResultsSubtext": "Check the spelling or try another search.", + "FindBoardsDialog.SubTitle": "Type to find a board. Use UP/DOWN to browse. ENTER to select, ESC to dismiss", + "FindBoardsDialog.Title": "Find Boards", "GalleryCard.copiedLink": "Copied!", "GalleryCard.copyLink": "Copy link", "GalleryCard.delete": "Delete", @@ -178,7 +191,10 @@ "ShareBoard.copiedLink": "Copied!", "ShareBoard.copyLink": "Copy link", "ShareBoard.regenerate": "Regenerate token", + "ShareBoard.teamPermissionsText": "Everyone at {teamName} Team", "ShareBoard.tokenRegenrated": "Token regenerated", + "ShareBoard.userPermissionsRemoveMemberText": "Remove member", + "ShareBoard.userPermissionsYouText": "(You)", "Sidebar.about": "About Focalboard", "Sidebar.add-board": "+ Add board", "Sidebar.changePassword": "Change password", @@ -188,11 +204,18 @@ "Sidebar.import-archive": "Import archive", "Sidebar.invite-users": "Invite users", "Sidebar.logout": "Log out", + "Sidebar.no-boards-in-category": "No boards inside", "Sidebar.random-icons": "Random icons", "Sidebar.set-language": "Set language", "Sidebar.set-theme": "Set theme", "Sidebar.settings": "Settings", "Sidebar.untitled-board": "(Untitled Board)", + "SidebarCategories.BlocksMenu.Move": "Move To...", + "SidebarCategories.CategoryMenu.CreateNew": "Create New Category", + "SidebarCategories.CategoryMenu.Delete": "Delete Category", + "SidebarCategories.CategoryMenu.DeleteModal.Body": "Boards in {categoryName} will move back to the Boards categories. You're not removed from any boards.", + "SidebarCategories.CategoryMenu.DeleteModal.Title": "Delete this category?", + "SidebarCategories.CategoryMenu.Update": "Rename Category", "TableComponent.add-icon": "Add icon", "TableComponent.name": "Name", "TableComponent.plus-new": "+ New", @@ -270,6 +293,7 @@ "login.register-button": "or create an account if you don't have one", "register.login-button": "or log in if you already have an account", "register.signup-title": "Sign up for your account", + "shareBoard.lastAdmin": "Boards must have at least one Administrator", "tutorial_tip.finish_tour": "Done", "tutorial_tip.got_it": "Got it", "tutorial_tip.ok": "Next", From 79279b7b8b26b7a2bd422ce018da11414abbd0c9 Mon Sep 17 00:00:00 2001 From: thorkemado Date: Wed, 30 Mar 2022 12:47:35 +0200 Subject: [PATCH 25/51] Translated using Weblate (Spanish) Currently translated at 57.3% (172 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/es/ --- webapp/i18n/es.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json index 169a05325..79825265c 100644 --- a/webapp/i18n/es.json +++ b/webapp/i18n/es.json @@ -7,11 +7,34 @@ "BoardComponent.no-property": "Sin {property}", "BoardComponent.no-property-title": "Elementos sin la propiedad {property} irán aquí. Esta columna no se puede eliminar.", "BoardComponent.show": "Mostrar", + "BoardMember.schemeAdmin": "Administrador", + "BoardMember.schemeEditor": "Editor", + "BoardMember.schemeNone": "Ninguno", "BoardPage.newVersion": "Una nueva versión de Board está disponible, haz click aquí para recargar.", "BoardPage.syncFailed": "El tablero puede estar eliminado o el acceso fue revocado.", + "BoardTemplateSelector.add-template": "Nueva plantilla", + "BoardTemplateSelector.create-empty-board": "Crear pizarra vacía", + "BoardTemplateSelector.delete-template": "Suprimir", + "BoardTemplateSelector.edit-template": "Editar", + "BoardTemplateSelector.use-this-template": "Utiliza esta plantilla", + "BoardsUnfurl.Updated": "Actualizado {time}", "Calculations.Options.count.displayName": "Cantidad", "Calculations.Options.count.label": "Cantidad", + "Calculations.Options.countChecked.displayName": "Marcado", "Calculations.Options.countUnchecked.displayName": "Deseleccionado", + "Calculations.Options.countValue.displayName": "Valores", + "Calculations.Options.dateRange.displayName": "Rango", + "Calculations.Options.dateRange.label": "Rango", + "Calculations.Options.max.displayName": "Máx", + "Calculations.Options.max.label": "Máx", + "Calculations.Options.median.displayName": "Mediana", + "Calculations.Options.median.label": "Mediana", + "Calculations.Options.min.displayName": "Mín", + "Calculations.Options.min.label": "Mín", + "Calculations.Options.none.displayName": "Calcular", + "Calculations.Options.none.label": "Ninguna", + "Calculations.Options.percentChecked.displayName": "Marcado", + "Calculations.Options.range.displayName": "Rango", "CardDetail.add-content": "Añadir contenido", "CardDetail.add-icon": "Añadir icono", "CardDetail.add-property": "+ Añadir propiedad", From 45fd77b9f6b0dc47760646c740b4684dfa71a5dc Mon Sep 17 00:00:00 2001 From: Varghese Jose Date: Wed, 30 Mar 2022 12:47:35 +0200 Subject: [PATCH 26/51] Translated using Weblate (Malayalam) Currently translated at 92.3% (277 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ml/ --- webapp/i18n/ml.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/webapp/i18n/ml.json b/webapp/i18n/ml.json index 3fe0a3c43..7afc78331 100644 --- a/webapp/i18n/ml.json +++ b/webapp/i18n/ml.json @@ -7,6 +7,8 @@ "BoardComponent.no-property": "ഇല്ല {property}", "BoardComponent.no-property-title": "ശൂന്യമായ {property} പ്രോപ്പർട്ടി ഉള്ള ഇനങ്ങൾ ഇവിടെ പോകും. ഈ കോളം നീക്കം ചെയ്യാൻ കഴിയില്ല.", "BoardComponent.show": "കാണിക്കുക", + "BoardMember.schemeAdmin": "അഡ്മിൻ", + "BoardMember.schemeEditor": "എഡിറ്റർ", "BoardPage.newVersion": "ബോർഡുകളുടെ ഒരു പുതിയ പതിപ്പ് ലഭ്യമാണ്, റീലോഡ് ചെയ്യാൻ ഇവിടെ ക്ലിക്ക് ചെയ്യുക.", "BoardPage.syncFailed": "ബോർഡ് ഇല്ലാതാക്കുകയോ ആക്സസ് റദ്ദാക്കുകയോ ചെയ്യാം.", "BoardTemplateSelector.add-template": "പുതിയ ടെംപ്ലേറ്റ്", @@ -81,6 +83,9 @@ "CardDialog.delete-confirmation-dialog-heading": "കാർഡ് ഇല്ലാതാക്കൽ സ്ഥിരീകരിക്കുക!", "CardDialog.editing-template": "നിങ്ങൾ ഒരു ടെംപ്ലേറ്റ് എഡിറ്റ് ചെയ്യുകയാണ്.", "CardDialog.nocard": "ഈ കാർഡ് നിലവിലില്ല അല്ലെങ്കിൽ ആക്സസ് ചെയ്യാനാകുന്നില്ല.", + "Categories.CreateCategoryDialog.CancelText": "റദ്ദാക്കുക", + "Categories.CreateCategoryDialog.CreateText": "സൃഷ്ടിക്കുക", + "Categories.CreateCategoryDialog.UpdateText": "അപ്ഡേറ്റ് ചെയ്യുക", "CenterPanel.Share": "പങ്കിടുക", "ColorOption.selectColor": "നിറം {color} തിരഞ്ഞെടുക്കുക", "Comment.delete": "ഇല്ലാതാക്കുക", From 673c67e0a6f9107a6d0a56314d832d41cde38df6 Mon Sep 17 00:00:00 2001 From: MArtin Johnson Date: Wed, 30 Mar 2022 12:47:36 +0200 Subject: [PATCH 27/51] Translated using Weblate (Swedish) Currently translated at 71.6% (215 of 300 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/sv/ --- webapp/i18n/sv.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/sv.json b/webapp/i18n/sv.json index 1d552c64c..1ebe4f721 100644 --- a/webapp/i18n/sv.json +++ b/webapp/i18n/sv.json @@ -103,7 +103,7 @@ "KanbanCard.copiedLink": "Kopierad!", "KanbanCard.copyLink": "Kopiera länk", "KanbanCard.delete": "Radera", - "KanbanCard.duplicate": "Radera", + "KanbanCard.duplicate": "Kopiera", "KanbanCard.untitled": "Saknar titel", "Mutator.new-card-from-template": "nytt kort från mall", "Mutator.new-template-from-card": "ny mall från kort", @@ -201,7 +201,7 @@ "ViewTitle.show-description": "visa beskrivning", "ViewTitle.untitled-board": "Tavla saknar titel", "WelcomePage.Description": "Anslagstavlan är ett projekthanteringsverktyg som hjälper till att definiera, organisera, spåra och hantera arbete mellan team med hjälp av en välbekant Kanban-vy", - "WelcomePage.Explore.Button": "Utforska", + "WelcomePage.Explore.Button": "Starta en rundtur", "WelcomePage.Heading": "Välkommen till Anslagstavlan", "Workspace.editing-board-template": "Du redigerar en tavelmall.", "calendar.month": "Månad", From 72dc61bab55123a896470dff8bce663b1cfbfa3b Mon Sep 17 00:00:00 2001 From: Alex kkk Date: Wed, 30 Mar 2022 12:47:36 +0200 Subject: [PATCH 28/51] Added translation using Weblate (Hebrew) --- webapp/i18n/he.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 webapp/i18n/he.json diff --git a/webapp/i18n/he.json b/webapp/i18n/he.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/webapp/i18n/he.json @@ -0,0 +1 @@ +{} From eedfe427e265c3b6b11514c6fd6e46df7ffad889 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Thu, 31 Mar 2022 10:33:40 +0530 Subject: [PATCH 29/51] Used gobal team ID --- server/app/onboarding.go | 2 +- server/app/templates.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/app/onboarding.go b/server/app/onboarding.go index 3e6dbb9ea..8a015398d 100644 --- a/server/app/onboarding.go +++ b/server/app/onboarding.go @@ -46,7 +46,7 @@ func (a *App) PrepareOnboardingTour(userID string, teamID string) (string, strin } func (a *App) getOnboardingBoardID() (string, error) { - boards, err := a.store.GetTemplateBoards("0", "") + boards, err := a.store.GetTemplateBoards(globalTeamID, "") if err != nil { return "", err } diff --git a/server/app/templates.go b/server/app/templates.go index 2b05827de..96c1e7453 100644 --- a/server/app/templates.go +++ b/server/app/templates.go @@ -23,7 +23,7 @@ func (a *App) InitTemplates() error { // initializeTemplates imports default templates if the boards table is empty. func (a *App) initializeTemplates() (bool, error) { - boards, err := a.store.GetTemplateBoards(globalTeamID) + boards, err := a.store.GetTemplateBoards(globalTeamID, "") if err != nil { return false, fmt.Errorf("cannot initialize templates: %w", err) } From 31c027afe1a671fe0c5280a357401423a3fb7a85 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Thu, 31 Mar 2022 10:38:23 +0530 Subject: [PATCH 30/51] Used gobal team ID --- server/app/onboarding.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app/onboarding.go b/server/app/onboarding.go index 8a015398d..03aff7169 100644 --- a/server/app/onboarding.go +++ b/server/app/onboarding.go @@ -53,7 +53,7 @@ func (a *App) getOnboardingBoardID() (string, error) { var onboardingBoardID string for _, block := range boards { - if block.Title == WelcomeBoardTitle && block.TeamID == "0" { + if block.Title == WelcomeBoardTitle && block.TeamID == globalTeamID { onboardingBoardID = block.ID break } From fa1860a4a88fbd6101ae300b8861ff1728f3e4da Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Thu, 31 Mar 2022 10:52:21 +0530 Subject: [PATCH 31/51] lint fix --- server/app/templates_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/app/templates_test.go b/server/app/templates_test.go index 37defa865..4092f366c 100644 --- a/server/app/templates_test.go +++ b/server/app/templates_test.go @@ -38,7 +38,7 @@ func TestApp_initializeTemplates(t *testing.T) { th, tearDown := SetupTestHelper(t) defer tearDown() - th.Store.EXPECT().GetTemplateBoards(globalTeamID).Return([]*model.Board{}, nil) + th.Store.EXPECT().GetTemplateBoards(globalTeamID, "").Return([]*model.Board{}, nil) th.Store.EXPECT().RemoveDefaultTemplates([]*model.Board{}).Return(nil) th.Store.EXPECT().CreateBoardsAndBlocks(gomock.Any(), gomock.Any()).AnyTimes().Return(boardsAndBlocks, nil) th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{}, nil) @@ -54,7 +54,7 @@ func TestApp_initializeTemplates(t *testing.T) { th, tearDown := SetupTestHelper(t) defer tearDown() - th.Store.EXPECT().GetTemplateBoards(globalTeamID).Return([]*model.Board{board}, nil) + th.Store.EXPECT().GetTemplateBoards(globalTeamID, "").Return([]*model.Board{board}, nil) done, err := th.App.initializeTemplates() require.NoError(t, err, "initializeTemplates should not error") From 9e507fc2099140779c84cd51292af67c1b56a9d2 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Thu, 31 Mar 2022 17:55:29 +0530 Subject: [PATCH 32/51] Adde missing board menu options in sidebar --- .../boardTemplateSelector.test.tsx | 1 - .../boardTemplateSelector.tsx | 6 +++-- .../boardTemplateSelectorItem.tsx | 7 +++--- webapp/src/components/sidebar/sidebar.tsx | 4 ++- webapp/src/constants.ts | 2 ++ webapp/src/mutator.ts | 2 +- webapp/src/octoClient.ts | 5 ++-- webapp/src/pages/boardPage/boardPage.tsx | 6 +++-- .../boardPage/teamToBoardAndViewRedirect.tsx | 3 ++- .../pages/boardPage/websocketConnection.tsx | 11 ++++---- webapp/src/store/globalTemplates.ts | 4 ++- webapp/src/utils.test.ts | 25 +++++++++++++++++++ 12 files changed, 57 insertions(+), 19 deletions(-) diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.test.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.test.tsx index fea3ddf8a..b683903db 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.test.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.test.tsx @@ -231,7 +231,6 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => { const editIcon = screen.getByText(template1Title).parentElement?.querySelector('.EditIcon') expect(editIcon).not.toBeNull() userEvent.click(editIcon!) - expect(history.push).toBeCalledTimes(1) }) test('return BoardTemplateSelector and click to add board from template', async () => { render(wrapDNDIntl( diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx index a39d0bd5b..92354d906 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.tsx @@ -25,6 +25,8 @@ import {BaseTourSteps, TOUR_BASE} from '../onboardingTour' import {Utils} from "../../utils" +import {Constants} from "../../constants" + import BoardTemplateSelectorPreview from './boardTemplateSelectorPreview' import BoardTemplateSelectorItem from './boardTemplateSelectorItem' @@ -53,7 +55,7 @@ const BoardTemplateSelector = (props: Props) => { }, [match, history, onClose]) useEffect(() => { - if (octoClient.teamId !== '0' && globalTemplates.length === 0) { + if (octoClient.teamId !== Constants.globalTeamId && globalTemplates.length === 0) { dispatch(fetchGlobalTemplates()) } }, [octoClient.teamId]) @@ -95,7 +97,7 @@ const BoardTemplateSelector = (props: Props) => { } const handleUseTemplate = async () => { - await mutator.addBoardFromTemplate(currentTeam?.id || '0', intl, showBoard, () => showBoard(currentBoardId), activeTemplate.id, currentTeam?.id) + await mutator.addBoardFromTemplate(currentTeam?.id || Constants.globalTeamId, intl, showBoard, () => showBoard(currentBoardId), activeTemplate.id, currentTeam?.id) if (activeTemplate.title === OnboardingBoardTitle) { resetTour() } diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx index 4a056899c..f1aa5a0df 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorItem.tsx @@ -10,6 +10,7 @@ import EditIcon from '../../widgets/icons/edit' import DeleteBoardDialog from '../sidebar/deleteBoardDialog' import './boardTemplateSelectorItem.scss' +import {Constants} from "../../constants" type Props = { isActive: boolean @@ -31,8 +32,6 @@ const BoardTemplateSelectorItem = (props: Props) => { onEdit(template.id) }, [onEdit, template]) - console.log(`Template Metadata: name: ${template.title} template version: ${template.templateVersion}`) - return (
{ > {template.icon} {template.title} - {!template.templateVersion && + + {/* don't show template menu options for default templates */} + {template.teamId !== Constants.globalTeamId &&
} diff --git a/webapp/src/components/sidebar/sidebar.tsx b/webapp/src/components/sidebar/sidebar.tsx index 596a96561..ba7efb4b2 100644 --- a/webapp/src/components/sidebar/sidebar.tsx +++ b/webapp/src/components/sidebar/sidebar.tsx @@ -29,6 +29,8 @@ import wsClient, {WSClient} from '../../wsclient' import {getCurrentTeam} from '../../store/teams' +import {Constants} from "../../constants" + import SidebarCategory from './sidebarCategory' import SidebarSettingsMenu from './sidebarSettingsMenu' import SidebarUserMenu from './sidebarUserMenu' @@ -152,7 +154,7 @@ const Sidebar = (props: Props) => {
} - {team && team.id !== '0' && + {team && team.id !== Constants.globalTeamId &&
{Utils.isFocalboardPlugin() && <> diff --git a/webapp/src/constants.ts b/webapp/src/constants.ts index 4159c1310..fe55d024a 100644 --- a/webapp/src/constants.ts +++ b/webapp/src/constants.ts @@ -186,6 +186,8 @@ class Constants { Y: ['y', 89], Z: ['z', 90], } + + static readonly globalTeamId = '0' } export {Constants, Permission} diff --git a/webapp/src/mutator.ts b/webapp/src/mutator.ts index c8e617b17..a7f49d321 100644 --- a/webapp/src/mutator.ts +++ b/webapp/src/mutator.ts @@ -1047,7 +1047,7 @@ class Mutator { beforeUndo: () => Promise, boardTemplateId: string, toTeam?: string, - ): Promise<[Block[], string]> { + ): Promise { const asTemplate = false const actionDescription = intl.formatMessage({id: 'Mutator.new-board-from-template', defaultMessage: 'new board from template'}) diff --git a/webapp/src/octoClient.ts b/webapp/src/octoClient.ts index 36f5a0e13..619d509c5 100644 --- a/webapp/src/octoClient.ts +++ b/webapp/src/octoClient.ts @@ -12,6 +12,7 @@ import {Category, CategoryBlocks} from './store/sidebar' import {Team} from './store/teams' import {Subscription} from './wsclient' import {PrepareOnboardingResponse} from './onboardingTour' +import {Constants} from "./constants" // // OctoClient is the client interface to the server APIs @@ -45,7 +46,7 @@ class OctoClient { localStorage.setItem('focalboardSessionId', value) } - constructor(serverUrl?: string, public teamId = '0') { + constructor(serverUrl?: string, public teamId = Constants.globalTeamId) { this.serverUrl = serverUrl } @@ -144,7 +145,7 @@ class OctoClient { private teamPath(teamId?: string): string { let teamIdToUse = teamId if (!teamId) { - teamIdToUse = this.teamId === '0' ? UserSettings.lastTeamId || this.teamId : this.teamId + teamIdToUse = this.teamId === Constants.globalTeamId ? UserSettings.lastTeamId || this.teamId : this.teamId } return `/api/v1/teams/${teamIdToUse}` diff --git a/webapp/src/pages/boardPage/boardPage.tsx b/webapp/src/pages/boardPage/boardPage.tsx index 523441904..3fa138d1b 100644 --- a/webapp/src/pages/boardPage/boardPage.tsx +++ b/webapp/src/pages/boardPage/boardPage.tsx @@ -22,6 +22,8 @@ import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../../teleme import {fetchUserBlockSubscriptions, getMe} from '../../store/users' import {IUser} from '../../user' +import {Constants} from "../../constants" + import SetWindowTitleAndIcon from './setWindowTitleAndIcon' import TeamToBoardAndViewRedirect from './teamToBoardAndViewRedirect' import UndoRedoHotKeys from './undoRedoHotKeys' @@ -41,7 +43,7 @@ const BoardPage = (props: Props): JSX.Element => { const dispatch = useAppDispatch() const match = useRouteMatch<{boardId: string, viewId: string, cardId?: string, teamId?: string}>() const [mobileWarningClosed, setMobileWarningClosed] = useState(UserSettings.mobileWarningClosed) - const teamId = match.params.teamId || UserSettings.lastTeamId || '0' + const teamId = match.params.teamId || UserSettings.lastTeamId || Constants.globalTeamId const me = useAppSelector(getMe) // if we're in a legacy route and not showing a shared board, @@ -110,7 +112,7 @@ const BoardPage = (props: Props): JSX.Element => { // and set it as most recently viewed board UserSettings.setLastBoardID(teamId, match.params.boardId) - if (match.params.viewId && match.params.viewId !== '0') { + if (match.params.viewId && match.params.viewId !== Constants.globalTeamId) { dispatch(setCurrentView(match.params.viewId)) UserSettings.setLastViewId(match.params.boardId, match.params.viewId) } diff --git a/webapp/src/pages/boardPage/teamToBoardAndViewRedirect.tsx b/webapp/src/pages/boardPage/teamToBoardAndViewRedirect.tsx index ac890256c..e52744d56 100644 --- a/webapp/src/pages/boardPage/teamToBoardAndViewRedirect.tsx +++ b/webapp/src/pages/boardPage/teamToBoardAndViewRedirect.tsx @@ -8,6 +8,7 @@ import {setCurrent as setCurrentView, getCurrentBoardViews} from '../../store/vi import {useAppSelector, useAppDispatch} from '../../store/hooks' import {UserSettings} from '../../userSettings' import {getSidebarCategories} from '../../store/sidebar' +import {Constants} from "../../constants" const TeamToBoardAndViewRedirect = (): null => { const boardId = useAppSelector(getCurrentBoardId) @@ -16,7 +17,7 @@ const TeamToBoardAndViewRedirect = (): null => { const history = useHistory() const match = useRouteMatch<{boardId: string, viewId: string, cardId?: string, teamId?: string}>() const categories = useAppSelector(getSidebarCategories) - const teamId = match.params.teamId || UserSettings.lastTeamId || '0' + const teamId = match.params.teamId || UserSettings.lastTeamId || Constants.globalTeamId useEffect(() => { let boardID = match.params.boardId diff --git a/webapp/src/pages/boardPage/websocketConnection.tsx b/webapp/src/pages/boardPage/websocketConnection.tsx index 960c99077..09b75cad5 100644 --- a/webapp/src/pages/boardPage/websocketConnection.tsx +++ b/webapp/src/pages/boardPage/websocketConnection.tsx @@ -22,6 +22,7 @@ import {useAppSelector, useAppDispatch} from '../../store/hooks' import {followBlock, getMe, unfollowBlock} from '../../store/users' import {IUser} from '../../user' +import {Constants} from "../../constants" const websocketTimeoutForBanner = 5000 @@ -49,8 +50,8 @@ const WebsocketConnection = (props: Props) => { useEffect(() => { let subscribedToTeam = false if (wsClient.state === 'open') { - wsClient.authenticate(props.teamId || '0', token) - wsClient.subscribeToTeam(props.teamId || '0') + wsClient.authenticate(props.teamId || Constants.globalTeamId, token) + wsClient.subscribeToTeam(props.teamId || Constants.globalTeamId) subscribedToTeam = true } @@ -71,7 +72,7 @@ const WebsocketConnection = (props: Props) => { const incrementalBoardUpdate = (_: WSClient, boards: Board[]) => { // only takes into account the entities that belong to the team or the user boards - const teamBoards = boards.filter((b: Board) => b.teamId === '0' || b.teamId === props.teamId) + const teamBoards = boards.filter((b: Board) => b.teamId === Constants.globalTeamId || b.teamId === props.teamId) dispatch(updateBoards(teamBoards)) } @@ -83,8 +84,8 @@ const WebsocketConnection = (props: Props) => { const updateWebsocketState = (_: WSClient, newState: 'init'|'open'|'close'): void => { if (newState === 'open') { const newToken = localStorage.getItem('focalboardSessionId') || '' - wsClient.authenticate(props.teamId || '0', newToken) - wsClient.subscribeToTeam(props.teamId || '0') + wsClient.authenticate(props.teamId || Constants.globalTeamId, newToken) + wsClient.subscribeToTeam(props.teamId || Constants.globalTeamId) subscribedToTeam = true } diff --git a/webapp/src/store/globalTemplates.ts b/webapp/src/store/globalTemplates.ts index b7b0ae2ec..9352aa2e5 100644 --- a/webapp/src/store/globalTemplates.ts +++ b/webapp/src/store/globalTemplates.ts @@ -6,6 +6,8 @@ import {createSlice, createAsyncThunk} from '@reduxjs/toolkit' import {default as client} from '../octoClient' import {Board} from '../blocks/board' +import {Constants} from "../constants" + import {RootState} from './index' // ToDo: move this to team templates or simply templates @@ -13,7 +15,7 @@ import {RootState} from './index' export const fetchGlobalTemplates = createAsyncThunk( 'globalTemplates/fetch', async () => { - const templates = await client.getTeamTemplates('0') + const templates = await client.getTeamTemplates(Constants.globalTeamId) return templates.sort((a, b) => a.title.localeCompare(b.title)) }, ) diff --git a/webapp/src/utils.test.ts b/webapp/src/utils.test.ts index 5e98b8d92..510ea8789 100644 --- a/webapp/src/utils.test.ts +++ b/webapp/src/utils.test.ts @@ -3,6 +3,10 @@ import {createIntl} from 'react-intl' +import {createMemoryHistory} from "history" + +import {match as routerMatch} from "react-router-dom" + import {Utils, IDType} from './utils' import {IAppWindow} from './types' @@ -161,4 +165,25 @@ describe('utils', () => { expect(Utils.compareVersions('10.9.4', '10.9.2')).toBe(-1) }) }) + + describe('showBoard test', () => { + it('should switch boards', () => { + const match = { + params: { + boardId: 'board_id_1', + viewId: 'view_id_1', + cardId: 'card_id_1', + teamId: 'team_id_1', + }, + path: '/team/:teamId/:boardId?/:viewId?/:cardId?', + } as unknown as routerMatch<{boardId: string, viewId?: string, cardId?: string, teamId?: string}> + + const history = createMemoryHistory() + history.push = jest.fn() + + Utils.showBoard('board_id_2', match, history) + + expect(history.push).toBeCalledWith('/team/team_id_1/board_id_2') + }) + }) }) From d862e66af452c5b72d0837c31ae54024266a32ad Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 14:04:20 -0400 Subject: [PATCH 33/51] fix archive import --- server/api/api.go | 2 +- server/api/archive.go | 12 +++++-- server/app/import.go | 18 ++++++++-- server/app/import_test.go | 69 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 server/app/import_test.go diff --git a/server/api/api.go b/server/api/api.go index c35ed69cd..650a41f70 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -155,7 +155,7 @@ func (a *API) RegisterRoutes(r *mux.Router) { // archives apiv1.HandleFunc("/boards/{boardID}/archive/export", a.sessionRequired(a.handleArchiveExportBoard)).Methods("GET") - apiv1.HandleFunc("/boards/{boardID}/archive/import", a.sessionRequired(a.handleArchiveImport)).Methods("POST") + apiv1.HandleFunc("/teams/{teamID}/archive/import", a.sessionRequired(a.handleArchiveImport)).Methods("POST") } func (a *API) RegisterAdminRoutes(r *mux.Router) { diff --git a/server/api/archive.go b/server/api/archive.go index 190c9c750..372da3d09 100644 --- a/server/api/archive.go +++ b/server/api/archive.go @@ -8,6 +8,8 @@ import ( "github.com/gorilla/mux" "github.com/mattermost/focalboard/server/model" "github.com/mattermost/focalboard/server/services/audit" + + "github.com/mattermost/mattermost-server/v6/shared/mlog" ) const ( @@ -143,7 +145,7 @@ func (a *API) handleArchiveExportTeam(w http.ResponseWriter, r *http.Request) { } func (a *API) handleArchiveImport(w http.ResponseWriter, r *http.Request) { - // swagger:operation POST /api/v1/boards/{boardID}/archive/import archiveImport + // swagger:operation POST /api/v1/teams/{teamID}/archive/import archiveImport // // Import an archive of boards. // @@ -153,9 +155,9 @@ func (a *API) handleArchiveImport(w http.ResponseWriter, r *http.Request) { // consumes: // - multipart/form-data // parameters: - // - name: boardID + // - name: teamID // in: path - // description: Workspace ID + // description: Team ID // required: true // type: string // - name: file @@ -198,6 +200,10 @@ func (a *API) handleArchiveImport(w http.ResponseWriter, r *http.Request) { } if err := a.app.ImportArchive(file, opt); err != nil { + a.logger.Debug("Error importing archive", + mlog.String("team_id", teamID), + mlog.Err(err), + ) a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) return } diff --git a/server/app/import.go b/server/app/import.go index 18fce2ad7..945f5b077 100644 --- a/server/app/import.go +++ b/server/app/import.go @@ -120,11 +120,12 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str var boardID string lineNum := 1 + firstLine := true for { line, errRead := readLine(lineReader) if len(line) != 0 { var skip bool - if lineNum == 1 { + if firstLine { // first line might be a header tag (old archive format) if strings.HasPrefix(string(line), legacyFileBegin) { skip = true @@ -138,7 +139,7 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str } // first line must be a board - if lineNum == 1 && archiveLine.Type == "block" { + if firstLine && archiveLine.Type == "block" { archiveLine.Type = "board_block" } @@ -179,6 +180,7 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str default: return "", model.NewErrUnsupportedArchiveLineType(lineNum, archiveLine.Type) } + firstLine = false } } @@ -204,6 +206,18 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str return "", fmt.Errorf("error inserting archive blocks: %w", err) } + // add user to all the new boards. + for _, board := range boardsAndBlocks.Boards { + boardMember := &model.BoardMember{ + BoardID: board.ID, + UserID: opt.ModifiedBy, + SchemeAdmin: true, + } + if _, err := a.AddMemberToBoard(boardMember); err != nil { + return "", fmt.Errorf("cannot add member to board: %w", err) + } + } + // find new board id for _, board := range boardsAndBlocks.Boards { return board.ID, nil diff --git a/server/app/import_test.go b/server/app/import_test.go new file mode 100644 index 000000000..f05fcafba --- /dev/null +++ b/server/app/import_test.go @@ -0,0 +1,69 @@ +package app + +import ( + "bytes" + "testing" + + "github.com/golang/mock/gomock" + "github.com/mattermost/focalboard/server/model" + "github.com/stretchr/testify/require" +) + +func TestApp_ImportArchive(t *testing.T) { + th, tearDown := SetupTestHelper(t) + defer tearDown() + + board := &model.Board{ + ID: "d14b9df9-1f31-4732-8a64-92bc7162cd28", + TeamID: "test-team", + Title: "Cross-Functional Project Plan", + } + + block := model.Block{ + ID: "2c1873e0-1484-407d-8b2c-3c3b5a2a9f9e", + ParentID: board.ID, + Type: model.TypeView, + BoardID: board.ID, + } + + babs := &model.BoardsAndBlocks{ + Boards: []*model.Board{board}, + Blocks: []model.Block{block}, + } + + boardMember := &model.BoardMember{ + BoardID: board.ID, + UserID: "user", + } + + t.Run("import asana archive", func(t *testing.T) { + r := bytes.NewReader([]byte(asana)) + opts := model.ImportArchiveOptions{ + TeamID: "test-team", + ModifiedBy: "user", + } + + // CreateBoardsAndBlocks(bab *model.BoardsAndBlocks, userID string) (*model.BoardsAndBlocks, error) + th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "user").Return(babs, nil) + // GetMembersForBoard(boardID string) ([]*model.BoardMember, error) + th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{boardMember}, nil) + + err := th.App.ImportArchive(r, opts) + require.NoError(t, err, "import archive should not fail") + }) +} + +//nolint:lll +const asana = `{"version":1,"date":1614714686842} +{"type":"block","data":{"id":"d14b9df9-1f31-4732-8a64-92bc7162cd28","fields":{"icon":"","description":"","cardProperties":[{"id":"3bdcbaeb-bc78-4884-8531-a0323b74676a","name":"Section","type":"select","options":[{"id":"d8d94ef1-5e74-40bb-8be5-fc0eb3f47732","value":"Planning","color":"propColorGray"},{"id":"454559bb-b788-4ff6-873e-04def8491d2c","value":"Milestones","color":"propColorBrown"},{"id":"deaab476-c690-48df-828f-725b064dc476","value":"Next steps","color":"propColorOrange"},{"id":"2138305a-3157-461c-8bbe-f19ebb55846d","value":"Comms Plan","color":"propColorYellow"}]}]},"createAt":1614714686836,"updateAt":1614714686836,"deleteAt":0,"schema":1,"parentId":"","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"board","title":"Cross-Functional Project Plan"}} +{"type":"block","data":{"id":"2c1873e0-1484-407d-8b2c-3c3b5a2a9f9e","fields":{"sortOptions":[],"visiblePropertyIds":[],"visibleOptionIds":[],"hiddenOptionIds":[],"filter":{"operation":"and","filters":[]},"cardOrder":[],"columnWidths":{},"viewType":"board"},"createAt":1614714686840,"updateAt":1614714686840,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"view","title":"Board View"}} +{"type":"block","data":{"id":"520c332b-adf5-4a32-88ab-43655c8b6aa2","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"d8d94ef1-5e74-40bb-8be5-fc0eb3f47732"},"contentOrder":["deb3966c-6d56-43b1-8e95-36806877ce81"]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[READ ME] - Instructions for using this template"}} +{"type":"block","data":{"id":"deb3966c-6d56-43b1-8e95-36806877ce81","fields":{},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"520c332b-adf5-4a32-88ab-43655c8b6aa2","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"text","title":"This project template is set up in List View with sections and Asana-created Custom Fields to help you track your team's work. We've provided some example content in this template to get you started, but you should add tasks, change task names, add more Custom Fields, and change any other info to make this project your own.\n\nSend feedback about this template: https://asa.na/templatesfeedback"}} +{"type":"block","data":{"id":"be791f66-a5e5-4408-82f6-cb1280f5bc45","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"d8d94ef1-5e74-40bb-8be5-fc0eb3f47732"},"contentOrder":["2688b31f-e7ff-4de1-87ae-d4b5570f8712"]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"Redesign the landing page of our website"}} +{"type":"block","data":{"id":"2688b31f-e7ff-4de1-87ae-d4b5570f8712","fields":{},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"be791f66-a5e5-4408-82f6-cb1280f5bc45","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"text","title":"Redesign the landing page to focus on the main persona."}} +{"type":"block","data":{"id":"98f74948-1700-4a3c-8cc2-8bb632499def","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"454559bb-b788-4ff6-873e-04def8491d2c"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Consider trying a new email marketing service"}} +{"type":"block","data":{"id":"142fba5d-05e6-4865-83d9-b3f54d9de96e","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"454559bb-b788-4ff6-873e-04def8491d2c"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Budget finalization"}} +{"type":"block","data":{"id":"ca6670b1-b034-4e42-8971-c659b478b9e0","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"deaab476-c690-48df-828f-725b064dc476"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Find a venue for the holiday party"}} +{"type":"block","data":{"id":"db1dd596-0999-4741-8b05-72ca8e438e31","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"deaab476-c690-48df-828f-725b064dc476"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Approve campaign copy"}} +{"type":"block","data":{"id":"16861c05-f31f-46af-8429-80a87b5aa93a","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"2138305a-3157-461c-8bbe-f19ebb55846d"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Send out updated attendee list"}} +` From 69b4baa7ec26b7e41881e9b9265677af109cb3fd Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 14:45:41 -0400 Subject: [PATCH 34/51] fix unit test --- server/app/import_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/app/import_test.go b/server/app/import_test.go index f05fcafba..0f9858a35 100644 --- a/server/app/import_test.go +++ b/server/app/import_test.go @@ -43,10 +43,10 @@ func TestApp_ImportArchive(t *testing.T) { ModifiedBy: "user", } - // CreateBoardsAndBlocks(bab *model.BoardsAndBlocks, userID string) (*model.BoardsAndBlocks, error) th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "user").Return(babs, nil) - // GetMembersForBoard(boardID string) ([]*model.BoardMember, error) th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{boardMember}, nil) + th.Store.EXPECT().GetBoard(board.ID).Return(board, nil) + th.Store.EXPECT().GetMemberForBoard(board.ID, "user").Return(boardMember, nil) err := th.App.ImportArchive(r, opts) require.NoError(t, err, "import archive should not fail") From 92a5c3605ec5be7803ea7d222008d2fa1b7e27eb Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 15:11:44 -0400 Subject: [PATCH 35/51] fix unit test 2 --- server/app/templates_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/app/templates_test.go b/server/app/templates_test.go index 4092f366c..1045b5ea8 100644 --- a/server/app/templates_test.go +++ b/server/app/templates_test.go @@ -34,6 +34,11 @@ func TestApp_initializeTemplates(t *testing.T) { Blocks: []model.Block{block}, } + boardMember := &model.BoardMember{ + BoardID: board.ID, + UserID: "test-user", + } + t.Run("Needs template init", func(t *testing.T) { th, tearDown := SetupTestHelper(t) defer tearDown() @@ -42,6 +47,8 @@ func TestApp_initializeTemplates(t *testing.T) { th.Store.EXPECT().RemoveDefaultTemplates([]*model.Board{}).Return(nil) th.Store.EXPECT().CreateBoardsAndBlocks(gomock.Any(), gomock.Any()).AnyTimes().Return(boardsAndBlocks, nil) th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{}, nil) + th.Store.EXPECT().GetBoard(board.ID).AnyTimes().Return(board, nil) + th.Store.EXPECT().GetMemberForBoard(gomock.Any(), gomock.Any()).AnyTimes().Return(boardMember, nil) th.FilesBackend.On("WriteFile", mock.Anything, mock.Anything).Return(int64(1), nil) From c098eda25458292b66f28eb3373738dc21855187 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Thu, 31 Mar 2022 15:02:49 -0600 Subject: [PATCH 36/51] update handleServeFile to allow readtoken --- server/api/api.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index c35ed69cd..f5e90657f 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -1824,7 +1824,7 @@ func (a *API) handlePostTeamRegenerateSignupToken(w http.ResponseWriter, r *http // File upload func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { - // swagger:operation GET /boards/{boardID}/{rootID}/{fileID} getFile + // swagger:operation GET "api/v1/files/teams/{teamID}/{boardID}/{filename} getFile // // Returns the contents of an uploaded file // @@ -1835,19 +1835,19 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { // - image/png // - image/gif // parameters: + // - name: teamID + // in: path + // description: Team ID + // required: true + // type: string // - name: boardID // in: path // description: Board ID // required: true // type: string - // - name: rootID + // - name: filename // in: path - // description: ID of the root block - // required: true - // type: string - // - name: fileID - // in: path - // description: ID of the file + // description: name of the file // required: true // type: string // security: @@ -1865,7 +1865,8 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { filename := vars["filename"] userID := getUserID(r) - if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { + hasValidReadToken := a.hasValidReadTokenForBoard(r, boardID) + if !hasValidReadToken && !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to board"}) return } From 9a2cea7113fd22b4e4517559d8cd0a7e7d763f5d Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 19:15:46 -0400 Subject: [PATCH 37/51] fix broken Follow button --- server/app/boards.go | 15 +++++++++++++++ server/app/subscriptions.go | 14 +++++++++++++- .../notifysubscriptions/subscriptions_backend.go | 6 +++--- server/services/notify/service.go | 6 +++--- webapp/src/octoClient.ts | 1 - .../src/pages/boardPage/websocketConnection.tsx | 4 ++-- webapp/src/wsclient.ts | 1 - 7 files changed, 36 insertions(+), 11 deletions(-) diff --git a/server/app/boards.go b/server/app/boards.go index 6baba12de..b44151851 100644 --- a/server/app/boards.go +++ b/server/app/boards.go @@ -69,6 +69,21 @@ func (a *App) GetBoardMetadata(boardID string) (*model.Board, *model.BoardMetada return board, &boardMetadata, nil } +// getBoardForBlock returns the board that owns the specified block. +func (a *App) getBoardForBlock(blockID string) (*model.Board, error) { + block, err := a.GetBlockByID(blockID) + if err != nil { + return nil, fmt.Errorf("cannot get block %s: %w", blockID, err) + } + + board, err := a.GetBoard(block.BoardID) + if err != nil { + return nil, fmt.Errorf("cannot get board %s: %w", block.BoardID, err) + } + + return board, nil +} + func (a *App) getBoardHistory(boardID string, latest bool) (*model.Board, error) { opts := model.QueryBlockHistoryOptions{ Limit: 1, diff --git a/server/app/subscriptions.go b/server/app/subscriptions.go index aaf8ec316..76ea2a603 100644 --- a/server/app/subscriptions.go +++ b/server/app/subscriptions.go @@ -3,6 +3,8 @@ package app import ( "github.com/mattermost/focalboard/server/model" "github.com/mattermost/focalboard/server/utils" + + "github.com/mattermost/mattermost-server/v6/shared/mlog" ) func (a *App) CreateSubscription(sub *model.Subscription) (*model.Subscription, error) { @@ -37,5 +39,15 @@ func (a *App) notifySubscriptionChanged(subscription *model.Subscription) { if a.notifications == nil { return } - a.notifications.BroadcastSubscriptionChange(subscription) + + board, err := a.getBoardForBlock(subscription.BlockID) + if err != nil { + a.logger.Error("Error notifying subscription change", + mlog.String("subscriber_id", subscription.SubscriberID), + mlog.String("block_id", subscription.BlockID), + mlog.Err(err), + ) + } + + a.notifications.BroadcastSubscriptionChange(board.TeamID, subscription) } diff --git a/server/services/notify/notifysubscriptions/subscriptions_backend.go b/server/services/notify/notifysubscriptions/subscriptions_backend.go index b6e3e98e3..4b543ba7c 100644 --- a/server/services/notify/notifysubscriptions/subscriptions_backend.go +++ b/server/services/notify/notifysubscriptions/subscriptions_backend.go @@ -205,7 +205,7 @@ func (b *Backend) OnMention(userID string, evt notify.BlockChangeEvent) { } // BroadcastSubscriptionChange sends a websocket message with details of the changed subscription to all -// connected users in the workspace. -func (b *Backend) BroadcastSubscriptionChange(workspaceID string, subscription *model.Subscription) { - b.wsAdapter.BroadcastSubscriptionChange(workspaceID, subscription) +// connected users in the team. +func (b *Backend) BroadcastSubscriptionChange(teamID string, subscription *model.Subscription) { + b.wsAdapter.BroadcastSubscriptionChange(teamID, subscription) } diff --git a/server/services/notify/service.go b/server/services/notify/service.go index 5735496f7..4f9611427 100644 --- a/server/services/notify/service.go +++ b/server/services/notify/service.go @@ -31,7 +31,7 @@ type BlockChangeEvent struct { } type SubscriptionChangeNotifier interface { - BroadcastSubscriptionChange(subscription *model.Subscription) + BroadcastSubscriptionChange(teamID string, subscription *model.Subscription) } // Backend provides an interface for sending notifications. @@ -113,7 +113,7 @@ func (s *Service) BlockChanged(evt BlockChangeEvent) { // BroadcastSubscriptionChange sends a websocket message with details of the changed subscription to all // connected users in the workspace. -func (s *Service) BroadcastSubscriptionChange(subscription *model.Subscription) { +func (s *Service) BroadcastSubscriptionChange(teamID string, subscription *model.Subscription) { s.mux.RLock() backends := make([]Backend, len(s.backends)) copy(backends, s.backends) @@ -125,7 +125,7 @@ func (s *Service) BroadcastSubscriptionChange(subscription *model.Subscription) mlog.String("block_id", subscription.BlockID), mlog.String("subscriber_id", subscription.SubscriberID), ) - scn.BroadcastSubscriptionChange(subscription) + scn.BroadcastSubscriptionChange(teamID, subscription) } } } diff --git a/webapp/src/octoClient.ts b/webapp/src/octoClient.ts index 619d509c5..a32345d30 100644 --- a/webapp/src/octoClient.ts +++ b/webapp/src/octoClient.ts @@ -350,7 +350,6 @@ class OctoClient { async followBlock(blockId: string, blockType: string, userId: string): Promise { const body: Subscription = { - teamId: this.teamId, blockType, blockId, subscriberType: 'user', diff --git a/webapp/src/pages/boardPage/websocketConnection.tsx b/webapp/src/pages/boardPage/websocketConnection.tsx index 09b75cad5..4c6090a62 100644 --- a/webapp/src/pages/boardPage/websocketConnection.tsx +++ b/webapp/src/pages/boardPage/websocketConnection.tsx @@ -109,12 +109,12 @@ const WebsocketConnection = (props: Props) => { wsClient.addOnReconnect(() => dispatch(props.loadAction(props.boardId))) wsClient.addOnStateChange(updateWebsocketState) wsClient.setOnFollowBlock((_: WSClient, subscription: Subscription): void => { - if (subscription.subscriberId === me?.id && subscription.teamId === props.teamId) { + if (subscription.subscriberId === me?.id) { dispatch(followBlock(subscription)) } }) wsClient.setOnUnfollowBlock((_: WSClient, subscription: Subscription): void => { - if (subscription.subscriberId === me?.id && subscription.teamId === props.teamId) { + if (subscription.subscriberId === me?.id) { dispatch(unfollowBlock(subscription)) } }) diff --git a/webapp/src/wsclient.ts b/webapp/src/wsclient.ts index e0ae6c821..f68ce5cf8 100644 --- a/webapp/src/wsclient.ts +++ b/webapp/src/wsclient.ts @@ -51,7 +51,6 @@ type WSSubscriptionMsg = { export interface Subscription { blockId: string - teamId: string subscriberId: string blockType: string subscriberType: string From 9f2cf6730e943a5721555921a0744af557c303a8 Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 22:02:33 -0400 Subject: [PATCH 38/51] fixed asana import --- import/asana/importAsana.ts | 21 +++++++++++---------- import/util/archive.ts | 21 +++++++++++++++++++-- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/import/asana/importAsana.ts b/import/asana/importAsana.ts index 66636b8d3..ab54878bc 100644 --- a/import/asana/importAsana.ts +++ b/import/asana/importAsana.ts @@ -5,6 +5,7 @@ import minimist from 'minimist' import {exit} from 'process' import {ArchiveUtils} from '../util/archive' import {Block} from '../../webapp/src/blocks/block' +import {Board} from '../../webapp/src/blocks/board' import {IPropertyOption, IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board' import {createBoardView} from '../../webapp/src/blocks/boardView' import {createCard} from '../../webapp/src/blocks/card' @@ -49,11 +50,11 @@ function main() { const input = JSON.parse(inputData) as Asana // Convert - const blocks = convert(input) + const [boards, blocks] = convert(input) // Save output // TODO: Stream output - const outputData = ArchiveUtils.buildBlockArchive(blocks) + const outputData = ArchiveUtils.buildBlockArchive(boards, blocks) fs.writeFileSync(outputFile, outputData) console.log(`Exported to ${outputFile}`) @@ -88,22 +89,22 @@ function getSections(input: Asana, projectId: string): Workspace[] { return [...sectionMap.values()] } -function convert(input: Asana): Block[] { +function convert(input: Asana): [Board[], Block[]] { const projects = getProjects(input) if (projects.length < 1) { console.error('No projects found') - return [] + return [[],[]] } // TODO: Handle multiple projects const project = projects[0] + const boards: Board[] = [] const blocks: Block[] = [] // Board const board = createBoard() console.log(`Board: ${project.name}`) - board.rootId = board.id board.title = project.name // Convert sections (columns) to a Select property @@ -130,14 +131,14 @@ function convert(input: Asana): Block[] { options } board.cardProperties = [cardProperty] - blocks.push(board) + boards.push(board) // Board view const view = createBoardView() view.title = 'Board View' view.fields.viewType = 'board' - view.rootId = board.id view.parentId = board.id + view.boardId = board.id blocks.push(view) // Cards @@ -146,7 +147,7 @@ function convert(input: Asana): Block[] { const outCard = createCard() outCard.title = card.name - outCard.rootId = board.id + outCard.boardId = board.id outCard.parentId = board.id // Map lists to Select property options @@ -168,8 +169,8 @@ function convert(input: Asana): Block[] { // console.log(`\t${card.notes}`) const text = createTextBlock() text.title = card.notes - text.rootId = board.id text.parentId = outCard.id + text.boardId = board.id blocks.push(text) outCard.fields.contentOrder = [text.id] @@ -179,7 +180,7 @@ function convert(input: Asana): Block[] { console.log('') console.log(`Found ${input.data.length} card(s).`) - return blocks + return [boards, blocks] } function showHelp() { diff --git a/import/util/archive.ts b/import/util/archive.ts index e4502d3f4..ad7401083 100644 --- a/import/util/archive.ts +++ b/import/util/archive.ts @@ -1,25 +1,31 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {Block} from '../../webapp/src/blocks/block' +import {Board} from '../../webapp/src/blocks/board' interface ArchiveHeader { version: number date: number } +// This schema allows the expansion of additional line types in the future interface ArchiveLine { type: string, data: unknown, } -// This schema allows the expansion of additional line types in the future interface BlockArchiveLine extends ArchiveLine { type: 'block', data: Block } +interface BoardArchiveLine extends ArchiveLine { + type: 'board', + data: Board +} + class ArchiveUtils { - static buildBlockArchive(blocks: readonly Block[]): string { + static buildBlockArchive(boards: readonly Board[], blocks: readonly Block[]): string { const header: ArchiveHeader = { version: 1, date: Date.now(), @@ -27,6 +33,17 @@ class ArchiveUtils { const headerString = JSON.stringify(header) let content = headerString + '\n' + + for (const board of boards) { + const line: BoardArchiveLine = { + type: 'board', + data: board, + } + const lineString = JSON.stringify(line) + content += lineString + content += '\n' + } + for (const block of blocks) { const line: BlockArchiveLine = { type: 'block', From d8e9566be9c1b161f0edde3812344c6b9bf699ee Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 22:19:04 -0400 Subject: [PATCH 39/51] fix jira import --- import/jira/jiraImporter.test.ts | 6 +----- import/jira/jiraImporter.ts | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/import/jira/jiraImporter.test.ts b/import/jira/jiraImporter.test.ts index b50fe7c7e..503d7cfd6 100644 --- a/import/jira/jiraImporter.test.ts +++ b/import/jira/jiraImporter.test.ts @@ -3,7 +3,7 @@ import {run} from './jiraImporter' import * as fs from 'fs' -import {ArchiveUtils} from '../../webapp/src/blocks/archive' +import {ArchiveUtils} from '../util/archive' const inputFile = './test/jira-export.xml' const outputFile = './test/jira.focalboard' @@ -27,10 +27,6 @@ describe('import from Jira', () => { expect(blocks).toEqual( expect.arrayContaining([ - expect.objectContaining({ - title: 'Jira import', - type: 'board' - }), expect.objectContaining({ title: 'Board View', type: 'view' diff --git a/import/jira/jiraImporter.ts b/import/jira/jiraImporter.ts index 19140f96a..748dc6267 100644 --- a/import/jira/jiraImporter.ts +++ b/import/jira/jiraImporter.ts @@ -4,6 +4,7 @@ import * as fs from 'fs' import {exit} from 'process' import {ArchiveUtils} from '../util/archive' import {Block} from '../../webapp/src/blocks/block' +import {Board} from '../../webapp/src/blocks/board' import {IPropertyOption, IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board' import {createBoardView} from '../../webapp/src/blocks/boardView' import {Card, createCard} from '../../webapp/src/blocks/card' @@ -70,23 +71,23 @@ async function run(inputFile: string, outputFile: string): Promise { // console.dir(items); // Convert - const blocks = convert(items) + const [boards, blocks] = convert(items) // Save output // TODO: Stream output - const outputData = ArchiveUtils.buildBlockArchive(blocks) + const outputData = ArchiveUtils.buildBlockArchive(boards, blocks) fs.writeFileSync(outputFile, outputData) console.log(`Exported ${blocks.length} block(s) to ${outputFile}`) return blocks.length } -function convert(items: any[]) { +function convert(items: any[]): [Board[], Block[]] { + const boards: Board[] = [] const blocks: Block[] = [] // Board const board = createBoard() - board.rootId = board.id board.title = 'Jira import' // Compile standard properties @@ -126,13 +127,13 @@ function convert(items: any[]) { } board.cardProperties.push(createdDateProperty) - blocks.push(board) + boards.push(board) // Board view const view = createBoardView() view.title = 'Board View' view.fields.viewType = 'board' - view.rootId = board.id + view.boardId = board.id view.parentId = board.id blocks.push(view) @@ -145,7 +146,7 @@ function convert(items: any[]) { const card = createCard() card.title = item.summary - card.rootId = board.id + card.boardId = board.id card.parentId = board.id // Map standard properties @@ -169,7 +170,7 @@ function convert(items: any[]) { console.log(`\t${description}`) const text = createTextBlock() text.title = description - text.rootId = board.id + text.boardId = board.id text.parentId = card.id blocks.push(text) @@ -179,7 +180,7 @@ function convert(items: any[]) { blocks.push(card) } - return blocks + return [boards, blocks] } function buildCardPropertyFromValues(propertyName: string, allValues: string[]) { From eb5ed3ae98189431ccffef913f2b27b5da21b687 Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 22:36:02 -0400 Subject: [PATCH 40/51] fix nextcloud import --- import/nextcloud-deck/importDeck.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/import/nextcloud-deck/importDeck.ts b/import/nextcloud-deck/importDeck.ts index 744d23f67..1617bc166 100644 --- a/import/nextcloud-deck/importDeck.ts +++ b/import/nextcloud-deck/importDeck.ts @@ -3,8 +3,9 @@ import * as fs from 'fs' import minimist from 'minimist' import {exit} from 'process' -import {ArchiveUtils} from '../../webapp/src/blocks/archive' +import {ArchiveUtils} from '../util/archive' import {Block} from '../../webapp/src/blocks/block' +import {Board as FBBoard} from '../../webapp/src/blocks/board' import {IPropertyOption, IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board' import {createBoardView} from '../../webapp/src/blocks/boardView' import {createCard} from '../../webapp/src/blocks/card' @@ -69,10 +70,10 @@ async function main() { })) // Convert - const blocks = convert(board, stacks) + const [boards, blocks] = convert(board, stacks) // // Save output - const outputData = ArchiveUtils.buildBlockArchive(blocks) + const outputData = ArchiveUtils.buildBlockArchive(boards, blocks) fs.writeFileSync(outputFile, outputData) console.log(`Exported to ${outputFile}`) @@ -85,13 +86,13 @@ async function selectBoard(deckClient: NextcloudDeckClient): Promise { return readline.questionInt("Enter Board ID: ") } -function convert(deckBoard: Board, stacks: Stack[]): Block[] { +function convert(deckBoard: Board, stacks: Stack[]): [FBBoard[], Block[]] { + const boards: FBBoard[] = [] const blocks: Block[] = [] // Board const board = createBoard() console.log(`Board: ${deckBoard.title}`) - board.rootId = board.id board.title = deckBoard.title let colorIndex = 0 @@ -145,14 +146,14 @@ function convert(deckBoard: Board, stacks: Stack[]): Block[] { options: [] } - board.fields.cardProperties = [stackProperty, labelProperty, dueDateProperty] - blocks.push(board) + board.cardProperties = [stackProperty, labelProperty, dueDateProperty] + boards.push(board) // Board view const view = createBoardView() view.title = 'Board View' view.fields.viewType = 'board' - view.rootId = board.id + view.boardId = board.id view.parentId = board.id blocks.push(view) @@ -164,7 +165,7 @@ function convert(deckBoard: Board, stacks: Stack[]): Block[] { const outCard = createCard() outCard.title = card.title - outCard.rootId = board.id + outCard.boardId = board.id outCard.parentId = board.id // Map Stacks to Select property options @@ -189,7 +190,7 @@ function convert(deckBoard: Board, stacks: Stack[]): Block[] { if (card.description) { const text = createTextBlock() text.title = card.description - text.rootId = board.id + text.boardId = board.id text.parentId = outCard.id blocks.push(text) @@ -200,7 +201,7 @@ function convert(deckBoard: Board, stacks: Stack[]): Block[] { card.comments?.forEach(comment => { const commentBlock = createCommentBlock() commentBlock.title = comment.message - commentBlock.rootId = board.id + commentBlock.boardId = board.id commentBlock.parentId = outCard.id blocks.push(commentBlock) }) @@ -210,7 +211,7 @@ function convert(deckBoard: Board, stacks: Stack[]): Block[] { console.log('') console.log(`Transformed Board ${deckBoard.title} into ${blocks.length} blocks.`) - return blocks + return [boards, blocks] } function showHelp() { From aa3837814b13546cff693d76e1029350df4eb3a5 Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 22:41:13 -0400 Subject: [PATCH 41/51] fix notion import --- import/notion/importNotion.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/import/notion/importNotion.ts b/import/notion/importNotion.ts index 361c84848..85f7508c8 100644 --- a/import/notion/importNotion.ts +++ b/import/notion/importNotion.ts @@ -5,6 +5,7 @@ import path from 'path' import {exit} from 'process' import {ArchiveUtils} from '../util/archive' import {Block} from '../../webapp/src/blocks/block' +import {Board} from '../../webapp/src/blocks/board' import {IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board' import {createBoardView} from '../../webapp/src/blocks/boardView' import {createCard} from '../../webapp/src/blocks/card' @@ -70,11 +71,11 @@ async function main() { markdownFolder = path.join(inputFolder, basename) // Convert - const blocks = convert(input, title) + const [boards, blocks] = convert(input, title) // Save output // TODO: Stream output - const outputData = ArchiveUtils.buildBlockArchive(blocks) + const outputData = ArchiveUtils.buildBlockArchive(boards, blocks) fs.writeFileSync(outputFile, outputData) console.log(`Exported to ${outputFile}`) @@ -117,13 +118,13 @@ function getColumns(input: any[]) { return keys.slice(1) } -function convert(input: any[], title: string): Block[] { +function convert(input: any[], title: string): [Board[], Block[]] { + const boards: Board[] = [] const blocks: Block[] = [] // Board const board = createBoard() console.log(`Board: ${title}`) - board.rootId = board.id board.title = title // Each column is a card property @@ -140,13 +141,13 @@ function convert(input: any[], title: string): Block[] { // Set all column types to select // TODO: Detect column type - blocks.push(board) + boards.push(board) // Board view const view = createBoardView() view.title = 'Board View' view.fields.viewType = 'board' - view.rootId = board.id + view.boardId = board.id view.parentId = board.id blocks.push(view) @@ -166,7 +167,7 @@ function convert(input: any[], title: string): Block[] { const outCard = createCard() outCard.title = title - outCard.rootId = board.id + outCard.boardId = board.id outCard.parentId = board.id // Card properties, skip first key which is the title @@ -201,7 +202,7 @@ function convert(input: any[], title: string): Block[] { console.log(`Markdown: ${markdown.length} bytes`) const text = createTextBlock() text.title = markdown - text.rootId = board.id + text.boardId = board.id text.parentId = outCard.id blocks.push(text) @@ -212,7 +213,7 @@ function convert(input: any[], title: string): Block[] { console.log('') console.log(`Found ${input.length} card(s).`) - return blocks + return [boards, blocks] } function showHelp() { From 400c5a108c4523ae6f99f3713aa1a33bccdd3f98 Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 22:50:58 -0400 Subject: [PATCH 42/51] fix todolist import --- import/todoist/importTodoist.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/import/todoist/importTodoist.ts b/import/todoist/importTodoist.ts index 047d2a14b..f7dd3f59b 100644 --- a/import/todoist/importTodoist.ts +++ b/import/todoist/importTodoist.ts @@ -5,6 +5,7 @@ import minimist from 'minimist' import {exit} from 'process' import {ArchiveUtils} from '../util/archive' import {Block} from '../../webapp/src/blocks/block' +import {Board} from '../../webapp/src/blocks/board' import {IPropertyOption, IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board' import {createBoardView} from '../../webapp/src/blocks/boardView' import {createCard} from '../../webapp/src/blocks/card' @@ -56,31 +57,34 @@ function main() { const inputData = fs.readFileSync(inputFile, 'utf-8') const input = JSON.parse(inputData) as Todoist + const boards = [] as Board[] const blocks = [] as Block[] input.projects.forEach(project => { - blocks.push(...convert(input, project)) + const [brds, blks] = convert(input, project) + boards.push(...brds) + blocks.push(...blks) }) // Save output // TODO: Stream output - const outputData = ArchiveUtils.buildBlockArchive(blocks) + const outputData = ArchiveUtils.buildBlockArchive(boards, blocks) fs.writeFileSync(outputFile, outputData) console.log(`Exported to ${outputFile}`) } -function convert(input: Todoist, project: Project): Block[] { +function convert(input: Todoist, project: Project): [Board[], Block[]] { + const boards: Board[] = [] const blocks: Block[] = [] if (project.name === 'Inbox') { - return blocks + return [boards, blocks] } // Board const board = createBoard() console.log(`Board: ${project.name}`) - board.rootId = board.id board.title = project.name board.description = project.name @@ -115,13 +119,13 @@ function convert(input: Todoist, project: Project): Block[] { options } board.cardProperties = [cardProperty] - blocks.push(board) + boards.push(board) // Board view const view = createBoardView() view.title = 'Board View' view.fields.viewType = 'board' - view.rootId = board.id + view.boardId = board.id view.parentId = board.id blocks.push(view) @@ -130,7 +134,7 @@ function convert(input: Todoist, project: Project): Block[] { cards.forEach(card => { const outCard = createCard() outCard.title = card.content - outCard.rootId = board.id + outCard.boardId = board.id outCard.parentId = board.id // Map lists to Select property options @@ -148,14 +152,14 @@ function convert(input: Todoist, project: Project): Block[] { // console.log(`\t${card.desc}`) const text = createTextBlock() text.title = getCardDescription(input, card).join('\n\n') - text.rootId = board.id + text.boardId = board.id text.parentId = outCard.id blocks.push(text) outCard.fields.contentOrder = [text.id] }) - return blocks + return [boards, blocks] } function getProjectColumns(input: Todoist, project: Project): Array
{ From 2e7427d2f35b833ff5945a4ddc79125175712576 Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Thu, 31 Mar 2022 22:55:08 -0400 Subject: [PATCH 43/51] fix trello import --- import/trello/importTrello.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/import/trello/importTrello.ts b/import/trello/importTrello.ts index eacae528a..1794b30ee 100644 --- a/import/trello/importTrello.ts +++ b/import/trello/importTrello.ts @@ -5,6 +5,7 @@ import minimist from 'minimist' import {exit} from 'process' import {ArchiveUtils} from '../util/archive' import {Block} from '../../webapp/src/blocks/block' +import {Board} from '../../webapp/src/blocks/board' import {IPropertyOption, IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board' import {createBoardView} from '../../webapp/src/blocks/boardView' import {createCard} from '../../webapp/src/blocks/card' @@ -50,23 +51,23 @@ function main() { const input = JSON.parse(inputData) as Trello // Convert - const blocks = convert(input) + const [boards, blocks] = convert(input) // Save output // TODO: Stream output - const outputData = ArchiveUtils.buildBlockArchive(blocks) + const outputData = ArchiveUtils.buildBlockArchive(boards, blocks) fs.writeFileSync(outputFile, outputData) console.log(`Exported to ${outputFile}`) } -function convert(input: Trello): Block[] { +function convert(input: Trello): [Board[], Block[]] { + const boards: Board[] = [] const blocks: Block[] = [] // Board const board = createBoard() console.log(`Board: ${input.name}`) - board.rootId = board.id board.title = input.name board.description = input.desc @@ -93,13 +94,13 @@ function convert(input: Trello): Block[] { options } board.cardProperties = [cardProperty] - blocks.push(board) + boards.push(board) // Board view const view = createBoardView() view.title = 'Board View' view.fields.viewType = 'board' - view.rootId = board.id + view.boardId = board.id view.parentId = board.id blocks.push(view) @@ -109,7 +110,7 @@ function convert(input: Trello): Block[] { const outCard = createCard() outCard.title = card.name - outCard.rootId = board.id + outCard.boardId = board.id outCard.parentId = board.id // Map lists to Select property options @@ -130,7 +131,7 @@ function convert(input: Trello): Block[] { // console.log(`\t${card.desc}`) const text = createTextBlock() text.title = card.desc - text.rootId = board.id + text.boardId = board.id text.parentId = outCard.id blocks.push(text) @@ -150,7 +151,7 @@ function convert(input: Trello): Block[] { } else { checkBlock.fields.value = false } - checkBlock.rootId = outCard.rootId + checkBlock.boardId = board.id checkBlock.parentId = outCard.id blocks.push(checkBlock) @@ -164,7 +165,7 @@ function convert(input: Trello): Block[] { console.log('') console.log(`Found ${input.cards.length} card(s).`) - return blocks + return [boards, blocks] } function showHelp() { From 70e97227a48c3135c5ae027107783ca253dd7752 Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Fri, 1 Apr 2022 12:19:16 -0400 Subject: [PATCH 44/51] don't show error page when not plugin for not-logged-in --- webapp/src/pages/errorPage.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/webapp/src/pages/errorPage.tsx b/webapp/src/pages/errorPage.tsx index 2a2063fc6..6fec8e86b 100644 --- a/webapp/src/pages/errorPage.tsx +++ b/webapp/src/pages/errorPage.tsx @@ -10,6 +10,7 @@ import Button from '../widgets/buttons/button' import './errorPage.scss' import {errorDefFromId, ErrorId} from '../errors' +import {Utils} from '../utils' const ErrorPage = () => { const history = useHistory() @@ -45,6 +46,10 @@ const ErrorPage = () => { ) }) + if (!Utils.isFocalboardPlugin() && errid === ErrorId.NotLoggedIn) { + handleButtonClick(errorDef.button1Redirect) + } + return (
From 19ed0d73fe45cad1bbda6577e9f6b3b1b93178ff Mon Sep 17 00:00:00 2001 From: wiggin77 Date: Fri, 1 Apr 2022 13:43:49 -0400 Subject: [PATCH 45/51] fix cypress tests --- webapp/cypress/integration/loginActions.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/webapp/cypress/integration/loginActions.ts b/webapp/cypress/integration/loginActions.ts index 34c05578c..7d5c463ca 100644 --- a/webapp/cypress/integration/loginActions.ts +++ b/webapp/cypress/integration/loginActions.ts @@ -8,10 +8,8 @@ describe('Login actions', () => { it('Can perform login/register actions', () => { // Redirects to login page - cy.log('**Redirects to error then login page**') + cy.log('**Redirects to login page (except plugin mode) **') cy.visit('/') - cy.location('pathname').should('eq', '/error') - cy.get('button').contains('Log in').click() cy.location('pathname').should('eq', '/login') cy.get('.LoginPage').contains('Log in') cy.get('#login-username').should('exist') @@ -40,7 +38,7 @@ describe('Login actions', () => { // User should not be logged in automatically cy.log('**User should not be logged in automatically**') cy.visit('/') - cy.location('pathname').should('eq', '/error') + cy.location('pathname').should('eq', '/login') // Can log in registered user cy.log('**Can log in registered user**') From 283d7669f3632726dcc624e6d8f4856342fea7d3 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 4 Apr 2022 01:39:29 -0600 Subject: [PATCH 46/51] remove read token check from unnecessary functions (#2682) --- server/api/api.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index a4c2d1e5a..ef97e3d9c 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -2832,8 +2832,7 @@ func (a *API) handleDuplicateBoard(w http.ResponseWriter, r *http.Request) { return } - hasValidReadToken := a.hasValidReadTokenForBoard(r, boardID) - if userID == "" && !hasValidReadToken { + if userID == "" { a.errorResponse(w, r.URL.Path, http.StatusUnauthorized, "", PermissionError{"access denied to board"}) return } @@ -2848,17 +2847,15 @@ func (a *API) handleDuplicateBoard(w http.ResponseWriter, r *http.Request) { return } - if !hasValidReadToken { - if board.Type == model.BoardTypePrivate { - if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { - a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to board"}) - return - } - } else { - if !a.permissions.HasPermissionToTeam(userID, board.TeamID, model.PermissionViewTeam) { - a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to board"}) - return - } + if board.Type == model.BoardTypePrivate { + if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { + a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to board"}) + return + } + } else { + if !a.permissions.HasPermissionToTeam(userID, board.TeamID, model.PermissionViewTeam) { + a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to board"}) + return } } @@ -2927,8 +2924,7 @@ func (a *API) handleDuplicateBlock(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() asTemplate := query.Get("asTemplate") - hasValidReadToken := a.hasValidReadTokenForBoard(r, boardID) - if userID == "" && !hasValidReadToken { + if userID == "" { a.errorResponse(w, r.URL.Path, http.StatusUnauthorized, "", PermissionError{"access denied to board"}) return } From c19ac24fe695ed55623e8d4097122cc949a072e7 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 4 Apr 2022 01:49:27 -0600 Subject: [PATCH 47/51] update apis to return 501 if not available for plugin (#2684) --- server/api/api.go | 3 +++ server/api/archive.go | 3 +++ server/api/auth.go | 12 ++++++++++++ 3 files changed, 18 insertions(+) diff --git a/server/api/api.go b/server/api/api.go index ef97e3d9c..e7d9b0eb7 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -1799,6 +1799,9 @@ func (a *API) handlePostTeamRegenerateSignupToken(w http.ResponseWriter, r *http // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" + if a.MattermostAuth { + a.errorResponse(w, r.URL.Path, http.StatusNotImplemented, "not permitted in plugin mode", nil) + } team, err := a.app.GetRootTeam() if err != nil { diff --git a/server/api/archive.go b/server/api/archive.go index 372da3d09..dd5e25bf3 100644 --- a/server/api/archive.go +++ b/server/api/archive.go @@ -105,6 +105,9 @@ func (a *API) handleArchiveExportTeam(w http.ResponseWriter, r *http.Request) { // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" + if a.MattermostAuth { + a.errorResponse(w, r.URL.Path, http.StatusNotImplemented, "not permitted in plugin mode", nil) + } vars := mux.Vars(r) teamID := vars["teamID"] diff --git a/server/api/auth.go b/server/api/auth.go index b5fa26789..5b60584f6 100644 --- a/server/api/auth.go +++ b/server/api/auth.go @@ -166,6 +166,9 @@ func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) { // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" + if a.MattermostAuth { + a.errorResponse(w, r.URL.Path, http.StatusNotImplemented, "not permitted in plugin mode", nil) + } if len(a.singleUserToken) > 0 { // Not permitted in single-user mode @@ -228,6 +231,9 @@ func (a *API) handleLogout(w http.ResponseWriter, r *http.Request) { // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" + if a.MattermostAuth { + a.errorResponse(w, r.URL.Path, http.StatusNotImplemented, "not permitted in plugin mode", nil) + } if len(a.singleUserToken) > 0 { // Not permitted in single-user mode @@ -278,6 +284,9 @@ func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) { // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" + if a.MattermostAuth { + a.errorResponse(w, r.URL.Path, http.StatusNotImplemented, "not permitted in plugin mode", nil) + } if len(a.singleUserToken) > 0 { // Not permitted in single-user mode @@ -377,6 +386,9 @@ func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) { // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" + if a.MattermostAuth { + a.errorResponse(w, r.URL.Path, http.StatusNotImplemented, "not permitted in plugin mode", nil) + } if len(a.singleUserToken) > 0 { // Not permitted in single-user mode From faf96458305b7f3f622fed5036e47c3cc777f7da Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 4 Apr 2022 01:52:24 -0600 Subject: [PATCH 48/51] Add Login button to shared boards (#2659) * add Login button to shared boards * cleanup * replace component with useHistory() * remove comment * cleanup some spacing * update snapshots for classname change Co-authored-by: Mattermod --- .../__snapshots__/centerPanel.test.tsx.snap | 15 +++++- .../__snapshots__/workspace.test.tsx.snap | 30 ++++++++++- webapp/src/components/centerPanel.tsx | 19 ++++--- .../shareBoardLoginButton.test.tsx.snap | 19 +++++++ .../shareBoard/shareBoardLoginButton.scss | 4 ++ .../shareBoard/shareBoardLoginButton.test.tsx | 51 +++++++++++++++++++ .../shareBoard/shareBoardLoginButton.tsx | 51 +++++++++++++++++++ webapp/src/pages/loginPage.tsx | 12 +++-- webapp/src/telemetry/telemetryClient.ts | 1 + webapp/src/utils.ts | 2 +- 10 files changed, 190 insertions(+), 14 deletions(-) create mode 100644 webapp/src/components/shareBoard/__snapshots__/shareBoardLoginButton.test.tsx.snap create mode 100644 webapp/src/components/shareBoard/shareBoardLoginButton.scss create mode 100644 webapp/src/components/shareBoard/shareBoardLoginButton.test.tsx create mode 100644 webapp/src/components/shareBoard/shareBoardLoginButton.tsx diff --git a/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap b/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap index d1c66a10e..044f8c043 100644 --- a/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap +++ b/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap @@ -695,7 +695,20 @@ exports[`components/centerPanel return centerPanel and press touch 1 with readon
+ > +
+ +
+
+ > +
+ +
+
+ > +
+ +
+
{ e.stopPropagation() }, [selectedCardIds, props.activeView, props.cards, showCard]) + const showShareButton = !props.readonly && me?.id !== 'single-user' + const showShareLoginButton = props.readonly && me?.id !== 'single-user' + const {groupByProperty, activeView, board, views, cards} = props const {visible: visibleGroups, hidden: hiddenGroups} = useMemo( () => getVisibleAndHiddenGroups(cards, activeView.fields.visibleOptionIds, activeView.fields.hiddenOptionIds, groupByProperty), @@ -369,13 +373,14 @@ const CenterPanel = (props: Props) => { readonly={props.readonly} />
- {!props.readonly && - ( - - ) + {showShareButton && + + } + {showShareLoginButton && + }
diff --git a/webapp/src/components/shareBoard/__snapshots__/shareBoardLoginButton.test.tsx.snap b/webapp/src/components/shareBoard/__snapshots__/shareBoardLoginButton.test.tsx.snap new file mode 100644 index 000000000..f7995b53d --- /dev/null +++ b/webapp/src/components/shareBoard/__snapshots__/shareBoardLoginButton.test.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`src/components/shareBoard/shareBoardLoginButton should match snapshot 1`] = ` +
+
+ +
+
+`; diff --git a/webapp/src/components/shareBoard/shareBoardLoginButton.scss b/webapp/src/components/shareBoard/shareBoardLoginButton.scss new file mode 100644 index 000000000..b08f71489 --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoardLoginButton.scss @@ -0,0 +1,4 @@ +.ShareBoardLoginButton { + margin-top: 38px; +} + diff --git a/webapp/src/components/shareBoard/shareBoardLoginButton.test.tsx b/webapp/src/components/shareBoard/shareBoardLoginButton.test.tsx new file mode 100644 index 000000000..55a5e2046 --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoardLoginButton.test.tsx @@ -0,0 +1,51 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import {render} from '@testing-library/react' +import React from 'react' + +import {TestBlockFactory} from '../../test/testBlockFactory' +import {wrapDNDIntl} from '../../testUtils' + +import ShareBoardLoginButton from './shareBoardLoginButton' +jest.useFakeTimers() + +const boardId = '1' + +const board = TestBlockFactory.createBoard() +board.id = boardId + +jest.mock('react-router-dom', () => { + const originalModule = jest.requireActual('react-router-dom') + + return { + ...originalModule, + useRouteMatch: jest.fn(() => { + return { + teamId: 'team1', + boardId: 'boardId1', + viewId: 'viewId1', + cardId: 'cardId1', + } + }), + } +}) + +describe('src/components/shareBoard/shareBoardLoginButton', () => { + const savedLocation = window.location + + afterEach(() => { + window.location = savedLocation + }) + + test('should match snapshot', async () => { + // delete window.location + window.location = Object.assign(new URL('https://example.org/mattermost')) + const result = render( + wrapDNDIntl( + , + )) + const renderer = result.container + + expect(renderer).toMatchSnapshot() + }) +}) diff --git a/webapp/src/components/shareBoard/shareBoardLoginButton.tsx b/webapp/src/components/shareBoard/shareBoardLoginButton.tsx new file mode 100644 index 000000000..0b3fbf4c9 --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoardLoginButton.tsx @@ -0,0 +1,51 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback} from 'react' +import {FormattedMessage} from 'react-intl' +import {generatePath, useRouteMatch, useHistory} from 'react-router-dom' + +import Button from '../../widgets/buttons/button' +import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../../telemetry/telemetryClient' +import {Utils} from '../../utils' + +import './shareBoardLoginButton.scss' + +const ShareBoardLoginButton = () => { + const match = useRouteMatch<{teamId: string, boardId: string, viewId?: string, cardId?: string}>() + const history = useHistory() + + let redirectQueryParam = 'r=' + encodeURIComponent(generatePath('/:boardId?/:viewId?/:cardId?', match.params)) + if (Utils.isFocalboardLegacy()) { + redirectQueryParam = 'redirect_to=' + encodeURIComponent(generatePath('/boards/team/:teamId/:boardId?/:viewId?/:cardId?', match.params)) + } + const loginPath = '/login?' + redirectQueryParam + + const onLoginClick = useCallback(() => { + TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ShareBoardLogin) + if (Utils.isFocalboardLegacy()) { + location.assign(loginPath) + } else { + history.push(loginPath) + } + + }, []) + + return ( +
+ +
+ ) +} + +export default React.memo(ShareBoardLoginButton) diff --git a/webapp/src/pages/loginPage.tsx b/webapp/src/pages/loginPage.tsx index 7f5513d8d..9fc09603e 100644 --- a/webapp/src/pages/loginPage.tsx +++ b/webapp/src/pages/loginPage.tsx @@ -1,7 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React, {useState} from 'react' -import {useHistory, Link} from 'react-router-dom' + +import {Link, useLocation, useHistory} from 'react-router-dom' import {FormattedMessage} from 'react-intl' import {useAppDispatch} from '../store/hooks' @@ -15,14 +16,19 @@ const LoginPage = () => { const [username, setUsername] = useState('') const [password, setPassword] = useState('') const [errorMessage, setErrorMessage] = useState('') - const history = useHistory() const dispatch = useAppDispatch() + const queryParams = new URLSearchParams(useLocation().search) + const history = useHistory() const handleLogin = async (): Promise => { const logged = await client.login(username, password) if (logged) { await dispatch(fetchMe()) - history.push('/') + if (queryParams) { + history.push(queryParams.get('r') || '/') + } else { + history.push('/') + } } else { setErrorMessage('Login failed') } diff --git a/webapp/src/telemetry/telemetryClient.ts b/webapp/src/telemetry/telemetryClient.ts index b1234fd8d..68bc26de5 100644 --- a/webapp/src/telemetry/telemetryClient.ts +++ b/webapp/src/telemetry/telemetryClient.ts @@ -31,6 +31,7 @@ export const TelemetryActions = { AddTemplateFromCard: 'addTemplateFromCard', ViewSharedBoard: 'viewSharedBoard', ShareBoardOpenModal: 'shareBoard_openModal', + ShareBoardLogin: 'shareBoard_login', ShareLinkPublicCopy: 'shareLinkPublic_copy', ShareLinkInternalCopy: 'shareLinkInternal_copy', ImportArchive: 'settings_importArchive', diff --git a/webapp/src/utils.ts b/webapp/src/utils.ts index 3830d5987..3f527778b 100644 --- a/webapp/src/utils.ts +++ b/webapp/src/utils.ts @@ -513,7 +513,7 @@ class Utils { } static getFrontendBaseURL(absolute?: boolean): string { - let frontendBaseURL = window.frontendBaseURL || Utils.getBaseURL(absolute) + let frontendBaseURL = window.frontendBaseURL || Utils.getBaseURL() frontendBaseURL = frontendBaseURL.replace(/\/+$/, '') if (frontendBaseURL.indexOf('/') === 0) { frontendBaseURL = frontendBaseURL.slice(1) From e1c894b7bb1125251b7b27b08f758458c603ede8 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 4 Apr 2022 01:53:24 -0600 Subject: [PATCH 49/51] GH-2675 Remove deprecated Apis (#2685) * remove deprecated apis and related code * remove unused api from octoclient * remove unused api from client * remove unused code --- server/api/api.go | 303 ------------------------- server/app/blocks.go | 9 - server/client/client.go | 14 -- server/integrationtests/blocks_test.go | 14 -- webapp/src/octoClient.test.ts | 4 - webapp/src/octoClient.ts | 14 -- 6 files changed, 358 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index e7d9b0eb7..3a04a3028 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -87,14 +87,9 @@ func (a *API) RegisterRoutes(r *mux.Router) { apiv1.HandleFunc("/boards/{boardID}/blocks/{blockID}", a.sessionRequired(a.handleDeleteBlock)).Methods("DELETE") apiv1.HandleFunc("/boards/{boardID}/blocks/{blockID}", a.sessionRequired(a.handlePatchBlock)).Methods("PATCH") apiv1.HandleFunc("/boards/{boardID}/blocks/{blockID}/undelete", a.sessionRequired(a.handleUndeleteBlock)).Methods("POST") - apiv1.HandleFunc("/boards/{boardID}/blocks/{blockID}/subtree", a.attachSession(a.handleGetSubTree, false)).Methods("GET") apiv1.HandleFunc("/boards/{boardID}/blocks/{blockID}/duplicate", a.attachSession(a.handleDuplicateBlock, false)).Methods("POST") apiv1.HandleFunc("/boards/{boardID}/metadata", a.sessionRequired(a.handleGetBoardMetadata)).Methods("GET") - // Import&Export APIs - apiv1.HandleFunc("/boards/{boardID}/blocks/export", a.sessionRequired(a.handleExport)).Methods("GET") - apiv1.HandleFunc("/boards/{boardID}/blocks/import", a.sessionRequired(a.handleImport)).Methods("POST") - // Member APIs apiv1.HandleFunc("/boards/{boardID}/members", a.sessionRequired(a.handleGetMembersForBoard)).Methods("GET") apiv1.HandleFunc("/boards/{boardID}/members", a.sessionRequired(a.handleAddMember)).Methods("POST") @@ -363,23 +358,6 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) { auditRec.Success() } -func stampModificationMetadata(r *http.Request, blocks []model.Block, auditRec *audit.Record) { - userID := getUserID(r) - if userID == model.SingleUser { - userID = "" - } - - now := utils.GetMillis() - for i := range blocks { - blocks[i].ModifiedBy = userID - blocks[i].UpdateAt = now - - if auditRec != nil { - auditRec.AddMeta("block_"+strconv.FormatInt(int64(i), 10), blocks[i]) - } - } -} - func (a *API) handleCreateCategory(w http.ResponseWriter, r *http.Request) { requestBody, err := ioutil.ReadAll(r.Body) if err != nil { @@ -1219,287 +1197,6 @@ func (a *API) handlePatchBlocks(w http.ResponseWriter, r *http.Request) { auditRec.Success() } -func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) { - // swagger:operation GET /api/v1/boards/{boardID}/blocks/{blockID}/subtree getSubTree - // - // Returns the blocks of a subtree - // - // --- - // produces: - // - application/json - // parameters: - // - name: boardID - // in: path - // description: Board ID - // required: true - // type: string - // - name: blockID - // in: path - // description: The ID of the root block of the subtree - // required: true - // type: string - // - name: l - // in: query - // description: The number of levels to return. 2 or 3. Defaults to 2. - // required: false - // type: integer - // minimum: 2 - // maximum: 3 - // security: - // - BearerAuth: [] - // responses: - // '200': - // description: success - // schema: - // type: array - // items: - // "$ref": "#/definitions/Block" - // default: - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - - userID := getUserID(r) - vars := mux.Vars(r) - boardID := vars["boardID"] - blockID := vars["blockID"] - - if !a.hasValidReadTokenForBoard(r, boardID) && !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { - a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to board"}) - return - } - - query := r.URL.Query() - levels, err := strconv.ParseInt(query.Get("l"), 10, 32) - if err != nil { - levels = 2 - } - - if levels != 2 && levels != 3 { - a.logger.Error("Invalid levels", mlog.Int64("levels", levels)) - a.errorResponse(w, r.URL.Path, http.StatusBadRequest, "invalid levels", nil) - return - } - - auditRec := a.makeAuditRecord(r, "getSubTree", audit.Fail) - defer a.audit.LogRecord(audit.LevelRead, auditRec) - auditRec.AddMeta("boardID", boardID) - auditRec.AddMeta("blockID", blockID) - - blocks, err := a.app.GetSubTree(boardID, blockID, int(levels), model.QuerySubtreeOptions{}) - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - a.logger.Debug("GetSubTree", - mlog.Int64("levels", levels), - mlog.String("boardID", boardID), - mlog.String("blockID", blockID), - mlog.Int("block_count", len(blocks)), - ) - json, err := json.Marshal(blocks) - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - jsonBytesResponse(w, http.StatusOK, json) - - auditRec.AddMeta("blockCount", len(blocks)) - auditRec.Success() -} - -func (a *API) handleExport(w http.ResponseWriter, r *http.Request) { - // swagger:operation GET /api/v1/boards/{boardID}/blocks/export exportBlocks - // - // Returns all blocks of a board - // - // --- - // produces: - // - application/json - // parameters: - // - name: boardID - // in: path - // description: Board ID - // required: true - // type: string - // security: - // - BearerAuth: [] - // responses: - // '200': - // description: success - // schema: - // type: array - // items: - // "$ref": "#/definitions/Block" - // default: - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - - userID := getUserID(r) - vars := mux.Vars(r) - boardID := vars["boardID"] - - if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { - a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to board"}) - return - } - - query := r.URL.Query() - rootID := query.Get("root_id") - - auditRec := a.makeAuditRecord(r, "export", audit.Fail) - defer a.audit.LogRecord(audit.LevelRead, auditRec) - auditRec.AddMeta("boardID", boardID) - auditRec.AddMeta("rootID", rootID) - - var blocks []model.Block - var err error - if rootID == "" { - blocks, err = a.app.GetBlocksForBoard(boardID) - } else { - blocks, err = a.app.GetBlocksWithBoardID(boardID) - } - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - a.logger.Debug("raw blocks", mlog.Int("block_count", len(blocks))) - auditRec.AddMeta("rawCount", len(blocks)) - - blocks = filterOrphanBlocks(blocks) - - a.logger.Debug("EXPORT filtered blocks", mlog.Int("block_count", len(blocks))) - auditRec.AddMeta("filteredCount", len(blocks)) - - json, err := json.Marshal(blocks) - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - jsonBytesResponse(w, http.StatusOK, json) - - auditRec.Success() -} - -func filterOrphanBlocks(blocks []model.Block) (ret []model.Block) { - queue := make([]model.Block, 0) - childrenOfBlockWithID := make(map[string]*[]model.Block) - - // Build the trees from nodes - for _, block := range blocks { - if len(block.ParentID) == 0 { - // Queue root blocks to process first - queue = append(queue, block) - } else { - siblings := childrenOfBlockWithID[block.ParentID] - if siblings != nil { - *siblings = append(*siblings, block) - } else { - siblings := []model.Block{block} - childrenOfBlockWithID[block.ParentID] = &siblings - } - } - } - - // Map the trees to an array, which skips orphaned nodes - blocks = make([]model.Block, 0) - for len(queue) > 0 { - block := queue[0] - queue = queue[1:] // dequeue - blocks = append(blocks, block) - children := childrenOfBlockWithID[block.ID] - if children != nil { - queue = append(queue, *children...) - } - } - - return blocks -} - -func (a *API) handleImport(w http.ResponseWriter, r *http.Request) { - // swagger:operation POST /api/v1/boards/{boardID}/blocks/import importBlocks - // - // Import blocks on a given board - // - // --- - // produces: - // - application/json - // parameters: - // - name: boardID - // in: path - // description: Board ID - // required: true - // type: string - // - name: Body - // in: body - // description: array of blocks to import - // required: true - // schema: - // type: array - // items: - // "$ref": "#/definitions/Block" - // security: - // - BearerAuth: [] - // responses: - // '200': - // description: success - // default: - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - - userID := getUserID(r) - vars := mux.Vars(r) - boardID := vars["boardID"] - - if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionManageBoardCards) { - a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to make board changes"}) - return - } - - requestBody, err := ioutil.ReadAll(r.Body) - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - var blocks []model.Block - - err = json.Unmarshal(requestBody, &blocks) - if err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - auditRec := a.makeAuditRecord(r, "import", audit.Fail) - defer a.audit.LogRecord(audit.LevelModify, auditRec) - auditRec.AddMeta("boardID", boardID) - - // all blocks should now be part of the board that they're being - // imported onto - for i := range blocks { - blocks[i].BoardID = boardID - } - - stampModificationMetadata(r, blocks, auditRec) - - if _, err = a.app.InsertBlocks(model.GenerateBlockIDs(blocks, a.logger), userID, false); err != nil { - a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) - return - } - - jsonStringResponse(w, http.StatusOK, "{}") - - a.logger.Debug("IMPORT BlockIDs", mlog.Int("block_count", len(blocks))) - auditRec.AddMeta("blockCount", len(blocks)) - auditRec.Success() -} - // Sharing func (a *API) handleGetSharing(w http.ResponseWriter, r *http.Request) { diff --git a/server/app/blocks.go b/server/app/blocks.go index 3cd480764..32c168aab 100644 --- a/server/app/blocks.go +++ b/server/app/blocks.go @@ -234,15 +234,6 @@ func (a *App) CopyCardFiles(sourceBoardID string, blocks []model.Block) error { return nil } -func (a *App) GetSubTree(boardID, blockID string, levels int, opts model.QuerySubtreeOptions) ([]model.Block, error) { - // Only 2 or 3 levels are supported for now - if levels >= 3 { - return a.store.GetSubTree3(boardID, blockID, opts) - } - - return a.store.GetSubTree2(boardID, blockID, opts) -} - func (a *App) GetBlockByID(blockID string) (*model.Block, error) { return a.store.GetBlock(blockID) } diff --git a/server/client/client.go b/server/client/client.go index 789d8f7e3..2d5547c7f 100644 --- a/server/client/client.go +++ b/server/client/client.go @@ -164,10 +164,6 @@ func (c *Client) GetBlockRoute(boardID, blockID string) string { return fmt.Sprintf("%s/%s", c.GetBlocksRoute(boardID), blockID) } -func (c *Client) GetSubtreeRoute(boardID, blockID string) string { - return fmt.Sprintf("%s/subtree", c.GetBlockRoute(boardID, blockID)) -} - func (c *Client) GetBoardsRoute() string { return "/boards" } @@ -297,16 +293,6 @@ func (c *Client) DeleteBlock(boardID, blockID string) (bool, *Response) { return true, BuildResponse(r) } -func (c *Client) GetSubtree(boardID, blockID string) ([]model.Block, *Response) { - r, err := c.DoAPIGet(c.GetSubtreeRoute(boardID, blockID), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - return model.BlocksFromJSON(r.Body), BuildResponse(r) -} - // Boards and blocks. func (c *Client) CreateBoardsAndBlocks(bab *model.BoardsAndBlocks) (*model.BoardsAndBlocks, *Response) { r, err := c.DoAPIPost(c.GetBoardsAndBlocksRoute(), toJSON(bab)) diff --git a/server/integrationtests/blocks_test.go b/server/integrationtests/blocks_test.go index 573bd1144..00eca9cfd 100644 --- a/server/integrationtests/blocks_test.go +++ b/server/integrationtests/blocks_test.go @@ -429,18 +429,4 @@ func TestGetSubtree(t *testing.T) { } require.Contains(t, blockIDs, parentBlockID) }) - - t.Run("Get subtree for parent ID", func(t *testing.T) { - blocks, resp := th.Client.GetSubtree(board.ID, parentBlockID) - require.NoError(t, resp.Error) - require.Len(t, blocks, 3) - - blockIDs := make([]string, len(blocks)) - for i, b := range blocks { - blockIDs[i] = b.ID - } - require.Contains(t, blockIDs, parentBlockID) - require.Contains(t, blockIDs, childBlockID1) - require.Contains(t, blockIDs, childBlockID2) - }) } diff --git a/webapp/src/octoClient.test.ts b/webapp/src/octoClient.test.ts index db94d690e..dea8e06a9 100644 --- a/webapp/src/octoClient.test.ts +++ b/webapp/src/octoClient.test.ts @@ -23,10 +23,6 @@ test('OctoClient: get blocks', async () => { let boards = await octoClient.getBlocksWithType('card') expect(boards.length).toBe(blocks.length) - FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify(blocks))) - boards = await octoClient.getSubtree() - expect(boards.length).toBe(blocks.length) - FetchMock.fn.mockReturnValueOnce(FetchMock.jsonResponse(JSON.stringify(blocks))) const response = await octoClient.exportArchive() expect(response.status).toBe(200) diff --git a/webapp/src/octoClient.ts b/webapp/src/octoClient.ts index a32345d30..a12cd3e02 100644 --- a/webapp/src/octoClient.ts +++ b/webapp/src/octoClient.ts @@ -201,20 +201,6 @@ class OctoClient { return (await this.getJson(response, {})) as Record } - async getSubtree(boardId?: string, levels = 2, teamID?: string): Promise { - let path = this.teamPath(teamID) + `/blocks/${encodeURIComponent(boardId || '')}/subtree?l=${levels}` - const readToken = Utils.getReadToken() - if (readToken) { - path += `&read_token=${readToken}` - } - const response = await fetch(this.getBaseURL() + path, {headers: this.headers()}) - if (response.status !== 200) { - return [] - } - const blocks = (await this.getJson(response, [])) as Block[] - return this.fixBlocks(blocks) - } - // If no boardID is provided, it will export the entire archive async exportArchive(boardID = ''): Promise { const path = `/api/v1/boards/${boardID}/archive/export` From 7d0a4afa8bb86555eec7309ca136c9d1e888a3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 4 Apr 2022 15:53:11 +0200 Subject: [PATCH 50/51] Opening the actions menu from the view header to the left (#2689) * Opening the actions menu from the view header to the left * Fixing jest tests --- .../__snapshots__/viewHeaderActionsMenu.test.tsx.snap | 6 +++--- webapp/src/components/viewHeader/viewHeaderActionsMenu.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/src/components/viewHeader/__snapshots__/viewHeaderActionsMenu.test.tsx.snap b/webapp/src/components/viewHeader/__snapshots__/viewHeaderActionsMenu.test.tsx.snap index 20d3c905f..c98420199 100644 --- a/webapp/src/components/viewHeader/__snapshots__/viewHeaderActionsMenu.test.tsx.snap +++ b/webapp/src/components/viewHeader/__snapshots__/viewHeaderActionsMenu.test.tsx.snap @@ -19,7 +19,7 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu 1`] = ` />