From 3fa0e082191654156279cb8349c6f5e1f696b4c9 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Wed, 22 Mar 2023 13:28:00 -0600 Subject: [PATCH 01/11] Fix category dialog (#4657) * test merge * update Dialog color for SidebarCategory * add semicolon * use the lowercase dialog class --- webapp/src/components/sidebar/sidebarCategory.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webapp/src/components/sidebar/sidebarCategory.scss b/webapp/src/components/sidebar/sidebarCategory.scss index 62eb4fb3e..24bdde230 100644 --- a/webapp/src/components/sidebar/sidebarCategory.scss +++ b/webapp/src/components/sidebar/sidebarCategory.scss @@ -6,6 +6,10 @@ margin-top: 0; } + .dialog { + color: rgba(var(--center-channel-color-rgb)); + } + .octo-sidebar-item { display: flex; flex-direction: row; From d8e1fb4832953fb31c54d4bc3542e2e4d4fe0fb7 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Wed, 22 Mar 2023 13:28:23 -0600 Subject: [PATCH 02/11] copy safe writeFileResponse into personal server/desktop (#4654) * copy safe writeFileResponse into personal server/desktop * lint fix --------- Co-authored-by: Mattermost Build --- server/api/files.go | 91 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/server/api/files.go b/server/api/files.go index bb0ddcfc7..c2a6eaa71 100644 --- a/server/api/files.go +++ b/server/api/files.go @@ -8,6 +8,8 @@ import ( "errors" "io" "net/http" + "net/url" + "strconv" "strings" "time" @@ -20,9 +22,30 @@ import ( mmModel "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/shared/mlog" - "github.com/mattermost/mattermost-server/v6/shared/web" ) +var UnsafeContentTypes = [...]string{ + "application/javascript", + "application/ecmascript", + "text/javascript", + "text/ecmascript", + "application/x-javascript", + "text/html", +} + +var MediaContentTypes = [...]string{ + "image/jpeg", + "image/png", + "image/bmp", + "image/gif", + "image/tiff", + "video/avi", + "video/mpeg", + "video/mp4", + "audio/mpeg", + "audio/wav", +} + // FileUploadResponse is the response to a file upload // swagger:model type FileUploadResponse struct { @@ -145,10 +168,74 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { } defer fileReader.Close() - web.WriteFileResponse(filename, fileInfo.MimeType, fileInfo.Size, time.Now(), "", fileReader, false, w, r) + mimeType := "" + var fileSize int64 + if fileInfo != nil { + mimeType = fileInfo.MimeType + fileSize = fileInfo.Size + } + writeFileResponse(filename, mimeType, fileSize, time.Now(), "", fileReader, false, w, r) auditRec.Success() } +func writeFileResponse(filename string, contentType string, contentSize int64, + lastModification time.Time, webserverMode string, fileReader io.ReadSeeker, forceDownload bool, w http.ResponseWriter, r *http.Request) { + w.Header().Set("Cache-Control", "private, no-cache") + w.Header().Set("X-Content-Type-Options", "nosniff") + + if contentSize > 0 { + contentSizeStr := strconv.Itoa(int(contentSize)) + if webserverMode == "gzip" { + w.Header().Set("X-Uncompressed-Content-Length", contentSizeStr) + } else { + w.Header().Set("Content-Length", contentSizeStr) + } + } + + if contentType == "" { + contentType = "application/octet-stream" + } else { + for _, unsafeContentType := range UnsafeContentTypes { + if strings.HasPrefix(contentType, unsafeContentType) { + contentType = "text/plain" + break + } + } + } + + w.Header().Set("Content-Type", contentType) + + var toDownload bool + if forceDownload { + toDownload = true + } else { + isMediaType := false + + for _, mediaContentType := range MediaContentTypes { + if strings.HasPrefix(contentType, mediaContentType) { + isMediaType = true + break + } + } + + toDownload = !isMediaType + } + + filename = url.PathEscape(filename) + + if toDownload { + w.Header().Set("Content-Disposition", "attachment;filename=\""+filename+"\"; filename*=UTF-8''"+filename) + } else { + w.Header().Set("Content-Disposition", "inline;filename=\""+filename+"\"; filename*=UTF-8''"+filename) + } + + // prevent file links from being embedded in iframes + w.Header().Set("X-Frame-Options", "DENY") + w.Header().Set("Content-Security-Policy", "Frame-ancestors 'none'") + + http.ServeContent(w, r, filename, lastModification, fileReader) +} + func (a *API) getFileInfo(w http.ResponseWriter, r *http.Request) { // swagger:operation GET /files/teams/{teamID}/{boardID}/{filename}/info getFile // From a1df107b249d7e28efe7850597e0e3e5051a0ba2 Mon Sep 17 00:00:00 2001 From: Filipe Date: Wed, 22 Mar 2023 12:03:54 +0100 Subject: [PATCH 03/11] Translated using Weblate (Portuguese) Currently translated at 7.5% (34 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/pt/ Translated using Weblate (Portuguese) Currently translated at 7.3% (33 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/pt/ --- webapp/i18n/pt.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/pt.json b/webapp/i18n/pt.json index 54ffe071d..975ddbc47 100644 --- a/webapp/i18n/pt.json +++ b/webapp/i18n/pt.json @@ -2,11 +2,35 @@ "AppBar.Tooltip": "Alternar quadros vinculados", "Attachment.Attachment-title": "Anexo", "AttachmentBlock.DeleteAction": "Apagar", - "AttachmentBlock.addElement": "Adicionar {tipo}", + "AttachmentBlock.addElement": "Adicionar {type}", "AttachmentBlock.delete": "Anexo apagado.", "AttachmentBlock.failed": "Este arquivo não pôde ser carregado pois ultrapassou o tamanho limite.", "AttachmentBlock.upload": "Carregando anexo.", "AttachmentBlock.uploadSuccess": "Anexo carregado.", "AttachmentElement.delete-confirmation-dialog-button-text": "Apagar", - "AttachmentElement.download": "Baixar" + "AttachmentElement.download": "Baixar", + "BoardComponent.delete": "Apagar", + "BoardComponent.hidden-columns": "Colunas escondidas", + "BoardComponent.hide": "Esconder", + "BoardComponent.new": "+ Novo", + "BoardComponent.no-property": "Não {property}", + "BoardComponent.show": "Mostrar", + "BoardMember.schemeAdmin": "Admin", + "BoardMember.schemeCommenter": "Comentador", + "BoardMember.schemeEditor": "Editor", + "BoardMember.schemeNone": "Nenhum", + "BoardPage.newVersion": "Está disponível uma nova versão do Boards, clique aqui para recarregar.", + "BoardPage.syncFailed": "O Board pode ter sido apagado ou o acesso revogado.", + "BoardTemplateSelector.add-template": "Criar novo modelo", + "BoardTemplateSelector.create-empty-board": "Criar um board vazio", + "BoardTemplateSelector.delete-template": "Apagar", + "BoardTemplateSelector.edit-template": "Editar", + "BoardTemplateSelector.plugin.no-content-title": "Criar um board", + "BoardTemplateSelector.title": "Criar um board", + "BoardTemplateSelector.use-this-template": "Usar este modelo", + "BoardsSwitcher.Title": "Encontrar boards", + "Calculations.Options.average.label": "Média", + "shareBoard.members-select-group": "Membros", + "shareBoard.unknown-channel-display-name": "Canal desconhecido", + "tutorial_tip.ok": "Próximo" } From ec759ac17a88324e1e72cf43ff192802d56dd452 Mon Sep 17 00:00:00 2001 From: Kaya Zeren Date: Wed, 22 Mar 2023 12:03:55 +0100 Subject: [PATCH 04/11] Translated using Weblate (Turkish) Currently translated at 100.0% (450 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/tr/ --- webapp/i18n/tr.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webapp/i18n/tr.json b/webapp/i18n/tr.json index 406721992..b3d14b065 100644 --- a/webapp/i18n/tr.json +++ b/webapp/i18n/tr.json @@ -221,7 +221,7 @@ "PropertyType.UpdatedTime": "Son güncelleme zamanı", "PropertyType.Url": "Adres", "PropertyValueElement.empty": "Boş", - "RegistrationLink.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. Devam etmek istiyor musunuz?", + "RegistrationLink.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. İlerlemek istiyor musunuz?", "RegistrationLink.copiedLink": "Kopyalandı!", "RegistrationLink.copyLink": "Bağlantıyı kopyala", "RegistrationLink.description": "Başkalarının hesap ekleyebilmesi için bu bağlantıyı paylaş:", @@ -232,7 +232,7 @@ "ShareBoard.ShareInternal": "İçeride paylaş", "ShareBoard.ShareInternalDescription": "İzni olan kullanıcılar bu bağlantıyı kullanabilecek.", "ShareBoard.Title": "Panoyu paylaş", - "ShareBoard.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. Devam etmek istiyor musunuz?", + "ShareBoard.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. İlerlemek istiyor musunuz?", "ShareBoard.copiedLink": "Kopyalandı!", "ShareBoard.copyLink": "Bağlantıyı kopyala", "ShareBoard.regenerate": "Kodu yeniden oluştur", @@ -395,6 +395,10 @@ "login.log-in-button": "Oturum aç", "login.log-in-title": "Oturum açın", "login.register-button": "ya da hesabınız yoksa bir hesap açın", + "new_channel_modal.create_board.empty_board_description": "Yeni boş bir pano oluştur", + "new_channel_modal.create_board.empty_board_title": "Boş pano", + "new_channel_modal.create_board.select_template_placeholder": "Bir kalıp seçin", + "new_channel_modal.create_board.title": "Bu kanal için bir pano oluştur", "notification-box-card-limit-reached.close-tooltip": "10 gün için sustur", "notification-box-card-limit-reached.contact-link": "yöneticinizi bilgilendirin", "notification-box-card-limit-reached.link": "Ücretli bir tarifeye geçin", From b3469c645c21be5a773716457fde5e66fc8138e8 Mon Sep 17 00:00:00 2001 From: Felipe Nogueira Date: Wed, 22 Mar 2023 12:03:55 +0100 Subject: [PATCH 05/11] Translated using Weblate (Portuguese (Brazil)) Currently translated at 96.4% (434 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/pt_BR/ --- webapp/i18n/pt_BR.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/webapp/i18n/pt_BR.json b/webapp/i18n/pt_BR.json index aa755dc2f..8e984b4b5 100644 --- a/webapp/i18n/pt_BR.json +++ b/webapp/i18n/pt_BR.json @@ -101,7 +101,7 @@ "CardDetailProperty.property-name-change-subtext": "digite de \"{oldPropType}\" para \"{newPropType}\"", "CardDetial.limited-link": "Saiba mais sobre nossos planos.", "CardDialog.delete-confirmation-dialog-button-text": "Excluir", - "CardDialog.delete-confirmation-dialog-heading": "Confirmar exclusão do card!", + "CardDialog.delete-confirmation-dialog-heading": "Confirmar exclusão do cartão", "CardDialog.editing-template": "Você está editando um template.", "CardDialog.nocard": "Esse card não existe ou não está acessível.", "Categories.CreateCategoryDialog.CancelText": "Cancelar", @@ -163,6 +163,7 @@ "FilterByText.placeholder": "filtrar texto", "FilterComponent.add-filter": "+ Adicionar filtro", "FilterComponent.delete": "Excluir", + "FilterValue.empty": "(vazio)", "FindBoardsDialog.IntroText": "Procurar por quadros", "FindBoardsDialog.NoResultsFor": "Sem resultado para \"{searchQuery}\"", "FindBoardsDialog.NoResultsSubtext": "Verifique a digitação ou tente outra busca.", @@ -271,6 +272,7 @@ "SidebarTour.SidebarCategories.Link": "Saiba mais", "SidebarTour.SidebarCategories.Title": "Categorias de barra lateral", "SiteStats.total_boards": "Total de boards", + "SiteStats.total_cards": "Total de cartões", "TableComponent.add-icon": "Adicionar Ícone", "TableComponent.name": "Nome", "TableComponent.plus-new": "+ Novo", @@ -368,7 +370,7 @@ "createImageBlock.failed": "Não foi possível enviar o arquivo. Limite de tamanho alcançado.", "default-properties.badges": "Comentários e descrição", "default-properties.title": "Título", - "error.back-to-home": "Volta para Home", + "error.back-to-home": "Volta para o início", "error.back-to-team": "Volta para o time", "error.board-not-found": "Quadro não encontrado.", "error.go-login": "Log in", @@ -385,7 +387,10 @@ "login.log-in-button": "Entrar", "login.log-in-title": "Entrar", "login.register-button": "ou criar uma conta se você ainda não tiver uma", + "new_channel_modal.create_board.empty_board_description": "Criar um novo quadro vazio", + "new_channel_modal.create_board.empty_board_title": "Quadro vazio", "new_channel_modal.create_board.select_template_placeholder": "Selecionar um modelo", + "new_channel_modal.create_board.title": "Criar um quadro para este canal", "notification-box-card-limit-reached.close-tooltip": "Soneca por 10 dias", "notification-box-card-limit-reached.contact-link": "notificar seu admin", "notification-box-card-limit-reached.link": "Atualizar para um plano pago", @@ -400,11 +405,11 @@ "person.add-user-to-board-warning": "{username} não é um membro de um board, e não será notificado.", "register.login-button": "ou entre se você já tem uma conta", "register.signup-title": "Registrar uma conta", - "rhs-board-non-admin-msg": "Você não é admin de um board", + "rhs-board-non-admin-msg": "Você não é um adminstrador do quadro", "rhs-boards.add": "Adicionar", "rhs-boards.dm": "DM", "rhs-boards.gm": "GM", - "rhs-boards.header.dm": "esta Direct Message", + "rhs-boards.header.dm": "esta mensagem direta", "rhs-boards.header.gm": "esta mensagem de grupo", "rhs-boards.last-update-at": "Última atualização em: {datetime}", "rhs-boards.link-boards-to-channel": "Vincular boards para {channelName}", From 1fff221da9e1ec5d6862aa3a3782eee2c77c78c8 Mon Sep 17 00:00:00 2001 From: Miguel de la Cruz Date: Mon, 27 Mar 2023 13:56:39 +0200 Subject: [PATCH 06/11] Adds escaping when normalizing table names for MySQL on DB helpers (#4653) Co-authored-by: Mattermost Build --- server/services/store/sqlstore/migrate.go | 32 +++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/server/services/store/sqlstore/migrate.go b/server/services/store/sqlstore/migrate.go index d3fd7e3c5..da48b3a5c 100644 --- a/server/services/store/sqlstore/migrate.go +++ b/server/services/store/sqlstore/migrate.go @@ -319,7 +319,7 @@ func (s *SQLStore) GetTemplateHelperFuncs() template.FuncMap { func (s *SQLStore) genAddColumnIfNeeded(tableName, columnName, datatype, constraint string) (string, error) { tableName = addPrefixIfNeeded(tableName, s.tablePrefix) - normTableName := normalizeTablename(s.schemaName, tableName) + normTableName := s.normalizeTablename(tableName) switch s.dbType { case model.SqliteDBType: @@ -358,7 +358,7 @@ func (s *SQLStore) genAddColumnIfNeeded(tableName, columnName, datatype, constra func (s *SQLStore) genDropColumnIfNeeded(tableName, columnName string) (string, error) { tableName = addPrefixIfNeeded(tableName, s.tablePrefix) - normTableName := normalizeTablename(s.schemaName, tableName) + normTableName := s.normalizeTablename(tableName) switch s.dbType { case model.SqliteDBType: @@ -395,7 +395,7 @@ func (s *SQLStore) genDropColumnIfNeeded(tableName, columnName string) (string, func (s *SQLStore) genCreateIndexIfNeeded(tableName, columns string) (string, error) { indexName := getIndexName(tableName, columns) tableName = addPrefixIfNeeded(tableName, s.tablePrefix) - normTableName := normalizeTablename(s.schemaName, tableName) + normTableName := s.normalizeTablename(tableName) switch s.dbType { case model.SqliteDBType: @@ -435,7 +435,7 @@ func (s *SQLStore) genRenameTableIfNeeded(oldTableName, newTableName string) (st oldTableName = addPrefixIfNeeded(oldTableName, s.tablePrefix) newTableName = addPrefixIfNeeded(newTableName, s.tablePrefix) - normOldTableName := normalizeTablename(s.schemaName, oldTableName) + normOldTableName := s.normalizeTablename(oldTableName) vars := map[string]string{ "schema": s.schemaName, @@ -466,14 +466,14 @@ func (s *SQLStore) genRenameTableIfNeeded(oldTableName, newTableName string) (st case model.PostgresDBType: return replaceVars(` do $$ - begin + begin if (SELECT COUNT(table_name) FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '[[new_table_name]]' AND table_schema = '[[schema]]' - ) = 0 then + ) = 0 then ALTER TABLE [[norm_old_table_name]] RENAME TO [[new_table_name]]; end if; - end$$; + end$$; `, vars), nil default: return "", ErrUnsupportedDatabaseType @@ -482,7 +482,7 @@ func (s *SQLStore) genRenameTableIfNeeded(oldTableName, newTableName string) (st func (s *SQLStore) genRenameColumnIfNeeded(tableName, oldColumnName, newColumnName, dataType string) (string, error) { tableName = addPrefixIfNeeded(tableName, s.tablePrefix) - normTableName := normalizeTablename(s.schemaName, tableName) + normTableName := s.normalizeTablename(tableName) vars := map[string]string{ "schema": s.schemaName, @@ -516,15 +516,15 @@ func (s *SQLStore) genRenameColumnIfNeeded(tableName, oldColumnName, newColumnNa case model.PostgresDBType: return replaceVars(` do $$ - begin + begin if (SELECT COUNT(table_name) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '[[table_name]]' AND table_schema = '[[schema]]' AND column_name = '[[new_column_name]]' - ) = 0 then + ) = 0 then ALTER TABLE [[norm_table_name]] RENAME COLUMN [[old_column_name]] TO [[new_column_name]]; end if; - end$$; + end$$; `, vars), nil default: return "", ErrUnsupportedDatabaseType @@ -620,7 +620,7 @@ func (s *SQLStore) doesColumnExist(tableName, columnName string) (bool, error) { func (s *SQLStore) genAddConstraintIfNeeded(tableName, constraintName, constraintType, constraintDefinition string) (string, error) { tableName = addPrefixIfNeeded(tableName, s.tablePrefix) - normTableName := normalizeTablename(s.schemaName, tableName) + normTableName := s.normalizeTablename(tableName) var query string @@ -686,8 +686,12 @@ func addPrefixIfNeeded(s, prefix string) string { return s } -func normalizeTablename(schemaName, tableName string) string { - if schemaName != "" && !strings.HasPrefix(tableName, schemaName+".") { +func (s *SQLStore) normalizeTablename(tableName string) string { + if s.schemaName != "" && !strings.HasPrefix(tableName, s.schemaName+".") { + schemaName := s.schemaName + if s.dbType == model.MysqlDBType { + schemaName = "`" + schemaName + "`" + } tableName = schemaName + "." + tableName } return tableName From d7af2be765b169008bca81df0fc89a95c42f8626 Mon Sep 17 00:00:00 2001 From: Miguel de la Cruz Date: Mon, 27 Mar 2023 23:47:32 +0200 Subject: [PATCH 07/11] Test fixing main after mono-repo (#4669) --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/dev-release.yml | 28 ++++++++++++++-------------- .github/workflows/lint-server.yml | 2 +- .github/workflows/prod-release.yml | 24 ++++++++++++------------ 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 783c58582..1e15579f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: repository: "mattermost/mattermost-server" fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Set up Go uses: actions/setup-go@v3 with: @@ -74,7 +74,7 @@ jobs: repository: "mattermost/mattermost-server" fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: npm ci run: | cd focalboard/webapp && npm ci && cd - @@ -132,7 +132,7 @@ jobs: repository: "mattermost/mattermost-server" fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Set up Go uses: actions/setup-go@v3 @@ -169,7 +169,7 @@ jobs: repository: "mattermost/mattermost-server" fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Set up Go uses: actions/setup-go@v3 diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 9ebf34df6..e3ad2ed85 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -8,14 +8,14 @@ on: workflow_dispatch: env: - BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} EXCLUDE_ENTERPRISE: true jobs: ubuntu: runs-on: ubuntu-18.04 - + steps: - uses: actions/checkout@v3 with: @@ -25,16 +25,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go @@ -101,16 +101,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go @@ -159,16 +159,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go @@ -229,16 +229,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go diff --git a/.github/workflows/lint-server.yml b/.github/workflows/lint-server.yml index 3c114e71b..5067a91f0 100644 --- a/.github/workflows/lint-server.yml +++ b/.github/workflows/lint-server.yml @@ -48,7 +48,7 @@ jobs: repository: "mattermost/mattermost-server" fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: set up golangci-lint run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.1 - name: lint diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 8ca6f4f98..051234c4a 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -21,16 +21,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go @@ -97,16 +97,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go @@ -156,16 +156,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go @@ -228,16 +228,16 @@ jobs: continue-on-error: true with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" ref: ${{ env.BRANCH_NAME }} - uses: actions/checkout@v3 if: steps.mattermostServer.outcome == 'failure' with: repository: "mattermost/mattermost-server" - fetch-depth: "20" + fetch-depth: "20" path: "mattermost-server" - ref : "master" + ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1" - name: Replace token 1 server run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go From 01b58cb45ac8d234f41fddd0733b4aa1d9788abd Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Wed, 29 Mar 2023 04:13:18 -0600 Subject: [PATCH 08/11] update ubuntu to latest (#4671) --- .github/workflows/ci.yml | 4 ++-- .github/workflows/dev-release.yml | 5 ++--- .github/workflows/lint-server.yml | 4 ++-- .github/workflows/prod-release.yml | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e15579f3..93155b4a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ env: jobs: ci-ubuntu-server: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest strategy: matrix: @@ -54,7 +54,7 @@ jobs: run: cd focalboard; make server-test-${{matrix['db']}} ci-ubuntu-webapp: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index e3ad2ed85..da022b71f 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -14,8 +14,7 @@ env: jobs: ubuntu: - runs-on: ubuntu-18.04 - + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: @@ -218,7 +217,7 @@ jobs: path: ${{ github.workspace }}/focalboard/win-wpf/dist/focalboard-win.zip plugin: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/lint-server.yml b/.github/workflows/lint-server.yml index 5067a91f0..3e505f1cd 100644 --- a/.github/workflows/lint-server.yml +++ b/.github/workflows/lint-server.yml @@ -13,7 +13,7 @@ env: jobs: down-migrations: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: @@ -26,7 +26,7 @@ jobs: golangci: name: plugin - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/setup-go@v3 with: diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 051234c4a..781843a68 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -9,7 +9,7 @@ env: jobs: ubuntu: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout @@ -216,7 +216,7 @@ jobs: path: ${{ github.workspace }}/focalboard/win-wpf/dist/focalboard-win.zip plugin-release: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout From 0192ef6f3f51dbebb4e212c24ae6e5fb2423f11d Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Wed, 29 Mar 2023 16:27:18 -0600 Subject: [PATCH 09/11] only send category updates to user (#4672) * only send category updates to user * remove unused function * Revert "remove unused function" This reverts commit 8c4fc9b2002635ea13c73cde31fb32845eca8cb2. * remove unused function * fix test --------- Co-authored-by: Mattermost Build --- server/ws/server.go | 64 +++++++++++++++------------------------- server/ws/server_test.go | 42 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/server/ws/server.go b/server/ws/server.go index 9df1b1f6e..dfa4af8a1 100644 --- a/server/ws/server.go +++ b/server/ws/server.go @@ -465,10 +465,15 @@ func (ws *Server) getListenersForBlock(blockID string) []*websocketSession { return ws.listenersByBlock[blockID] } -// getListenersForTeam returns the listeners subscribed to a +// getListenersForUser returns the listener for a user subscribed to a // team changes. -func (ws *Server) getListenersForTeam(teamID string) []*websocketSession { - return ws.listenersByTeam[teamID] +func (ws *Server) getListenerForUser(teamID, userID string) *websocketSession { + for _, listener := range ws.listenersByTeam[teamID] { + if listener.userID == userID { + return listener + } + } + return nil } // getListenersForTeamAndBoard returns the listeners subscribed to a @@ -567,16 +572,10 @@ func (ws *Server) BroadcastCategoryChange(category model.Category) { Category: &category, } - listeners := ws.getListenersForTeam(category.TeamID) - ws.logger.Debug("listener(s) for teamID", - mlog.Int("listener_count", len(listeners)), - mlog.String("teamID", category.TeamID), - mlog.String("categoryID", category.ID), - ) - - for _, listener := range listeners { - ws.logger.Debug("Broadcast block change", - mlog.Int("listener_count", len(listeners)), + listener := ws.getListenerForUser(category.TeamID, category.UserID) + if listener != nil { + ws.logger.Debug("Broadcast category change", + mlog.String("userID", category.UserID), mlog.String("teamID", category.TeamID), mlog.String("categoryID", category.ID), mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()), @@ -596,15 +595,10 @@ func (ws *Server) BroadcastCategoryReorder(teamID, userID string, categoryOrder TeamID: teamID, } - listeners := ws.getListenersForTeam(teamID) - ws.logger.Debug("listener(s) for teamID", - mlog.Int("listener_count", len(listeners)), - mlog.String("teamID", teamID), - ) - - for _, listener := range listeners { + listener := ws.getListenerForUser(teamID, userID) + if listener != nil { ws.logger.Debug("Broadcast category order change", - mlog.Int("listener_count", len(listeners)), + mlog.String("userID", userID), mlog.String("teamID", teamID), mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()), ) @@ -624,21 +618,17 @@ func (ws *Server) BroadcastCategoryBoardsReorder(teamID, userID, categoryID stri TeamID: teamID, } - listeners := ws.getListenersForTeam(teamID) - ws.logger.Debug("listener(s) for teamID", - mlog.Int("listener_count", len(listeners)), - mlog.String("teamID", teamID), - ) - - for _, listener := range listeners { + listener := ws.getListenerForUser(teamID, userID) + if listener != nil { ws.logger.Debug("Broadcast board category order change", - mlog.Int("listener_count", len(listeners)), + mlog.String("userID", userID), mlog.String("teamID", teamID), + mlog.String("categoryID", categoryID), mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()), ) if err := listener.WriteJSON(message); err != nil { - ws.logger.Error("broadcast category order change error", mlog.Err(err)) + ws.logger.Error("broadcast category boards order change error", mlog.Err(err)) listener.conn.Close() } } @@ -651,16 +641,10 @@ func (ws *Server) BroadcastCategoryBoardChange(teamID, userID string, boardCateg BoardCategories: boardCategories, } - listeners := ws.getListenersForTeam(teamID) - ws.logger.Debug("listener(s) for teamID", - mlog.Int("listener_count", len(listeners)), - mlog.String("teamID", teamID), - mlog.Int("numEntries", len(boardCategories)), - ) - - for _, listener := range listeners { - ws.logger.Debug("Broadcast block change", - mlog.Int("listener_count", len(listeners)), + listener := ws.getListenerForUser(teamID, userID) + if listener != nil { + ws.logger.Debug("Broadcast category board change", + mlog.String("userID", userID), mlog.String("teamID", teamID), mlog.Int("numEntries", len(boardCategories)), mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()), diff --git a/server/ws/server_test.go b/server/ws/server_test.go index ba56bacc1..fa728e2cd 100644 --- a/server/ws/server_test.go +++ b/server/ws/server_test.go @@ -101,6 +101,48 @@ func TestTeamSubscription(t *testing.T) { require.Empty(t, server.listenersByTeam[teamID]) require.Empty(t, server.listenersByTeam[teamID2]) }) + + t.Run("Subscribe users to team retrieve by user", func(t *testing.T) { + userID1 := "fake-user-id" + userSession1 := &websocketSession{ + conn: &websocket.Conn{}, + mu: sync.Mutex{}, + userID: userID1, + teams: []string{}, + blocks: []string{}, + } + userID2 := "fake-user-id2" + userSession2 := &websocketSession{ + conn: &websocket.Conn{}, + mu: sync.Mutex{}, + userID: userID2, + teams: []string{}, + blocks: []string{}, + } + teamID := "fake-team-id" + + server.addListener(session) + server.subscribeListenerToTeam(session, teamID) + server.addListener(userSession1) + server.subscribeListenerToTeam(userSession1, teamID) + server.addListener(userSession2) + server.subscribeListenerToTeam(userSession2, teamID) + + require.Len(t, server.listeners, 3) + require.Len(t, server.listenersByTeam[teamID], 3) + + listener := server.getListenerForUser(teamID, userID1) + require.NotNil(t, listener) + require.Equal(t, listener.userID, userID1) + + server.removeListener(session) + server.removeListener(userSession1) + server.removeListener(userSession2) + + require.Empty(t, server.listeners) + require.Empty(t, server.listenersByTeam[teamID]) + require.Empty(t, server.getListenerForUser(teamID, userID1)) + }) } func TestBlocksSubscription(t *testing.T) { From 263492415037606da1c59250306a50b487f1517d Mon Sep 17 00:00:00 2001 From: Harshil Sharma <18575143+harshilsharma63@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:20:50 +0530 Subject: [PATCH 10/11] Cherrypicking 22606 to old focalboard (#4673) --- .../store/sqlstore/data_migrations.go | 6 ++-- .../migrationstests/boards_migrator_test.go | 3 ++ ...uplicate_category_boards_migration_test.go | 28 +++++++++++++++++++ .../fixtures/test33_with_deleted_data.sql | 6 ++-- .../fixtures/test33_with_no_deleted_data.sql | 6 ++-- .../fixtures/test37_valid_data.sql | 6 ++-- .../test37_valid_data_no_hidden_boards.sql | 6 ++-- ...id_data_preference_but_no_hidden_board.sql | 6 ++-- .../fixtures/test37_valid_data_sqlite.sql | 6 ++-- ..._sqlite_preference_but_no_hidden_board.sql | 6 ++-- ...testDeDuplicateCategoryBoardsMigration.sql | 9 ++++++ 11 files changed, 70 insertions(+), 18 deletions(-) create mode 100644 server/services/store/sqlstore/migrationstests/de_duplicate_category_boards_migration_test.go create mode 100644 server/services/store/sqlstore/migrationstests/fixtures/testDeDuplicateCategoryBoardsMigration.sql diff --git a/server/services/store/sqlstore/data_migrations.go b/server/services/store/sqlstore/data_migrations.go index bbc7e2218..1794965ca 100644 --- a/server/services/store/sqlstore/data_migrations.go +++ b/server/services/store/sqlstore/data_migrations.go @@ -868,10 +868,8 @@ func (s *SQLStore) doesDuplicateCategoryBoardsExist() (bool, error) { } func (s *SQLStore) runMySQLDeDuplicateCategoryBoardsMigration() error { - query := "WITH duplicates AS (SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, board_id) AS rownum " + - "FROM " + s.tablePrefix + "category_boards) " + - "DELETE " + s.tablePrefix + "category_boards FROM " + s.tablePrefix + "category_boards " + - "JOIN duplicates USING(id) WHERE duplicates.rownum > 1;" + query := "DELETE FROM " + s.tablePrefix + "category_boards WHERE id NOT IN " + + "(SELECT * FROM ( SELECT min(id) FROM " + s.tablePrefix + "category_boards GROUP BY user_id, board_id ) as data)" if _, err := s.db.Exec(query); err != nil { s.logger.Error("Failed to de-duplicate data in category_boards table", mlog.Err(err)) } diff --git a/server/services/store/sqlstore/migrationstests/boards_migrator_test.go b/server/services/store/sqlstore/migrationstests/boards_migrator_test.go index 9ec8ea3b2..2808d96fa 100644 --- a/server/services/store/sqlstore/migrationstests/boards_migrator_test.go +++ b/server/services/store/sqlstore/migrationstests/boards_migrator_test.go @@ -246,6 +246,9 @@ func (bm *BoardsMigrator) MigrateToStep(step int) error { func (bm *BoardsMigrator) Interceptors() map[int]foundation.Interceptor { return map[int]foundation.Interceptor{ 18: bm.store.RunDeletedMembershipBoardsMigration, + 35: func() error { + return bm.store.RunDeDuplicateCategoryBoardsMigration(35) + }, } } diff --git a/server/services/store/sqlstore/migrationstests/de_duplicate_category_boards_migration_test.go b/server/services/store/sqlstore/migrationstests/de_duplicate_category_boards_migration_test.go new file mode 100644 index 000000000..863cb43ff --- /dev/null +++ b/server/services/store/sqlstore/migrationstests/de_duplicate_category_boards_migration_test.go @@ -0,0 +1,28 @@ +package migrationstests + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRunDeDuplicateCategoryBoardsMigration(t *testing.T) { + th, tearDown := SetupTestHelper(t) + defer tearDown() + + if th.IsSQLite() { + t.Skip("SQLite is not supported for this") + } + + th.f.MigrateToStepSkippingLastInterceptor(35). + ExecFile("./fixtures/testDeDuplicateCategoryBoardsMigration.sql") + + th.f.RunInterceptor(35) + + // verifying count of rows + var count int + countQuery := "SELECT COUNT(*) FROM focalboard_category_boards" + row := th.f.DB().QueryRow(countQuery) + err := row.Scan(&count) + assert.NoError(t, err) + assert.Equal(t, 4, count) +} diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test33_with_deleted_data.sql b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_deleted_data.sql index c5593660b..f9e0b1a73 100644 --- a/server/services/store/sqlstore/migrationstests/fixtures/test33_with_deleted_data.sql +++ b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_deleted_data.sql @@ -1,6 +1,8 @@ -INSERT INTO focalboard_category_boards values +INSERT INTO focalboard_category_boards +(id, user_id, category_id, board_id, create_at, update_at, delete_at, sort_order) +values ('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0), ('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0), ('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 1672988834402, 0), ('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0), -('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 1672988834402, 0); \ No newline at end of file +('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 1672988834402, 0); diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test33_with_no_deleted_data.sql b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_no_deleted_data.sql index 5f4337bb2..5bc32a714 100644 --- a/server/services/store/sqlstore/migrationstests/fixtures/test33_with_no_deleted_data.sql +++ b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_no_deleted_data.sql @@ -1,6 +1,8 @@ -INSERT INTO focalboard_category_boards values +INSERT INTO focalboard_category_boards +(id, user_id, category_id, board_id, create_at, update_at, delete_at, sort_order) +values ('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0), ('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0), ('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 0, 0), ('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0), -('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 0, 0); \ No newline at end of file +('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 0, 0); diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data.sql index 8f75da315..552703ec6 100644 --- a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data.sql +++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data.sql @@ -1,4 +1,6 @@ -INSERT INTO focalboard_category_boards VALUES +INSERT INTO focalboard_category_boards +(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden) +VALUES ('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false), ('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false), ('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false), @@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES INSERT INTO Preferences VALUES ('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'), -('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]'); \ No newline at end of file +('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]'); diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_no_hidden_boards.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_no_hidden_boards.sql index 2986bad28..3b6508f5c 100644 --- a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_no_hidden_boards.sql +++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_no_hidden_boards.sql @@ -1,6 +1,8 @@ -INSERT INTO focalboard_category_boards VALUES +INSERT INTO focalboard_category_boards +(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden) +VALUES ('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false), ('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false), ('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false), ('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false), -('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false); \ No newline at end of file +('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false); diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_preference_but_no_hidden_board.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_preference_but_no_hidden_board.sql index 943932e54..75e127332 100644 --- a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_preference_but_no_hidden_board.sql +++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_preference_but_no_hidden_board.sql @@ -1,4 +1,6 @@ -INSERT INTO focalboard_category_boards VALUES +INSERT INTO focalboard_category_boards +(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden) +VALUES ('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false), ('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false), ('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false), @@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES INSERT INTO Preferences VALUES ('user-id-1', 'focalboard', 'hiddenBoardIDs', ''), -('user-id-2', 'focalboard', 'hiddenBoardIDs', ''); \ No newline at end of file +('user-id-2', 'focalboard', 'hiddenBoardIDs', ''); diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite.sql index 114273c30..deb74ccaf 100644 --- a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite.sql +++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite.sql @@ -1,4 +1,6 @@ -INSERT INTO focalboard_category_boards VALUES +INSERT INTO focalboard_category_boards +(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden) +VALUES ('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false), ('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false), ('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false), @@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES INSERT INTO focalboard_preferences VALUES ('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'), -('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]'); \ No newline at end of file +('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]'); diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql index 4b45cacdf..62dc2b708 100644 --- a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql +++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql @@ -1,4 +1,6 @@ -INSERT INTO focalboard_category_boards VALUES +INSERT INTO focalboard_category_boards +(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden) +VALUES ('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false), ('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false), ('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false), @@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES INSERT INTO focalboard_preferences VALUES ('user-id-1', 'focalboard', 'hiddenBoardIDs', ''), -('user-id-2', 'focalboard', 'hiddenBoardIDs', ''); \ No newline at end of file +('user-id-2', 'focalboard', 'hiddenBoardIDs', ''); diff --git a/server/services/store/sqlstore/migrationstests/fixtures/testDeDuplicateCategoryBoardsMigration.sql b/server/services/store/sqlstore/migrationstests/fixtures/testDeDuplicateCategoryBoardsMigration.sql new file mode 100644 index 000000000..69a7dc9bd --- /dev/null +++ b/server/services/store/sqlstore/migrationstests/fixtures/testDeDuplicateCategoryBoardsMigration.sql @@ -0,0 +1,9 @@ +INSERT INTO focalboard_category_boards(id, user_id, category_id, board_id, create_at, update_at, sort_order) +VALUES + ('id_1', 'user_id_1', 'category_id_1', 'board_id_1', 0, 0, 0), + ('id_2', 'user_id_1', 'category_id_2', 'board_id_1', 0, 0, 0), + ('id_3', 'user_id_1', 'category_id_3', 'board_id_1', 0, 0, 0), + ('id_4', 'user_id_2', 'category_id_4', 'board_id_2', 0, 0, 0), + ('id_5', 'user_id_2', 'category_id_5', 'board_id_2', 0, 0, 0), + ('id_6', 'user_id_3', 'category_id_6', 'board_id_3', 0, 0, 0), + ('id_7', 'user_id_4', 'category_id_6', 'board_id_4', 0, 0, 0); From 13c33acb7da5910c371262e9b4a18a498ad08b6a Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Thu, 30 Mar 2023 03:45:32 -0600 Subject: [PATCH 11/11] MM-51795 Remove free cloud server message (#4677) * remove free cloud server message * remove selector for cloud message * remove unused import --- webapp/i18n/en.json | 14 +- .../__snapshots__/cloudMessage.test.tsx.snap | 73 ------- .../src/components/messages/cloudMessage.scss | 38 ---- .../components/messages/cloudMessage.test.tsx | 190 ------------------ .../src/components/messages/cloudMessage.tsx | 114 ----------- .../components/messages/versionMessage.tsx | 2 +- webapp/src/pages/boardPage/boardPage.scss | 1 - webapp/src/pages/boardPage/boardPage.tsx | 2 - webapp/src/store/users.ts | 18 -- webapp/src/userSettings.ts | 9 - 10 files changed, 13 insertions(+), 448 deletions(-) delete mode 100644 webapp/src/components/messages/__snapshots__/cloudMessage.test.tsx.snap delete mode 100644 webapp/src/components/messages/cloudMessage.scss delete mode 100644 webapp/src/components/messages/cloudMessage.test.tsx delete mode 100644 webapp/src/components/messages/cloudMessage.tsx diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index b7a90daf5..6dd6746d6 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -1,5 +1,7 @@ { - "AppBar.Tooltip": "Toggle linked boards", + "AdminBadge.SystemAdmin": "Admin", + "AdminBadge.TeamAdmin": "Team Admin", + "AppBar.Tooltip": "Toggle Linked Boards", "Attachment.Attachment-title": "Attachment", "AttachmentBlock.DeleteAction": "delete", "AttachmentBlock.addElement": "add {type}", @@ -115,7 +117,6 @@ "CenterPanel.Login": "Login", "CenterPanel.Share": "Share", "ChannelIntro.CreateBoard": "Create a board", - "CloudMessage.cloud-server": "Get your own free cloud server.", "ColorOption.selectColor": "Select {color} Color", "Comment.delete": "Delete", "CommentsList.send": "Send", @@ -138,6 +139,7 @@ "ContentBlock.moveDown": "Move down", "ContentBlock.moveUp": "Move up", "ContentBlock.text": "text", + "DateFilter.empty": "Empty", "DateRange.clear": "Clear", "DateRange.empty": "Empty", "DateRange.endDate": "End date", @@ -156,10 +158,14 @@ "Filter.ends-with": "ends with", "Filter.includes": "includes", "Filter.is": "is", + "Filter.is-after": "is after", + "Filter.is-before": "is before", "Filter.is-empty": "is empty", "Filter.is-not-empty": "is not empty", "Filter.is-not-set": "is not set", "Filter.is-set": "is set", + "Filter.isafter": "is after", + "Filter.isbefore": "is before", "Filter.not-contains": "doesn't contain", "Filter.not-ends-with": "doesn't end with", "Filter.not-includes": "doesn't include", @@ -306,6 +312,7 @@ "ValueSelector.valueSelector": "Value selector", "ValueSelectorLabel.openMenu": "Open menu", "VersionMessage.help": "Check out what's new in this version.", + "VersionMessage.learn-more": "Learn more", "View.AddView": "Add view", "View.Board": "Board", "View.DeleteView": "Delete view", @@ -360,6 +367,9 @@ "WelcomePage.StartUsingIt.Text": "Start using it", "Workspace.editing-board-template": "You're editing a board template.", "badge.guest": "Guest", + "boardPage.confirm-join-button": "Join", + "boardPage.confirm-join-text": "You are about to join a private board without explicitly being added by the board admin. Are you sure you wish to join this private board?", + "boardPage.confirm-join-title": "Join private board", "boardSelector.confirm-link-board": "Link board to channel", "boardSelector.confirm-link-board-button": "Yes, link board", "boardSelector.confirm-link-board-subtext": "When you link \"{boardName}\" to the channel, all members of the channel (existing and new) will be able to edit it. This excludes members who are guests. You can unlink a board from a channel at any time.", diff --git a/webapp/src/components/messages/__snapshots__/cloudMessage.test.tsx.snap b/webapp/src/components/messages/__snapshots__/cloudMessage.test.tsx.snap deleted file mode 100644 index 66a121574..000000000 --- a/webapp/src/components/messages/__snapshots__/cloudMessage.test.tsx.snap +++ /dev/null @@ -1,73 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/messages/CloudMessage not plugin mode, close message 1`] = `
`; - -exports[`components/messages/CloudMessage not plugin mode, show message, close message 1`] = ` -
-
- - -
-
-`; - -exports[`components/messages/CloudMessage not plugin mode, single user, close message 1`] = ` -
-
- - -
-
-`; - -exports[`components/messages/CloudMessage plugin mode, no display 1`] = `
`; diff --git a/webapp/src/components/messages/cloudMessage.scss b/webapp/src/components/messages/cloudMessage.scss deleted file mode 100644 index f6c86bba5..000000000 --- a/webapp/src/components/messages/cloudMessage.scss +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -.CloudMessage { - background-color: rgb(var(--sidebar-text-active-border-rgb)); - display: flex; - flex-direction: row; - align-items: center; - text-align: center; - font-weight: 600; - - div { - width: 100%; - } - - > .banner { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - padding: 10px; - color: #fff; - - .CompassIcon { - font-size: 18px; - margin-right: 2px; - } - - .Button { - margin-left: 8px; - background-color: rgba(255, 255, 255, 0.16); - } - } - - .IconButton { - float: right; - color: #fff; - } -} diff --git a/webapp/src/components/messages/cloudMessage.test.tsx b/webapp/src/components/messages/cloudMessage.test.tsx deleted file mode 100644 index cd399ede4..000000000 --- a/webapp/src/components/messages/cloudMessage.test.tsx +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react' -import {Provider as ReduxProvider} from 'react-redux' - -import {render, screen} from '@testing-library/react' -import {mocked} from 'jest-mock' -import userEvent from '@testing-library/user-event' - -import configureStore from 'redux-mock-store' - -import {Utils} from '../../utils' - -import {IUser} from '../../user' - -import {wrapIntl} from '../../testUtils' - -import client from '../../octoClient' - -import {UserSettings} from '../../userSettings' - -import CloudMessage from './cloudMessage' - -jest.mock('../../utils') -jest.mock('../../octoClient') -const mockedOctoClient = mocked(client, true) - -describe('components/messages/CloudMessage', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - - const mockedUtils = mocked(Utils, true) - const mockStore = configureStore([]) - - test('plugin mode, no display', () => { - mockedUtils.isFocalboardPlugin.mockReturnValue(true) - - const me: IUser = { - id: 'user-id-1', - username: 'username_1', - email: '', - nickname: '', - firstname: '', - lastname: '', - props: {}, - create_at: 0, - update_at: 0, - is_bot: false, - is_guest: false, - roles: 'system_user', - } - const state = { - users: { - me, - }, - } - - const store = mockStore(state) - - const component = wrapIntl( - - - , - ) - - const {container} = render(component) - expect(container).toMatchSnapshot() - }) - - test('not plugin mode, close message', () => { - const me: IUser = { - id: 'user-id-1', - username: 'username_1', - email: '', - nickname: '', - firstname: '', - lastname: '', - create_at: 0, - update_at: 0, - is_bot: false, - is_guest: false, - roles: 'system_user', - props: {}, - } - const state = { - users: { - me, - myConfig: { - cloudMessageCanceled: {value: 'true'}, - }, - }, - } - const store = mockStore(state) - mockedUtils.isFocalboardPlugin.mockReturnValue(false) - - const component = wrapIntl( - - - , - ) - - const {container} = render(component) - expect(container).toMatchSnapshot() - }) - - test('not plugin mode, show message, close message', () => { - const me: IUser = { - id: 'user-id-1', - username: 'username_1', - email: '', - nickname: '', - firstname: '', - lastname: '', - props: {}, - create_at: 0, - update_at: 0, - is_bot: false, - is_guest: false, - roles: 'system_user', - } - const state = { - users: { - me, - }, - } - const store = mockStore(state) - mockedUtils.isFocalboardPlugin.mockReturnValue(false) - - const component = wrapIntl( - - - , - ) - - const {container} = render(component) - expect(container).toMatchSnapshot() - - const buttonElement = screen.getByRole('button', {name: 'Close dialog'}) - userEvent.click(buttonElement) - expect(mockedOctoClient.patchUserConfig).toBeCalledWith('user-id-1', { - updatedFields: { - cloudMessageCanceled: 'true', - }, - }) - }) - - test('not plugin mode, single user, close message', () => { - const me: IUser = { - id: 'single-user', - username: 'single-user', - email: 'single-user', - nickname: '', - firstname: '', - lastname: '', - props: {}, - create_at: 0, - update_at: Date.now() - (1000 * 60 * 60 * 24), //24 hours, - is_bot: false, - is_guest: false, - roles: 'system_user', - } - const state = { - users: { - me, - }, - } - const store = mockStore(state) - const hideCloudMessageSpy = jest.spyOn(UserSettings, 'hideCloudMessage', 'set') - - mockedUtils.isFocalboardPlugin.mockReturnValue(false) - - const component = wrapIntl( - - - , - ) - - const {container} = render(component) - expect(container).toMatchSnapshot() - - const buttonElement = screen.getByRole('button', {name: 'Close dialog'}) - userEvent.click(buttonElement) - - expect(mockedOctoClient.patchUserConfig).toBeCalledTimes(0) - expect(hideCloudMessageSpy).toHaveBeenCalledWith(true) - expect(UserSettings.hideCloudMessage).toBe(true) - }) -}) diff --git a/webapp/src/components/messages/cloudMessage.tsx b/webapp/src/components/messages/cloudMessage.tsx deleted file mode 100644 index a35bd8e65..000000000 --- a/webapp/src/components/messages/cloudMessage.tsx +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -import React from 'react' - -import {useIntl, FormattedMessage} from 'react-intl' - -import {Utils} from '../../utils' -import IconButton from '../../widgets/buttons/iconButton' -import Button from '../../widgets/buttons/button' - -import CloseIcon from '../../widgets/icons/close' - -import {useAppSelector, useAppDispatch} from '../../store/hooks' -import octoClient from '../../octoClient' -import {IUser, UserConfigPatch} from '../../user' -import {getMe, patchProps, getCloudMessageCanceled} from '../../store/users' -import {UserSettings} from '../../userSettings' - -import CompassIcon from '../../widgets/icons/compassIcon' -import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../telemetry/telemetryClient' - -import './cloudMessage.scss' -const signupURL = 'https://mattermost.com/pricing' -const displayAfter = (1000 * 60 * 60 * 24) //24 hours - -const CloudMessage = React.memo(() => { - const intl = useIntl() - const dispatch = useAppDispatch() - const me = useAppSelector(getMe) - const cloudMessageCanceled = useAppSelector(getCloudMessageCanceled) - - const closeDialogText = intl.formatMessage({ - id: 'Dialog.closeDialog', - defaultMessage: 'Close dialog', - }) - - const onClose = async () => { - if (me) { - if (me.id === 'single-user') { - UserSettings.hideCloudMessage = true - dispatch(patchProps([ - { - user_id: me.id, - category: 'focalboard', - name: 'cloudMessageCanceled', - value: 'true', - }, - ])) - return - } - const patch: UserConfigPatch = { - updatedFields: { - cloudMessageCanceled: 'true', - }, - } - - const patchedProps = await octoClient.patchUserConfig(me.id, patch) - if (patchedProps) { - dispatch(patchProps(patchedProps)) - } - } - } - - if (Utils.isFocalboardPlugin() || cloudMessageCanceled) { - return null - } - - if (me) { - const installTime = Date.now() - me.create_at - if (installTime < displayAfter) { - return null - } - } - - return ( -
-
- - - - - -
- - } - title={closeDialogText} - size='small' - /> -
- ) -}) -export default CloudMessage diff --git a/webapp/src/components/messages/versionMessage.tsx b/webapp/src/components/messages/versionMessage.tsx index 1c21d27f1..e08791df2 100644 --- a/webapp/src/components/messages/versionMessage.tsx +++ b/webapp/src/components/messages/versionMessage.tsx @@ -71,7 +71,7 @@ const VersionMessage = React.memo(() => { }} > diff --git a/webapp/src/pages/boardPage/boardPage.scss b/webapp/src/pages/boardPage/boardPage.scss index 7b1a3c4a0..fcf6fe9bc 100644 --- a/webapp/src/pages/boardPage/boardPage.scss +++ b/webapp/src/pages/boardPage/boardPage.scss @@ -1,7 +1,6 @@ .BoardPage { position: relative; - .CloudMessage:not(:first-child), .VersionMessage:not(:first-child) { position: absolute; top: 0; diff --git a/webapp/src/pages/boardPage/boardPage.tsx b/webapp/src/pages/boardPage/boardPage.tsx index 35d9399f5..a2b4e953b 100644 --- a/webapp/src/pages/boardPage/boardPage.tsx +++ b/webapp/src/pages/boardPage/boardPage.tsx @@ -6,7 +6,6 @@ import {FormattedMessage, useIntl} from 'react-intl' import {useRouteMatch, useHistory} from 'react-router-dom' import Workspace from '../../components/workspace' -import CloudMessage from '../../components/messages/cloudMessage' import VersionMessage from '../../components/messages/versionMessage' import octoClient from '../../octoClient' import {Subscription, WSClient} from '../../wsclient' @@ -300,7 +299,6 @@ const BoardPage = (props: Props): JSX.Element => { - {!mobileWarningClosed && diff --git a/webapp/src/store/users.ts b/webapp/src/store/users.ts index 0bb90dec9..cb5a71a0a 100644 --- a/webapp/src/store/users.ts +++ b/webapp/src/store/users.ts @@ -10,10 +10,6 @@ import {Utils} from '../utils' import {Subscription} from '../wsclient' -// TODO: change this whene the initial load is complete -// import {initialLoad} from './initialLoad' -import {UserSettings} from '../userSettings' - import {initialLoad} from './initialLoad' import {RootState} from './index' @@ -169,20 +165,6 @@ export const getOnboardingTourCategory = createSelector( (myConfig): string => (myConfig.tourCategory ? myConfig.tourCategory.value : ''), ) -export const getCloudMessageCanceled = createSelector( - getMe, - getMyConfig, - (me, myConfig): boolean => { - if (!me) { - return false - } - if (me.id === 'single-user') { - return UserSettings.hideCloudMessage - } - return Boolean(myConfig.cloudMessageCanceled?.value) - }, -) - export const getVersionMessageCanceled = createSelector( getMe, getMyConfig, diff --git a/webapp/src/userSettings.ts b/webapp/src/userSettings.ts index 61d02a4a3..3b1fc504a 100644 --- a/webapp/src/userSettings.ts +++ b/webapp/src/userSettings.ts @@ -17,7 +17,6 @@ export enum UserSettingKey { RandomIcons = 'randomIcons', MobileWarningClosed = 'mobileWarningClosed', WelcomePageViewed = 'welcomePageViewed', - HideCloudMessage = 'hideCloudMessage', NameFormat = 'nameFormat' } @@ -149,14 +148,6 @@ export class UserSettings { UserSettings.set(UserSettingKey.MobileWarningClosed, String(newValue)) } - static get hideCloudMessage(): boolean { - return localStorage.getItem(UserSettingKey.HideCloudMessage) === 'true' - } - - static set hideCloudMessage(newValue: boolean) { - localStorage.setItem(UserSettingKey.HideCloudMessage, JSON.stringify(newValue)) - } - static get nameFormat(): string | null { return UserSettings.get(UserSettingKey.NameFormat) }