mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-24 13:43:12 +02:00
Merge branch 'main' into compliance-history-export
This commit is contained in:
commit
a422f213d4
@ -49,6 +49,11 @@ func (a *serviceAPIAdapter) GetDirectChannel(userID1, userID2 string) (*mm_model
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *serviceAPIAdapter) GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error) {
|
||||
channel, appErr := a.api.channelService.GetDirectChannelOrCreate(userID1, userID2)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *serviceAPIAdapter) GetChannelByID(channelID string) (*mm_model.Channel, error) {
|
||||
channel, appErr := a.api.channelService.GetChannelByID(channelID)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
|
@ -54,6 +54,12 @@ func (a *pluginAPIAdapter) GetDirectChannel(userID1, userID2 string) (*mm_model.
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *pluginAPIAdapter) GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error) {
|
||||
// plugin API's GetDirectChannel will create channel if it does not exist.
|
||||
channel, appErr := a.api.GetDirectChannel(userID1, userID2)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *pluginAPIAdapter) GetChannelByID(channelID string) (*mm_model.Channel, error) {
|
||||
channel, appErr := a.api.GetChannel(channelID)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
|
@ -80,6 +80,9 @@ func (b *BoardsApp) OnConfigurationChange() error {
|
||||
if mmconfig.PluginSettings.Plugins[PluginName][SharedBoardsName] == true {
|
||||
enableShareBoards = true
|
||||
}
|
||||
if mmconfig.ProductSettings.EnablePublicSharedBoards != nil {
|
||||
enableShareBoards = *mmconfig.ProductSettings.EnablePublicSharedBoards
|
||||
}
|
||||
configuration := &configuration{
|
||||
EnablePublicSharedBoards: enableShareBoards,
|
||||
}
|
||||
|
3
mattermost-plugin/server/manifest.go
generated
3
mattermost-plugin/server/manifest.go
generated
@ -45,8 +45,7 @@ const manifestStr = `
|
||||
"type": "bool",
|
||||
"help_text": "This allows board editors to share boards that can be accessed by anyone with the link.",
|
||||
"placeholder": "",
|
||||
"default": false,
|
||||
"hosting": ""
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -92,14 +92,25 @@ func (a *App) writeArchiveBoard(zw *zip.Writer, board model.Board, opt model.Exp
|
||||
return err
|
||||
}
|
||||
if block.Type == model.TypeImage {
|
||||
filename, err := extractImageFilename(block)
|
||||
if err != nil {
|
||||
filename, err2 := extractImageFilename(block)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
files = append(files, filename)
|
||||
}
|
||||
}
|
||||
|
||||
boardMembers, err := a.GetMembersForBoard(board.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, boardMember := range boardMembers {
|
||||
if err = a.writeArchiveBoardMemberLine(w, boardMember); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// write the files
|
||||
for _, filename := range files {
|
||||
if err := a.writeArchiveFile(zw, filename, board.ID, opt); err != nil {
|
||||
@ -109,6 +120,31 @@ func (a *App) writeArchiveBoard(zw *zip.Writer, board model.Board, opt model.Exp
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeArchiveBoardMemberLine writes a single boardMember to the archive.
|
||||
func (a *App) writeArchiveBoardMemberLine(w io.Writer, boardMember *model.BoardMember) error {
|
||||
bm, err := json.Marshal(&boardMember)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
line := model.ArchiveLine{
|
||||
Type: "boardMember",
|
||||
Data: bm,
|
||||
}
|
||||
|
||||
bm, err = json.Marshal(&line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(bm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(newline)
|
||||
return err
|
||||
}
|
||||
|
||||
// writeArchiveBlockLine writes a single block to the archive.
|
||||
func (a *App) writeArchiveBlockLine(w io.Writer, block *model.Block) error {
|
||||
b, err := json.Marshal(&block)
|
||||
|
@ -137,6 +137,7 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
}
|
||||
now := utils.GetMillis()
|
||||
var boardID string
|
||||
var boardMembers []*model.BoardMember
|
||||
|
||||
lineNum := 1
|
||||
firstLine := true
|
||||
@ -196,6 +197,12 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
block.UpdateAt = now
|
||||
block.BoardID = boardID
|
||||
boardsAndBlocks.Blocks = append(boardsAndBlocks.Blocks, block)
|
||||
case "boardMember":
|
||||
var boardMember *model.BoardMember
|
||||
if err2 := json.Unmarshal(archiveLine.Data, &boardMember); err2 != nil {
|
||||
return "", fmt.Errorf("invalid board Member in archive line %d: %w", lineNum, err2)
|
||||
}
|
||||
boardMembers = append(boardMembers, boardMember)
|
||||
default:
|
||||
return "", model.NewErrUnsupportedArchiveLineType(lineNum, archiveLine.Type)
|
||||
}
|
||||
@ -212,6 +219,13 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
lineNum++
|
||||
}
|
||||
|
||||
// loop to remove the people how are not part of the team and system
|
||||
for i := len(boardMembers) - 1; i >= 0; i-- {
|
||||
if _, err := a.GetUser(boardMembers[i].UserID); err != nil {
|
||||
boardMembers = append(boardMembers[:i], boardMembers[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
a.fixBoardsandBlocks(boardsAndBlocks, opt)
|
||||
|
||||
var err error
|
||||
@ -225,16 +239,22 @@ 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 (if not the fake system user).
|
||||
if opt.ModifiedBy != model.SystemUserID {
|
||||
for _, board := range boardsAndBlocks.Boards {
|
||||
boardMember := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: opt.ModifiedBy,
|
||||
SchemeAdmin: true,
|
||||
// add users to all the new boards (if not the fake system user).
|
||||
for _, board := range boardsAndBlocks.Boards {
|
||||
for _, boardMember := range boardMembers {
|
||||
bm := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: boardMember.UserID,
|
||||
Roles: boardMember.Roles,
|
||||
MinimumRole: boardMember.MinimumRole,
|
||||
SchemeAdmin: boardMember.SchemeAdmin,
|
||||
SchemeEditor: boardMember.SchemeEditor,
|
||||
SchemeCommenter: boardMember.SchemeCommenter,
|
||||
SchemeViewer: boardMember.SchemeViewer,
|
||||
Synthetic: boardMember.Synthetic,
|
||||
}
|
||||
if _, err := a.AddMemberToBoard(boardMember); err != nil {
|
||||
return "", fmt.Errorf("cannot add member to board: %w", err)
|
||||
if _, err2 := a.AddMemberToBoard(bm); err2 != nil {
|
||||
return "", fmt.Errorf("cannot add member to board: %w", err2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,6 @@ func TestApp_ImportArchive(t *testing.T) {
|
||||
|
||||
th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "user").Return(babs, nil)
|
||||
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)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user", "test-team")
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
@ -62,6 +60,64 @@ func TestApp_ImportArchive(t *testing.T) {
|
||||
err := th.App.ImportArchive(r, opts)
|
||||
require.NoError(t, err, "import archive should not fail")
|
||||
})
|
||||
|
||||
t.Run("import board archive", func(t *testing.T) {
|
||||
r := bytes.NewReader([]byte(boardArchive))
|
||||
opts := model.ImportArchiveOptions{
|
||||
TeamID: "test-team",
|
||||
ModifiedBy: "f1tydgc697fcbp8ampr6881jea",
|
||||
}
|
||||
|
||||
bm1 := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: "f1tydgc697fcbp8ampr6881jea",
|
||||
}
|
||||
|
||||
bm2 := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: "hxxzooc3ff8cubsgtcmpn8733e",
|
||||
}
|
||||
|
||||
bm3 := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: "nto73edn5ir6ifimo5a53y1dwa",
|
||||
}
|
||||
|
||||
user1 := &model.User{
|
||||
ID: "f1tydgc697fcbp8ampr6881jea",
|
||||
}
|
||||
|
||||
user2 := &model.User{
|
||||
ID: "hxxzooc3ff8cubsgtcmpn8733e",
|
||||
}
|
||||
|
||||
user3 := &model.User{
|
||||
ID: "nto73edn5ir6ifimo5a53y1dwa",
|
||||
}
|
||||
|
||||
th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "f1tydgc697fcbp8ampr6881jea").Return(babs, nil)
|
||||
th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{bm1, bm2, bm3}, nil)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("f1tydgc697fcbp8ampr6881jea", "test-team")
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
ID: "boards_category_id",
|
||||
Name: "Boards",
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetMembersForUser("f1tydgc697fcbp8ampr6881jea").Return([]*model.BoardMember{}, nil)
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("f1tydgc697fcbp8ampr6881jea", "test-team", false).Return([]*model.Board{}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("f1tydgc697fcbp8ampr6881jea", utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetBoard(board.ID).AnyTimes().Return(board, nil)
|
||||
th.Store.EXPECT().GetMemberForBoard(board.ID, "f1tydgc697fcbp8ampr6881jea").AnyTimes().Return(bm1, nil)
|
||||
th.Store.EXPECT().GetMemberForBoard(board.ID, "hxxzooc3ff8cubsgtcmpn8733e").AnyTimes().Return(bm2, nil)
|
||||
th.Store.EXPECT().GetMemberForBoard(board.ID, "nto73edn5ir6ifimo5a53y1dwa").AnyTimes().Return(bm3, nil)
|
||||
th.Store.EXPECT().GetUserByID("f1tydgc697fcbp8ampr6881jea").AnyTimes().Return(user1, nil)
|
||||
th.Store.EXPECT().GetUserByID("hxxzooc3ff8cubsgtcmpn8733e").AnyTimes().Return(user2, nil)
|
||||
th.Store.EXPECT().GetUserByID("nto73edn5ir6ifimo5a53y1dwa").AnyTimes().Return(user3, nil)
|
||||
|
||||
boardID, err := th.App.ImportBoardJSONL(r, opts)
|
||||
require.Equal(t, board.ID, boardID, "Board ID should be same")
|
||||
require.NoError(t, err, "import archive should not fail")
|
||||
})
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
@ -78,3 +134,12 @@ const asana = `{"version":1,"date":1614714686842}
|
||||
{"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"}}
|
||||
`
|
||||
|
||||
//nolint:lll
|
||||
const boardArchive = `{"type":"board","data":{"id":"bfoi6yy6pa3yzika53spj7pq9ee","teamId":"wsmqbtwb5jb35jb3mtp85c8a9h","channelId":"","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","type":"P","minimumRole":"","title":"Custom","description":"","icon":"","showDescription":false,"isTemplate":false,"templateVersion":0,"properties":{},"cardProperties":[{"id":"aonihehbifijmx56aqzu3cc7w1r","name":"Status","options":[],"type":"select"},{"id":"aohjkzt769rxhtcz1o9xcoce5to","name":"Person","options":[],"type":"person"}],"createAt":1672750481591,"updateAt":1672750481591,"deleteAt":0}}
|
||||
{"type":"block","data":{"id":"ckpc3b1dp3pbw7bqntfryy9jbzo","parentId":"bjaqxtbyqz3bu7pgyddpgpms74a","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","schema":1,"type":"card","title":"Test","fields":{"contentOrder":[],"icon":"","isTemplate":false,"properties":{"aohjkzt769rxhtcz1o9xcoce5to":"hxxzooc3ff8cubsgtcmpn8733e"}},"createAt":1672750481612,"updateAt":1672845003530,"deleteAt":0,"boardId":"bfoi6yy6pa3yzika53spj7pq9ee"}}
|
||||
{"type":"block","data":{"id":"v7tdajwpm47r3u8duedk89bhxar","parentId":"bpypang3a3errqstj1agx9kuqay","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","schema":1,"type":"view","title":"Board view","fields":{"cardOrder":["crsyw7tbr3pnjznok6ppngmmyya","c5titiemp4pgaxbs4jksgybbj4y"],"collapsedOptionIds":[],"columnCalculations":{},"columnWidths":{},"defaultTemplateId":"","filter":{"filters":[],"operation":"and"},"hiddenOptionIds":[],"kanbanCalculations":{},"sortOptions":[],"viewType":"board","visibleOptionIds":[],"visiblePropertyIds":["aohjkzt769rxhtcz1o9xcoce5to"]},"createAt":1672750481626,"updateAt":1672750481626,"deleteAt":0,"boardId":"bfoi6yy6pa3yzika53spj7pq9ee"}}
|
||||
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"f1tydgc697fcbp8ampr6881jea","roles":"","minimumRole":"","schemeAdmin":false,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":true,"synthetic":false}}
|
||||
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"hxxzooc3ff8cubsgtcmpn8733e","roles":"","minimumRole":"","schemeAdmin":false,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":true,"synthetic":false}}
|
||||
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"nto73edn5ir6ifimo5a53y1dwa","roles":"","minimumRole":"","schemeAdmin":true,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":false,"synthetic":false}}
|
||||
`
|
||||
|
@ -199,6 +199,21 @@ func (mr *MockServicesAPIMockRecorder) GetDirectChannel(arg0, arg1 interface{})
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDirectChannel", reflect.TypeOf((*MockServicesAPI)(nil).GetDirectChannel), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetDirectChannelOrCreate mocks base method.
|
||||
func (m *MockServicesAPI) GetDirectChannelOrCreate(arg0, arg1 string) (*model.Channel, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetDirectChannelOrCreate", arg0, arg1)
|
||||
ret0, _ := ret[0].(*model.Channel)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetDirectChannelOrCreate indicates an expected call of GetDirectChannelOrCreate.
|
||||
func (mr *MockServicesAPIMockRecorder) GetDirectChannelOrCreate(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDirectChannelOrCreate", reflect.TypeOf((*MockServicesAPI)(nil).GetDirectChannelOrCreate), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetFileInfo mocks base method.
|
||||
func (m *MockServicesAPI) GetFileInfo(arg0 string) (*model.FileInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -30,6 +30,7 @@ var FocalboardBot = &mm_model.Bot{
|
||||
type ServicesAPI interface {
|
||||
// Channels service
|
||||
GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
GetChannelByID(channelID string) (*mm_model.Channel, error)
|
||||
GetChannelMember(channelID string, userID string) (*mm_model.ChannelMember, error)
|
||||
GetChannelsForTeamForUser(teamID string, userID string, includeDeleted bool) (mm_model.ChannelList, error)
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
)
|
||||
|
||||
type servicesAPI interface {
|
||||
// GetDirectChannel gets a direct message channel.
|
||||
// If the channel does not exist it will create it.
|
||||
GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
// GetDirectChannelOrCreate gets a direct message channel,
|
||||
// or creates one if it does not already exist
|
||||
GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
|
||||
// CreatePost creates a post.
|
||||
CreatePost(post *mm_model.Post) (*mm_model.Post, error)
|
||||
|
@ -53,7 +53,7 @@ func (pd *PluginDelivery) getDirectChannelID(teamID string, subscriberID string,
|
||||
return "", fmt.Errorf("cannot find user: %w", err)
|
||||
}
|
||||
channel, err := pd.getDirectChannel(teamID, user.Id, botID)
|
||||
if err != nil {
|
||||
if err != nil || channel == nil {
|
||||
return "", fmt.Errorf("cannot get direct channel: %w", err)
|
||||
}
|
||||
return channel.Id, nil
|
||||
@ -70,5 +70,5 @@ func (pd *PluginDelivery) getDirectChannel(teamID string, userID string, botID s
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot add bot to team %s: %w", teamID, err)
|
||||
}
|
||||
return pd.api.GetDirectChannel(userID, botID)
|
||||
return pd.api.GetDirectChannelOrCreate(userID, botID)
|
||||
}
|
||||
|
@ -101,6 +101,10 @@ func (m servicesAPIMock) GetDirectChannel(userID1, userID2 string) (*mm_model.Ch
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m servicesAPIMock) GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m servicesAPIMock) CreatePost(post *mm_model.Post) (*mm_model.Post, error) {
|
||||
return post, nil
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ describe('Create and delete board / card', () => {
|
||||
cy.contains('Project Tasks').should('exist')
|
||||
|
||||
// Create empty board
|
||||
cy.contains('Create empty board').should('exist').click({force: true})
|
||||
cy.contains('Create an empty board').should('exist').click({force: true})
|
||||
cy.get('.BoardComponent').should('exist')
|
||||
cy.get('.Editable.title').invoke('attr', 'placeholder').should('contain', 'Untitled board')
|
||||
|
||||
|
@ -17,5 +17,5 @@ Cypress.Commands.add('uiCreateEmptyBoard', () => {
|
||||
cy.log('Create new empty board')
|
||||
|
||||
cy.contains('+ Add board').should('be.visible').click().wait(500)
|
||||
return cy.contains('Create empty board').click({force: true}).wait(1000)
|
||||
return cy.contains('Create an empty board').click({force: true}).wait(1000)
|
||||
})
|
||||
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"AppBar.Tooltip": "Toggle Linked Boards",
|
||||
"AppBar.Tooltip": "Toggle linked boards",
|
||||
"Attachment.Attachment-title": "Attachment",
|
||||
"AttachmentBlock.DeleteAction": "delete",
|
||||
"AttachmentBlock.addElement": "add {type}",
|
||||
"AttachmentBlock.delete": "Attachment Deleted Successfully.",
|
||||
"AttachmentBlock.failed": "Unable to upload the file. Attachment size limit reached.",
|
||||
"AttachmentBlock.delete": "Attachment deleted.",
|
||||
"AttachmentBlock.failed": "This file couldn't be uploaded as the file size limit has been reached.",
|
||||
"AttachmentBlock.upload": "Attachment uploading.",
|
||||
"AttachmentBlock.uploadSuccess": "Attachment uploaded successfull.",
|
||||
"AttachmentBlock.uploadSuccess": "Attachment uploaded.",
|
||||
"AttachmentElement.delete-confirmation-dialog-button-text": "Delete",
|
||||
"AttachmentElement.download": "Download",
|
||||
"AttachmentElement.upload-percentage": "Uploading...({uploadPercent}%)",
|
||||
@ -16,7 +16,7 @@
|
||||
"BoardComponent.hide": "Hide",
|
||||
"BoardComponent.new": "+ New",
|
||||
"BoardComponent.no-property": "No {property}",
|
||||
"BoardComponent.no-property-title": "Items with an empty {property} property will go here. This column cannot be removed.",
|
||||
"BoardComponent.no-property-title": "Items with an empty {property} property will go here. This column can't be removed.",
|
||||
"BoardComponent.show": "Show",
|
||||
"BoardMember.schemeAdmin": "Admin",
|
||||
"BoardMember.schemeCommenter": "Commenter",
|
||||
@ -27,7 +27,7 @@
|
||||
"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": "Create new template",
|
||||
"BoardTemplateSelector.create-empty-board": "Create empty board",
|
||||
"BoardTemplateSelector.create-empty-board": "Create an empty board",
|
||||
"BoardTemplateSelector.delete-template": "Delete",
|
||||
"BoardTemplateSelector.description": "Add a board to the sidebar using any of the templates defined below or start from scratch.",
|
||||
"BoardTemplateSelector.edit-template": "Edit",
|
||||
@ -35,7 +35,7 @@
|
||||
"BoardTemplateSelector.plugin.no-content-title": "Create a board",
|
||||
"BoardTemplateSelector.title": "Create a board",
|
||||
"BoardTemplateSelector.use-this-template": "Use this template",
|
||||
"BoardsSwitcher.Title": "Find Boards",
|
||||
"BoardsSwitcher.Title": "Find boards",
|
||||
"BoardsUnfurl.Limited": "Additional details are hidden due to the card being archived",
|
||||
"BoardsUnfurl.Remainder": "+{remainder} more",
|
||||
"BoardsUnfurl.Updated": "Updated {time}",
|
||||
@ -88,7 +88,7 @@
|
||||
"CardDetail.add-icon": "Add icon",
|
||||
"CardDetail.add-property": "+ Add a property",
|
||||
"CardDetail.addCardText": "add card text",
|
||||
"CardDetail.limited-body": "Upgrade to our Professional or Enterprise plan to view archived cards, have unlimited views per boards, unlimited cards and more.",
|
||||
"CardDetail.limited-body": "Upgrade to our Professional or Enterprise plan.",
|
||||
"CardDetail.limited-button": "Upgrade",
|
||||
"CardDetail.limited-title": "This card is hidden",
|
||||
"CardDetail.moveContent": "Move card content",
|
||||
@ -103,9 +103,9 @@
|
||||
"CardDetailProperty.property-deleted": "Deleted {propertyName} successfully!",
|
||||
"CardDetailProperty.property-name-change-subtext": "type from \"{oldPropType}\" to \"{newPropType}\"",
|
||||
"CardDetial.limited-link": "Learn more about our plans.",
|
||||
"CardDialog.delete-confirmation-dialog-attachment": "Confirm Attachment delete!",
|
||||
"CardDialog.delete-confirmation-dialog-attachment": "Confirm attachment delete",
|
||||
"CardDialog.delete-confirmation-dialog-button-text": "Delete",
|
||||
"CardDialog.delete-confirmation-dialog-heading": "Confirm card delete!",
|
||||
"CardDialog.delete-confirmation-dialog-heading": "Confirm card delete",
|
||||
"CardDialog.editing-template": "You're editing a template.",
|
||||
"CardDialog.nocard": "This card doesn't exist or is inaccessible.",
|
||||
"Categories.CreateCategoryDialog.CancelText": "Cancel",
|
||||
@ -187,7 +187,7 @@
|
||||
"OnboardingTour.AddComments.Title": "Add comments",
|
||||
"OnboardingTour.AddDescription.Body": "Add a description to your card so your teammates know what the card is about.",
|
||||
"OnboardingTour.AddDescription.Title": "Add description",
|
||||
"OnboardingTour.AddProperties.Body": "Add various properties to cards to make them more powerful!",
|
||||
"OnboardingTour.AddProperties.Body": "Add various properties to cards to make them more powerful.",
|
||||
"OnboardingTour.AddProperties.Title": "Add properties",
|
||||
"OnboardingTour.AddView.Body": "Go here to create a new view to organise your board using different layouts.",
|
||||
"OnboardingTour.AddView.Title": "Add a new view",
|
||||
@ -237,11 +237,11 @@
|
||||
"ShareBoard.copyLink": "Copy link",
|
||||
"ShareBoard.regenerate": "Regenerate token",
|
||||
"ShareBoard.searchPlaceholder": "Search for people and channels",
|
||||
"ShareBoard.teamPermissionsText": "Everyone at {teamName} Team",
|
||||
"ShareBoard.teamPermissionsText": "Everyone at {teamName} team",
|
||||
"ShareBoard.tokenRegenrated": "Token regenerated",
|
||||
"ShareBoard.userPermissionsRemoveMemberText": "Remove member",
|
||||
"ShareBoard.userPermissionsYouText": "(You)",
|
||||
"ShareTemplate.Title": "Share Template",
|
||||
"ShareTemplate.Title": "Share template",
|
||||
"ShareTemplate.searchPlaceholder": "Search for people",
|
||||
"Sidebar.about": "About Focalboard",
|
||||
"Sidebar.add-board": "+ Add board",
|
||||
@ -277,8 +277,8 @@
|
||||
"SidebarTour.SidebarCategories.Body": "All your boards are now organized under your new sidebar. No more switching between workspaces. One-time custom categories based on your prior workspaces may have automatically been created for you as part of your v7.2 upgrade. These can be removed or edited to your preference.",
|
||||
"SidebarTour.SidebarCategories.Link": "Learn more",
|
||||
"SidebarTour.SidebarCategories.Title": "Sidebar categories",
|
||||
"SiteStats.total_boards": "Total Boards",
|
||||
"SiteStats.total_cards": "Total Cards",
|
||||
"SiteStats.total_boards": "Total boards",
|
||||
"SiteStats.total_cards": "Total cards",
|
||||
"TableComponent.add-icon": "Add icon",
|
||||
"TableComponent.name": "Name",
|
||||
"TableComponent.plus-new": "+ New",
|
||||
@ -342,9 +342,9 @@
|
||||
"ViewLimitDialog.Heading": "Views per board limit reached",
|
||||
"ViewLimitDialog.PrimaryButton.Title.Admin": "Upgrade",
|
||||
"ViewLimitDialog.PrimaryButton.Title.RegularUser": "Notify Admin",
|
||||
"ViewLimitDialog.Subtext.Admin": "Upgrade to our Professional or Enterprise plan to have unlimited views per boards, unlimited cards, and more.",
|
||||
"ViewLimitDialog.Subtext.Admin": "Upgrade to our Professional or Enterprise plan.",
|
||||
"ViewLimitDialog.Subtext.Admin.PricingPageLink": "Learn more about our plans.",
|
||||
"ViewLimitDialog.Subtext.RegularUser": "Notify your Admin to upgrade to our Professional or Enterprise plan to have unlimited views per boards, unlimited cards, and more.",
|
||||
"ViewLimitDialog.Subtext.RegularUser": "Notify your Admin to upgrade to our Professional or Enterprise plan.",
|
||||
"ViewLimitDialog.UpgradeImg.AltText": "upgrade image",
|
||||
"ViewLimitDialog.notifyAdmin.Success": "Your admin has been notified",
|
||||
"ViewTitle.hide-description": "hide description",
|
||||
@ -375,10 +375,10 @@
|
||||
"centerPanel.undefined": "No {propertyName}",
|
||||
"centerPanel.unknown-user": "Unknown user",
|
||||
"cloudMessage.learn-more": "Learn more",
|
||||
"createImageBlock.failed": "Unable to upload the file. File size limit reached.",
|
||||
"createImageBlock.failed": "This file couldn't be uploaded as the file size limit has been reached.",
|
||||
"default-properties.badges": "Comments and description",
|
||||
"default-properties.title": "Title",
|
||||
"error.back-to-home": "Back to Home",
|
||||
"error.back-to-home": "Back to home",
|
||||
"error.back-to-team": "Back to team",
|
||||
"error.board-not-found": "Board not found.",
|
||||
"error.go-login": "Log in",
|
||||
@ -390,7 +390,7 @@
|
||||
"generic.previous": "Previous",
|
||||
"guest-no-board.subtitle": "You don't have access to any board in this team yet, please wait until somebody adds you to any board.",
|
||||
"guest-no-board.title": "No boards yet",
|
||||
"imagePaste.upload-failed": "Some files not uploaded. File size limit reached",
|
||||
"imagePaste.upload-failed": "Some files weren't uploaded because the file size limit has been reached.",
|
||||
"limitedCard.title": "Cards hidden",
|
||||
"login.log-in-button": "Log in",
|
||||
"login.log-in-title": "Log in",
|
||||
@ -406,15 +406,15 @@
|
||||
"person.add-user-to-board-confirm-button": "Add to board",
|
||||
"person.add-user-to-board-permissions": "Permissions",
|
||||
"person.add-user-to-board-question": "Do you want to add {username} to the board?",
|
||||
"person.add-user-to-board-warning": "{username} is not a member of the board, and will not receive any notifications about it.",
|
||||
"person.add-user-to-board-warning": "{username} isn't a member of the board, and won't receive any notifications about it.",
|
||||
"register.login-button": "or log in if you already have an account",
|
||||
"register.signup-title": "Sign up for your account",
|
||||
"rhs-board-non-admin-msg": "You are not an admin of the board",
|
||||
"rhs-board-non-admin-msg": "You're not an admin of the board",
|
||||
"rhs-boards.add": "Add",
|
||||
"rhs-boards.dm": "DM",
|
||||
"rhs-boards.gm": "GM",
|
||||
"rhs-boards.header.dm": "this Direct Message",
|
||||
"rhs-boards.header.gm": "this Group Message",
|
||||
"rhs-boards.header.dm": "this direct message",
|
||||
"rhs-boards.header.gm": "this group message",
|
||||
"rhs-boards.last-update-at": "Last update at: {datetime}",
|
||||
"rhs-boards.link-boards-to-channel": "Link boards to {channelName}",
|
||||
"rhs-boards.linked-boards": "Linked boards",
|
||||
@ -445,4 +445,4 @@
|
||||
"tutorial_tip.ok": "Next",
|
||||
"tutorial_tip.out": "Opt out of these tips.",
|
||||
"tutorial_tip.seen": "Seen this before?"
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@
|
||||
"FilterByText.placeholder": "過濾文字",
|
||||
"FilterComponent.add-filter": "+ 增加過濾條件",
|
||||
"FilterComponent.delete": "刪除",
|
||||
"FindBoardsDialog.IntroText": "查詢面板",
|
||||
"FindBoardsDialog.IntroText": "查詢看板",
|
||||
"FindBoardsDialog.NoResultsFor": "「{searchQuery}」搜尋未果",
|
||||
"FindBoardsDialog.NoResultsSubtext": "檢查錯字或嘗試其他搜尋.",
|
||||
"FindBoardsDialog.SubTitle": "輸入已找到面板.使用 <b>UP/DOWN</b>來瀏覽.<b>ENTER</b>來搜尋, <b>ESC</b> 來取消",
|
||||
@ -313,8 +313,9 @@
|
||||
"View.NewTemplateDefaultTitle": "沒有標題的模板",
|
||||
"View.NewTemplateTitle": "沒有標題",
|
||||
"View.Table": "圖表",
|
||||
"ViewHeader.add-template": "+ 新範本",
|
||||
"ViewHeader.add-template": "新範本",
|
||||
"ViewHeader.delete-template": "刪除",
|
||||
"ViewHeader.display-by": "依據{property}顯示",
|
||||
"ViewHeader.edit-template": "編輯",
|
||||
"ViewHeader.empty-card": "清空卡片",
|
||||
"ViewHeader.export-board-archive": "匯出版面打包檔",
|
||||
@ -331,11 +332,14 @@
|
||||
"ViewHeader.set-default-template": "設為預設",
|
||||
"ViewHeader.sort": "排序",
|
||||
"ViewHeader.untitled": "無標題",
|
||||
"ViewHeader.view-header-menu": "查看標題菜單",
|
||||
"ViewHeader.view-menu": "查看菜單",
|
||||
"ViewLimitDialog.Heading": "已達到每個看板觀看限制",
|
||||
"ViewLimitDialog.PrimaryButton.Title.Admin": "升級",
|
||||
"ViewLimitDialog.PrimaryButton.Title.RegularUser": "通知管理者",
|
||||
"ViewLimitDialog.Subtext.Admin": "升級到專業版或企業版,獲得每個看板無限瀏覽、無限卡片,以及更多。",
|
||||
"ViewLimitDialog.Subtext.Admin.PricingPageLink": "了解更多我們的計畫",
|
||||
"ViewLimitDialog.Subtext.Admin.PricingPageLink": "了解更多我們的計畫。",
|
||||
"ViewLimitDialog.Subtext.RegularUser": "通知你的管理員升級到專業版或是企業版,獲得無限使用看板、卡片、更多。",
|
||||
"ViewLimitDialog.UpgradeImg.AltText": "升級圖片",
|
||||
"ViewLimitDialog.notifyAdmin.Success": "已經通知管理者",
|
||||
"ViewTitle.hide-description": "隱藏敘述",
|
||||
@ -344,14 +348,17 @@
|
||||
"ViewTitle.remove-icon": "移除圖示",
|
||||
"ViewTitle.show-description": "顯示敘述",
|
||||
"ViewTitle.untitled-board": "未命名版面",
|
||||
"WelcomePage.Description": "看板是一個專案管理工具,可以使用熟悉的圖表幫助我們定義、組織、追蹤和管理跨團隊工作。",
|
||||
"WelcomePage.Explore.Button": "探索",
|
||||
"WelcomePage.Heading": "歡迎來到版面",
|
||||
"WelcomePage.Heading": "歡迎來到看板",
|
||||
"WelcomePage.NoThanks.Text": "不需要,自己想辦法",
|
||||
"WelcomePage.StartUsingIt.Text": "開始使用",
|
||||
"Workspace.editing-board-template": "您正在編輯版面範本。",
|
||||
"badge.guest": "訪客",
|
||||
"boardSelector.confirm-link-board": "連結看板與頻道",
|
||||
"boardSelector.confirm-link-board-button": "是,連結看版",
|
||||
"boardSelector.confirm-link-board-subtext": "當你將\"{boardName}\"連接到頻道時,該頻道的所有成員(現有的和新的)都可以編輯。並不包含訪客身分。你可以在任何時候從一個頻道上取消看板的連接。",
|
||||
"boardSelector.confirm-link-board-subtext-with-other-channel": "當你將\"{boardName}\"連接到頻道時,該頻道的所有成員(現有的和新的)都可以編輯。並不包含訪客身分。{lineBreak} 看板目前正連接到另一個頻道。如果选择在這裡連接它,將取消另一個連接。",
|
||||
"boardSelector.create-a-board": "建立看版",
|
||||
"boardSelector.link": "連結",
|
||||
"boardSelector.search-for-boards": "搜尋看板",
|
||||
@ -369,12 +376,12 @@
|
||||
"error.board-not-found": "沒有找到看板.",
|
||||
"error.go-login": "登入",
|
||||
"error.invalid-read-only-board": "沒有權限進入此看版.登入後才能訪問.",
|
||||
"error.not-logged-in": "已被登出,請再次登入使用看板",
|
||||
"error.not-logged-in": "已被登出,請再次登入使用看板。",
|
||||
"error.page.title": "很抱歉,發生了些錯誤",
|
||||
"error.team-undefined": "不是有效的團隊",
|
||||
"error.team-undefined": "不是有效的團隊。",
|
||||
"error.unknown": "發生一個錯誤。",
|
||||
"generic.previous": "上一篇",
|
||||
"guest-no-board.subtitle": "你尚未有權限進入此看板,請等人把你加入任何看板",
|
||||
"guest-no-board.subtitle": "你尚未有權限進入此看板,請等人把你加入任何看板。",
|
||||
"guest-no-board.title": "尚未有看版",
|
||||
"imagePaste.upload-failed": "有些檔案無法上傳.檔案大小達上限",
|
||||
"limitedCard.title": "影藏卡片",
|
||||
@ -386,7 +393,8 @@
|
||||
"notification-box-card-limit-reached.link": "升級到付費版",
|
||||
"notification-box-card-limit-reached.title": "將看板上{cards}卡片隱藏",
|
||||
"notification-box-cards-hidden.title": "此行為隱藏了其他卡片",
|
||||
"notification-box.card-limit-reached.not-admin.text": "要存取已封存的卡片,你可以點擊{contactLink}升級到付費版",
|
||||
"notification-box.card-limit-reached.not-admin.text": "要存取已封存的卡片,你可以點擊{contactLink}升級到付費版。",
|
||||
"notification-box.card-limit-reached.text": "已達卡片上限,觀看舊卡片請點{link}",
|
||||
"person.add-user-to-board": "將{username} 加入看板",
|
||||
"person.add-user-to-board-confirm-button": "新增看板",
|
||||
"person.add-user-to-board-permissions": "權限",
|
||||
@ -396,22 +404,34 @@
|
||||
"register.signup-title": "註冊您的帳戶",
|
||||
"rhs-board-non-admin-msg": "你不是看板的管理者",
|
||||
"rhs-boards.add": "新增",
|
||||
"rhs-boards.dm": "私人訊息",
|
||||
"rhs-boards.gm": "群組訊息",
|
||||
"rhs-boards.header.dm": "此私人訊息",
|
||||
"rhs-boards.header.gm": "此群組訊息",
|
||||
"rhs-boards.last-update-at": "最後更新日: {datetime}",
|
||||
"rhs-boards.link-boards-to-channel": "將看板連接到{channelName}",
|
||||
"rhs-boards.linked-boards": "連結看板",
|
||||
"rhs-boards.no-boards-linked-to-channel": "還沒有看板與{channelName}連接",
|
||||
"rhs-boards.no-boards-linked-to-channel-description": "看板是一個專案管理工具,可以使用熟悉的圖表幫助我們定義、組織、追蹤和管理跨團隊工作。",
|
||||
"rhs-boards.unlink-board": "未連結看版",
|
||||
"rhs-boards.unlink-board1": "未連結看版",
|
||||
"rhs-channel-boards-header.title": "板塊",
|
||||
"share-board.publish": "發布",
|
||||
"share-board.share": "分享",
|
||||
"shareBoard.channels-select-group": "頻道",
|
||||
"shareBoard.confirm-change-team-role.body": "此看板上所有低於\"{role}\"的人都將<b>被提升到{role}</b>。你確定要改變這個看板最低角色?",
|
||||
"shareBoard.confirm-change-team-role.confirmBtnText": "改變最小的看板規則",
|
||||
"shareBoard.confirm-change-team-role.title": "改變最小的看板規則",
|
||||
"shareBoard.confirm-link-channel": "連接看板到頻道",
|
||||
"shareBoard.confirm-link-channel-button": "連接頻道",
|
||||
"shareBoard.confirm-link-channel-button-with-other-channel": "解除連接或連接這",
|
||||
"shareBoard.confirm-link-channel-subtext": "當你連接頻道到看板,該頻道所有成員(包含新的與現有的)都可以編輯,不包括訪客",
|
||||
"shareBoard.confirm-link-channel-subtext": "當你連接頻道到看板,該頻道所有成員(包含新的與現有的)都可以編輯,不包括訪客。",
|
||||
"shareBoard.confirm-link-channel-subtext-with-other-channel": "當你將一個頻道連接到看板時,該頻道的所有成員(現有的和新的)都可以編輯。並不包含訪客身分{lineBreak}看板目前正連接到另一個頻道。如果选择在這裡連接它,將取消另一個連接。",
|
||||
"shareBoard.confirm-unlink.body": "當你取消頻道與看板連接,所有頻道成員(現在和新的)都將無法失去查看權限,除非單獨獲得許可。",
|
||||
"shareBoard.confirm-unlink.confirmBtnText": "解除連結頻道",
|
||||
"shareBoard.confirm-unlink.title": "從看板上取消頻道連接",
|
||||
"shareBoard.lastAdmin": "看板必須有一位管理者",
|
||||
"shareBoard.members-select-group": "會員",
|
||||
"shareBoard.unknown-channel-display-name": "未知管道",
|
||||
"tutorial_tip.finish_tour": "完成",
|
||||
"tutorial_tip.got_it": "了解",
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Utils} from './utils'
|
||||
import {Card} from './blocks/card'
|
||||
import {IPropertyTemplate, IPropertyOption, BoardGroup} from './blocks/board'
|
||||
|
||||
@ -17,7 +16,7 @@ function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty
|
||||
}
|
||||
groups.push(group)
|
||||
} else {
|
||||
Utils.logError(`groupCardsByOptions: Missing option with id: ${optionId}`)
|
||||
// if optionId not found, its an old (deleted) option that can be ignored
|
||||
}
|
||||
} else {
|
||||
// Empty group
|
||||
|
@ -280,6 +280,10 @@ describe('src/cardFilter', () => {
|
||||
}
|
||||
|
||||
test('verify isBefore clause', () => {
|
||||
const filterClauseIsBeforeEmpty = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: []})
|
||||
const resulta = CardFilter.isClauseMet(filterClauseIsBeforeEmpty, [template], dateCard)
|
||||
expect(resulta).toBeTruthy()
|
||||
|
||||
const filterClauseIsBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [beforeRange.toString()]})
|
||||
const result = CardFilter.isClauseMet(filterClauseIsBefore, [template], dateCard)
|
||||
expect(result).toBeFalsy()
|
||||
@ -294,6 +298,10 @@ describe('src/cardFilter', () => {
|
||||
})
|
||||
|
||||
test('verify isAfter clauses', () => {
|
||||
const filterClauseIsAfterEmpty = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: []})
|
||||
const resulta = CardFilter.isClauseMet(filterClauseIsAfterEmpty, [template], dateCard)
|
||||
expect(resulta).toBeTruthy()
|
||||
|
||||
const filterClauseIsAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [afterRange.toString()]})
|
||||
const result = CardFilter.isClauseMet(filterClauseIsAfter, [template], dateCard)
|
||||
expect(result).toBeFalsy()
|
||||
@ -308,6 +316,10 @@ describe('src/cardFilter', () => {
|
||||
})
|
||||
|
||||
test('verify is clause', () => {
|
||||
const filterClauseIsEmpty = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: []})
|
||||
const resulta = CardFilter.isClauseMet(filterClauseIsEmpty, [template], dateCard)
|
||||
expect(resulta).toBeTruthy()
|
||||
|
||||
const filterClauseIsBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [beforeRange.toString()]})
|
||||
const result = CardFilter.isClauseMet(filterClauseIsBefore, [template], dateCard)
|
||||
expect(result).toBeFalsy()
|
||||
|
@ -175,6 +175,9 @@ class CardFilter {
|
||||
return !(value as string || '').endsWith(filter.values[0]?.toLowerCase())
|
||||
}
|
||||
case 'isBefore': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
if (dateValue !== undefined) {
|
||||
const numericFilter = parseInt(filter.values[0], 10)
|
||||
if (template && (template.type === 'createdTime' || template.type === 'updatedTime')) {
|
||||
@ -192,6 +195,9 @@ class CardFilter {
|
||||
return false
|
||||
}
|
||||
case 'isAfter': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
if (dateValue !== undefined) {
|
||||
const numericFilter = parseInt(filter.values[0], 10)
|
||||
if (template && (template.type === 'createdTime' || template.type === 'updatedTime')) {
|
||||
|
@ -94,6 +94,7 @@ const GlobalHeaderSettingsMenu = (props: Props) => {
|
||||
name={intl.formatMessage({id: 'Sidebar.random-icons', defaultMessage: 'Random icons'})}
|
||||
isOn={randomIcons}
|
||||
onClick={async () => toggleRandomIcons()}
|
||||
suppressItemClicked={true}
|
||||
/>
|
||||
{me?.is_guest !== true &&
|
||||
<Menu.Text
|
||||
|
@ -163,6 +163,7 @@ const SidebarSettingsMenu = (props: Props) => {
|
||||
name={intl.formatMessage({id: 'Sidebar.random-icons', defaultMessage: 'Random icons'})}
|
||||
isOn={randomIcons}
|
||||
onClick={async () => toggleRandomIcons()}
|
||||
suppressItemClicked={true}
|
||||
/>
|
||||
</Menu>
|
||||
</MenuWrapper>
|
||||
|
@ -124,7 +124,7 @@
|
||||
</ul>
|
||||
<div class="secondary-footer__copy">
|
||||
<small class="disclaimer"><span class="mm-copyright" style="margin-right: 1rem;">© Mattermost, Inc.
|
||||
2022.</span> <span><a href="https://mattermost.com/terms-of-use/" title="Terms of Service">Terms
|
||||
2023.</span> <span><a href="https://mattermost.com/terms-of-use/" title="Terms of Service">Terms
|
||||
of Use</a> <span style="margin: 0 0.5rem;">|</span> <a
|
||||
href="https://mattermost.com/privacy-policy/" title="Privacy Policy">Privacy Policy</a> <span
|
||||
style="margin: 0 0.5rem;">|</span> <a href="https://mattermost.com/privacy-policy/cookies/"
|
||||
@ -186,4 +186,4 @@
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
|
||||
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="{{ "js/tabs.js" | absURL }}"></script>
|
||||
<script src="{{ "js/main.js" | absURL }}"></script>
|
||||
<script src="{{ "js/main.js" | absURL }}"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user