From 0cdad1f41d8cc2002e7c265702099dcd4e418547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 1 Feb 2023 15:30:14 +0100 Subject: [PATCH 01/35] MM-48246 - ab test open RHS with linked board; add telemetry info --- .../webapp/src/components/rhsChannelBoardItem.tsx | 13 ++++++++++++- webapp/src/telemetry/telemetryClient.ts | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx index 5f60e71a8..43ee7841d 100644 --- a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx +++ b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx @@ -6,6 +6,7 @@ import {FormattedMessage, useIntl} from 'react-intl' import mutator from '../../../../webapp/src/mutator' import {Utils} from '../../../../webapp/src/utils' import {getCurrentTeam} from '../../../../webapp/src/store/teams' +import {getCurrentChannel} from '../../../../webapp/src/store/channels' import {createBoard, Board} from '../../../../webapp/src/blocks/board' import {useAppSelector} from '../../../../webapp/src/store/hooks' import IconButton from '../../../../webapp/src/widgets/buttons/iconButton' @@ -17,8 +18,10 @@ import CompassIcon from '../../../../webapp/src/widgets/icons/compassIcon' import {Permission} from '../../../../webapp/src/constants' -import './rhsChannelBoardItem.scss' import BoardPermissionGate from '../../../../webapp/src/components/permissions/boardPermissionGate' +import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../../../../webapp/src/telemetry/telemetryClient' + +import './rhsChannelBoardItem.scss' const windowAny = (window as SuiteWindow) @@ -35,7 +38,15 @@ const RHSChannelBoardItem = (props: Props) => { return null } + const currentChannel = useAppSelector(getCurrentChannel) + if (!currentChannel) { + return null + } + const handleBoardClicked = (boardID: string) => { + // send the telemetry information for the clicked board + TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, {teamID: team.id, channelID: currentChannel.id}) + window.open(`${windowAny.frontendBaseURL}/team/${team.id}/${boardID}`, '_blank', 'noopener') } diff --git a/webapp/src/telemetry/telemetryClient.ts b/webapp/src/telemetry/telemetryClient.ts index 460e9d247..1787315da 100644 --- a/webapp/src/telemetry/telemetryClient.ts +++ b/webapp/src/telemetry/telemetryClient.ts @@ -50,6 +50,7 @@ export const TelemetryActions = { LimitCardLimitReached: 'limit_cardLimitReached', LimitCardLimitLinkOpen: 'limit_cardLimitLinkOpen', VersionMoreInfo: 'version_more_info', + ClickChannelsRHSBoard: 'click_channels_RHS_board', } interface IEventProps { From b4ea125a63ab10133ad3f97849c7687dd7bf5397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 1 Feb 2023 18:53:02 +0100 Subject: [PATCH 02/35] add board info --- .../webapp/src/components/rhsChannelBoardItem.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx index 43ee7841d..e796af1e5 100644 --- a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx +++ b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx @@ -45,7 +45,8 @@ const RHSChannelBoardItem = (props: Props) => { const handleBoardClicked = (boardID: string) => { // send the telemetry information for the clicked board - TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, {teamID: team.id, channelID: currentChannel.id}) + const extraData = {teamID: team.id, channelID: currentChannel.id, board: boardID} + TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, extraData) window.open(`${windowAny.frontendBaseURL}/team/${team.id}/${boardID}`, '_blank', 'noopener') } From 4030866a286c77ee14de1961f215a3336a9c498a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Mon, 6 Feb 2023 14:31:25 +0100 Subject: [PATCH 03/35] remove innecessary telemetry channel information --- .../webapp/src/components/rhsChannelBoardItem.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx index e796af1e5..1f03dc003 100644 --- a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx +++ b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx @@ -6,7 +6,6 @@ import {FormattedMessage, useIntl} from 'react-intl' import mutator from '../../../../webapp/src/mutator' import {Utils} from '../../../../webapp/src/utils' import {getCurrentTeam} from '../../../../webapp/src/store/teams' -import {getCurrentChannel} from '../../../../webapp/src/store/channels' import {createBoard, Board} from '../../../../webapp/src/blocks/board' import {useAppSelector} from '../../../../webapp/src/store/hooks' import IconButton from '../../../../webapp/src/widgets/buttons/iconButton' @@ -38,14 +37,9 @@ const RHSChannelBoardItem = (props: Props) => { return null } - const currentChannel = useAppSelector(getCurrentChannel) - if (!currentChannel) { - return null - } - const handleBoardClicked = (boardID: string) => { // send the telemetry information for the clicked board - const extraData = {teamID: team.id, channelID: currentChannel.id, board: boardID} + const extraData = {teamID: team.id, board: boardID} TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, extraData) window.open(`${windowAny.frontendBaseURL}/team/${team.id}/${boardID}`, '_blank', 'noopener') From 5b1c53dffa7bb70fbaeb2fe6040a23df12706037 Mon Sep 17 00:00:00 2001 From: Benjamin Cooke Date: Wed, 22 Feb 2023 16:21:12 -0500 Subject: [PATCH 04/35] updating date when prop has changed --- webapp/src/properties/date/date.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webapp/src/properties/date/date.tsx b/webapp/src/properties/date/date.tsx index 23d764a1b..e2c285b5e 100644 --- a/webapp/src/properties/date/date.tsx +++ b/webapp/src/properties/date/date.tsx @@ -1,6 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useMemo, useState, useCallback} from 'react' +import React, {useMemo, useState, useCallback, useEffect} from 'react' import {useIntl} from 'react-intl' import {DateUtils} from 'react-day-picker' import MomentLocaleUtils from 'react-day-picker/moment' @@ -58,6 +58,12 @@ function DateRange(props: PropertyProps): JSX.Element { const [value, setValue] = useState(propertyValue) const intl = useIntl() + useEffect(() => { + if (value !== propertyValue) { + setValue(propertyValue) + } + }, [propertyValue, setValue]) + const onChange = useCallback((newValue) => { if (value !== newValue) { setValue(newValue) From ae44d6f2bfc78d36e9060a9c52ac8945695822c9 Mon Sep 17 00:00:00 2001 From: Benjamin Cooke Date: Thu, 23 Feb 2023 14:27:52 -0500 Subject: [PATCH 05/35] unit test --- .../date/__snapshots__/date.test.tsx.snap | 17 ++++++++++ webapp/src/properties/date/date.test.tsx | 32 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/webapp/src/properties/date/__snapshots__/date.test.tsx.snap b/webapp/src/properties/date/__snapshots__/date.test.tsx.snap index 861e203c5..231f47e8a 100644 --- a/webapp/src/properties/date/__snapshots__/date.test.tsx.snap +++ b/webapp/src/properties/date/__snapshots__/date.test.tsx.snap @@ -34,6 +34,23 @@ exports[`properties/dateRange handle clear 1`] = ` `; +exports[`properties/dateRange returns component with new date after prop change 1`] = ` +
+
+ +
+
+`; + exports[`properties/dateRange returns default correctly 1`] = `
{ expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, JSON.stringify({from: today})) }) + + test('returns component with new date after prop change', () => { + const component = wrapIntl( + , + ) + + const {container, rerender} = render(component) + + rerender( + wrapIntl( + + ) + ) + + expect(container).toMatchSnapshot() + }) }) From 30c9da952efa2aff4c2c1e817cdddcb2ba1bbc09 Mon Sep 17 00:00:00 2001 From: Benjamin Cooke Date: Thu, 23 Feb 2023 15:35:04 -0500 Subject: [PATCH 06/35] lint --- webapp/src/properties/date/date.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/src/properties/date/date.test.tsx b/webapp/src/properties/date/date.test.tsx index d1989425b..dfabdf706 100644 --- a/webapp/src/properties/date/date.test.tsx +++ b/webapp/src/properties/date/date.test.tsx @@ -315,7 +315,7 @@ describe('properties/dateRange', () => { expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, JSON.stringify({from: today})) }) - + test('returns component with new date after prop change', () => { const component = wrapIntl( { board={{...board}} card={{...card}} propertyTemplate={propertyTemplate} - /> - ) + />, + ), ) expect(container).toMatchSnapshot() From 7e0b4955e2c0fb4b0eedd8b26ad0a2e884beb3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Mon, 27 Feb 2023 12:52:09 +0100 Subject: [PATCH 07/35] MM-50804 - add reference to template id to action function --- .../webapp/src/components/createBoardFromTemplate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mattermost-plugin/webapp/src/components/createBoardFromTemplate.tsx b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.tsx index af9c81560..dd7768a63 100644 --- a/mattermost-plugin/webapp/src/components/createBoardFromTemplate.tsx +++ b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.tsx @@ -73,7 +73,7 @@ const CreateBoardFromTemplate = (props: Props) => { let boardsAndBlocks = undefined - if (selectedBoardTemplateId === EMPTY_BOARD) { + if (templateIdRef.current === EMPTY_BOARD) { boardsAndBlocks = await mutator.addEmptyBoard(teamId, intl) } else { boardsAndBlocks = await mutator.duplicateBoard(templateIdRef.current as string, ACTION_DESCRIPTION, asTemplate, undefined, undefined, teamId) From 8f3407291d7eee1864169d3b792f653b9b9bb3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 1 Mar 2023 00:34:43 +0100 Subject: [PATCH 08/35] update telemetry id Co-authored-by: Maria A Nunez --- webapp/src/telemetry/telemetryClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/telemetry/telemetryClient.ts b/webapp/src/telemetry/telemetryClient.ts index 1787315da..c722929ff 100644 --- a/webapp/src/telemetry/telemetryClient.ts +++ b/webapp/src/telemetry/telemetryClient.ts @@ -50,7 +50,7 @@ export const TelemetryActions = { LimitCardLimitReached: 'limit_cardLimitReached', LimitCardLimitLinkOpen: 'limit_cardLimitLinkOpen', VersionMoreInfo: 'version_more_info', - ClickChannelsRHSBoard: 'click_channels_RHS_board', + ClickChannelsRHSBoard: 'click_board_in_channels_RHS', } interface IEventProps { From 73b8f0740640e1bfe11eefb9be1509579a0d9feb Mon Sep 17 00:00:00 2001 From: Benjamin Calisto Date: Mon, 27 Feb 2023 16:23:59 +0100 Subject: [PATCH 09/35] Translated using Weblate (Spanish) Currently translated at 48.6% (219 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/es/ --- webapp/i18n/es.json | 88 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json index da03bb0d1..9de79aac9 100644 --- a/webapp/i18n/es.json +++ b/webapp/i18n/es.json @@ -1,4 +1,15 @@ { + "AppBar.Tooltip": "Alternar tableros vinculados", + "Attachment.Attachment-title": "Archivos adjuntos", + "AttachmentBlock.DeleteAction": "borrar", + "AttachmentBlock.addElement": "agregar {type}", + "AttachmentBlock.delete": "Archivo adjunto eliminado.", + "AttachmentBlock.failed": "Este archivo no puede subirse debido a que excede el límite de tamaño de archivo.", + "AttachmentBlock.upload": "Subiendo archivo adjunto.", + "AttachmentBlock.uploadSuccess": "Archivo adjunto subido.", + "AttachmentElement.delete-confirmation-dialog-button-text": "Borrar", + "AttachmentElement.download": "Descargar", + "AttachmentElement.upload-percentage": "Subiendo...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Añadir un grupo", "BoardComponent.delete": "Borrar", "BoardComponent.hidden-columns": "Columnas Ocultas", @@ -10,21 +21,39 @@ "BoardMember.schemeAdmin": "Administrador", "BoardMember.schemeEditor": "Editor", "BoardMember.schemeNone": "Ninguno", - "BoardPage.newVersion": "Una nueva versión de Board está disponible, haz click aquí para recargar.", - "BoardPage.syncFailed": "El tablero puede estar eliminado o el acceso fue revocado.", - "BoardTemplateSelector.add-template": "Nueva plantilla", - "BoardTemplateSelector.create-empty-board": "Crear pizarra vacía", - "BoardTemplateSelector.delete-template": "Suprimir", + "BoardMember.schemeViewer": "Visualizador", + "BoardMember.unlinkChannel": "Desvincular", + "BoardPage.newVersion": "Una nueva versión de Boards está disponible, haz clic aquí para recargar.", + "BoardPage.syncFailed": "El tablero puede haber sido eliminado o el acceso revocado.", + "BoardTemplateSelector.add-template": "Crear nueva plantilla", + "BoardTemplateSelector.create-empty-board": "Crear un tablero vacío", + "BoardTemplateSelector.delete-template": "Eliminar", + "BoardTemplateSelector.description": "Agregar un tablero a la barra lateral usando alguna de las plantillas definidas a continuación o empezar desde cero.", "BoardTemplateSelector.edit-template": "Editar", + "BoardTemplateSelector.plugin.no-content-description": "Agregar un tablero a la barra lateral usando alguna de las plantillas definidas a continuación o empezar desde cero.", + "BoardTemplateSelector.plugin.no-content-title": "Crear un tablero", + "BoardTemplateSelector.title": "Crear un tablero", "BoardTemplateSelector.use-this-template": "Utiliza esta plantilla", + "BoardsSwitcher.Title": "Encontrar tableros", + "BoardsUnfurl.Limited": "Los detalles adicionales están ocultos debido a que la tarjeta ha sido archivada", "BoardsUnfurl.Updated": "Actualizado {time}", - "Calculations.Options.count.displayName": "Cantidad", - "Calculations.Options.count.label": "Cantidad", + "Calculations.Options.average.displayName": "Promedio", + "Calculations.Options.average.label": "Promedio", + "Calculations.Options.count.displayName": "Contar", + "Calculations.Options.count.label": "Contar", "Calculations.Options.countChecked.displayName": "Marcado", + "Calculations.Options.countChecked.label": "Contar marcados", "Calculations.Options.countUnchecked.displayName": "Deseleccionado", + "Calculations.Options.countUnchecked.label": "Contar no marcados", + "Calculations.Options.countUniqueValue.displayName": "Único", + "Calculations.Options.countUniqueValue.label": "Contar valores únicos", "Calculations.Options.countValue.displayName": "Valores", "Calculations.Options.dateRange.displayName": "Rango", "Calculations.Options.dateRange.label": "Rango", + "Calculations.Options.earliest.displayName": "Más antiguo", + "Calculations.Options.earliest.label": "Más antiguo", + "Calculations.Options.latest.displayName": "Último", + "Calculations.Options.latest.label": "Último", "Calculations.Options.max.displayName": "Máx", "Calculations.Options.max.label": "Máx", "Calculations.Options.median.displayName": "Mediana", @@ -34,17 +63,54 @@ "Calculations.Options.none.displayName": "Calcular", "Calculations.Options.none.label": "Ninguna", "Calculations.Options.percentChecked.displayName": "Marcado", + "Calculations.Options.percentChecked.label": "Porcentaje marcado", + "Calculations.Options.percentUnchecked.displayName": "Desmarcado", + "Calculations.Options.percentUnchecked.label": "Porcentaje desmarcado", "Calculations.Options.range.displayName": "Rango", + "Calculations.Options.range.label": "Rango", + "Calculations.Options.sum.displayName": "Suma", + "Calculations.Options.sum.label": "Suma", + "CalendarCard.untitled": "Sin título", + "CardActionsMenu.copiedLink": "¡Copiado!", + "CardActionsMenu.copyLink": "Copiar hipervínculo", + "CardActionsMenu.delete": "Eliminar", + "CardActionsMenu.duplicate": "Duplicar", + "CardBadges.title-checkboxes": "Casillas de verificación", + "CardBadges.title-comments": "Comentarios", + "CardBadges.title-description": "Esta tarjeta tiene una descripción", + "CardDetail.Attach": "Adjuntar", + "CardDetail.Follow": "Seguir", + "CardDetail.Following": "Siguiendo", "CardDetail.add-content": "Añadir contenido", "CardDetail.add-icon": "Añadir icono", "CardDetail.add-property": "+ Añadir propiedad", - "CardDetail.addCardText": "añade texto a la tarjeta", - "CardDetail.moveContent": "mover contenido de la tarjeta", + "CardDetail.addCardText": "agregar texto a la tarjeta", + "CardDetail.limited-body": "Mejorar a nuestro plan Professional o Enterprise.", + "CardDetail.limited-button": "Mejorar", + "CardDetail.limited-title": "Esta tarjeta está oculta", + "CardDetail.moveContent": "Mover contenido de la tarjeta", "CardDetail.new-comment-placeholder": "Añadir un comentario...", - "CardDetailProperty.confirm-delete-subtext": "¿Estas seguro de que quieres eliminar la propiedad \"{nombre de la propiedad}\"? Al eliminarla se borrará la propiedad de todas las tarjetas de este tablero.", - "CardDetailProperty.property-deleted": "¡{nombre de la propiedad} ha sido eliminado exitosamente!", + "CardDetailProperty.confirm-delete-heading": "Confirmar eliminación de la propiedad", + "CardDetailProperty.confirm-delete-subtext": "¿Estás seguro de que quieres eliminar la propiedad \"{propertyName}\"? Al eliminarla también se removerá la propiedad en todas las tarjetas de este tablero.", + "CardDetailProperty.confirm-property-name-change-subtext": "¿Estás seguro de que quieres cambiar la propiedad \"{propertyName}\" {customText}? Esto puede afectar a los valores en {numOfCards} tarjeta(s) en este tablero, lo que puede resultar en una pérdida de datos.", + "CardDetailProperty.confirm-property-type-change": "Confirmar cambio de tipo de la propiedad", + "CardDetailProperty.delete-action-button": "Eliminar", + "CardDetailProperty.property-change-action-button": "Modificar propiedad", + "CardDetailProperty.property-changed": "¡Propiedad modificada exitosamente!", + "CardDetailProperty.property-deleted": "¡La propiedad {propertyName} ha sido eliminada exitosamente!", + "CardDetial.limited-link": "Aprende más sobre nuestros planes.", + "CardDialog.delete-confirmation-dialog-attachment": "Confirmar eliminación del archivo adjunto", + "CardDialog.delete-confirmation-dialog-button-text": "Eliminar", + "CardDialog.delete-confirmation-dialog-heading": "Confirmar eliminación de la tarjeta", "CardDialog.editing-template": "Estás editando una plantilla.", "CardDialog.nocard": "Esta tarjeta no existe o es inaccesible.", + "Categories.CreateCategoryDialog.CancelText": "Cancelar", + "Categories.CreateCategoryDialog.CreateText": "Crear", + "Categories.CreateCategoryDialog.Placeholder": "Pon nombre a la categoría", + "Categories.CreateCategoryDialog.UpdateText": "Actualizar", + "CenterPanel.Login": "Ingresar", + "CenterPanel.Share": "Compartir", + "ChannelIntro.CreateBoard": "Crear un tablero", "ColorOption.selectColor": "Seleccionar {color} Color", "Comment.delete": "Borrar", "CommentsList.send": "Enviar", From 0aa09a21438e66015add7719d96e4a06cc8f3c05 Mon Sep 17 00:00:00 2001 From: Moataz Muhammad Date: Mon, 27 Feb 2023 16:23:59 +0100 Subject: [PATCH 10/35] Translated using Weblate (Arabic) Currently translated at 46.0% (207 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ar/ --- webapp/i18n/ar.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/webapp/i18n/ar.json b/webapp/i18n/ar.json index 55a58f534..8eeca566e 100644 --- a/webapp/i18n/ar.json +++ b/webapp/i18n/ar.json @@ -1,5 +1,15 @@ { "AppBar.Tooltip": "اخيار الالواح المرتبطة", + "Attachment.Attachment-title": "المرفق", + "AttachmentBlock.DeleteAction": "حذف", + "AttachmentBlock.addElement": "اضافة {type}", + "AttachmentBlock.delete": "تم حذف المرفق.", + "AttachmentBlock.failed": "تعذر تحميل هذا الملف حيث تم الوصول إلى الحد الأقصى لحجم الملفات.", + "AttachmentBlock.upload": "يتم الآن تحميل المرفق.", + "AttachmentBlock.uploadSuccess": "تم تحميل المرفق.", + "AttachmentElement.delete-confirmation-dialog-button-text": "حذف", + "AttachmentElement.download": "تحميل", + "AttachmentElement.upload-percentage": "جاري التحمي... ({uploadPercent}%)", "BoardComponent.add-a-group": "+ إضافة مجموعة", "BoardComponent.delete": "حذف", "BoardComponent.hidden-columns": "الأعمدة المخفية", @@ -155,9 +165,15 @@ "FindBoardsDialog.NoResultsFor": "لا يوجد نتيجة للبحث \"{searchQuery}\"", "FindBoardsDialog.NoResultsSubtext": "اختر بحث آخر أو تأكد من الأخطاء الإملائية.", "FindBoardsDialog.Title": "البحث عن ألواح", + "GroupBy.ungroup": "إلغاء التجميع", + "KanbanCard.untitled": "بدون عنوان", + "Mutator.new-card-from-template": "بطاقة جديدة من نموذج", + "Mutator.new-template-from-card": "نموذج جديد من بطاقة", "OnboardingTour.AddComments.Title": "إضافة تعليقات", "OnboardingTour.AddDescription.Title": "اضافة وصف", "OnboardingTour.AddProperties.Title": "إضافة خواص", + "OnboardingTour.AddView.Body": "انتقل هنا لإنشاء عرض جديد لتنظيم لوحتك باستخدام تخطيطات مختلفة.", + "OnboardingTour.AddView.Title": "إضافة عرض جديد", "OnboardingTour.CopyLink.Title": "نسخ الرابط", "PropertyMenu.Delete": "حذف", "PropertyMenu.changeType": "تغيير نوع الخاصية", From c341385c470ddd3aff7cd828d89246867e1b5c22 Mon Sep 17 00:00:00 2001 From: 11sma <11isma@gmail.com> Date: Mon, 27 Feb 2023 16:23:59 +0100 Subject: [PATCH 11/35] Translated using Weblate (Catalan) Currently translated at 43.3% (195 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ca/ --- webapp/i18n/ca.json | 78 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/webapp/i18n/ca.json b/webapp/i18n/ca.json index 6e94e02cf..b6386314c 100644 --- a/webapp/i18n/ca.json +++ b/webapp/i18n/ca.json @@ -1,17 +1,81 @@ { + "Attachment.Attachment-title": "Adjunt", + "AttachmentBlock.DeleteAction": "esborra", + "AttachmentBlock.addElement": "afegir {type}", + "AttachmentBlock.delete": "Adjunt esborrat.", + "AttachmentBlock.failed": "Aquest fitxer no pot ser afegit ja que el límit de tamany de fitxer ha estat assolit.", + "AttachmentBlock.upload": "Adjunt afegint-se.", + "AttachmentBlock.uploadSuccess": "Adjunt afegit.", + "AttachmentElement.delete-confirmation-dialog-button-text": "Esborra", + "AttachmentElement.download": "Descarrega", + "AttachmentElement.upload-percentage": "Afegint...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Afegir un grup", "BoardComponent.delete": "Eliminar", "BoardComponent.hidden-columns": "Columnes ocultes", "BoardComponent.hide": "Amagar", "BoardComponent.new": "+ Nou", "BoardComponent.no-property": "Sense {property}", - "BoardComponent.no-property-title": "Els elements amb una propietat {property} buida anirán aquí. Aquesta col·lumna no pot elimiar-se.", + "BoardComponent.no-property-title": "Els elements amb una propietat {property} buida anirán aquí. Aquesta col·lumna no pot eliminar-se.", "BoardComponent.show": "Mostrar", + "BoardMember.schemeAdmin": "Admin", + "BoardMember.schemeCommenter": "Comentarista", + "BoardMember.schemeEditor": "Editor", + "BoardMember.schemeNone": "Cap", + "BoardMember.schemeViewer": "Visualitzador", + "BoardPage.newVersion": "Una nova versió de Boards és disponible, clica aquí per recarregar.", + "BoardPage.syncFailed": "El tauler podria ser eliminat o revocat l'accés.", + "BoardTemplateSelector.add-template": "Crea una nova plantilla", + "BoardTemplateSelector.create-empty-board": "Crea un taulell buit", + "BoardTemplateSelector.delete-template": "Esborra", + "BoardTemplateSelector.description": "Afegeix el taulell a la barra lateral usant alguna de les plantilles definides a sota o comença des de zero.", + "BoardTemplateSelector.edit-template": "Edita", + "BoardTemplateSelector.plugin.no-content-description": "Afegeix el taulell a la barra lateral usant alguna de les plantilles definides a sota o comença des de zero.", + "BoardTemplateSelector.plugin.no-content-title": "Crea un taulell", + "BoardTemplateSelector.title": "Crea un taulell", + "BoardTemplateSelector.use-this-template": "Utilitza aquesta plantilla", + "BoardsSwitcher.Title": "Busca taulells", + "BoardsUnfurl.Updated": "Actualitzat {time}", + "Calculations.Options.average.displayName": "Promig", + "Calculations.Options.average.label": "Promig", + "Calculations.Options.countChecked.displayName": "Comprovat", + "Calculations.Options.countUniqueValue.displayName": "Únic", + "Calculations.Options.countUniqueValue.label": "Compta valors únics", + "Calculations.Options.countValue.displayName": "Valors", + "Calculations.Options.dateRange.displayName": "Rang", + "Calculations.Options.dateRange.label": "Rang", + "Calculations.Options.earliest.displayName": "Proper", + "Calculations.Options.earliest.label": "Proper", + "Calculations.Options.latest.displayName": "Últim", + "Calculations.Options.latest.label": "Últim", + "Calculations.Options.max.displayName": "Màxim", + "Calculations.Options.max.label": "Màxim", + "Calculations.Options.min.displayName": "Mínim", + "Calculations.Options.min.label": "Mínim", + "Calculations.Options.none.displayName": "Calcula", + "Calculations.Options.none.label": "Cap", + "Calculations.Options.percentChecked.displayName": "Completat", + "Calculations.Options.percentChecked.label": "Percentatge completat", + "Calculations.Options.percentUnchecked.displayName": "No finalitzat", + "Calculations.Options.percentUnchecked.label": "Percentatge no finalitzat", + "Calculations.Options.range.displayName": "Rang", + "Calculations.Options.range.label": "Rang", + "Calculations.Options.sum.displayName": "Suma", + "Calculations.Options.sum.label": "Suma", + "CalendarCard.untitled": "Sense títol", + "CardActionsMenu.copiedLink": "Copiat!", + "CardActionsMenu.copyLink": "Còpia l'enllaç", + "CardActionsMenu.delete": "Esborra", + "CardActionsMenu.duplicate": "Duplica", + "CardBadges.title-comments": "Comentaris", + "CardBadges.title-description": "Aquesta tarjeta té una descripció", + "CardDetail.Attach": "Adjunta", + "CardDetail.Follow": "Segueix", + "CardDetail.Following": "Segueix", "CardDetail.add-content": "Afegeix contingut", "CardDetail.add-icon": "Afegeix icona", "CardDetail.add-property": "+ Afegeix propietat", "CardDetail.addCardText": "afegeix text a la targeta", - "CardDetail.moveContent": "mou el contingut de la targeta", + "CardDetail.moveContent": "Mou el contingut de la targeta", "CardDetail.new-comment-placeholder": "Afegeix un comentari...", "CardDialog.editing-template": "Estas editant una plantilla.", "CardDialog.nocard": "Aquesta targeta no existeix o és innaccesible.", @@ -47,18 +111,18 @@ "PropertyMenu.changeType": "Canviar el tipus de propietat", "PropertyMenu.typeTitle": "Tipus", "PropertyType.Checkbox": "casella de verificació", - "PropertyType.CreatedBy": "Creat Per", + "PropertyType.CreatedBy": "Creada per", "PropertyType.CreatedTime": "Moment de creació", "PropertyType.Date": "Data", "PropertyType.Email": "Correu electrònic", - "PropertyType.MultiSelect": "Multi selecció", + "PropertyType.MultiSelect": "Selecció múltiple", "PropertyType.Number": "Número", "PropertyType.Person": "Persona", "PropertyType.Phone": "Telèfon", "PropertyType.Select": "Selecciona", "PropertyType.Text": "Text", - "PropertyType.UpdatedBy": "Actualitzat per", - "PropertyType.UpdatedTime": "Moment de actualització", + "PropertyType.UpdatedBy": "Última actualització feta per", + "PropertyType.UpdatedTime": "Moment d'actualització", "RegistrationLink.confirmRegenerateToken": "Això invalidarà enllaços compartits anteriorment. Continuar?", "RegistrationLink.copiedLink": "Copiat!", "RegistrationLink.copyLink": "Copiar enllaç", @@ -113,7 +177,7 @@ "ViewHeader.group-by": "Agrupar per: {property}", "ViewHeader.new": "Nou", "ViewHeader.properties": "Propietats", - "ViewHeader.search-text": "Cercar text", + "ViewHeader.search-text": "Cerca tarjetes", "ViewHeader.select-a-template": "Selecciona una plantilla", "ViewHeader.sort": "Ordenar", "ViewHeader.untitled": "Sense títol", From b553a6f21641dc3ba9d7f38512ec51e4cd3cf391 Mon Sep 17 00:00:00 2001 From: Filip Antala Date: Mon, 27 Feb 2023 16:23:59 +0100 Subject: [PATCH 12/35] Translated using Weblate (Slovak) Currently translated at 61.5% (277 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/sk/ --- webapp/i18n/sk.json | 166 ++++++++++++++++++++++++++++++++------------ 1 file changed, 123 insertions(+), 43 deletions(-) diff --git a/webapp/i18n/sk.json b/webapp/i18n/sk.json index eff2c85eb..7d5945f89 100644 --- a/webapp/i18n/sk.json +++ b/webapp/i18n/sk.json @@ -1,14 +1,40 @@ { + "Attachment.Attachment-title": "Príloha", + "AttachmentBlock.DeleteAction": "odstrániť", + "AttachmentBlock.delete": "Príloha odstránená.", + "AttachmentBlock.failed": "Tento súbor nebol nahratý, pretože presiahol veľkostný limit.", + "AttachmentBlock.upload": "Príloha sa nahráva.", + "AttachmentBlock.uploadSuccess": "Príloha bola nahratá.", + "AttachmentElement.delete-confirmation-dialog-button-text": "Odstrániť", + "AttachmentElement.download": "Stiahnuť", + "AttachmentElement.upload-percentage": "Nahrávam... ({uploadPercent}%)", "BoardComponent.add-a-group": "+ Pridaj skupinu", - "BoardComponent.delete": "Mazať", - "BoardComponent.hidden-columns": "Skryté stľpce", + "BoardComponent.delete": "Odstrániť", + "BoardComponent.hidden-columns": "Skryté stĺpce", "BoardComponent.hide": "Skryť", "BoardComponent.new": "+ Nový", "BoardComponent.no-property": "žiadna {property}", "BoardComponent.no-property-title": "Položky s prázdnou {property} pôjdu tu. Tento stĺpec nemožno vymazať.", "BoardComponent.show": "Ukáž", + "BoardMember.schemeAdmin": "Administrátor", + "BoardMember.schemeCommenter": "Komentátor", + "BoardMember.schemeEditor": "Editor", + "BoardMember.schemeNone": "Žiadny", + "BoardMember.schemeViewer": "Sledovateľ", + "BoardMember.unlinkChannel": "Odpojiť", "BoardPage.newVersion": "Nová verzia je dostupná, kliknite tu pre znovu načítanie.", - "BoardPage.syncFailed": "Nástenka môže byť vymazaná, alebo prístup odobraný.", + "BoardPage.syncFailed": "Nástenka môže byť vymazaná alebo prístup odobraný.", + "BoardTemplateSelector.add-template": "Vytvoriť novú šablónu", + "BoardTemplateSelector.create-empty-board": "Vytvoriť prázdnu nástenku", + "BoardTemplateSelector.delete-template": "Odstrániť", + "BoardTemplateSelector.description": "Pridajte nástenku do bočného panelu pomocou ktorýchkoľvek šablón definovaných dole alebo začnite od začiatku.", + "BoardTemplateSelector.edit-template": "Upraviť", + "BoardTemplateSelector.plugin.no-content-description": "Pridajte nástenku do bočného panelu pomocou ktorýchkoľvek šablón dole alebo začnite od začiatku.", + "BoardTemplateSelector.plugin.no-content-title": "Vytvoriť nástenku", + "BoardTemplateSelector.title": "Vytvoriť nástenku", + "BoardTemplateSelector.use-this-template": "Použiť túto šablónu", + "BoardsSwitcher.Title": "Hľadať nástenky", + "BoardsUnfurl.Limited": "Ďalšie detaily sú skryté, pretože je karta archivovaná", "BoardsUnfurl.Remainder": "+{remainder} viac", "BoardsUnfurl.Updated": "Upravené {time}", "Calculations.Options.average.displayName": "Priemer", @@ -16,13 +42,13 @@ "Calculations.Options.count.displayName": "Počet", "Calculations.Options.count.label": "Počet", "Calculations.Options.countChecked.displayName": "Označené", - "Calculations.Options.countChecked.label": "Spočítaj označené", + "Calculations.Options.countChecked.label": "Počítať označené", "Calculations.Options.countUnchecked.displayName": "Neoznačené", - "Calculations.Options.countUnchecked.label": "Spočítaj neoznačené", + "Calculations.Options.countUnchecked.label": "Počítať neoznačené", "Calculations.Options.countUniqueValue.displayName": "Unikátne", - "Calculations.Options.countUniqueValue.label": "Spočítaj unikátne hodnoty", + "Calculations.Options.countUniqueValue.label": "Počítať unikátne hodnoty", "Calculations.Options.countValue.displayName": "Hodnoty", - "Calculations.Options.countValue.label": "Spočítaj hodnoty", + "Calculations.Options.countValue.label": "Počítať hodnotu", "Calculations.Options.dateRange.displayName": "Rozsah", "Calculations.Options.dateRange.label": "Rozsah", "Calculations.Options.earliest.displayName": "Prvý", @@ -31,76 +57,130 @@ "Calculations.Options.latest.label": "Posledný", "Calculations.Options.max.displayName": "Max", "Calculations.Options.max.label": "Max", - "Calculations.Options.median.displayName": "Median", - "Calculations.Options.median.label": "Median", + "Calculations.Options.median.displayName": "Medián", + "Calculations.Options.median.label": "Medián", "Calculations.Options.min.displayName": "Min", "Calculations.Options.min.label": "Min", - "Calculations.Options.none.displayName": "Vypočítaj", + "Calculations.Options.none.displayName": "Vypočítať", "Calculations.Options.none.label": "Nič", - "Calculations.Options.percentChecked.displayName": "Skontrolované", - "Calculations.Options.percentChecked.label": "Percent Skontrolovaných", + "Calculations.Options.percentChecked.displayName": "Označené", + "Calculations.Options.percentChecked.label": "Percent skontrolovaných", "Calculations.Options.percentUnchecked.displayName": "Neskontrolované", "Calculations.Options.percentUnchecked.label": "Percent neskontrolovaných", "Calculations.Options.range.displayName": "Rozsah", "Calculations.Options.range.label": "Rozsah", "Calculations.Options.sum.displayName": "Súčet", "Calculations.Options.sum.label": "Súčet", - "CardDetail.Follow": "Sleduj", - "CardDetail.Following": "Sledujúce", - "CardDetail.add-content": "Pridaj obsah", - "CardDetail.add-icon": "Pridaj ikonu", - "CardDetail.add-property": "+ Pridaj vlastnosť", - "CardDetail.addCardText": "Pridaj text karty", - "CardDetail.moveContent": "presuň obsah karty", - "CardDetail.new-comment-placeholder": "Pridaj komentár ...", - "CardDetailProperty.confirm-delete-heading": "Potvrď vymazanie vlastnosti", - "CardDetailProperty.confirm-delete-subtext": "Skutočne chcete vymazať \"{propertyName}\"? Mazaním ju odstránite zo všetkých kariet na tabuli.", - "CardDetailProperty.confirm-property-name-change-subtext": "Skutočne chcete vymazať \"{propertyName}\" {customText}? Ovplyvní to hodnoty na {numOfCards} kartách na tabuli, a môže viesť k strate dát.", - "CardDetailProperty.confirm-property-type-change": "Potvrď zmenu typu vlastnosti!", + "CalendarCard.untitled": "Bez názvu", + "CardActionsMenu.copiedLink": "Skopírované!", + "CardActionsMenu.copyLink": "Skopírovať odkaz", + "CardActionsMenu.delete": "Odstrániť", + "CardActionsMenu.duplicate": "Duplikovať", + "CardBadges.title-checkboxes": "Začiarkávacie políčka", + "CardBadges.title-comments": "Komentáre", + "CardBadges.title-description": "Táto karta má popis", + "CardDetail.Attach": "Priložiť", + "CardDetail.Follow": "Sledovať", + "CardDetail.Following": "Sledujúci", + "CardDetail.add-content": "Pridať obsah", + "CardDetail.add-icon": "Pridať ikonu", + "CardDetail.add-property": "+ Pridať vlastnosť", + "CardDetail.addCardText": "pridať text karty", + "CardDetail.limited-body": "Vylepšiť na náš Professional alebo Enterprise plán.", + "CardDetail.limited-button": "Zmeniť plán", + "CardDetail.limited-title": "Táto karta je skrytá", + "CardDetail.moveContent": "Presunúť obsah karty", + "CardDetail.new-comment-placeholder": "Pridať komentár...", + "CardDetailProperty.confirm-delete-heading": "Potvrdiť vymazanie vlastnosti", + "CardDetailProperty.confirm-delete-subtext": "Skutočne chcete vymazať hodnotu \"{propertyName}\"? Bude odstránená zo všetkých kariet na tejto tabuli.", + "CardDetailProperty.confirm-property-name-change-subtext": "Skutočne chcete zmeniť hodnotu \"{propertyName}\" {customText}? Ovplyvní to {numOfCards} kariet na tabuli a môže viesť k strate dát.", + "CardDetailProperty.confirm-property-type-change": "Potvrdiť zmenu typu vlastnosti", "CardDetailProperty.delete-action-button": "Odstrániť", "CardDetailProperty.property-change-action-button": "Zmeniť vlastnosť", "CardDetailProperty.property-changed": "Zmena vlastnosti úspešná!", - "CardDetailProperty.property-deleted": "Mazanie {propertyName} úspešné!", - "CardDetailProperty.property-name-change-subtext": "typ od \"{oldPropType}\" do \"{newPropType}\"", - "CardDialog.editing-template": "Editujete template.", - "CardDialog.nocard": "Karta neexistuje alebo je neprístupná.", - "ColorOption.selectColor": "Vyber {color} farbu", + "CardDetailProperty.property-deleted": "Odstránenie {propertyName} úspešné!", + "CardDetailProperty.property-name-change-subtext": "typ z \"{oldPropType}\" na \"{newPropType}\"", + "CardDetial.limited-link": "Dozvedieť sa viac o našich plánoch.", + "CardDialog.delete-confirmation-dialog-attachment": "Potvrdiť odstránenie prílohy", + "CardDialog.delete-confirmation-dialog-button-text": "Odstrániť", + "CardDialog.delete-confirmation-dialog-heading": "Potvrdiť odstránenie karty", + "CardDialog.editing-template": "Upravujete šablónu.", + "CardDialog.nocard": "Táto karta neexistuje alebo nie je prístupná.", + "Categories.CreateCategoryDialog.CancelText": "Zrušiť", + "Categories.CreateCategoryDialog.CreateText": "Vytvoriť", + "Categories.CreateCategoryDialog.Placeholder": "Nazvite Vašu kategóriu", + "Categories.CreateCategoryDialog.UpdateText": "Zmeniť", + "CenterPanel.Login": "Prihlásiť sa", + "CenterPanel.Share": "Zdieľať", + "ChannelIntro.CreateBoard": "Vytvoriť nástenku", + "CloudMessage.cloud-server": "Získajte vlastný cloudový server zadarmo.", + "ColorOption.selectColor": "Vyberte {color} farbu", "Comment.delete": "Odstrániť", - "CommentsList.send": "Poslať", + "CommentsList.send": "Odoslať", + "ConfirmPerson.empty": "Prázdne", + "ConfirmPerson.search": "Vyhľadať...", "ConfirmationDialog.cancel-action": "Zrušiť", "ConfirmationDialog.confirm-action": "Potvrdiť", "ContentBlock.Delete": "Odstrániť", - "ContentBlock.DeleteAction": "Odstrániť", - "ContentBlock.addElement": "pridaj {type}", - "ContentBlock.checkbox": "checkbox", + "ContentBlock.DeleteAction": "odstrániť", + "ContentBlock.addElement": "pridať {type}", + "ContentBlock.checkbox": "začiarkávacie pole", "ContentBlock.divider": "oddeľovač", - "ContentBlock.editCardCheckbox": "označený-checkbox", + "ContentBlock.editCardCheckbox": "Začiarknuté pole", "ContentBlock.editCardCheckboxText": "upraviť text karty", "ContentBlock.editCardText": "upraviť text karty", "ContentBlock.editText": "Upraviť text...", "ContentBlock.image": "obrázok", - "ContentBlock.insertAbove": "vlož nad", - "ContentBlock.moveDown": "Presuň dole", - "ContentBlock.moveUp": "Presuň hore", + "ContentBlock.insertAbove": "Vložiť nad", + "ContentBlock.moveBlock": "presunúť obsah karty", + "ContentBlock.moveDown": "Presunúť dole", + "ContentBlock.moveUp": "Presunúť hore", "ContentBlock.text": "text", + "DateRange.clear": "Vyčistiť", + "DateRange.empty": "Prázdny", + "DateRange.endDate": "Koncový dátum", + "DateRange.today": "Dnes", "DeleteBoardDialog.confirm-cancel": "Zrušiť", "DeleteBoardDialog.confirm-delete": "Odstrániť", "DeleteBoardDialog.confirm-info": "Naozaj chcete odstrániť nástenku “{boardTitle}”? Odstránením vymažete všetky karty na tabuli.", + "DeleteBoardDialog.confirm-info-template": "Naozaj chcete odstrániť nástenkovú šablónu \"{boardTitle}\"?", "DeleteBoardDialog.confirm-tite": "Potvrďte odstránenie nástenky", - "Dialog.closeDialog": "Zavrieť dialog", + "DeleteBoardDialog.confirm-tite-template": "Potvrdiť odstránenie šablóny nástenky", + "Dialog.closeDialog": "Zatvoriť dialógové okno", "EditableDayPicker.today": "Dnes", - "Error.mobileweb": "Mobile web support is currently in early beta. Not all functionality may be present.", - "Error.websocket-closed": "Websocket pripojenie zlyhalo - prerušené. Skontrolujte konfiguráciu servera ak problém pretrváva.", + "Error.mobileweb": "Podpora pre mobilné prehliadače je v skorej bete. Niektoré funkcionality môžu chýbať.", + "Error.websocket-closed": "Websocket pripojenie zlyhalo - bolo prerušené. Pokiaľ problém pretrváva, skontrolujte konfiguráciu servera.", + "Filter.contains": "obsahuje", + "Filter.ends-with": "končí s", "Filter.includes": "zahŕňa", + "Filter.is": "je", "Filter.is-empty": "je prázdny", "Filter.is-not-empty": "nie je prázdny", + "Filter.is-not-set": "nie je nastavený", + "Filter.is-set": "je nastavený", + "Filter.not-contains": "neobsahuje", + "Filter.not-ends-with": "nekončí s", "Filter.not-includes": "nezahŕňa", + "Filter.not-starts-with": "nezačína s", + "Filter.starts-with": "začína s", + "FilterByText.placeholder": "text filtra", "FilterComponent.add-filter": "+ Pridaj filter", "FilterComponent.delete": "Odstrániť", + "FilterValue.empty": "(prázdny)", + "FindBoardsDialog.IntroText": "Vyhľadať nástenky", + "FindBoardsDialog.NoResultsFor": "Žiadne výsledky pre \"{searchQuery}\"", + "FindBoardsDialog.NoResultsSubtext": "Skontrolujte pravopis alebo vyskúšajte iný pojem.", + "FindBoardsDialog.SubTitle": "Nájdite nástenku písaním. Použite HORE/DOLE na prehliadanie, ENTER na vybratie a ESC na zrušenie", + "FindBoardsDialog.Title": "Nájsť nástenky", + "GroupBy.hideEmptyGroups": "Skryť {count} prázdnych skupín", + "GroupBy.showHiddenGroups": "Zobraziť {count} prázdnych skupín", "GroupBy.ungroup": "Zrušiť zoskupenie", - "KanbanCard.untitled": "Nepomenované", - "Mutator.new-card-from-template": "nová karta z template-u", - "Mutator.new-template-from-card": "nový template z karty", + "HideBoard.MenuOption": "Skryť nástenku", + "KanbanCard.untitled": "Bez názvu", + "MentionSuggestion.is-not-board-member": "(nie je členom nástenky)", + "Mutator.new-board-from-template": "nová nástenka zo šablóny", + "Mutator.new-card-from-template": "nová karta zo šablóny", + "Mutator.new-template-from-card": "nová šablóna z karty", "PropertyMenu.Delete": "Odstrániť", "PropertyMenu.changeType": "Zmeniť vlastnosť", "PropertyMenu.selectType": "Vybrať vlastnosť", From bd7608ff11aa63d16fd391ba680aa9eb3066b786 Mon Sep 17 00:00:00 2001 From: hattori611 Date: Mon, 27 Feb 2023 16:24:00 +0100 Subject: [PATCH 13/35] Translated using Weblate (Vietnamese) Currently translated at 6.6% (30 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/vi/ --- webapp/i18n/vi.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webapp/i18n/vi.json b/webapp/i18n/vi.json index 921d2f784..aec863456 100644 --- a/webapp/i18n/vi.json +++ b/webapp/i18n/vi.json @@ -1,4 +1,7 @@ { + "AppBar.Tooltip": "Chuyển sang các bảng đã liên kết", + "Attachment.Attachment-title": "Đính kèm", + "AttachmentBlock.DeleteAction": "xóa", "BoardComponent.add-a-group": "+ Thêm nhóm", "BoardComponent.delete": "Xóa", "BoardComponent.hidden-columns": "Cột ẩn", From 805e0d93dc30c5decbb5419ad766e3982affaaec Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Thu, 2 Mar 2023 04:09:32 -0700 Subject: [PATCH 14/35] disable patches on read-only unmount (#4608) --- webapp/src/properties/url/url.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/webapp/src/properties/url/url.tsx b/webapp/src/properties/url/url.tsx index 047fe3490..5280ead1b 100644 --- a/webapp/src/properties/url/url.tsx +++ b/webapp/src/properties/url/url.tsx @@ -35,11 +35,14 @@ const URLProperty = (props: PropertyProps): JSX.Element => { if (value !== (props.card.fields.properties[props.propertyTemplate?.id || ''] || '')) { mutator.changePropertyValue(props.board.id, props.card, props.propertyTemplate?.id || '', value) } - }, [props.card, props.propertyTemplate, value]) + }, [props.board.id, props.card, props.propertyTemplate?.id, value]) const saveTextPropertyRef = useRef<() => void>(saveTextProperty) - saveTextPropertyRef.current = saveTextProperty - + if (props.readOnly) { + saveTextPropertyRef.current = () => null + } else { + saveTextPropertyRef.current = saveTextProperty + } useEffect(() => { return () => { saveTextPropertyRef.current && saveTextPropertyRef.current() From c01776e92a59317dc122bee4d03a18958def5580 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Thu, 2 Mar 2023 16:35:51 -0700 Subject: [PATCH 15/35] update version to v7.10.0 --- mattermost-plugin/plugin.json | 2 +- mattermost-plugin/server/manifest.go | 2 +- server/model/version.go | 1 + webapp/package-lock.json | 4 ++-- webapp/package.json | 2 +- .../__snapshots__/globalHeader.test.tsx.snap | 2 +- .../__snapshots__/sidebar.test.tsx.snap | 20 +++++++++---------- webapp/src/constants.ts | 4 ++-- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/mattermost-plugin/plugin.json b/mattermost-plugin/plugin.json index 7c458cc74..a02f513bc 100644 --- a/mattermost-plugin/plugin.json +++ b/mattermost-plugin/plugin.json @@ -6,7 +6,7 @@ "support_url": "https://github.com/mattermost/focalboard/issues", "release_notes_url": "https://github.com/mattermost/focalboard/releases", "icon_path": "assets/starter-template-icon.svg", - "version": "7.9.0", + "version": "7.10.0", "min_server_version": "7.2.0", "server": { "executables": { diff --git a/mattermost-plugin/server/manifest.go b/mattermost-plugin/server/manifest.go index db79def9f..d950ee0ca 100644 --- a/mattermost-plugin/server/manifest.go +++ b/mattermost-plugin/server/manifest.go @@ -20,7 +20,7 @@ const manifestStr = ` "support_url": "https://github.com/mattermost/focalboard/issues", "release_notes_url": "https://github.com/mattermost/focalboard/releases", "icon_path": "assets/starter-template-icon.svg", - "version": "7.9.0", + "version": "7.10.0", "min_server_version": "7.2.0", "server": { "executables": { diff --git a/server/model/version.go b/server/model/version.go index cace1f357..52fe08e79 100644 --- a/server/model/version.go +++ b/server/model/version.go @@ -8,6 +8,7 @@ import ( // It should be maintained in chronological order with most current // release at the front of the list. var versions = []string{ + "7.10.0", "7.9.0", "7.8.0", "7.7.0", diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 65f21b94b..9de98ccbd 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,12 +1,12 @@ { "name": "focalboard", - "version": "7.9.0", + "version": "7.10.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "focalboard", - "version": "7.9.0", + "version": "7.10.0", "dependencies": { "@draft-js-plugins/editor": "^4.1.2", "@draft-js-plugins/emoji": "^4.6.0", diff --git a/webapp/package.json b/webapp/package.json index d26d1e3ae..935c76e31 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "focalboard", - "version": "7.9.0", + "version": "7.10.0", "private": true, "description": "", "scripts": { diff --git a/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap b/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap index a348d7b30..ffd6d4819 100644 --- a/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap +++ b/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap @@ -10,7 +10,7 @@ exports[`components/sidebar/GlobalHeader header menu should match snapshot 1`] = /> diff --git a/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap b/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap index 8fc1ab28c..4533b2b83 100644 --- a/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap +++ b/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap @@ -51,9 +51,9 @@ exports[`components/sidebarSidebar dont show hidden boards 1`] = ` >
- v7.9.0 + v7.10.0
@@ -252,9 +252,9 @@ exports[`components/sidebarSidebar should assign default category if current boa >
- v7.9.0 + v7.10.0
@@ -508,9 +508,9 @@ exports[`components/sidebarSidebar shouldnt do any category assignment is board >
- v7.9.0 + v7.10.0
@@ -919,9 +919,9 @@ exports[`components/sidebarSidebar sidebar hidden 1`] = ` >
- v7.9.0 + v7.10.0
@@ -1213,9 +1213,9 @@ exports[`components/sidebarSidebar some categories hidden 1`] = ` >
- v7.9.0 + v7.10.0
diff --git a/webapp/src/constants.ts b/webapp/src/constants.ts index d900092b1..9331eebe4 100644 --- a/webapp/src/constants.ts +++ b/webapp/src/constants.ts @@ -37,8 +37,8 @@ class Constants { static readonly titleColumnId = '__title' static readonly badgesColumnId = '__badges' - static readonly versionString = '7.9.0' - static readonly versionDisplayString = 'Mar 2023' + static readonly versionString = '7.10.0' + static readonly versionDisplayString = 'Apr 2023' static readonly archiveHelpPage = 'https://docs.mattermost.com/boards/migrate-to-boards.html' static readonly imports = [ From 39dbdbba7721ba5bd589c83c01a5fa2a620a1afb Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Fri, 3 Mar 2023 15:10:23 +0530 Subject: [PATCH 16/35] Added data migration to de-duplicate data from category_boards table --- .../store/sqlstore/data_migrations.go | 96 ++++++++++++++++++- server/services/store/sqlstore/migrate.go | 10 ++ ...ategory_board_add_unique_constraint.up.sql | 2 +- 3 files changed, 102 insertions(+), 6 deletions(-) diff --git a/server/services/store/sqlstore/data_migrations.go b/server/services/store/sqlstore/data_migrations.go index ae1974792..a505cb97d 100644 --- a/server/services/store/sqlstore/data_migrations.go +++ b/server/services/store/sqlstore/data_migrations.go @@ -21,11 +21,12 @@ const ( // query, so we want to stay safely below. CategoryInsertBatch = 1000 - TemplatesToTeamsMigrationKey = "TemplatesToTeamsMigrationComplete" - UniqueIDsMigrationKey = "UniqueIDsMigrationComplete" - CategoryUUIDIDMigrationKey = "CategoryUuidIdMigrationComplete" - TeamLessBoardsMigrationKey = "TeamLessBoardsMigrationComplete" - DeletedMembershipBoardsMigrationKey = "DeletedMembershipBoardsMigrationComplete" + TemplatesToTeamsMigrationKey = "TemplatesToTeamsMigrationComplete" + UniqueIDsMigrationKey = "UniqueIDsMigrationComplete" + CategoryUUIDIDMigrationKey = "CategoryUuidIdMigrationComplete" + TeamLessBoardsMigrationKey = "TeamLessBoardsMigrationComplete" + DeletedMembershipBoardsMigrationKey = "DeletedMembershipBoardsMigrationComplete" + DeDuplicateCategoryBoardTableMigrationKey = "DeDuplicateCategoryBoardTableComplete" ) func (s *SQLStore) getBlocksWithSameID(db sq.BaseRunner) ([]*model.Block, error) { @@ -790,3 +791,88 @@ func (s *SQLStore) getCollationAndCharset(tableName string) (string, string, err return collation, charSet, nil } + +func (s *SQLStore) RunDeDuplicateCategoryBoardsMigration(currentMigration int) error { + setting, err := s.GetSystemSetting(DeDuplicateCategoryBoardTableMigrationKey) + if err != nil { + return fmt.Errorf("cannot get DeDuplicateCategoryBoardTableMigration state: %w", err) + } + + // If the migration is already completed, do not run it again. + if hasAlreadyRun, _ := strconv.ParseBool(setting); hasAlreadyRun { + return nil + } + + if currentMigration >= (deDuplicateCategoryBoards + 1) { + // if the migration for which we're fixing the data is already applied, + // no need to check fix anything + + if err := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); err != nil { + return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", err) + } + return nil + } + + needed, err := s.doesDuplicateCategoryBoardsExist() + if err != nil { + return err + } + + if !needed { + if err := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); err != nil { + return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", err) + } + } + + if s.dbType == model.MysqlDBType { + return s.runMySQLDeDuplicateCategoryBoardsMigration() + } else if s.dbType == model.PostgresDBType { + return s.runPostgresDeDuplicateCategoryBoardsMigration() + } + + if err := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); err != nil { + return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", err) + } + + return nil +} + +func (s *SQLStore) doesDuplicateCategoryBoardsExist() (bool, error) { + subQuery := s.getQueryBuilder(s.db). + Select("user_id", "board_id", "count(*) AS count"). + From(s.tablePrefix+"category_boards"). + GroupBy("user_id", "board_id"). + Having("count(*) > 1") + + query := s.getQueryBuilder(s.db). + Select("COUNT(*)"). + FromSelect(subQuery, "duplicate_dataset") + + row := query.QueryRow() + + count := 0 + if err := row.Scan(&count); err != nil { + s.logger.Error("Error occurred reading number of duplicate records in category_boards table", mlog.Err(err)) + return false, err + } + + return count > 0, nil +} + +func (s *SQLStore) runMySQLDeDuplicateCategoryBoardsMigration() error { + query := fmt.Sprintf("WITH duplicates AS (SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, board_id) AS rownum FROM %[1]scategory_boards) DELETE %[1]scategory_boards FROM %[1]scategory_boards JOIN duplicates USING(id) WHERE duplicates.rownum > 1;", s.tablePrefix) + if _, err := s.db.Exec(query); err != nil { + s.logger.Error("Failed to de-duplicate data in category_boards table", mlog.Err(err)) + } + + return nil +} + +func (s *SQLStore) runPostgresDeDuplicateCategoryBoardsMigration() error { + query := fmt.Sprintf("WITH duplicates AS (SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, board_id) AS rownum FROM %[1]scategory_boards) DELETE FROM %[1]scategory_boards USING duplicates WHERE %[1]scategory_boards.id = duplicates.id AND duplicates.rownum > 1;", s.tablePrefix) + if _, err := s.db.Exec(query); err != nil { + s.logger.Error("Failed to de-duplicate data in category_boards table", mlog.Err(err)) + } + + return nil +} diff --git a/server/services/store/sqlstore/migrate.go b/server/services/store/sqlstore/migrate.go index afd9463ac..dd1be4d5f 100644 --- a/server/services/store/sqlstore/migrate.go +++ b/server/services/store/sqlstore/migrate.go @@ -36,6 +36,7 @@ const ( uniqueIDsMigrationRequiredVersion = 14 teamLessBoardsMigrationRequiredVersion = 18 categoriesUUIDIDMigrationRequiredVersion = 20 + deDuplicateCategoryBoards = 35 tempSchemaMigrationTableName = "temp_schema_migration" ) @@ -248,6 +249,15 @@ func (s *SQLStore) runMigrationSequence(engine *morph.Morph, driver drivers.Driv return err } + if mErr := s.ensureMigrationsAppliedUpToVersion(engine, driver, deDuplicateCategoryBoards); mErr != nil { + return mErr + } + + currentMigrationVersion := len(appliedMigrations) + if mErr := s.RunDeDuplicateCategoryBoardsMigration(currentMigrationVersion); err != nil { + return mErr + } + s.logger.Debug("== Applying all remaining migrations ====================", mlog.Int("current_version", len(appliedMigrations)), ) diff --git a/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.up.sql b/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.up.sql index 8858033b0..4a658bdb3 100644 --- a/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.up.sql +++ b/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.up.sql @@ -23,4 +23,4 @@ SELECT id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden FROM {{.prefix}}category_boards_old; DROP TABLE {{.prefix}}category_boards_old; -{{end}} \ No newline at end of file +{{end}} From 7195a2bf1d9e10210f0d2ae137b5e78df984a490 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Fri, 3 Mar 2023 15:45:24 +0530 Subject: [PATCH 17/35] Lint fix --- mattermost-plugin/server/manifest.go | 3 +-- .../store/sqlstore/data_migrations.go | 22 ++++++++++++------- server/services/store/sqlstore/migrate.go | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/mattermost-plugin/server/manifest.go b/mattermost-plugin/server/manifest.go index db79def9f..941134c75 100644 --- a/mattermost-plugin/server/manifest.go +++ b/mattermost-plugin/server/manifest.go @@ -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 } ] } diff --git a/server/services/store/sqlstore/data_migrations.go b/server/services/store/sqlstore/data_migrations.go index a505cb97d..8d0a3dad3 100644 --- a/server/services/store/sqlstore/data_migrations.go +++ b/server/services/store/sqlstore/data_migrations.go @@ -807,8 +807,8 @@ func (s *SQLStore) RunDeDuplicateCategoryBoardsMigration(currentMigration int) e // if the migration for which we're fixing the data is already applied, // no need to check fix anything - if err := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); err != nil { - return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", err) + if mErr := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); mErr != nil { + return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", mErr) } return nil } @@ -819,8 +819,8 @@ func (s *SQLStore) RunDeDuplicateCategoryBoardsMigration(currentMigration int) e } if !needed { - if err := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); err != nil { - return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", err) + if mErr := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); mErr != nil { + return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", mErr) } } @@ -830,8 +830,8 @@ func (s *SQLStore) RunDeDuplicateCategoryBoardsMigration(currentMigration int) e return s.runPostgresDeDuplicateCategoryBoardsMigration() } - if err := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); err != nil { - return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", err) + if mErr := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); mErr != nil { + return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", mErr) } return nil @@ -860,7 +860,10 @@ func (s *SQLStore) doesDuplicateCategoryBoardsExist() (bool, error) { } func (s *SQLStore) runMySQLDeDuplicateCategoryBoardsMigration() error { - query := fmt.Sprintf("WITH duplicates AS (SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, board_id) AS rownum FROM %[1]scategory_boards) DELETE %[1]scategory_boards FROM %[1]scategory_boards JOIN duplicates USING(id) WHERE duplicates.rownum > 1;", s.tablePrefix) + 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;" if _, err := s.db.Exec(query); err != nil { s.logger.Error("Failed to de-duplicate data in category_boards table", mlog.Err(err)) } @@ -869,7 +872,10 @@ func (s *SQLStore) runMySQLDeDuplicateCategoryBoardsMigration() error { } func (s *SQLStore) runPostgresDeDuplicateCategoryBoardsMigration() error { - query := fmt.Sprintf("WITH duplicates AS (SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, board_id) AS rownum FROM %[1]scategory_boards) DELETE FROM %[1]scategory_boards USING duplicates WHERE %[1]scategory_boards.id = duplicates.id AND duplicates.rownum > 1;", s.tablePrefix) + query := "WITH duplicates AS (SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, board_id) AS rownum " + + "FROM " + s.tablePrefix + "category_boards) " + + "DELETE FROM " + s.tablePrefix + "category_boards USING duplicates " + + "WHERE " + s.tablePrefix + "category_boards.id = duplicates.id AND duplicates.rownum > 1;" 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/migrate.go b/server/services/store/sqlstore/migrate.go index dd1be4d5f..d3fd7e3c5 100644 --- a/server/services/store/sqlstore/migrate.go +++ b/server/services/store/sqlstore/migrate.go @@ -254,7 +254,7 @@ func (s *SQLStore) runMigrationSequence(engine *morph.Morph, driver drivers.Driv } currentMigrationVersion := len(appliedMigrations) - if mErr := s.RunDeDuplicateCategoryBoardsMigration(currentMigrationVersion); err != nil { + if mErr := s.RunDeDuplicateCategoryBoardsMigration(currentMigrationVersion); mErr != nil { return mErr } From 1e3c03354724425b4448faf1ee40fd4e702a4a25 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Fri, 3 Mar 2023 15:55:52 +0530 Subject: [PATCH 18/35] shortcircuit for mysql --- server/services/store/sqlstore/data_migrations.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/services/store/sqlstore/data_migrations.go b/server/services/store/sqlstore/data_migrations.go index 8d0a3dad3..15d86129d 100644 --- a/server/services/store/sqlstore/data_migrations.go +++ b/server/services/store/sqlstore/data_migrations.go @@ -793,6 +793,14 @@ func (s *SQLStore) getCollationAndCharset(tableName string) (string, string, err } func (s *SQLStore) RunDeDuplicateCategoryBoardsMigration(currentMigration int) error { + // not supported for SQLite + if s.dbType == model.SqliteDBType { + if mErr := s.setSystemSetting(s.db, DeDuplicateCategoryBoardTableMigrationKey, strconv.FormatBool(true)); mErr != nil { + return fmt.Errorf("cannot mark migration %s as completed: %w", "RunDeDuplicateCategoryBoardsMigration", mErr) + } + return nil + } + setting, err := s.GetSystemSetting(DeDuplicateCategoryBoardTableMigrationKey) if err != nil { return fmt.Errorf("cannot get DeDuplicateCategoryBoardTableMigration state: %w", err) From f9aa6c1668a7f3dd91c89e2f720a5c9958cb312a Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Fri, 3 Mar 2023 18:49:53 +0530 Subject: [PATCH 19/35] Fixed server test --- server/services/store/storetests/system.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/services/store/storetests/system.go b/server/services/store/storetests/system.go index d6fa83a99..6a59826aa 100644 --- a/server/services/store/storetests/system.go +++ b/server/services/store/storetests/system.go @@ -10,8 +10,9 @@ import ( // these system settings are created when running the data migrations, // so they will be present after the tests setup. var dataMigrationSystemSettings = map[string]string{ - "UniqueIDsMigrationComplete": "true", - "CategoryUuidIdMigrationComplete": "true", + "UniqueIDsMigrationComplete": "true", + "CategoryUuidIdMigrationComplete": "true", + "DeDuplicateCategoryBoardTableComplete": "true", } func addBaseSettings(m map[string]string) map[string]string { From 56954e0491844a956ac095f63b97d72b68aa2523 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Sat, 4 Mar 2023 12:20:05 +0500 Subject: [PATCH 20/35] Updating welcome screen for small sizes (#4609) --- .../__snapshots__/welcomePage.test.tsx.snap | 112 ++++++++++-------- webapp/src/pages/welcome/welcomePage.scss | 21 +++- webapp/src/pages/welcome/welcomePage.tsx | 104 ++++++++-------- 3 files changed, 138 insertions(+), 99 deletions(-) diff --git a/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap b/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap index 2dfc52f90..ef9ac5df4 100644 --- a/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap +++ b/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap @@ -18,32 +18,40 @@ exports[`pages/welcome Welcome Page shows Explore Page 1`] = ` > Boards is a project management tool that helps define, organize, track, and manage work across teams using a familiar Kanban board view. - Boards Welcome Image - Boards Welcome Image - -
+ Boards Welcome Image +
+ + +
+ @@ -67,32 +75,40 @@ exports[`pages/welcome Welcome Page shows Explore Page with subpath 1`] = ` > Boards is a project management tool that helps define, organize, track, and manage work across teams using a familiar Kanban board view. - Boards Welcome Image - Boards Welcome Image - - + Boards Welcome Image +
+ + +
+ diff --git a/webapp/src/pages/welcome/welcomePage.scss b/webapp/src/pages/welcome/welcomePage.scss index d6810e1bf..ebeb7f2b6 100644 --- a/webapp/src/pages/welcome/welcomePage.scss +++ b/webapp/src/pages/welcome/welcomePage.scss @@ -10,6 +10,26 @@ @media (max-height: 768px) { justify-content: flex-start; height: auto; + padding-top: 40px; + } + + .WelcomePage__content { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + + @media (max-height: 800px) { + flex-direction: column-reverse; + margin-top: 16px; + } + } + + .WelcomePage__buttons { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; } > div { @@ -34,7 +54,6 @@ } .skip { - margin-top: 12px; color: rgba(var(--link-color-rgb), 1); cursor: pointer; diff --git a/webapp/src/pages/welcome/welcomePage.tsx b/webapp/src/pages/welcome/welcomePage.tsx index df7a6edbf..723f5c3f5 100644 --- a/webapp/src/pages/welcome/welcomePage.tsx +++ b/webapp/src/pages/welcome/welcomePage.tsx @@ -127,59 +127,63 @@ const WelcomePage = () => { /> - {/* This image will be rendered on large screens over 2000px */} - Boards Welcome Image +
+ {/* This image will be rendered on large screens over 2000px */} + Boards Welcome Image - {/* This image will be rendered on small screens below 2000px */} - Boards Welcome Image + {/* This image will be rendered on small screens below 2000px */} + Boards Welcome Image - {me?.is_guest !== true && - } +
+ {me?.is_guest !== true && + } - {me?.is_guest !== true && - - - } - {me?.is_guest === true && - } + {me?.is_guest !== true && + + + } + {me?.is_guest === true && + } +
+
) From 24d6ad48e7cc7b35daba309dc7993bb4103711f7 Mon Sep 17 00:00:00 2001 From: Vidar Haarr Date: Fri, 3 Mar 2023 14:25:16 +0100 Subject: [PATCH 21/35] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegian?= =?UTF-8?q?=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 40.6% (183 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/nb_NO/ --- webapp/i18n/nb_NO.json | 169 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 5 deletions(-) diff --git a/webapp/i18n/nb_NO.json b/webapp/i18n/nb_NO.json index 3b6c0ce38..604a40885 100644 --- a/webapp/i18n/nb_NO.json +++ b/webapp/i18n/nb_NO.json @@ -1,14 +1,42 @@ { + "AppBar.Tooltip": "Veksle lenkede tavler", + "Attachment.Attachment-title": "Vedlegg", + "AttachmentBlock.DeleteAction": "slett", + "AttachmentBlock.addElement": "legg til {type}", + "AttachmentBlock.delete": "Vedlegg slettet.", + "AttachmentBlock.failed": "Denne filen kunne ikke lastes opp fordi størrelsesgrensen er nådd.", + "AttachmentBlock.upload": "Vedlegg lastes opp.", + "AttachmentBlock.uploadSuccess": "Vedlegg lastet opp.", + "AttachmentElement.delete-confirmation-dialog-button-text": "Slett", + "AttachmentElement.download": "Last ned", + "AttachmentElement.upload-percentage": "Laster opp ...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Legg til gruppe", "BoardComponent.delete": "Slett", "BoardComponent.hidden-columns": "Skjulte kolonner", "BoardComponent.hide": "Skjul", "BoardComponent.new": "+ Ny", "BoardComponent.no-property": "Ingen {property}", - "BoardComponent.no-property-title": "Elementer med en tom {property} område kommer hit. Denne kolonnen kan ikke fjernes.", + "BoardComponent.no-property-title": "Elementer med tom {property} atributt legges her. Denne kolonnen kan ikke fjernes.", "BoardComponent.show": "Vis", + "BoardMember.schemeAdmin": "Admin", + "BoardMember.schemeCommenter": "Kommentator", + "BoardMember.schemeEditor": "Redaktør", + "BoardMember.schemeNone": "Ingen", + "BoardMember.schemeViewer": "Viser", + "BoardMember.unlinkChannel": "Fjern lenke", "BoardPage.newVersion": "En ny versjon av Boards er tilgjengelig, klikk her for å laste inn på nytt.", "BoardPage.syncFailed": "Tavle kan slettes eller adgangen trekkes tilbake.", + "BoardTemplateSelector.add-template": "Lag ny mal", + "BoardTemplateSelector.create-empty-board": "Opprett tom tavle", + "BoardTemplateSelector.delete-template": "Slett", + "BoardTemplateSelector.description": "Legg til en tavle til sidestolpen med hvilken mal du vil fra listen under, eller start med en helt tom tavle.", + "BoardTemplateSelector.edit-template": "Rediger", + "BoardTemplateSelector.plugin.no-content-description": "Legg til en tavle i sidestolpen med hvilken mal du vil, eller start med en tom tavle.", + "BoardTemplateSelector.plugin.no-content-title": "Lag ny tavle", + "BoardTemplateSelector.title": "Lag ny tavle", + "BoardTemplateSelector.use-this-template": "Bruk denne malen", + "BoardsSwitcher.Title": "Finn tavle", + "BoardsUnfurl.Limited": "Flere detaljer er skjult fordi kortet er arkivert", "BoardsUnfurl.Remainder": "+{remainder} mer", "BoardsUnfurl.Updated": "Oppdatert {time}", "Calculations.Options.average.displayName": "Gjennomsnitt", @@ -16,11 +44,142 @@ "Calculations.Options.count.displayName": "Antall", "Calculations.Options.count.label": "Antall", "Calculations.Options.countChecked.displayName": "Avkrysset", - "Calculations.Options.countChecked.label": "Antall avsjekket", + "Calculations.Options.countChecked.label": "Antall valgt", "Calculations.Options.countUnchecked.displayName": "Ikke avmerket", - "Calculations.Options.countUnchecked.label": "Antall Ikke Avmerket", + "Calculations.Options.countUnchecked.label": "Antall ikke valgt", "Calculations.Options.countUniqueValue.displayName": "Unik", - "Calculations.Options.countUniqueValue.label": "Antall Unike Verdier", + "Calculations.Options.countUniqueValue.label": "Antall unike verdier", "Calculations.Options.countValue.displayName": "Verdier", - "Calculations.Options.countValue.label": "Antall Verdier" + "Calculations.Options.countValue.label": "Antall verdier", + "Calculations.Options.dateRange.displayName": "Tidsrom", + "Calculations.Options.dateRange.label": "Tidsrom", + "Calculations.Options.earliest.displayName": "Tiligst", + "Calculations.Options.earliest.label": "Tiligst", + "Calculations.Options.latest.displayName": "Senest", + "Calculations.Options.latest.label": "Senest", + "Calculations.Options.max.displayName": "Maks", + "Calculations.Options.max.label": "Maks", + "Calculations.Options.median.displayName": "Median", + "Calculations.Options.median.label": "Median", + "Calculations.Options.min.displayName": "Min", + "Calculations.Options.min.label": "Min", + "Calculations.Options.none.displayName": "Kalkulèr", + "Calculations.Options.none.label": "Ingen", + "Calculations.Options.percentChecked.displayName": "Valgt", + "Calculations.Options.percentChecked.label": "Prosent valgt", + "Calculations.Options.percentUnchecked.displayName": "Ikke valgt", + "Calculations.Options.percentUnchecked.label": "Prosent ikke valgt", + "Calculations.Options.range.displayName": "Tidsrom", + "Calculations.Options.range.label": "Tidsrom", + "Calculations.Options.sum.displayName": "Sum", + "Calculations.Options.sum.label": "Sum", + "CalendarCard.untitled": "Uten navn", + "CardActionsMenu.copiedLink": "Kopiert!", + "CardActionsMenu.copyLink": "Kopier lenke", + "CardActionsMenu.delete": "Slett", + "CardActionsMenu.duplicate": "Dupliser", + "CardBadges.title-checkboxes": "Avkrysningsbokser", + "CardBadges.title-comments": "Kommentarer", + "CardBadges.title-description": "Dette kortet har en beskrivelsestekst", + "CardDetail.Attach": "Legg ved", + "CardDetail.Follow": "Følg", + "CardDetail.Following": "Følger", + "CardDetail.add-content": "Legg til innhold", + "CardDetail.add-icon": "Legg til ikon", + "CardDetail.add-property": "+ Legg til en verdi", + "CardDetail.addCardText": "legg inn tekst i kortet", + "CardDetail.limited-body": "Oppgrader til vår profesjonelle eller bedriftsplan.", + "CardDetail.limited-button": "Oppgrader", + "CardDetail.limited-title": "Dette kortet er skjult", + "CardDetail.moveContent": "Flytt innholdet", + "CardDetail.new-comment-placeholder": "Legg til kommentar ...", + "CardDetailProperty.confirm-delete-heading": "Bekreft sletting av verdi", + "CardDetailProperty.confirm-delete-subtext": "Er du sikker på at du vil slette verdien \"{propertyName}\"? Dette vil fjerne verdien fra alle kortene på denne tavlen.", + "CardDetailProperty.confirm-property-name-change-subtext": "Er du sikker på at du vil endre verdien \"{propertyName}\" {customText}? Dette vil påvirke verdien på {numOfCards} kort på denne tavlen, og kan forårsake at du mister informasjon.", + "CardDetailProperty.confirm-property-type-change": "Bekreft endring av verditype", + "CardDetailProperty.delete-action-button": "Slett", + "CardDetailProperty.property-change-action-button": "Endre verdi", + "CardDetailProperty.property-changed": "Verdi endret!", + "CardDetailProperty.property-deleted": "Fjernet {propertyName}!", + "CardDetailProperty.property-name-change-subtext": "type fra \"{oldPropType}\" til \"{newPropType}\"", + "CardDetial.limited-link": "Lær mer om våre planer.", + "CardDialog.delete-confirmation-dialog-attachment": "Bekreft sletting av vedlegg", + "CardDialog.delete-confirmation-dialog-button-text": "Slett", + "CardDialog.delete-confirmation-dialog-heading": "Bekreft sletting av kort", + "CardDialog.editing-template": "Du redigerer en mal.", + "CardDialog.nocard": "Dette kortet eksisterer ikke eller du har ikke tilgang.", + "Categories.CreateCategoryDialog.CancelText": "Avbryt", + "Categories.CreateCategoryDialog.CreateText": "Opprett", + "Categories.CreateCategoryDialog.Placeholder": "Navngi kategorien", + "Categories.CreateCategoryDialog.UpdateText": "Oppdater", + "CenterPanel.Login": "Logg inn", + "CenterPanel.Share": "Del", + "ChannelIntro.CreateBoard": "Opprett tavle", + "CloudMessage.cloud-server": "Få din egen gratis skytjener.", + "ColorOption.selectColor": "Velg {color} farge", + "Comment.delete": "Slett", + "CommentsList.send": "Send", + "ConfirmPerson.empty": "Tom", + "ConfirmPerson.search": "Søk ...", + "ConfirmationDialog.cancel-action": "Avbryt", + "ConfirmationDialog.confirm-action": "Bekreft", + "ContentBlock.Delete": "Slett", + "ContentBlock.DeleteAction": "slett", + "ContentBlock.addElement": "legg til {type}", + "ContentBlock.checkbox": "avkrysningsboks", + "ContentBlock.divider": "avdeler", + "ContentBlock.editCardCheckbox": "krysset-boks", + "ContentBlock.editCardCheckboxText": "rediger kort tekst", + "ContentBlock.editCardText": "rediger kort tekst", + "ContentBlock.editText": "Rediger tekst ...", + "ContentBlock.image": "bilde", + "ContentBlock.insertAbove": "Sett inn over", + "ContentBlock.moveBlock": "flytt kort innhold", + "ContentBlock.moveDown": "Flytt ned", + "ContentBlock.moveUp": "Flytt opp", + "ContentBlock.text": "tekst", + "DateRange.clear": "Tøm", + "DateRange.empty": "Tom", + "DateRange.endDate": "Sluttdato", + "DateRange.today": "I dag", + "DeleteBoardDialog.confirm-cancel": "Avbryt", + "DeleteBoardDialog.confirm-delete": "Slett", + "DeleteBoardDialog.confirm-info": "Er du sikker på at du vil slette tavlen \"{boardTitle}\"? Dette vil slette alle kortene på tavlen.", + "DeleteBoardDialog.confirm-info-template": "Er du sikker på at du vil slette tavlemalen \"{boardTitle}\"?", + "DeleteBoardDialog.confirm-tite": "Bekreft sletting av tavle", + "DeleteBoardDialog.confirm-tite-template": "Bekreft sletting av tavlemal", + "Dialog.closeDialog": "Lukk", + "EditableDayPicker.today": "I dag", + "Error.mobileweb": "Støtte for bruk i nettleser på mobil er i tidlig beta. Alt vil ikke fungere.", + "Error.websocket-closed": "Problemer med kobling til tjeneren. Sjekk konfigurasjonen hvis problemet vedvarer.", + "Filter.contains": "inneholder", + "Filter.ends-with": "ender med", + "Filter.includes": "inkluderer", + "Filter.is": "er", + "Filter.is-empty": "er tom", + "Filter.is-not-empty": "er ikke tom", + "Filter.is-not-set": "er ikke satt", + "Filter.is-set": "er satt", + "Filter.not-contains": "inkluderer ikke", + "Filter.not-ends-with": "ender ikke med", + "Filter.not-includes": "inkluderer ikke", + "Filter.not-starts-with": "starter ikke med", + "Filter.starts-with": "starter med", + "FilterByText.placeholder": "filtrer tekst", + "FilterComponent.add-filter": "+ Nytt filter", + "FilterComponent.delete": "Slett", + "FilterValue.empty": "(tom)", + "FindBoardsDialog.IntroText": "Søk etter tavle", + "FindBoardsDialog.NoResultsFor": "Ingen resultat for \"{searchQuery}\"", + "FindBoardsDialog.NoResultsSubtext": "Sjekk stavingen eller søk på noe annet.", + "FindBoardsDialog.SubTitle": "Skriv for å finne en tavle. Bruk opp/ned for å navigere. Enter for å velge, eller Esc for å avbryte", + "FindBoardsDialog.Title": "Finn tavle", + "GroupBy.hideEmptyGroups": "Skjul {count} tomme grupper", + "GroupBy.showHiddenGroups": "Vis {count} tomme grupper", + "GroupBy.ungroup": "Fjern fra gruppe", + "HideBoard.MenuOption": "Skjul tavlen", + "KanbanCard.untitled": "Uten navn", + "Mutator.new-board-from-template": "ny tavle fra mal", + "Mutator.new-card-from-template": "nytt kort fra mal", + "Mutator.new-template-from-card": "ny mal fra kort" } From 8fe781a16cbe7231372c48b741b9955e64ee598f Mon Sep 17 00:00:00 2001 From: Dmitry Scherbak Date: Fri, 3 Mar 2023 14:25:16 +0100 Subject: [PATCH 22/35] Translated using Weblate (Russian) Currently translated at 75.7% (341 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/ru/ --- webapp/i18n/ru.json | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/webapp/i18n/ru.json b/webapp/i18n/ru.json index b2a7419f8..f6b502b65 100644 --- a/webapp/i18n/ru.json +++ b/webapp/i18n/ru.json @@ -2,14 +2,14 @@ "AppBar.Tooltip": "Переключить связанные доски", "Attachment.Attachment-title": "Вложение", "AttachmentBlock.DeleteAction": "Удалить", - "AttachmentBlock.addElement": "добавить", - "AttachmentBlock.delete": "Вложение успешно удалено.", - "AttachmentBlock.failed": "Не удалось загрузить файл. Достигнут предел размера вложения.", + "AttachmentBlock.addElement": "добавить {type}", + "AttachmentBlock.delete": "Вложение удалено.", + "AttachmentBlock.failed": "Не удалось загрузить файл, так как превышена квота на размер файла.", "AttachmentBlock.upload": "Загрузка вложения.", - "AttachmentBlock.uploadSuccess": "Вложение успешно загружено.", + "AttachmentBlock.uploadSuccess": "Вложение загружено.", "AttachmentElement.delete-confirmation-dialog-button-text": "Удалить", "AttachmentElement.download": "Скачать", - "AttachmentElement.upload-percentage": "Загрузка", + "AttachmentElement.upload-percentage": "Загрузка...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Добавить группу", "BoardComponent.delete": "Удалить", "BoardComponent.hidden-columns": "Скрытые столбцы", @@ -31,12 +31,12 @@ "BoardTemplateSelector.delete-template": "Удалить", "BoardTemplateSelector.description": "Добавьте доску на боковую панель, используя любой из шаблонов, описанных ниже, или начните с нуля.", "BoardTemplateSelector.edit-template": "Изменить", - "BoardTemplateSelector.plugin.no-content-description": "Добавьте доску на боковую панель, используя любой из указанных ниже шаблонов, или начните с нуля.{lineBreak} Участники \"{teamName}\" будут иметь доступ к созданным здесь доскам.", + "BoardTemplateSelector.plugin.no-content-description": "Добавьте доску на боковую панель, используя любой из указанных ниже шаблонов, или начните с нуля.", "BoardTemplateSelector.plugin.no-content-title": "Создать доску", "BoardTemplateSelector.title": "Создать доску", "BoardTemplateSelector.use-this-template": "Использовать этот шаблон", "BoardsSwitcher.Title": "Найти доски", - "BoardsUnfurl.Limited": "Информация скрыта в связи с тем, что карточка находится в архиве", + "BoardsUnfurl.Limited": "Информация скрыта, потому что карточка находится в архиве", "BoardsUnfurl.Remainder": "+{remainder} ещё", "BoardsUnfurl.Updated": "Обновлено {time}", "Calculations.Options.average.displayName": "Среднее", @@ -103,9 +103,9 @@ "CardDetailProperty.property-deleted": "{propertyName} успешно удалено!", "CardDetailProperty.property-name-change-subtext": "тип из \"{oldPropType}\" в \"{newPropType}\"", "CardDetial.limited-link": "Узнайте больше о наших планах.", - "CardDialog.delete-confirmation-dialog-attachment": "Подтвердите удаление вложения!", + "CardDialog.delete-confirmation-dialog-attachment": "Подтвердите удаление вложения", "CardDialog.delete-confirmation-dialog-button-text": "Удалить", - "CardDialog.delete-confirmation-dialog-heading": "Подтвердите удаление карточки!", + "CardDialog.delete-confirmation-dialog-heading": "Подтвердите удаление карточки", "CardDialog.editing-template": "Вы редактируете шаблон.", "CardDialog.nocard": "Эта карточка не существует или недоступна.", "Categories.CreateCategoryDialog.CancelText": "Отмена", @@ -114,10 +114,13 @@ "Categories.CreateCategoryDialog.UpdateText": "Обновить", "CenterPanel.Login": "Логин", "CenterPanel.Share": "Поделиться", + "ChannelIntro.CreateBoard": "Создать доску", "CloudMessage.cloud-server": "Получите свой бесплатный облачный сервер.", "ColorOption.selectColor": "Выберите цвет {color}", "Comment.delete": "Удалить", "CommentsList.send": "Отправить", + "ConfirmPerson.empty": "Пусто", + "ConfirmPerson.search": "Поиск...", "ConfirmationDialog.cancel-action": "Отмена", "ConfirmationDialog.confirm-action": "Подтвердить", "ContentBlock.Delete": "Удалить", @@ -165,6 +168,7 @@ "FilterByText.placeholder": "фильтровать текст", "FilterComponent.add-filter": "+ Добавить фильтр", "FilterComponent.delete": "Удалить", + "FilterValue.empty": "(пусто)", "FindBoardsDialog.IntroText": "Поиск досок", "FindBoardsDialog.NoResultsFor": "Нет результатов для \"{searchQuery}\"", "FindBoardsDialog.NoResultsSubtext": "Проверьте правильность написания или попробуйте другой запрос.", @@ -183,7 +187,7 @@ "OnboardingTour.AddComments.Title": "Добавить комментарии", "OnboardingTour.AddDescription.Body": "Добавьте описание к своей карточке, чтобы Ваши коллеги по команде знали, о чем эта карточка.", "OnboardingTour.AddDescription.Title": "Добавить описание", - "OnboardingTour.AddProperties.Body": "Добавляйте различные свойства карточкам, чтобы сделать их более мощными!", + "OnboardingTour.AddProperties.Body": "Добавляйте различные свойства карточкам, чтобы сделать их более значительными.", "OnboardingTour.AddProperties.Title": "Добавить свойства", "OnboardingTour.AddView.Body": "Перейдите сюда, чтобы создать новый вид для организации доски с использованием различных макетов.", "OnboardingTour.AddView.Title": "Добавить новый вид", @@ -284,6 +288,7 @@ "TableHeaderMenu.insert-right": "Вставить справа", "TableHeaderMenu.sort-ascending": "Сортировать по возрастанию", "TableHeaderMenu.sort-descending": "Сортировать по убыванию", + "TableRow.DuplicateCard": "дублировать карточку", "TableRow.MoreOption": "Больше действий", "TableRow.open": "Открыть", "TopBar.give-feedback": "Дать обратную связь", @@ -351,10 +356,13 @@ "WelcomePage.Explore.Button": "Исследовать", "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": "Связывание доски \"{boardName}\" с каналом даст всем участникам канала доступ на редактирование доски. Вы можете в любое время отвязать доску о канала.", + "boardSelector.confirm-link-board-subtext-with-other-channel": "Привязка \"{boardName}\" с каналом приведет к возможности её редактирования всеми участниками канала (существующими и новыми). Кроме гостей канала.{lineBreak} Эта доска сейчас связана с другим каналом. Он будет отключен, если вы решите изменить привязку.", "boardSelector.create-a-board": "Создать доску", "boardSelector.link": "Ссылка", "boardSelector.search-for-boards": "Поиск досок", @@ -363,6 +371,8 @@ "calendar.month": "Месяц", "calendar.today": "СЕГОДНЯ", "calendar.week": "Неделя", + "centerPanel.undefined": "Отсутствует {propertyName}", + "centerPanel.unknown-user": "Неизвестный пользователь", "cloudMessage.learn-more": "Учить больше", "createImageBlock.failed": "Не удалось загрузить файл. Достигнут предел размера файла.", "default-properties.badges": "Комментарии и описание", @@ -377,11 +387,15 @@ "error.team-undefined": "Не корректная команда.", "error.unknown": "Произошла ошибка.", "generic.previous": "Предыдущий", - "imagePaste.upload-failed": "Некоторые файлы не загружены. Достигнут предел размера файла", + "imagePaste.upload-failed": "Некоторые файлы не загружены из-за превышения квоты на размер файла.", "limitedCard.title": "Карточки скрыты", "login.log-in-button": "Вход в систему", "login.log-in-title": "Вход в систему", "login.register-button": "или создать аккаунт, если у Вас его нет", + "new_channel_modal.create_board.empty_board_description": "Создать новую пустую доску", + "new_channel_modal.create_board.empty_board_title": "Пустая доска", + "new_channel_modal.create_board.select_template_placeholder": "Выбрать шаблон", + "new_channel_modal.create_board.title": "Создать доску для этого канала", "notification-box-card-limit-reached.close-tooltip": "Отложить на 10 дней", "notification-box-card-limit-reached.contact-link": "уведомить Вашего администратора", "notification-box-card-limit-reached.link": "Перейти на платный тариф", @@ -389,13 +403,18 @@ "notification-box-cards-hidden.title": "Это действие скрыло другую карточку", "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": "Разрешения", + "person.add-user-to-board-question": "Вы хотите добавить {username} на доску?", "register.login-button": "или войти в систему, если у вас уже есть аккаунт", "register.signup-title": "Зарегистрируйте свой аккаунт", + "rhs-board-non-admin-msg": "Вы не являетесь администратором этой доски", "rhs-boards.add": "Добавить", "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": "К каналу {channelName} пока не подключены доски", "rhs-boards.no-boards-linked-to-channel-description": "Доски — это инструмент управления проектами, который помогает определять, организовывать, отслеживать и управлять работой между командами, используя знакомое представление доски Канбан.", "rhs-boards.unlink-board": "Отвязать доску", "rhs-channel-boards-header.title": "Доски", From 6b5c263e5627ebbeeec4b14f83755d90308a148a Mon Sep 17 00:00:00 2001 From: Felipe Nogueira Date: Fri, 3 Mar 2023 14:25:16 +0100 Subject: [PATCH 23/35] Translated using Weblate (Portuguese (Brazil)) Currently translated at 94.2% (424 of 450 strings) Translation: Focalboard/webapp Translate-URL: https://translate.mattermost.com/projects/focalboard/webapp/pt_BR/ --- webapp/i18n/pt_BR.json | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/webapp/i18n/pt_BR.json b/webapp/i18n/pt_BR.json index 5cc319d38..aa755dc2f 100644 --- a/webapp/i18n/pt_BR.json +++ b/webapp/i18n/pt_BR.json @@ -1,5 +1,12 @@ { - "AppBar.Tooltip": "Ativar Boards vinculados", + "AppBar.Tooltip": "Ativar boards vinculados", + "Attachment.Attachment-title": "Anexo", + "AttachmentBlock.DeleteAction": "apagar", + "AttachmentBlock.addElement": "adicionar {type}", + "AttachmentBlock.delete": "Anexo apagado.", + "AttachmentBlock.uploadSuccess": "Anexo enviado.", + "AttachmentElement.delete-confirmation-dialog-button-text": "Apagar", + "AttachmentElement.upload-percentage": "Enviando...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Adicione um grupo", "BoardComponent.delete": "Excluir", "BoardComponent.hidden-columns": "Colunas ocultas", @@ -16,8 +23,8 @@ "BoardMember.unlinkChannel": "Desvincular", "BoardPage.newVersion": "Uma nova versão do Boards está disponível, clique aqui para recarregar.", "BoardPage.syncFailed": "O Board pode ter sido excluído ou o acesso revogado.", - "BoardTemplateSelector.add-template": "Novo modelo", - "BoardTemplateSelector.create-empty-board": "Criar board vazio", + "BoardTemplateSelector.add-template": "Criar novo modelo", + "BoardTemplateSelector.create-empty-board": "Criar um board vazio", "BoardTemplateSelector.delete-template": "Excluir", "BoardTemplateSelector.description": "Adicione um quadro à barra lateral usando qualquer um dos modelos definidos abaixo ou comece do zero.", "BoardTemplateSelector.edit-template": "Editar", @@ -25,7 +32,7 @@ "BoardTemplateSelector.plugin.no-content-title": "Criar um board", "BoardTemplateSelector.title": "Criar um board", "BoardTemplateSelector.use-this-template": "Use este template", - "BoardsSwitcher.Title": "Encontrar Boards", + "BoardsSwitcher.Title": "Encontrar boards", "BoardsUnfurl.Limited": "Detalhes adicionais estão ocultos devido ao cartão ter sido arquivado", "BoardsUnfurl.Remainder": "+{remainder} mais", "BoardsUnfurl.Updated": "Atualizado {time}", @@ -71,6 +78,7 @@ "CardBadges.title-checkboxes": "Caixa de seleção", "CardBadges.title-comments": "Comentários", "CardBadges.title-description": "Este cartão tem uma descrição", + "CardDetail.Attach": "Anexar", "CardDetail.Follow": "Seguir", "CardDetail.Following": "Seguindo", "CardDetail.add-content": "Adicionar conteúdo", @@ -102,10 +110,13 @@ "Categories.CreateCategoryDialog.UpdateText": "Atualizar", "CenterPanel.Login": "Login", "CenterPanel.Share": "Compartilhar", + "ChannelIntro.CreateBoard": "Criar um board", "CloudMessage.cloud-server": "Obtenha seu próprio cloud server de graça.", "ColorOption.selectColor": "Selecione {color} Cor", "Comment.delete": "Excluir", "CommentsList.send": "Enviar", + "ConfirmPerson.empty": "Vazio", + "ConfirmPerson.search": "Buscar...", "ConfirmationDialog.cancel-action": "Cancelar", "ConfirmationDialog.confirm-action": "Confirmar", "ContentBlock.Delete": "Excluir", @@ -181,6 +192,7 @@ "OnboardingTour.ShareBoard.Body": "Você pode compartilhar seu board internament, com seu time, ou public para permitir visibilidade fora da sua organização.", "OnboardingTour.ShareBoard.Title": "Compartilhar quadro", "PersonProperty.board-members": "Membros do Board", + "PersonProperty.me": "Eu", "PersonProperty.non-board-members": "Não membros do board", "PropertyMenu.Delete": "Excluir", "PropertyMenu.changeType": "Alterar tipo da propriedade", @@ -235,6 +247,7 @@ "Sidebar.import-archive": "Importar arquivo", "Sidebar.invite-users": "Convidar usuários", "Sidebar.logout": "Sair", + "Sidebar.new-category.badge": "Novo", "Sidebar.no-boards-in-category": "Nenhum board", "Sidebar.product-tour": "Tour pelo produto", "Sidebar.random-icons": "Ícones aleatórios", @@ -257,6 +270,7 @@ "SidebarTour.SidebarCategories.Body": "Todos seus boards agora são organizados sob sua nova barra lateral. Não é mais necessárioa alternar entre espaços de trabalho. Categorias personalizadas em suas estações prévias de trabalho foram automaticamente criadas para você como parte do seu upgrade para v7.2. Estas podem ser removidas ou editadas de acordo com a sua preferência.", "SidebarTour.SidebarCategories.Link": "Saiba mais", "SidebarTour.SidebarCategories.Title": "Categorias de barra lateral", + "SiteStats.total_boards": "Total de boards", "TableComponent.add-icon": "Adicionar Ícone", "TableComponent.name": "Nome", "TableComponent.plus-new": "+ Novo", @@ -267,6 +281,7 @@ "TableHeaderMenu.insert-right": "Inserir à direita", "TableHeaderMenu.sort-ascending": "Ordem ascendente", "TableHeaderMenu.sort-descending": "Ordem descendente", + "TableRow.MoreOption": "Mais ações", "TableRow.open": "Abrir", "TopBar.give-feedback": "Dar feedback", "URLProperty.copiedLink": "Copiado!", @@ -348,6 +363,7 @@ "calendar.month": "Mês", "calendar.today": "HOJE", "calendar.week": "Semana", + "centerPanel.unknown-user": "Usuário desconhecido", "cloudMessage.learn-more": "Saiba mais", "createImageBlock.failed": "Não foi possível enviar o arquivo. Limite de tamanho alcançado.", "default-properties.badges": "Comentários e descrição", @@ -369,6 +385,7 @@ "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.select_template_placeholder": "Selecionar um modelo", "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", @@ -388,14 +405,14 @@ "rhs-boards.dm": "DM", "rhs-boards.gm": "GM", "rhs-boards.header.dm": "esta Direct Message", - "rhs-boards.header.gm": "Este Gruop Message", + "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}", "rhs-boards.linked-boards": "Boards vinculados", "rhs-boards.no-boards-linked-to-channel": "Nenhum board está vinculado a {channelName} ainda", "rhs-boards.no-boards-linked-to-channel-description": "Boards é uma ferramenta de gerenciamento de projeto que ajuda a definir, organizar, rastrear e gerenciar o trabalho entre times, usando uma visualização de quadro estilo Kaban familiar.", "rhs-boards.unlink-board": "Desvincular board", - "rhs-boards.unlink-board1": "Desvincular board Hello", + "rhs-boards.unlink-board1": "Desvincular board", "rhs-channel-boards-header.title": "Boards", "share-board.publish": "Publicar", "share-board.share": "Compartilhar", From d16bbcbe10fba7d525c836c7c84d3cacbe4eb3c1 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 6 Mar 2023 08:48:30 -0700 Subject: [PATCH 24/35] Fix table multiperson (#4613) * clear overflow on multiperson * clear overflow on multiperson --- webapp/src/components/table/table.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/src/components/table/table.scss b/webapp/src/components/table/table.scss index bbc9ef118..6d36a7215 100644 --- a/webapp/src/components/table/table.scss +++ b/webapp/src/components/table/table.scss @@ -203,6 +203,7 @@ width: inherit; } + .MultiPerson.octo-propertyvalue, .Person.octo-propertyvalue, .DateRange.octo-propertyvalue { overflow: unset; From fe29ec882619d0fd0fd2a271c605061d478c1a55 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 6 Mar 2023 12:26:39 -0700 Subject: [PATCH 25/35] check permissions to channel before patching via api --- server/app/boards.go | 13 +++++++++ server/app/boards_test.go | 61 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/server/app/boards.go b/server/app/boards.go index 2ddee918f..d31bd573c 100644 --- a/server/app/boards.go +++ b/server/app/boards.go @@ -355,12 +355,15 @@ func (a *App) PatchBoard(patch *model.BoardPatch, boardID, userID string) (*mode var oldMembers []*model.BoardMember if patch.Type != nil || patch.ChannelID != nil { + testChannel := "" if patch.ChannelID != nil && *patch.ChannelID == "" { var err error oldMembers, err = a.GetMembersForBoard(boardID) if err != nil { a.logger.Error("Unable to get the board members", mlog.Err(err)) } + } else if patch.ChannelID != nil && *patch.ChannelID != "" { + testChannel = *patch.ChannelID } board, err := a.store.GetBoard(boardID) @@ -372,7 +375,17 @@ func (a *App) PatchBoard(patch *model.BoardPatch, boardID, userID string) (*mode } oldChannelID = board.ChannelID isTemplate = board.IsTemplate + if testChannel == "" { + testChannel = oldChannelID + } + + if testChannel != "" { + if !a.permissions.HasPermissionToChannel(userID, testChannel, model.PermissionCreatePost) { + return nil, model.NewErrPermission("access denied to channel") + } + } } + updatedBoard, err := a.store.PatchBoard(boardID, patch, userID) if err != nil { return nil, err diff --git a/server/app/boards_test.go b/server/app/boards_test.go index fc9771e54..ee851fdb8 100644 --- a/server/app/boards_test.go +++ b/server/app/boards_test.go @@ -399,6 +399,67 @@ func TestPatchBoard(t *testing.T) { require.NoError(t, err) require.Equal(t, boardID, patchedBoard.ID) }) + + t.Run("patch type channel, user without post permissions", func(t *testing.T) { + const boardID = "board_id_1" + const userID = "user_id_2" + const teamID = "team_id_1" + + channelID := "myChannel" + patchType := model.BoardTypeOpen + patch := &model.BoardPatch{ + Type: &patchType, + ChannelID: &channelID, + } + + // Type not nil, will cause board to be reteived + // to check isTemplate + th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{ + ID: boardID, + TeamID: teamID, + IsTemplate: true, + }, nil).Times(1) + + th.API.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionCreatePost).Return(false).Times(1) + _, err := th.App.PatchBoard(patch, boardID, userID) + require.Error(t, err) + }) + + t.Run("patch type remove channel, user without post permissions", func(t *testing.T) { + const boardID = "board_id_1" + const userID = "user_id_2" + const teamID = "team_id_1" + + channelID := "myChannel" + clearChannel := "" + patchType := model.BoardTypeOpen + patch := &model.BoardPatch{ + Type: &patchType, + ChannelID: &clearChannel, + } + + // Type not nil, will cause board to be reteived + // to check isTemplate + th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{ + ID: boardID, + TeamID: teamID, + IsTemplate: true, + ChannelID: channelID, + }, nil).Times(2) + + th.API.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionCreatePost).Return(false).Times(1) + + th.API.EXPECT().HasPermissionToTeam(userID, teamID, model.PermissionManageTeam).Return(false).Times(1) + // Should call GetMembersForBoard 2 times + // for WS BroadcastBoardChange + // for AddTeamMembers check + // We are returning the user as a direct Board Member, so BroadcastMemberDelete won't be called + th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{{BoardID: boardID, UserID: userID, SchemeEditor: true}}, nil).Times(1) + + _, err := th.App.PatchBoard(patch, boardID, userID) + require.Error(t, err) + }) + } func TestGetBoardCount(t *testing.T) { From 5ac5eadbeeecbfbcfe7db3e74726585f73b00d99 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Mon, 6 Mar 2023 15:10:58 -0700 Subject: [PATCH 26/35] adding a successful test --- server/app/boards_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/server/app/boards_test.go b/server/app/boards_test.go index ee851fdb8..7830c9297 100644 --- a/server/app/boards_test.go +++ b/server/app/boards_test.go @@ -185,6 +185,7 @@ func TestPatchBoard(t *testing.T) { // Type not null will retrieve team members th.Store.EXPECT().GetUsersByTeam(teamID, "", false, false).Return([]*model.User{}, nil) + th.Store.EXPECT().GetUserByID(userID).Return(&model.User{ID: userID, Username: "UserName"}, nil) th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return( &model.Board{ @@ -425,6 +426,44 @@ func TestPatchBoard(t *testing.T) { require.Error(t, err) }) + t.Run("patch type channel, user with post permissions", func(t *testing.T) { + const boardID = "board_id_1" + const userID = "user_id_2" + const teamID = "team_id_1" + + channelID := "myChannel" + patch := &model.BoardPatch{ + ChannelID: &channelID, + } + + // Type not nil, will cause board to be reteived + // to check isTemplate + th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{ + ID: boardID, + TeamID: teamID, + }, nil).Times(2) + + th.API.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionCreatePost).Return(true).Times(1) + + th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return( + &model.Board{ + ID: boardID, + TeamID: teamID, + }, + nil) + + // Should call GetMembersForBoard 2 times + // - for WS BroadcastBoardChange + // - for AddTeamMembers check + th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil).Times(2) + + th.Store.EXPECT().PostMessage(utils.Anything, "", "").Times(1) + + patchedBoard, err := th.App.PatchBoard(patch, boardID, userID) + require.NoError(t, err) + require.Equal(t, boardID, patchedBoard.ID) + }) + t.Run("patch type remove channel, user without post permissions", func(t *testing.T) { const boardID = "board_id_1" const userID = "user_id_2" From fa72286427d0d11bc10638625ad581795cfbc46f Mon Sep 17 00:00:00 2001 From: Harshil Sharma <18575143+harshilsharma63@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:21:53 +0530 Subject: [PATCH 27/35] Board teamless file path (#4577) * Updated upload and donwload code * Removed archived file handling as we no longer have cloud limits * Fixed server lint * Restored unused * CI * Added new tests * Fixed integration tests * Added Path column for personal server's FileInfo table * Removed sophesticated, elegant debug logs --------- Co-authored-by: Mattermost Build --- server/api/api.go | 2 +- server/api/files.go | 27 +----- server/app/files.go | 41 ++++++++- server/app/files_test.go | 90 +++++++++++++++++-- server/client/client.go | 9 ++ server/services/store/sqlstore/file.go | 4 + .../000039_add_path_to_file_info.down.sql | 1 + .../000039_add_path_to_file_info.up.sql | 1 + server/utils/utils.go | 5 ++ 9 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql create mode 100644 server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql diff --git a/server/api/api.go b/server/api/api.go index ec376a9c0..be0287257 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -227,7 +227,7 @@ func jsonStringResponse(w http.ResponseWriter, code int, message string) { //nol fmt.Fprint(w, message) } -func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { +func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { //nolint:unparam setResponseHeader(w, "Content-Type", "application/json") w.WriteHeader(code) _, _ = w.Write(json) diff --git a/server/api/files.go b/server/api/files.go index d9f6aa109..bb0ddcfc7 100644 --- a/server/api/files.go +++ b/server/api/files.go @@ -123,37 +123,12 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { auditRec.AddMeta("teamID", board.TeamID) auditRec.AddMeta("filename", filename) - fileInfo, err := a.app.GetFileInfo(filename) + fileInfo, fileReader, err := a.app.GetFile(board.TeamID, boardID, filename) if err != nil && !model.IsErrNotFound(err) { a.errorResponse(w, r, err) return } - if fileInfo != nil && fileInfo.Archived { - fileMetadata := map[string]interface{}{ - "archived": true, - "name": fileInfo.Name, - "size": fileInfo.Size, - "extension": fileInfo.Extension, - } - - data, jsonErr := json.Marshal(fileMetadata) - if jsonErr != nil { - a.logger.Error("failed to marshal archived file metadata", mlog.String("filename", filename), mlog.Err(jsonErr)) - a.errorResponse(w, r, jsonErr) - return - } - - jsonBytesResponse(w, http.StatusBadRequest, data) - return - } - - fileReader, err := a.app.GetFileReader(board.TeamID, boardID, filename) - if err != nil && !errors.Is(err, app.ErrFileNotFound) { - a.errorResponse(w, r, err) - return - } - if errors.Is(err, app.ErrFileNotFound) && board.ChannelID != "" { // prior to moving from workspaces to teams, the filepath was constructed from // workspaceID, which is the channel ID in plugin mode. diff --git a/server/app/files.go b/server/app/files.go index 081b78e14..1474a291a 100644 --- a/server/app/files.go +++ b/server/app/files.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/mattermost/focalboard/server/model" mmModel "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/focalboard/server/utils" @@ -28,7 +29,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin createdFilename := utils.NewID(utils.IDTypeNone) fullFilename := fmt.Sprintf(`%s%s`, createdFilename, fileExtension) - filePath := filepath.Join(teamID, rootID, fullFilename) + filePath := filepath.Join(utils.GetBaseFilePath(), fullFilename) fileSize, appErr := a.filesBackend.WriteFile(reader, filePath) if appErr != nil { @@ -45,7 +46,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin CreateAt: now, UpdateAt: now, DeleteAt: 0, - Path: emptyString, + Path: filePath, ThumbnailPath: emptyString, PreviewPath: emptyString, Name: filename, @@ -59,6 +60,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin Content: "", RemoteId: nil, } + err := a.store.SaveFileInfo(fileInfo) if err != nil { return "", err @@ -77,6 +79,7 @@ func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) { // will be the fileinfo id. parts := strings.Split(filename, ".") fileInfoID := parts[0][1:] + fileInfo, err := a.store.GetFileInfo(fileInfoID) if err != nil { return nil, err @@ -85,6 +88,40 @@ func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) { return fileInfo, nil } +func (a *App) GetFile(teamID, rootID, fileName string) (*mmModel.FileInfo, filestore.ReadCloseSeeker, error) { + fileInfo, err := a.GetFileInfo(fileName) + if err != nil && !model.IsErrNotFound(err) { + a.logger.Error("111") + return nil, nil, err + } + + var filePath string + + if fileInfo != nil && fileInfo.Path != "" { + filePath = fileInfo.Path + } else { + filePath = filepath.Join(teamID, rootID, fileName) + } + + exists, err := a.filesBackend.FileExists(filePath) + if err != nil { + a.logger.Error(fmt.Sprintf("GetFile: Failed to check if file exists as path. Path: %s, error: %e", filePath, err)) + return nil, nil, err + } + + if !exists { + return nil, nil, ErrFileNotFound + } + + reader, err := a.filesBackend.Reader(filePath) + if err != nil { + a.logger.Error(fmt.Sprintf("GetFile: Failed to get file reader of existing file at path: %s, error: %e", filePath, err)) + return nil, nil, err + } + + return fileInfo, reader, nil +} + func (a *App) GetFileReader(teamID, rootID, filename string) (filestore.ReadCloseSeeker, error) { filePath := filepath.Join(teamID, rootID, filename) exists, err := a.filesBackend.FileExists(filePath) diff --git a/server/app/files_test.go b/server/app/files_test.go index 11e4991b8..b39327f7b 100644 --- a/server/app/files_test.go +++ b/server/app/files_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" @@ -195,8 +196,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, testBoardID, paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) fileName = paths[2] return int64(10) } @@ -219,8 +220,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, "test-board-id", paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) assert.Equal(t, "jpg", strings.Split(paths[2], ".")[1]) return int64(10) } @@ -243,8 +244,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, "test-board-id", paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) assert.Equal(t, "jpg", strings.Split(paths[2], ".")[1]) return int64(10) } @@ -304,3 +305,80 @@ func TestGetFileInfo(t *testing.T) { assert.Nil(t, fetchedFileInfo) }) } + +func TestGetFile(t *testing.T) { + th, _ := SetupTestHelper(t) + + t.Run("when FileInfo exists", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(&mmModel.FileInfo{ + Id: "fileInfoID", + Path: "/path/to/file/fileName.txt", + }, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + mockedFileBackend.On("Reader", "/path/to/file/fileName.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "/path/to/file/fileName.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.NotNil(t, fileInfo) + assert.NotNil(t, seeker) + }) + + t.Run("when FileInfo doesn't exist", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(nil, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + + mockedFileBackend.On("Reader", "teamID/boardID/7fileInfoID.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "teamID/boardID/7fileInfoID.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.Nil(t, fileInfo) + assert.NotNil(t, seeker) + }) + + t.Run("when FileInfo exists but FileInfo.Path is not set", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(&mmModel.FileInfo{ + Id: "fileInfoID", + Path: "", + }, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + mockedFileBackend.On("Reader", "teamID/boardID/7fileInfoID.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "teamID/boardID/7fileInfoID.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.NotNil(t, fileInfo) + assert.NotNil(t, seeker) + }) +} diff --git a/server/client/client.go b/server/client/client.go index 6be594f40..8c794ccfe 100644 --- a/server/client/client.go +++ b/server/client/client.go @@ -380,6 +380,8 @@ func (c *Client) GetCards(boardID string, page int, perPage int) ([]*model.Card, return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var cards []*model.Card if err := json.NewDecoder(r.Body).Decode(&cards); err != nil { return nil, BuildErrorResponse(r, err) @@ -398,6 +400,8 @@ func (c *Client) PatchCard(cardID string, cardPatch *model.CardPatch, disableNot return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var cardNew *model.Card if err := json.NewDecoder(r.Body).Decode(&cardNew); err != nil { return nil, BuildErrorResponse(r, err) @@ -412,6 +416,8 @@ func (c *Client) GetCard(cardID string) (*model.Card, *Response) { return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var card *model.Card if err := json.NewDecoder(r.Body).Decode(&card); err != nil { return nil, BuildErrorResponse(r, err) @@ -450,6 +456,7 @@ func (c *Client) DeleteCategory(teamID, categoryID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } @@ -1049,6 +1056,7 @@ func (c *Client) HideBoard(teamID, categoryID, boardID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } @@ -1058,5 +1066,6 @@ func (c *Client) UnhideBoard(teamID, categoryID, boardID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } diff --git a/server/services/store/sqlstore/file.go b/server/services/store/sqlstore/file.go index c1e189f3d..5825244c9 100644 --- a/server/services/store/sqlstore/file.go +++ b/server/services/store/sqlstore/file.go @@ -22,6 +22,7 @@ func (s *SQLStore) saveFileInfo(db sq.BaseRunner, fileInfo *mmModel.FileInfo) er "extension", "size", "delete_at", + "path", "archived", ). Values( @@ -31,6 +32,7 @@ func (s *SQLStore) saveFileInfo(db sq.BaseRunner, fileInfo *mmModel.FileInfo) er fileInfo.Extension, fileInfo.Size, fileInfo.DeleteAt, + fileInfo.Path, false, ) @@ -57,6 +59,7 @@ func (s *SQLStore) getFileInfo(db sq.BaseRunner, id string) (*mmModel.FileInfo, "extension", "size", "archived", + "path", ). From(s.tablePrefix + "file_info"). Where(sq.Eq{"Id": id}) @@ -73,6 +76,7 @@ func (s *SQLStore) getFileInfo(db sq.BaseRunner, id string) (*mmModel.FileInfo, &fileInfo.Extension, &fileInfo.Size, &fileInfo.Archived, + &fileInfo.Path, ) if err != nil { diff --git a/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql new file mode 100644 index 000000000..027b7d63f --- /dev/null +++ b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql new file mode 100644 index 000000000..af7d5e8fe --- /dev/null +++ b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql @@ -0,0 +1 @@ +{{ addColumnIfNeeded "file_info" "path" "varchar(512)" "" }} \ No newline at end of file diff --git a/server/utils/utils.go b/server/utils/utils.go index 7a1ad9763..46326dd66 100644 --- a/server/utils/utils.go +++ b/server/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "encoding/json" + "path" "reflect" "time" @@ -120,3 +121,7 @@ func DedupeStringArr(arr []string) []string { return dedupedArr } + +func GetBaseFilePath() string { + return path.Join("boards", time.Now().Format("20060102")) +} From dda3b8f13a521edbdfef6bc401db1762adccc223 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Tue, 7 Mar 2023 11:01:52 -0700 Subject: [PATCH 28/35] lint fixes --- server/app/boards_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/app/boards_test.go b/server/app/boards_test.go index 7830c9297..8cd3679f2 100644 --- a/server/app/boards_test.go +++ b/server/app/boards_test.go @@ -469,7 +469,7 @@ func TestPatchBoard(t *testing.T) { const userID = "user_id_2" const teamID = "team_id_1" - channelID := "myChannel" + const channelID = "myChannel" clearChannel := "" patchType := model.BoardTypeOpen patch := &model.BoardPatch{ @@ -498,7 +498,6 @@ func TestPatchBoard(t *testing.T) { _, err := th.App.PatchBoard(patch, boardID, userID) require.Error(t, err) }) - } func TestGetBoardCount(t *testing.T) { From e59b61937f8197bea16e7754205a130ebdcda83e Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Thu, 9 Mar 2023 11:20:32 +0530 Subject: [PATCH 29/35] Counting on specific column --- server/services/store/sqlstore/data_migrations.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/services/store/sqlstore/data_migrations.go b/server/services/store/sqlstore/data_migrations.go index 15d86129d..bbc7e2218 100644 --- a/server/services/store/sqlstore/data_migrations.go +++ b/server/services/store/sqlstore/data_migrations.go @@ -853,7 +853,7 @@ func (s *SQLStore) doesDuplicateCategoryBoardsExist() (bool, error) { Having("count(*) > 1") query := s.getQueryBuilder(s.db). - Select("COUNT(*)"). + Select("COUNT(user_id)"). FromSelect(subQuery, "duplicate_dataset") row := query.QueryRow() From 41fbd5fecc2e05152dfeb590676ce7d97e1c4510 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Thu, 9 Mar 2023 17:53:45 +0100 Subject: [PATCH 30/35] Enable cross-compilation of Linux webapp via Docker (#4528) Relates to: #4470 Co-authored-by: Mattermost Build --- Dockerfile.build | 36 +++++++++++++++++++----------------- Makefile | 4 ++-- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Dockerfile.build b/Dockerfile.build index 6cf014765..922c28c1d 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -1,10 +1,10 @@ -# This dockerfile is used to build Focalboard for Linux -# it builds all the parts inside the container and the last stage just holds the -# package that can be extracted using docker cp command -# ie -# docker build -f Dockerfile.build --no-cache -t focalboard-build:dirty . -# docker run --rm -v /tmp/dist:/tmp -d --name test focalboard-build:dirty /bin/sh -c 'sleep 1000' -# docker cp test:/dist/focalboard-server-linux-amd64.tar.gz . +# This Dockerfile is used to build Focalboard for Linux. It builds all the parts inside the image +# and the last stage just holds the package which is then copied back to the host. +# +# docker buildx build -f Dockerfile.build --no-cache --platform linux/amd64 -t focalboard-build:dirty --output out . +# docker buildx build -f Dockerfile.build --no-cache --platform linux/arm64 -t focalboard-build:dirty --output out . +# +# Afterwards the packages can be found in the ./out folder. # build frontend FROM node:16.3.0@sha256:ca6daf1543242acb0ca59ff425509eab7defb9452f6ae07c156893db06c7a9a4 AS frontend @@ -12,8 +12,10 @@ FROM node:16.3.0@sha256:ca6daf1543242acb0ca59ff425509eab7defb9452f6ae07c156893db WORKDIR /webapp COPY webapp . -RUN npm install --no-optional -RUN npm run pack +### 'CPPFLAGS="-DPNG_ARM_NEON_OPT=0"' Needed To Avoid Bug Described in: https://github.com/imagemin/optipng-bin/issues/118#issuecomment-1019838562 +### Can be Removed when Ticket will be Closed +RUN CPPFLAGS="-DPNG_ARM_NEON_OPT=0" npm install --no-optional && \ + npm run pack # build backend and package FROM golang:1.18.3@sha256:b203dc573d81da7b3176264bfa447bd7c10c9347689be40540381838d75eebef AS backend @@ -21,13 +23,13 @@ FROM golang:1.18.3@sha256:b203dc573d81da7b3176264bfa447bd7c10c9347689be405403818 COPY . . COPY --from=frontend /webapp/pack webapp/pack +ARG TARGETARCH + # RUN apt-get update && apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev -y -RUN make server-linux -RUN make server-linux-package-docker +RUN EXCLUDE_PLUGIN=true EXCLUDE_SERVER=true EXCLUDE_ENTERPRISE=true make server-linux arch=${TARGETARCH} +RUN make server-linux-package-docker arch=${TARGETARCH} -# just hold the packages to output later -FROM alpine:3.12@sha256:c75ac27b49326926b803b9ed43bf088bc220d22556de1bc5f72d742c91398f69 AS dist - -WORKDIR /dist - -COPY --from=backend /go/dist/focalboard-server-linux-amd64.tar.gz . +# Copy package back to host +FROM scratch AS dist +ARG TARGETARCH +COPY --from=backend /go/dist/focalboard-server-linux-${TARGETARCH}.tar.gz . diff --git a/Makefile b/Makefile index b6e13291c..9d74bef1f 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ endif server-linux: setup-go-work ## Build server for Linux. mkdir -p bin/linux $(eval LDFLAGS += -X "github.com/mattermost/focalboard/server/model.Edition=linux") - cd server; env GOOS=linux GOARCH=amd64 go build -ldflags '$(LDFLAGS)' -tags '$(BUILD_TAGS)' -o ../bin/linux/focalboard-server ./main + cd server; env GOOS=linux GOARCH=$(arch) go build -ldflags '$(LDFLAGS)' -tags '$(BUILD_TAGS)' -o ../bin/linux/focalboard-server ./main server-docker: setup-go-work ## Build server for Docker Architectures. mkdir -p bin/docker @@ -101,7 +101,7 @@ server-linux-package-docker: cp NOTICE.txt package/${PACKAGE_FOLDER} cp webapp/NOTICE.txt package/${PACKAGE_FOLDER}/webapp-NOTICE.txt mkdir -p dist - cd package && tar -czvf ../dist/focalboard-server-linux-amd64.tar.gz ${PACKAGE_FOLDER} + cd package && tar -czvf ../dist/focalboard-server-linux-$(arch).tar.gz ${PACKAGE_FOLDER} rm -rf package generate: ## Install and run code generators. From 3fa0e082191654156279cb8349c6f5e1f696b4c9 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Wed, 22 Mar 2023 13:28:00 -0600 Subject: [PATCH 31/35] 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 32/35] 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 1fff221da9e1ec5d6862aa3a3782eee2c77c78c8 Mon Sep 17 00:00:00 2001 From: Miguel de la Cruz Date: Mon, 27 Mar 2023 13:56:39 +0200 Subject: [PATCH 33/35] 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 34/35] 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 35/35] 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