diff --git a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_sidebar_spec.ts b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_sidebar_spec.ts index 9c67b1250b..232f62acdf 100644 --- a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_sidebar_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_sidebar_spec.ts @@ -69,7 +69,7 @@ describe('Verify Accessibility Support in Channel Sidebar Navigation', () => { cy.uiGetLHSAddChannelButton().focus().tab().tab({shift: true}); // * Verify if the Plus button has focus - cy.findByRole('button', {name: 'Add Channel Dropdown'}).should('be.focused'); + cy.uiGetLHSAddChannelButton().should('be.focused'); cy.focused().tab(); // * Verify if the Plus button has focus diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts index bf2f6e9011..f7b71b4115 100644 --- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts @@ -140,7 +140,7 @@ function createCategoryFromSidebarMenu() { const categoryName = `category-${getRandomId()}`; // # Click on the sidebar menu dropdown - cy.findByLabelText('Add Channel Dropdown').click(); + cy.uiGetLHSAddChannelButton().click(); // # Click on create category link cy.findByText('Create New Category').should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts index 69086ce680..82e3b4a086 100644 --- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts @@ -23,7 +23,7 @@ describe('Category sorting', () => { it('MM-T3916 Create Category character limit', () => { // # Click on the sidebar menu dropdown - cy.findByLabelText('Add Channel Dropdown').click(); + cy.uiGetLHSAddChannelButton().click(); // # Click on create category link cy.findByText('Create New Category').should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts index 30918ebbb3..4822d10432 100644 --- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts @@ -90,7 +90,7 @@ function createCategoryFromSidebarMenu() { const categoryName = `category-${getRandomId()}`; // # Click on the sidebar menu dropdown - cy.findByLabelText('Add Channel Dropdown').click(); + cy.uiGetLHSAddChannelButton().click(); // # Click on create category link cy.findByText('Create New Category').should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts index 17114e21aa..4aad6c337a 100644 --- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts @@ -62,7 +62,7 @@ describe('Channel sidebar', () => { cy.url().should('include', `/${teamName}/channels/town-square`); // # Click the New Channel Dropdown button - cy.get('.AddChannelDropdown_dropdownButton').should('be.visible').click(); + cy.uiGetLHSAddChannelButton().should('be.visible').click(); // # Click the Browse Channels dropdown item cy.get('.AddChannelDropdown .MenuItem:contains(Browse Channels) button').should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/emoji/recently_used_emoji_1_spec.ts b/e2e-tests/cypress/tests/integration/channels/emoji/recently_used_emoji_1_spec.ts index 0d016ea33d..fcaeb3b38b 100644 --- a/e2e-tests/cypress/tests/integration/channels/emoji/recently_used_emoji_1_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/emoji/recently_used_emoji_1_spec.ts @@ -105,7 +105,10 @@ describe('Recent Emoji', () => { cy.uiGetPostTextBox().type('{enter} {enter}').wait(TIMEOUTS.TWO_SEC); // # Hover over the last post by opening dot menu on it - cy.clickPostDotMenu(); + cy.getLastPostId().then((postId) => { + // # Click on post dot menu so we can check for reaction icon + cy.get(`#post_${postId}`).trigger('mouseover'); + }); cy.get('#recent_reaction_0').should('exist').then((recentReaction) => { // * Assert that custom emoji is present as most recent in quick reaction menu @@ -152,7 +155,10 @@ describe('Recent Emoji', () => { cy.reload(); // # Hover over the last post by opening dot menu on it - cy.clickPostDotMenu(); + cy.getLastPostId().then((postId) => { + // # Click on post dot menu so we can check for reaction icon + cy.get(`#post_${postId}`).trigger('mouseover'); + }); cy.get('#recent_reaction_0').should('exist').then((recentReaction) => { // * Assert that instead of custom emoji the system emoji is present as most recent in quick reaction menu diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.js index 8626033949..08f25fc8d4 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.js @@ -196,7 +196,7 @@ describe('Verify Accessibility Support in different input fields', () => { cy.get('#FormattingControl_ul').should('be.focused').and('have.attr', 'aria-label', 'bulleted list').tab(); // * Verify if the focus is on the numbered list button - cy.get('#FormattingControl_ol').should('be.focused').and('have.attr', 'aria-label', 'numbered list').tab(); + cy.get('#FormattingControl_ol').should('be.focused').and('have.attr', 'aria-label', 'numbered list').tab().tab(); // * Verify if the focus is on the formatting options button cy.get('#toggleFormattingBarButton').should('be.focused').and('have.attr', 'aria-label', 'formatting').tab(); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts index aa12dae759..396869cd8e 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts @@ -223,4 +223,15 @@ describe('Guest Account - Guest User Invitation Flow', () => { // * Verify invite more button is present cy.findByTestId('invite-more').should('be.visible'); }); + + it('hides the copy link button when inviting guests', () => { + // # Open team menu and click 'Invite People' + cy.uiOpenTeamMenu('Invite People'); + + // # Select Guest + cy.findByTestId('inviteGuestLink').should('be.visible').click(); + + // * The button "Copy invite link" should not exist + cy.findByTestId('InviteView__copyInviteLink').should('not.exist'); + }); }); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.js index fda7dca517..79dbf33ab8 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.js @@ -159,7 +159,7 @@ describe('LDAP guest', () => { // # Create team if no membership cy.skipOrCreateTeam(testSettings, getRandomId()).then(() => { // * Verify user is a member - cy.findByRole('button', {name: 'Add Channel Dropdown'}).should('exist'); + cy.uiGetLHSAddChannelButton().should('exist'); // # Demote the user demoteUserToGuest(user2Data); @@ -173,7 +173,7 @@ describe('LDAP guest', () => { cy.uiAddDirectMessage().should('exist'); // * Check the user is a guest - cy.findByRole('button', {name: 'Add Channel Dropdown'}).should('not.exist'); + cy.uiGetLHSAddChannelButton().should('not.exist'); }); }); }); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/system_scheme_permission_part2_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/system_scheme_permission_part2_spec.js index 53e063aef9..33ae5b8c77 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/system_scheme_permission_part2_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/system_scheme_permission_part2_spec.js @@ -53,7 +53,7 @@ describe('System Scheme', () => { cy.findByTestId('systemScheme-link').should('be.visible').click().wait(TIMEOUTS.HALF_SEC); // # Click on `Reset to defaults` - cy.findByTestId('resetPermissionsToDefault').should('be.visible').click().wait(TIMEOUTS.HALF_SEC); + cy.findByTestId('resetPermissionsToDefault').scrollIntoView().should('be.visible').click().wait(TIMEOUTS.HALF_SEC); // # Confirm the dialog cy.get('#confirmModalButton').click().wait(TIMEOUTS.TWO_SEC); @@ -78,10 +78,10 @@ describe('System Scheme', () => { cy.findByTestId('all_users-private_channel-create_private_channel-checkbox').should('not.have.class', 'checked'); // # Click on `Reset to defaults` - cy.findByTestId('resetPermissionsToDefault').should('be.visible').click().wait(TIMEOUTS.HALF_SEC); + cy.findByTestId('resetPermissionsToDefault').scrollIntoView().should('be.visible').click().wait(TIMEOUTS.HALF_SEC); // # Confirm the dialog - cy.get('#confirmModalButton').click().wait(TIMEOUTS.HALF_SEC); + cy.get('#confirmModalButton').scrollIntoView().click().wait(TIMEOUTS.HALF_SEC); // # Save changes cy.get('#saveSetting').click().wait(TIMEOUTS.TWO_SEC); diff --git a/e2e-tests/cypress/tests/integration/channels/mark_as_unread/helpers.js b/e2e-tests/cypress/tests/integration/channels/mark_as_unread/helpers.js index 4482bcb782..fbcbea8f4b 100644 --- a/e2e-tests/cypress/tests/integration/channels/mark_as_unread/helpers.js +++ b/e2e-tests/cypress/tests/integration/channels/mark_as_unread/helpers.js @@ -21,6 +21,7 @@ export function markAsUnreadShouldBeAbsent(postId, prefix = 'post', location = ' within(() => { cy.findByText('Mark as Unread').should('not.exist'); }); + cy.get('body').type('esc'); } export function switchToChannel(channel) { diff --git a/e2e-tests/cypress/tests/support/ui/sidebar_left.ts b/e2e-tests/cypress/tests/support/ui/sidebar_left.ts index 1168b705dd..58ae372f82 100644 --- a/e2e-tests/cypress/tests/support/ui/sidebar_left.ts +++ b/e2e-tests/cypress/tests/support/ui/sidebar_left.ts @@ -32,7 +32,7 @@ Cypress.Commands.add('uiOpenTeamMenu', (item = '') => { Cypress.Commands.add('uiGetLHSAddChannelButton', () => { return cy.uiGetLHS(). - findByRole('button', {name: 'Add Channel Dropdown'}); + find('.AddChannelDropdown_dropdownButton'); }); Cypress.Commands.add('uiGetLHSTeamMenu', () => { @@ -89,7 +89,7 @@ Cypress.Commands.add('uiGetLhsSection', (section) => { }); Cypress.Commands.add('uiBrowseOrCreateChannel', (item) => { - cy.findByRole('button', {name: 'Add Channel Dropdown'}). + cy.get('.AddChannelDropdown_dropdownButton'). should('be.visible'). click(); cy.get('.dropdown-menu').should('be.visible'); diff --git a/e2e-tests/playwright/support/server/default_config.ts b/e2e-tests/playwright/support/server/default_config.ts index 5d6e56f7d5..33d545537b 100644 --- a/e2e-tests/playwright/support/server/default_config.ts +++ b/e2e-tests/playwright/support/server/default_config.ts @@ -697,6 +697,7 @@ const defaultServerConfig: AdminConfig = { ThreadsEverywhere: false, GlobalDrafts: true, OnboardingTourTips: true, + AppsSidebarCategory: false, }, ImportSettings: { Directory: './import', diff --git a/model/config.go b/model/config.go index 6c5306950d..4868229bbf 100644 --- a/model/config.go +++ b/model/config.go @@ -314,7 +314,7 @@ type ServiceSettings struct { RestrictLinkPreviews *string `access:"site_posts"` EnableTesting *bool `access:"environment_developer,write_restrictable,cloud_restrictable"` EnableDeveloper *bool `access:"environment_developer,write_restrictable,cloud_restrictable"` - DeveloperFlags *string `access:"environment_developer"` + DeveloperFlags *string `access:"environment_developer,cloud_restrictable"` EnableClientPerformanceDebugging *bool `access:"environment_developer,write_restrictable,cloud_restrictable"` EnableOpenTracing *bool `access:"write_restrictable,cloud_restrictable"` EnableSecurityFixAlert *bool `access:"environment_smtp,write_restrictable,cloud_restrictable"` diff --git a/model/draft.go b/model/draft.go index a9741e5727..73d1e69f98 100644 --- a/model/draft.go +++ b/model/draft.go @@ -81,9 +81,11 @@ func (o *Draft) GetProps() StringInterface { func (o *Draft) PreSave() { if o.CreateAt == 0 { o.CreateAt = GetMillis() + o.UpdateAt = o.CreateAt + } else { + o.UpdateAt = GetMillis() } - o.UpdateAt = o.CreateAt o.DeleteAt = 0 o.PreCommit() } @@ -100,8 +102,3 @@ func (o *Draft) PreCommit() { // There's a rare bug where the client sends up duplicate FileIds so protect against that o.FileIds = RemoveDuplicateStrings(o.FileIds) } - -func (o *Draft) PreUpdate() { - o.UpdateAt = GetMillis() - o.PreCommit() -} diff --git a/model/draft_test.go b/model/draft_test.go index 2e931c31dd..dec7e56a17 100644 --- a/model/draft_test.go +++ b/model/draft_test.go @@ -65,16 +65,3 @@ func TestDraftPreSave(t *testing.T) { assert.LessOrEqual(t, o.CreateAt, past) } - -func TestDraftPreUpdate(t *testing.T) { - o := Draft{Message: "test"} - o.PreUpdate() - - assert.NotEqual(t, 0, o.UpdateAt) - - past := GetMillis() - 1 - o = Draft{Message: "test", UpdateAt: past} - o.PreSave() - - assert.GreaterOrEqual(t, o.UpdateAt, past) -} diff --git a/model/feature_flags.go b/model/feature_flags.go index 689ff32fd6..792ae834e9 100644 --- a/model/feature_flags.go +++ b/model/feature_flags.go @@ -74,6 +74,8 @@ type FeatureFlags struct { GlobalDrafts bool OnboardingTourTips bool + + AppsSidebarCategory bool } func (f *FeatureFlags) SetDefaults() { @@ -95,13 +97,14 @@ func (f *FeatureFlags) SetDefaults() { f.SendWelcomePost = true f.PostPriority = true f.PeopleProduct = false - f.WorkTemplate = false + f.WorkTemplate = true f.ReduceOnBoardingTaskList = false f.ThreadsEverywhere = false f.GlobalDrafts = true f.WysiwygEditor = false f.OnboardingAutoShowLinkedBoard = false f.OnboardingTourTips = true + f.AppsSidebarCategory = false } func (f *FeatureFlags) Plugins() map[string]string { diff --git a/model/hosted_customer.go b/model/hosted_customer.go index 543ea12b74..608892e5e5 100644 --- a/model/hosted_customer.go +++ b/model/hosted_customer.go @@ -8,6 +8,12 @@ type BootstrapSelfHostedSignupRequest struct { Reset bool `json:"reset"` } +type SubscribeNewsletterRequest struct { + Email string `json:"email"` + ServerID string `json:"server_id"` + SubscribedContent string `json:"subscribed_content"` +} + type BootstrapSelfHostedSignupResponse struct { Progress string `json:"progress"` // email listed on the JWT claim diff --git a/server/boards/api/files.go b/server/boards/api/files.go index 344933fc5d..4a7eb5e6e7 100644 --- a/server/boards/api/files.go +++ b/server/boards/api/files.go @@ -145,6 +145,12 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { _ = a.app.MoveFile(board.ChannelID, board.TeamID, boardID, filename) } + if err != nil { + // if err is still not nil then it is an error other than `not found` so we must + // return the error to the requestor. fileReader and Fileinfo are nil in this case. + a.errorResponse(w, r, err) + } + defer fileReader.Close() mimeType := "" diff --git a/server/channels/api4/drafts.go b/server/channels/api4/drafts.go index 2b11bf258a..bfd1112f94 100644 --- a/server/channels/api4/drafts.go +++ b/server/channels/api4/drafts.go @@ -62,7 +62,7 @@ func upsertDraft(c *Context, w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) if err := json.NewEncoder(w).Encode(dt); err != nil { - mlog.Warn("Error while writing response", mlog.Err(err)) + c.Logger.Warn("Error while writing response", mlog.Err(err)) } } @@ -94,7 +94,7 @@ func getDrafts(c *Context, w http.ResponseWriter, r *http.Request) { } if err := json.NewEncoder(w).Encode(drafts); err != nil { - mlog.Warn("Error while writing response", mlog.Err(err)) + c.Logger.Warn("Error while writing response", mlog.Err(err)) } } diff --git a/server/channels/api4/hosted_customer.go b/server/channels/api4/hosted_customer.go index bbed311ea4..ba791c28cc 100644 --- a/server/channels/api4/hosted_customer.go +++ b/server/channels/api4/hosted_customer.go @@ -36,6 +36,8 @@ func (api *API) InitHostedCustomer() { api.BaseRoutes.HostedCustomer.Handle("/invoices", api.APISessionRequired(selfHostedInvoices)).Methods("GET") // GET /api/v4/hosted_customer/invoices/{invoice_id:in_[A-Za-z0-9]+}/pdf api.BaseRoutes.HostedCustomer.Handle("/invoices/{invoice_id:in_[A-Za-z0-9]+}/pdf", api.APISessionRequired(selfHostedInvoicePDF)).Methods("GET") + + api.BaseRoutes.HostedCustomer.Handle("/subscribe-newsletter", api.APIHandler(handleSubscribeToNewsletter)).Methods(http.MethodPost) } func ensureSelfHostedAdmin(c *Context, where string) { @@ -293,3 +295,33 @@ func selfHostedInvoicePDF(c *Context, w http.ResponseWriter, r *http.Request) { r, ) } + +func handleSubscribeToNewsletter(c *Context, w http.ResponseWriter, r *http.Request) { + const where = "Api4.handleSubscribeToNewsletter" + ensured := ensureCloudInterface(c, where) + if !ensured { + return + } + + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + c.Err = model.NewAppError(where, "api.cloud.app_error", nil, "", http.StatusBadRequest).Wrap(err) + return + } + + req := new(model.SubscribeNewsletterRequest) + err = json.Unmarshal(bodyBytes, req) + if err != nil { + c.Err = model.NewAppError(where, "api.cloud.request_error", nil, "", http.StatusBadRequest).Wrap(err) + return + } + + req.ServerID = c.App.Srv().TelemetryId() + + if err := c.App.Cloud().SubscribeToNewsletter("", req); err != nil { + c.Err = model.NewAppError(where, "api.server.cws.subscribe_to_newsletter.app_error", nil, "CWS Server failed to subscribe to newsletter.", http.StatusInternalServerError).Wrap(err) + return + } + + ReturnStatusOK(w) +} diff --git a/server/channels/api4/user.go b/server/channels/api4/user.go index ba2ab6e336..11d4a71e0f 100644 --- a/server/channels/api4/user.go +++ b/server/channels/api4/user.go @@ -2372,10 +2372,14 @@ func createUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) { audit.AddEventParameter(auditRec, "user_id", c.Params.UserId) defer c.LogAuditRec(auditRec) - if user, err := c.App.GetUser(c.Params.UserId); err == nil { - audit.AddEventParameterAuditable(auditRec, "user", user) + user, err := c.App.GetUser(c.Params.UserId) + if err != nil { + c.Err = err + return } + audit.AddEventParameterAuditable(auditRec, "user", user) + if c.AppContext.Session().IsOAuth { c.SetPermissionError(model.PermissionCreateUserAccessToken) c.Err.DetailedError += ", attempted access by oauth app" @@ -2405,6 +2409,11 @@ func createUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) { return } + if user.IsSystemAdmin() && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) { + c.SetPermissionError(model.PermissionManageSystem) + return + } + accessToken.UserId = c.Params.UserId accessToken.Token = "" diff --git a/server/channels/api4/user_test.go b/server/channels/api4/user_test.go index 893d7f6aca..b6dde5e2a1 100644 --- a/server/channels/api4/user_test.go +++ b/server/channels/api4/user_test.go @@ -4339,7 +4339,38 @@ func TestCreateUserAccessToken(t *testing.T) { CheckForbiddenStatus(t, resp) }) - t.Run("create user access token for basic user as as system admin", func(t *testing.T) { + t.Run("create user access token for another user, with permission", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableUserAccessTokens = true }) + th.AddPermissionToRole(model.PermissionEditOtherUsers.Id, model.SystemUserManagerRoleId) + th.App.UpdateUserRoles(th.Context, th.BasicUser.Id, model.SystemUserManagerRoleId+" "+model.SystemUserAccessTokenRoleId, false) + + rtoken, _, err := th.Client.CreateUserAccessToken(th.BasicUser2.Id, "test token") + require.NoError(t, err) + assert.Equal(t, th.BasicUser2.Id, rtoken.UserId) + + oldSessionToken := th.Client.AuthToken + defer func() { th.Client.AuthToken = oldSessionToken }() + + assertToken(t, th, rtoken, th.BasicUser2.Id) + }) + + t.Run("create user access token for system admin, as system user manager", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableUserAccessTokens = true }) + th.AddPermissionToRole(model.PermissionEditOtherUsers.Id, model.SystemUserManagerRoleId) + th.App.UpdateUserRoles(th.Context, th.BasicUser.Id, model.SystemUserManagerRoleId+" "+model.SystemUserAccessTokenRoleId, false) + + _, resp, err := th.Client.CreateUserAccessToken(th.SystemAdminUser.Id, "test token") + require.Error(t, err) + CheckForbiddenStatus(t, resp) + }) + + t.Run("create user access token for basic user as a system admin", func(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/server/channels/api4/work_templates.go b/server/channels/api4/work_templates.go index a2d26e7b0e..2f6836682e 100644 --- a/server/channels/api4/work_templates.go +++ b/server/channels/api4/work_templates.go @@ -22,23 +22,6 @@ func areWorkTemplatesEnabled(c *Context) *model.AppError { return model.NewAppError("areWorkTemplatesEnabled", "api.work_templates.disabled", nil, "feature flag is off", http.StatusNotFound) } - // we have to make sure that playbooks plugin is enabled and board is a product - pbActive, err := c.App.IsPluginActive(model.PluginIdPlaybooks) - if err != nil { - return model.NewAppError("areWorkTemplatesEnabled", "api.work_templates.disabled", nil, "", http.StatusInternalServerError).Wrap(err) - } - if !pbActive { - return model.NewAppError("areWorkTemplatesEnabled", "api.work_templates.disabled", nil, "playbook plugin not active", http.StatusNotFound) - } - - hasBoard, err := c.App.HasBoardProduct() - if err != nil { - return model.NewAppError("areWorkTemplatesEnabled", "api.work_templates.disabled", nil, "", http.StatusInternalServerError).Wrap(err) - } - if !hasBoard { - return model.NewAppError("areWorkTemplatesEnabled", "api.work_templates.disabled", nil, "board product not found", http.StatusNotFound) - } - return nil } diff --git a/server/channels/app/app_iface.go b/server/channels/app/app_iface.go index cf749c3fc9..eb30bc0779 100644 --- a/server/channels/app/app_iface.go +++ b/server/channels/app/app_iface.go @@ -482,7 +482,6 @@ type AppIface interface { CreateChannelWithUser(c request.CTX, channel *model.Channel, userID string) (*model.Channel, *model.AppError) CreateCommand(cmd *model.Command) (*model.Command, *model.AppError) CreateCommandWebhook(commandID string, args *model.CommandArgs) (*model.CommandWebhook, *model.AppError) - CreateDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) CreateEmoji(c request.CTX, sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError) CreateGroup(group *model.Group) (*model.Group, *model.AppError) CreateGroupChannel(c request.CTX, userIDs []string, creatorId string) (*model.Channel, *model.AppError) @@ -1121,7 +1120,6 @@ type AppIface interface { UpdateChannelPrivacy(c request.CTX, oldChannel *model.Channel, user *model.User) (*model.Channel, *model.AppError) UpdateCommand(oldCmd, updatedCmd *model.Command) (*model.Command, *model.AppError) UpdateConfig(f func(*model.Config)) - UpdateDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) UpdateEphemeralPost(c request.CTX, userID string, post *model.Post) *model.Post UpdateExpiredDNDStatuses() ([]*model.Status, error) UpdateGroup(group *model.Group) (*model.Group, *model.AppError) diff --git a/server/channels/app/draft.go b/server/channels/app/draft.go index 3521adea71..a8ca1ef9d2 100644 --- a/server/channels/app/draft.go +++ b/server/channels/app/draft.go @@ -35,33 +35,6 @@ func (a *App) GetDraft(userID, channelID, rootID string) (*model.Draft, *model.A } func (a *App) UpsertDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) { - if !a.Config().FeatureFlags.GlobalDrafts || !*a.Config().ServiceSettings.AllowSyncedDrafts { - return nil, model.NewAppError("UpsertDraft", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented) - } - - dt, dErr := a.Srv().Store().Draft().Get(draft.UserId, draft.ChannelId, draft.RootId, true) - var notFoundErr *store.ErrNotFound - if dErr != nil && !errors.As(dErr, ¬FoundErr) { - return nil, model.NewAppError("UpsertDraft", "app.select_error", nil, dErr.Error(), http.StatusInternalServerError) - } - - var err *model.AppError - if dt == nil { - dt, err = a.CreateDraft(c, draft, connectionID) - if err != nil { - return nil, err - } - } else { - dt, err = a.UpdateDraft(c, draft, connectionID) - if err != nil { - return nil, err - } - } - - return dt, nil -} - -func (a *App) CreateDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) { if !a.Config().FeatureFlags.GlobalDrafts || !*a.Config().ServiceSettings.AllowSyncedDrafts { return nil, model.NewAppError("CreateDraft", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented) } @@ -83,7 +56,7 @@ func (a *App) CreateDraft(c *request.Context, draft *model.Draft, connectionID s return nil, model.NewAppError("CreateDraft", "app.user.get.app_error", nil, nErr.Error(), http.StatusInternalServerError) } - dt, nErr := a.Srv().Store().Draft().Save(draft) + dt, nErr := a.Srv().Store().Draft().Upsert(draft) if nErr != nil { return nil, model.NewAppError("CreateDraft", "app.draft.save.app_error", nil, nErr.Error(), http.StatusInternalServerError) } @@ -101,46 +74,6 @@ func (a *App) CreateDraft(c *request.Context, draft *model.Draft, connectionID s return dt, nil } -func (a *App) UpdateDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) { - if !a.Config().FeatureFlags.GlobalDrafts { - return nil, model.NewAppError("UpsertDraft", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented) - } - - // Check that channel exists and has not been deleted - channel, errCh := a.Srv().Store().Channel().Get(draft.ChannelId, true) - if errCh != nil { - err := model.NewAppError("UpdateDraft", "api.context.invalid_param.app_error", map[string]interface{}{"Name": "draft.channel_id"}, errCh.Error(), http.StatusBadRequest) - return nil, err - } - - if channel.DeleteAt != 0 { - err := model.NewAppError("UpdateDraft", "api.draft.create_draft.can_not_draft_to_deleted.error", nil, "", http.StatusBadRequest) - return nil, err - } - - _, nErr := a.Srv().Store().User().Get(context.Background(), draft.UserId) - if nErr != nil { - return nil, model.NewAppError("UpdateDraft", "app.user.get.app_error", nil, nErr.Error(), http.StatusInternalServerError) - } - - dt, nErr := a.Srv().Store().Draft().Update(draft) - if nErr != nil { - return nil, model.NewAppError("UpdateDraft", "app.draft.update.app_error", nil, nErr.Error(), http.StatusInternalServerError) - } - - dt = a.prepareDraftWithFileInfos(draft.UserId, dt) - - message := model.NewWebSocketEvent(model.WebsocketEventDraftUpdated, "", draft.ChannelId, draft.UserId, nil, connectionID) - draftJSON, jsonErr := json.Marshal(dt) - if jsonErr != nil { - mlog.Warn("Failed to encode draft to JSON", mlog.Err(jsonErr)) - } - message.Add("draft", string(draftJSON)) - a.Publish(message) - - return dt, nil -} - func (a *App) GetDraftsForUser(userID, teamID string) ([]*model.Draft, *model.AppError) { if !a.Config().FeatureFlags.GlobalDrafts || !*a.Config().ServiceSettings.AllowSyncedDrafts { return nil, model.NewAppError("GetDraftsForUser", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented) diff --git a/server/channels/app/draft_test.go b/server/channels/app/draft_test.go index 287ce82430..4b52efd8e9 100644 --- a/server/channels/app/draft_test.go +++ b/server/channels/app/draft_test.go @@ -81,34 +81,41 @@ func TestUpsertDraft(t *testing.T) { user := th.BasicUser channel := th.BasicChannel - draft1 := &model.Draft{ - CreateAt: 00001, - UpdateAt: 00001, + draft := &model.Draft{ UserId: user.Id, ChannelId: channel.Id, - Message: "draft1", + Message: "draft", } - draft2 := &model.Draft{ - CreateAt: 00001, - UpdateAt: 00002, - UserId: user.Id, - ChannelId: channel.Id, - Message: "draft2", - } - - _, createDraftErr := th.App.CreateDraft(th.Context, draft1, "") - assert.Nil(t, createDraftErr) - t.Run("upsert draft", func(t *testing.T) { - draftResp, err := th.App.UpsertDraft(th.Context, draft2, "") + _, err := th.App.UpsertDraft(th.Context, draft, "") assert.Nil(t, err) - assert.Equal(t, draft2.Message, draftResp.Message) - assert.Equal(t, draft2.ChannelId, draftResp.ChannelId) - assert.Equal(t, draft2.CreateAt, draftResp.CreateAt) + drafts, err := th.App.GetDraftsForUser(user.Id, th.BasicTeam.Id) + assert.Nil(t, err) + assert.Len(t, drafts, 1) + draft1 := drafts[0] - assert.NotEqual(t, draft1.UpdateAt, draftResp.UpdateAt) + assert.Equal(t, "draft", draft1.Message) + assert.Equal(t, channel.Id, draft1.ChannelId) + assert.Greater(t, draft1.CreateAt, int64(0)) + + draft = &model.Draft{ + UserId: user.Id, + ChannelId: channel.Id, + Message: "updated draft", + } + _, err = th.App.UpsertDraft(th.Context, draft, "") + assert.Nil(t, err) + + drafts, err = th.App.GetDraftsForUser(user.Id, th.BasicTeam.Id) + assert.Nil(t, err) + assert.Len(t, drafts, 1) + draft2 := drafts[0] + + assert.Equal(t, "updated draft", draft2.Message) + assert.Equal(t, channel.Id, draft2.ChannelId) + assert.Equal(t, draft1.CreateAt, draft2.CreateAt) }) t.Run("upsert draft feature flag", func(t *testing.T) { @@ -123,7 +130,7 @@ func TestUpsertDraft(t *testing.T) { defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.GlobalDrafts = true }) defer th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowSyncedDrafts = true }) - _, err := th.App.UpsertDraft(th.Context, draft1, "") + _, err := th.App.UpsertDraft(th.Context, draft, "") assert.NotNil(t, err) }) } @@ -160,7 +167,7 @@ func TestCreateDraft(t *testing.T) { } t.Run("create draft", func(t *testing.T) { - draftResp, err := th.App.CreateDraft(th.Context, draft1, "") + draftResp, err := th.App.UpsertDraft(th.Context, draft1, "") assert.Nil(t, err) assert.Equal(t, draft1.Message, draftResp.Message) @@ -178,29 +185,13 @@ func TestCreateDraft(t *testing.T) { draftWithFiles := draft2 draftWithFiles.FileIds = []string{fileResp.Id} - draftResp, err := th.App.CreateDraft(th.Context, draftWithFiles, "") + draftResp, err := th.App.UpsertDraft(th.Context, draftWithFiles, "") assert.Nil(t, err) assert.Equal(t, draftWithFiles.Message, draftResp.Message) assert.Equal(t, draftWithFiles.ChannelId, draftResp.ChannelId) assert.ElementsMatch(t, draftWithFiles.FileIds, draftResp.FileIds) }) - - t.Run("create draft feature flag", func(t *testing.T) { - os.Setenv("MM_FEATUREFLAGS_GLOBALDRAFTS", "false") - defer os.Unsetenv("MM_FEATUREFLAGS_GLOBALDRAFTS") - os.Setenv("MM_SERVICESETTINGS_ALLOWSYNCEDDRAFTS", "false") - defer os.Unsetenv("MM_SERVICESETTINGS_ALLOWSYNCEDDRAFTS") - - th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.GlobalDrafts = false }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowSyncedDrafts = false }) - - defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.GlobalDrafts = true }) - defer th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowSyncedDrafts = true }) - - _, err := th.App.CreateDraft(th.Context, draft1, "") - assert.NotNil(t, err) - }) } func TestUpdateDraft(t *testing.T) { @@ -217,34 +208,14 @@ func TestUpdateDraft(t *testing.T) { channel := th.BasicChannel draft1 := &model.Draft{ - CreateAt: 00001, - UpdateAt: 00001, UserId: user.Id, ChannelId: channel.Id, Message: "draft1", } - draft2 := &model.Draft{ - CreateAt: 00001, - UpdateAt: 00002, - UserId: user.Id, - ChannelId: channel.Id, - Message: "draft2", - } - - _, createDraftErr := th.App.CreateDraft(th.Context, draft1, "") + _, createDraftErr := th.App.UpsertDraft(th.Context, draft1, "") assert.Nil(t, createDraftErr) - t.Run("update draft", func(t *testing.T) { - draftResp, err := th.App.UpdateDraft(th.Context, draft2, "") - assert.Nil(t, err) - - assert.Equal(t, draft2.Message, draftResp.Message) - assert.Equal(t, draft2.ChannelId, draftResp.ChannelId) - - assert.NotEqual(t, draft1.UpdateAt, draftResp.UpdateAt) - }) - t.Run("update draft with files", func(t *testing.T) { // upload file sent, readFileErr := testutils.ReadTestFile("test.png") @@ -256,29 +227,17 @@ func TestUpdateDraft(t *testing.T) { draftWithFiles := draft1 draftWithFiles.FileIds = []string{fileResp.Id} - draftResp, err := th.App.UpdateDraft(th.Context, draft1, "") + _, err := th.App.UpsertDraft(th.Context, draft1, "") assert.Nil(t, err) + drafts, err := th.App.GetDraftsForUser(user.Id, th.BasicTeam.Id) + assert.Nil(t, err) + + draftResp := drafts[0] assert.Equal(t, draftWithFiles.Message, draftResp.Message) assert.Equal(t, draftWithFiles.ChannelId, draftResp.ChannelId) assert.ElementsMatch(t, draftWithFiles.FileIds, draftResp.FileIds) }) - - t.Run("create draft feature flag", func(t *testing.T) { - os.Setenv("MM_FEATUREFLAGS_GLOBALDRAFTS", "false") - defer os.Unsetenv("MM_FEATUREFLAGS_GLOBALDRAFTS") - os.Setenv("MM_SERVICESETTINGS_ALLOWSYNCEDDRAFTS", "false") - defer os.Unsetenv("MM_SERVICESETTINGS_ALLOWSYNCEDDRAFTS") - - th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.GlobalDrafts = false }) - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowSyncedDrafts = false }) - - defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.GlobalDrafts = true }) - defer th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowSyncedDrafts = true }) - - _, err := th.App.UpdateDraft(th.Context, draft1, "") - assert.NotNil(t, err) - }) } func TestGetDraftsForUser(t *testing.T) { @@ -312,10 +271,10 @@ func TestGetDraftsForUser(t *testing.T) { Message: "draft2", } - _, createDraftErr1 := th.App.CreateDraft(th.Context, draft1, "") + _, createDraftErr1 := th.App.UpsertDraft(th.Context, draft1, "") assert.Nil(t, createDraftErr1) - _, createDraftErr2 := th.App.CreateDraft(th.Context, draft2, "") + _, createDraftErr2 := th.App.UpsertDraft(th.Context, draft2, "") assert.Nil(t, createDraftErr2) t.Run("get drafts", func(t *testing.T) { @@ -340,7 +299,7 @@ func TestGetDraftsForUser(t *testing.T) { draftWithFiles := draft1 draftWithFiles.FileIds = []string{fileResp.Id} - draftResp, updateDraftErr := th.App.UpdateDraft(th.Context, draft1, "") + draftResp, updateDraftErr := th.App.UpsertDraft(th.Context, draft1, "") assert.Nil(t, updateDraftErr) assert.Equal(t, draftWithFiles.Message, draftResp.Message) @@ -397,7 +356,7 @@ func TestDeleteDraft(t *testing.T) { Message: "draft1", } - _, createDraftErr := th.App.CreateDraft(th.Context, draft1, "") + _, createDraftErr := th.App.UpsertDraft(th.Context, draft1, "") assert.Nil(t, createDraftErr) t.Run("delete draft", func(t *testing.T) { @@ -411,7 +370,7 @@ func TestDeleteDraft(t *testing.T) { assert.Equal(t, draft1.ChannelId, draftResp.ChannelId) }) - t.Run("get drafts feature flag", func(t *testing.T) { + t.Run("delete drafts feature flag", func(t *testing.T) { os.Setenv("MM_FEATUREFLAGS_GLOBALDRAFTS", "false") defer os.Unsetenv("MM_FEATUREFLAGS_GLOBALDRAFTS") os.Setenv("MM_SERVICESETTINGS_ALLOWSYNCEDDRAFTS", "false") diff --git a/server/channels/app/opentracing/opentracing_layer.go b/server/channels/app/opentracing/opentracing_layer.go index 4a4cd72441..6e522bb971 100644 --- a/server/channels/app/opentracing/opentracing_layer.go +++ b/server/channels/app/opentracing/opentracing_layer.go @@ -2026,28 +2026,6 @@ func (a *OpenTracingAppLayer) CreateDefaultMemberships(c *request.Context, param return resultVar0 } -func (a *OpenTracingAppLayer) CreateDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateDraft") - - a.ctx = newCtx - a.app.Srv().Store().SetContext(newCtx) - defer func() { - a.app.Srv().Store().SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0, resultVar1 := a.app.CreateDraft(c, draft, connectionID) - - if resultVar1 != nil { - span.LogFields(spanlog.Error(resultVar1)) - ext.Error.Set(span, true) - } - - return resultVar0, resultVar1 -} - func (a *OpenTracingAppLayer) CreateEmoji(c request.CTX, sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateEmoji") @@ -17381,28 +17359,6 @@ func (a *OpenTracingAppLayer) UpdateDNDStatusOfUsers() { a.app.UpdateDNDStatusOfUsers() } -func (a *OpenTracingAppLayer) UpdateDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateDraft") - - a.ctx = newCtx - a.app.Srv().Store().SetContext(newCtx) - defer func() { - a.app.Srv().Store().SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0, resultVar1 := a.app.UpdateDraft(c, draft, connectionID) - - if resultVar1 != nil { - span.LogFields(spanlog.Error(resultVar1)) - ext.Error.Set(span, true) - } - - return resultVar0, resultVar1 -} - func (a *OpenTracingAppLayer) UpdateEphemeralPost(c request.CTX, userID string, post *model.Post) *model.Post { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateEphemeralPost") diff --git a/server/channels/app/slashcommands/command_loadtest.go b/server/channels/app/slashcommands/command_loadtest.go index cd7ab9f217..a0b02267b4 100644 --- a/server/channels/app/slashcommands/command_loadtest.go +++ b/server/channels/app/slashcommands/command_loadtest.go @@ -629,7 +629,7 @@ func (*LoadTestProvider) URLCommand(a *app.App, c request.CTX, args *model.Comma // provide a shortcut to easily access tests stored in doc/developer/tests if !strings.HasPrefix(url, "http") { - url = "https://raw.githubusercontent.com/mattermost/mattermost-server/master/tests/" + url + url = "https://raw.githubusercontent.com/mattermost/mattermost-server/master/server/tests/" + url if path.Ext(url) == "" { url += ".md" @@ -683,7 +683,7 @@ func (*LoadTestProvider) JsonCommand(a *app.App, c request.CTX, args *model.Comm // provide a shortcut to easily access tests stored in doc/developer/tests if !strings.HasPrefix(url, "http") { - url = "https://raw.githubusercontent.com/mattermost/mattermost-server/master/tests/" + url + url = "https://raw.githubusercontent.com/mattermost/mattermost-server/master/server/tests/" + url if path.Ext(url) == "" { url += ".json" diff --git a/server/channels/app/worktemplates/templates.yaml b/server/channels/app/worktemplates/templates.yaml index 008c16c0e2..d935f9308c 100644 --- a/server/channels/app/worktemplates/templates.yaml +++ b/server/channels/app/worktemplates/templates.yaml @@ -9,16 +9,16 @@ visibility: public description: channel: id: "worktemplate.product_teams.feature_release.description.channel" - defaultMessage: "Chat with your team in a Feature Release channel that connects easily with your boards, playbooks and app bots." + defaultMessage: "Chat with your team about any release blockers and changes in a channel that connects easily with your boards, playbooks and other integrations." board: id: "worktemplate.product_teams.feature_release.description.board" - defaultMessage: "Use our Meeting Agenda board template for recurring meetings like standup and our Project Tasks board to manage the progress of tasks along the way." + defaultMessage: "Keep meetings on track with the Meeting Agenda board. Manage your workload with the Project Tasks board." playbook: id: "worktemplate.product_teams.feature_release.description.playbook" - defaultMessage: "Create transparent workflows across development teams to ensure your feature development process is seamless." + defaultMessage: "Boost cross-functional team collaboration with task checklists and automation that support your feature development process. When you’re done, run a retrospective and make improvements for your next release." integration: id: "worktemplate.product_teams.feature_release.description.integration" - defaultMessage: "Increase productivity in your channel by integrating a Jira bot and Github bot. These will be downloaded for you." + defaultMessage: "Increase productivity in your channel by integrating your most commonly used tools to support your feature release, such as GitHub. These will be downloaded for you." illustration: "/static/worktemplates/integrations.png" content: - channel: @@ -42,7 +42,7 @@ content: template: "Product Release" name: "Feature release" id: product-release-playbook - illustration: "/static/worktemplates/playbooks/product_release.png" + illustration: "/static/worktemplates/product_teams/feature_release/playbook.png" - integration: id: jira - integration: @@ -57,21 +57,15 @@ description: channel: id: worktemplate.product_teams.goals_and_okrs.channel defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel. board: id: worktemplate.product_teams.goals_and_okrs.board defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. integration: id: worktemplate.product_teams.goals_and_okrs.integration defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you. illustration: /static/worktemplates/integrations.png content: - channel: @@ -103,23 +97,15 @@ description: channel: id: worktemplate.product_teams.bug_bash.channel defaultMessage: >- - Get organized and bash all the bugs with this project! Build momentum and - measure progress using included Playbook, Board, and Channel. - board: - id: worktemplate.product_teams.bug_bash.board - defaultMessage: >- - Get organized and bash all the bugs with this project! Build momentum and - measure progress using included Playbook, Board, and Channel. + Plan and manage bug reports and resolutions in a single channel, that’s easily accessible to your team and organization. playbook: id: worktemplate.product_teams.bug_bash.playbook defaultMessage: >- - Get organized and bash all the bugs with this project! Build momentum and - measure progress using included Playbook, Board, and Channel. + Use checklists to assign testing areas and automated tasks to run a comprehensive bug bash process. Use a retrospective to review your process and improve it for next time. integration: id: worktemplate.product_teams.bug_bash.integration defaultMessage: >- - Get organized and bash all the bugs with this project! Build momentum and - measure progress using included Playbook, Board, and Channel. + Increase productivity in your channel by integrating your most commonly used tools, such as Jira, to track your bug bash progress. These will be downloaded for you. illustration: /static/worktemplates/integrations.png content: - playbook: @@ -144,24 +130,15 @@ description: channel: id: worktemplate.product_teams.sprint_planning.channel defaultMessage: >- - Use a Project to make sprint planning a breeze. The channel keeps the - conversation and questions focused. The sprint plan keeps everyone on task - for the week and the Retrospective board brings the team together to - continuously improve. + Chat with your team in a channel that connects easily with your boards and integrations. board: id: worktemplate.product_teams.sprint_planning.board defaultMessage: >- - Use a Project to make sprint planning a breeze. The channel keeps the - conversation and questions focused. The sprint plan keeps everyone on task - for the week and the Retrospective board brings the team together to - continuously improve. + Track your team's progress toward weekly goals with sprint breakdowns, prioritization, owner assignment, and comments. integration: id: worktemplate.product_teams.sprint_planning.integration defaultMessage: >- - Use a Project to make sprint planning a breeze. The channel keeps the - conversation and questions focused. The sprint plan keeps everyone on task - for the week and the Retrospective board brings the team together to - continuously improve. + Increase productivity in your channel by integrating your most commonly used tools, such as Zoom. These will be downloaded for you. illustration: /static/worktemplates/integrations.png content: - channel: @@ -185,10 +162,10 @@ visibility: public description: channel: id: worktemplate.product_teams.product_roadmap.channel - defaultMessage: Description of why the channel(s) are needed + defaultMessage: Chat with your team about your customers' feedback, prioritization, and get aligned on progress together. board: id: worktemplate.product_teams.product_roadmap.board - defaultMessage: Description of why the board(s) are needed + defaultMessage: Use the Product Roadmap board to manage user feedback, assign resources, view deliverables in a calendar view, and prioritize issues. content: - channel: id: channel-1674851139450 @@ -212,13 +189,13 @@ visibility: public description: channel: id: "worktemplate.devops.incident_resolution.description.channel" - defaultMessage: "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way." + defaultMessage: "Chat with your team about priorities, add stakeholders, provide updates, and work toward a resolution in a single channel." board: id: "worktemplate.devops.incident_resolution.description.board" - defaultMessage: "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way." + defaultMessage: "Use the Incident Resolution board to support repeatable processes and assign defined tasks across the team." playbook: id: "worktemplate.devops.incident_resolution.description.playbook" - defaultMessage: "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way." + defaultMessage: "Use checklists and automation to bring in key team members, and share how your incident is tracking toward resolution." content: - playbook: id: irpb @@ -245,30 +222,30 @@ visibility: public description: channel: id: worktemplate.devops.product_release.channel - defaultMessage: Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page. + defaultMessage: Chat with your team about daily milestones, any blockers, and changes to deliverables, easily and quickly. board: id: worktemplate.devops.product_release.board - defaultMessage: Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page. + defaultMessage: Use the Product Release board to support your release timeframe and process, ensuring everyone knows which tasks are due. playbook: id: worktemplate.devops.product_release.playbook - defaultMessage: Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page. + defaultMessage: Create repeatable workflows that are easy to follow and implement so product releases are reliable and on time. content: - playbook: id: playbook-1674851385983 template: Product Release name: Product Release illustration: /static/worktemplates/playbooks/product_release.png - - channel: - id: channel-1674851385983 - illustration: /static/worktemplates/devops/product_release/channel.png - name: Product Release - playbook: playbook-1674851385983 - board: id: board-1674851386432 template: a4ec399ab4f2088b1051c3cdf1dde4c3 name: Product Release illustration: /static/worktemplates/boards/project_tasks.png channel: channel-1674851385983 + - channel: + id: channel-1674851385983 + illustration: /static/worktemplates/devops/product_release/channel.png + name: Product Release + playbook: playbook-1674851385983 --- ###################### # COMPANY WIDE @@ -282,21 +259,15 @@ description: channel: id: worktemplate.companywide.goals_and_okrs.channel defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel. board: id: worktemplate.companywide.goals_and_okrs.board defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. integration: id: worktemplate.companywide.goals_and_okrs.integration defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you. illustration: /static/worktemplates/integrations.png content: - channel: @@ -315,24 +286,21 @@ content: id: 'companywide/create_project:v1' category: companywide useCase: Create a project -illustration: /static/worktemplates/companywide/create_project/create_project.svg +illustration: /static/worktemplates/companywide/create_project/create_project.png visibility: public description: channel: id: worktemplate.companywide.create_project.channel defaultMessage: >- - Plan a Roadmap using this Project Board and collaborate on topic in the - channel created with this template. + Chat with your team about your new project and decide how you’re going to structure it, in a collaborative channel. board: id: worktemplate.companywide.create_project.board defaultMessage: >- - Plan a Roadmap using this Project Board and collaborate on topic in the - channel created with this template. + Use a Kanban board to define and track your project tasks and progress. integration: id: worktemplate.companywide.create_project.integration defaultMessage: >- - Plan a Roadmap using this Project Board and collaborate on topic in the - channel created with this template. + Increase productivity in your channel by integrating your most commonly used tools. These will be downloaded for you. illustration: /static/worktemplates/integrations.png content: - channel: @@ -365,21 +333,15 @@ description: channel: id: worktemplate.leadership.goals_and_okrs.channel defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel. board: id: worktemplate.leadership.goals_and_okrs.board defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. integration: id: worktemplate.leadership.goals_and_okrs.integration defaultMessage: >- - Clear focus is essential to team success and with this Project you can - document the team’s goals and OKR’s as well as post updates in the - dedicated channel. + Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you. illustration: /static/worktemplates/integrations.png content: - channel: diff --git a/server/channels/app/worktemplates/worktemplate_generated.go b/server/channels/app/worktemplates/worktemplate_generated.go index 4e99e0dd1e..95e77edee5 100644 --- a/server/channels/app/worktemplates/worktemplate_generated.go +++ b/server/channels/app/worktemplates/worktemplate_generated.go @@ -37,7 +37,6 @@ func init() { _ = T("worktemplate.product_teams.goals_and_okrs.board") _ = T("worktemplate.product_teams.goals_and_okrs.integration") _ = T("worktemplate.product_teams.bug_bash.channel") - _ = T("worktemplate.product_teams.bug_bash.board") _ = T("worktemplate.product_teams.bug_bash.playbook") _ = T("worktemplate.product_teams.bug_bash.integration") _ = T("worktemplate.product_teams.sprint_planning.channel") @@ -92,22 +91,22 @@ var wt00a1b44a5831c0a3acb14787b3fdd352 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.product_teams.feature_release.description.channel", - DefaultMessage: "Chat with your team in a Feature Release channel that connects easily with your boards, playbooks and app bots.", + DefaultMessage: "Chat with your team about any release blockers and changes in a channel that connects easily with your boards, playbooks and other integrations.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.product_teams.feature_release.description.board", - DefaultMessage: "Use our Meeting Agenda board template for recurring meetings like standup and our Project Tasks board to manage the progress of tasks along the way.", + DefaultMessage: "Keep meetings on track with the Meeting Agenda board. Manage your workload with the Project Tasks board.", Illustration: "", }, Playbook: &TranslatableString{ ID: "worktemplate.product_teams.feature_release.description.playbook", - DefaultMessage: "Create transparent workflows across development teams to ensure your feature development process is seamless.", + DefaultMessage: "Boost cross-functional team collaboration with task checklists and automation that support your feature development process. When you’re done, run a retrospective and make improvements for your next release.", Illustration: "", }, Integration: &TranslatableString{ ID: "worktemplate.product_teams.feature_release.description.integration", - DefaultMessage: "Increase productivity in your channel by integrating a Jira bot and Github bot. These will be downloaded for you.", + DefaultMessage: "Increase productivity in your channel by integrating your most commonly used tools to support your feature release, such as GitHub. These will be downloaded for you.", Illustration: "/static/worktemplates/integrations.png", }, }, @@ -144,7 +143,7 @@ var wt00a1b44a5831c0a3acb14787b3fdd352 = &WorkTemplate{ Template: "Product Release", Name: "Feature release", ID: "product-release-playbook", - Illustration: "/static/worktemplates/playbooks/product_release.png", + Illustration: "/static/worktemplates/product_teams/feature_release/playbook.png", }, }, { @@ -170,18 +169,18 @@ var wt5baa68055bf9ea423273662e01ccc575 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.product_teams.goals_and_okrs.channel", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.product_teams.goals_and_okrs.board", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. ", Illustration: "", }, Integration: &TranslatableString{ ID: "worktemplate.product_teams.goals_and_okrs.integration", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you.", Illustration: "/static/worktemplates/integrations.png", }, }, @@ -231,22 +230,18 @@ var wtfeb56bc6a8f277c47b503bd1c92d830e = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.product_teams.bug_bash.channel", - DefaultMessage: "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel.", - Illustration: "", - }, - Board: &TranslatableString{ - ID: "worktemplate.product_teams.bug_bash.board", - DefaultMessage: "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel.", + DefaultMessage: "Plan and manage bug reports and resolutions in a single channel, that’s easily accessible to your team and organization.", Illustration: "", }, + Playbook: &TranslatableString{ ID: "worktemplate.product_teams.bug_bash.playbook", - DefaultMessage: "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel.", + DefaultMessage: "Use checklists to assign testing areas and automated tasks to run a comprehensive bug bash process. Use a retrospective to review your process and improve it for next time.", Illustration: "", }, Integration: &TranslatableString{ ID: "worktemplate.product_teams.bug_bash.integration", - DefaultMessage: "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel.", + DefaultMessage: "Increase productivity in your channel by integrating your most commonly used tools, such as Jira, to track your bug bash progress. These will be downloaded for you.", Illustration: "/static/worktemplates/integrations.png", }, }, @@ -286,18 +281,18 @@ var wt8d2ef53deac5517eb349dc5de6150196 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.product_teams.sprint_planning.channel", - DefaultMessage: "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve.", + DefaultMessage: "Chat with your team in a channel that connects easily with your boards and integrations.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.product_teams.sprint_planning.board", - DefaultMessage: "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve.", + DefaultMessage: "Track your team's progress toward weekly goals with sprint breakdowns, prioritization, owner assignment, and comments. ", Illustration: "", }, Integration: &TranslatableString{ ID: "worktemplate.product_teams.sprint_planning.integration", - DefaultMessage: "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve.", + DefaultMessage: "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom. These will be downloaded for you.", Illustration: "/static/worktemplates/integrations.png", }, }, @@ -338,12 +333,12 @@ var wt00ab91a945627f4a624957dd80490bb2 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.product_teams.product_roadmap.channel", - DefaultMessage: "Description of why the channel(s) are needed", + DefaultMessage: "Chat with your team about your customers' feedback, prioritization, and get aligned on progress together.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.product_teams.product_roadmap.board", - DefaultMessage: "Description of why the board(s) are needed", + DefaultMessage: "Use the Product Roadmap board to manage user feedback, assign resources, view deliverables in a calendar view, and prioritize issues.", Illustration: "", }, }, @@ -379,17 +374,17 @@ var wtce19b9352a59d6a5d26f292d83e84377 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.devops.incident_resolution.description.channel", - DefaultMessage: "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way.", + DefaultMessage: "Chat with your team about priorities, add stakeholders, provide updates, and work toward a resolution in a single channel.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.devops.incident_resolution.description.board", - DefaultMessage: "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way.", + DefaultMessage: "Use the Incident Resolution board to support repeatable processes and assign defined tasks across the team.", Illustration: "", }, Playbook: &TranslatableString{ ID: "worktemplate.devops.incident_resolution.description.playbook", - DefaultMessage: "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way.", + DefaultMessage: "Use checklists and automation to bring in key team members, and share how your incident is tracking toward resolution.", Illustration: "", }, }, @@ -433,17 +428,17 @@ var wt37406285a41c18bcdeb881189f7acde0 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.devops.product_release.channel", - DefaultMessage: "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page.", + DefaultMessage: "Chat with your team about daily milestones, any blockers, and changes to deliverables, easily and quickly.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.devops.product_release.board", - DefaultMessage: "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page.", + DefaultMessage: "Use the Product Release board to support your release timeframe and process, ensuring everyone knows which tasks are due.", Illustration: "", }, Playbook: &TranslatableString{ ID: "worktemplate.devops.product_release.playbook", - DefaultMessage: "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page.", + DefaultMessage: "Create repeatable workflows that are easy to follow and implement so product releases are reliable and on time.", Illustration: "", }, }, @@ -456,15 +451,6 @@ var wt37406285a41c18bcdeb881189f7acde0 = &WorkTemplate{ Illustration: "/static/worktemplates/playbooks/product_release.png", }, }, - { - Channel: &Channel{ - ID: "channel-1674851385983", - Name: "Product Release", - Purpose: "", - Playbook: "playbook-1674851385983", - Illustration: "/static/worktemplates/devops/product_release/channel.png", - }, - }, { Board: &Board{ ID: "board-1674851386432", @@ -474,6 +460,15 @@ var wt37406285a41c18bcdeb881189f7acde0 = &WorkTemplate{ Illustration: "/static/worktemplates/boards/project_tasks.png", }, }, + { + Channel: &Channel{ + ID: "channel-1674851385983", + Name: "Product Release", + Purpose: "", + Playbook: "playbook-1674851385983", + Illustration: "/static/worktemplates/devops/product_release/channel.png", + }, + }, }, } @@ -487,18 +482,18 @@ var wtf7b846d35810f8272eeb9a1a562025b5 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.companywide.goals_and_okrs.channel", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.companywide.goals_and_okrs.board", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. ", Illustration: "", }, Integration: &TranslatableString{ ID: "worktemplate.companywide.goals_and_okrs.integration", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you.", Illustration: "/static/worktemplates/integrations.png", }, }, @@ -533,24 +528,24 @@ var wtb9ab412890c2410c7b49eec8f12e7edc = &WorkTemplate{ ID: "companywide/create_project:v1", Category: "companywide", UseCase: "Create a project", - Illustration: "/static/worktemplates/companywide/create_project/create_project.svg", + Illustration: "/static/worktemplates/companywide/create_project/create_project.png", Visibility: "public", Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.companywide.create_project.channel", - DefaultMessage: "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template.", + DefaultMessage: "Chat with your team about your new project and decide how you’re going to structure it, in a collaborative channel.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.companywide.create_project.board", - DefaultMessage: "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template.", + DefaultMessage: "Use a Kanban board to define and track your project tasks and progress.", Illustration: "", }, Integration: &TranslatableString{ ID: "worktemplate.companywide.create_project.integration", - DefaultMessage: "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template.", + DefaultMessage: "Increase productivity in your channel by integrating your most commonly used tools. These will be downloaded for you.", Illustration: "/static/worktemplates/integrations.png", }, }, @@ -601,18 +596,18 @@ var wt32ab773bfe021e3d4913931041552559 = &WorkTemplate{ Description: Description{ Channel: &TranslatableString{ ID: "worktemplate.leadership.goals_and_okrs.channel", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel.", Illustration: "", }, Board: &TranslatableString{ ID: "worktemplate.leadership.goals_and_okrs.board", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. ", Illustration: "", }, Integration: &TranslatableString{ ID: "worktemplate.leadership.goals_and_okrs.integration", - DefaultMessage: "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel.", + DefaultMessage: "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you.", Illustration: "/static/worktemplates/integrations.png", }, }, diff --git a/server/channels/einterfaces/cloud.go b/server/channels/einterfaces/cloud.go index b5d6a75b68..70cdc4676a 100644 --- a/server/channels/einterfaces/cloud.go +++ b/server/channels/einterfaces/cloud.go @@ -47,4 +47,5 @@ type CloudInterface interface { CheckCWSConnection(userId string) error SelfServeDeleteWorkspace(userID string, deletionRequest *model.WorkspaceDeletionRequest) error + SubscribeToNewsletter(userID string, req *model.SubscribeNewsletterRequest) error } diff --git a/server/channels/einterfaces/mocks/CloudInterface.go b/server/channels/einterfaces/mocks/CloudInterface.go index db7c86acc2..2dd2711650 100644 --- a/server/channels/einterfaces/mocks/CloudInterface.go +++ b/server/channels/einterfaces/mocks/CloudInterface.go @@ -540,6 +540,20 @@ func (_m *CloudInterface) SelfServeDeleteWorkspace(userID string, deletionReques return r0 } +// SubscribeToNewsletter provides a mock function with given fields: userID, req +func (_m *CloudInterface) SubscribeToNewsletter(userID string, req *model.SubscribeNewsletterRequest) error { + ret := _m.Called(userID, req) + + var r0 error + if rf, ok := ret.Get(0).(func(string, *model.SubscribeNewsletterRequest) error); ok { + r0 = rf(userID, req) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateCloudCustomer provides a mock function with given fields: userID, customerInfo func (_m *CloudInterface) UpdateCloudCustomer(userID string, customerInfo *model.CloudCustomerInfo) (*model.CloudCustomer, error) { ret := _m.Called(userID, customerInfo) diff --git a/server/channels/store/opentracinglayer/opentracinglayer.go b/server/channels/store/opentracinglayer/opentracinglayer.go index 15d32a18e2..723b8a4775 100644 --- a/server/channels/store/opentracinglayer/opentracinglayer.go +++ b/server/channels/store/opentracinglayer/opentracinglayer.go @@ -3315,34 +3315,16 @@ func (s *OpenTracingLayerDraftStore) GetDraftsForUser(userID string, teamID stri return result, err } -func (s *OpenTracingLayerDraftStore) Save(d *model.Draft) (*model.Draft, error) { +func (s *OpenTracingLayerDraftStore) Upsert(d *model.Draft) (*model.Draft, error) { origCtx := s.Root.Store.Context() - span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DraftStore.Save") + span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DraftStore.Upsert") s.Root.Store.SetContext(newCtx) defer func() { s.Root.Store.SetContext(origCtx) }() defer span.Finish() - result, err := s.DraftStore.Save(d) - if err != nil { - span.LogFields(spanlog.Error(err)) - ext.Error.Set(span, true) - } - - return result, err -} - -func (s *OpenTracingLayerDraftStore) Update(d *model.Draft) (*model.Draft, error) { - origCtx := s.Root.Store.Context() - span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DraftStore.Update") - s.Root.Store.SetContext(newCtx) - defer func() { - s.Root.Store.SetContext(origCtx) - }() - - defer span.Finish() - result, err := s.DraftStore.Update(d) + result, err := s.DraftStore.Upsert(d) if err != nil { span.LogFields(spanlog.Error(err)) ext.Error.Set(span, true) diff --git a/server/channels/store/retrylayer/retrylayer.go b/server/channels/store/retrylayer/retrylayer.go index 07997b61ac..3603bd7f7e 100644 --- a/server/channels/store/retrylayer/retrylayer.go +++ b/server/channels/store/retrylayer/retrylayer.go @@ -3703,32 +3703,11 @@ func (s *RetryLayerDraftStore) GetDraftsForUser(userID string, teamID string) ([ } -func (s *RetryLayerDraftStore) Save(d *model.Draft) (*model.Draft, error) { +func (s *RetryLayerDraftStore) Upsert(d *model.Draft) (*model.Draft, error) { tries := 0 for { - result, err := s.DraftStore.Save(d) - if err == nil { - return result, nil - } - if !isRepeatableError(err) { - return result, err - } - tries++ - if tries >= 3 { - err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures") - return result, err - } - timepkg.Sleep(100 * timepkg.Millisecond) - } - -} - -func (s *RetryLayerDraftStore) Update(d *model.Draft) (*model.Draft, error) { - - tries := 0 - for { - result, err := s.DraftStore.Update(d) + result, err := s.DraftStore.Upsert(d) if err == nil { return result, nil } diff --git a/server/channels/store/sqlstore/draft_store.go b/server/channels/store/sqlstore/draft_store.go index b5b7dee743..7d4137a8e5 100644 --- a/server/channels/store/sqlstore/draft_store.go +++ b/server/channels/store/sqlstore/draft_store.go @@ -88,7 +88,7 @@ func (s *SqlDraftStore) Get(userId, channelId, rootId string, includeDeleted boo return &dt, nil } -func (s *SqlDraftStore) Save(draft *model.Draft) (*model.Draft, error) { +func (s *SqlDraftStore) Upsert(draft *model.Draft) (*model.Draft, error) { draft.PreSave() maxDraftSize := s.GetMaxDraftSize() if err := draft.IsValid(maxDraftSize); err != nil { @@ -96,6 +96,13 @@ func (s *SqlDraftStore) Save(draft *model.Draft) (*model.Draft, error) { } builder := s.getQueryBuilder().Insert("Drafts").Columns(draftSliceColumns()...).Values(draftToSlice(draft)...) + + if s.DriverName() == model.DatabaseDriverMysql { + builder = builder.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE UpdateAt = ?, Message = ?, Props = ?, FileIds = ?, Priority = ?, DeleteAt = ?", draft.UpdateAt, draft.Message, draft.Props, draft.FileIds, draft.Priority, 0)) + } else { + builder = builder.SuffixExpr(sq.Expr("ON CONFLICT (UserId, ChannelId, RootId) DO UPDATE SET UpdateAt = ?, Message = ?, Props = ?, FileIds = ?, Priority = ?, DeleteAt = ?", draft.UpdateAt, draft.Message, draft.Props, draft.FileIds, draft.Priority, 0)) + } + query, args, err := builder.ToSql() if err != nil { @@ -103,36 +110,7 @@ func (s *SqlDraftStore) Save(draft *model.Draft) (*model.Draft, error) { } if _, err = s.GetMasterX().Exec(query, args...); err != nil { - return nil, errors.Wrap(err, "failed to save Draft") - } - - return draft, nil -} - -func (s *SqlDraftStore) Update(draft *model.Draft) (*model.Draft, error) { - draft.PreUpdate() - - maxDraftSize := s.GetMaxDraftSize() - if err := draft.IsValid(maxDraftSize); err != nil { - return nil, err - } - - query := s.getQueryBuilder(). - Update("Drafts"). - Set("UpdateAt", draft.UpdateAt). - Set("Message", draft.Message). - Set("Props", draft.Props). - Set("FileIds", draft.FileIds). - Set("Priority", draft.Priority). - Set("DeleteAt", 0). - Where(sq.Eq{ - "UserId": draft.UserId, - "ChannelId": draft.ChannelId, - "RootId": draft.RootId, - }) - - if _, err := s.GetMasterX().ExecBuilder(query); err != nil { - return nil, errors.Wrapf(err, "failed to update Draft with channelid=%s", draft.ChannelId) + return nil, errors.Wrap(err, "failed to upsert Draft") } return draft, nil diff --git a/server/channels/store/sqlstore/store.go b/server/channels/store/sqlstore/store.go index a1d7380b38..8000384e95 100644 --- a/server/channels/store/sqlstore/store.go +++ b/server/channels/store/sqlstore/store.go @@ -237,7 +237,9 @@ func SetupConnection(connType string, dataSource string, settings *model.SqlSett } for i := 0; i < DBPingAttempts; i++ { - mlog.Info("Pinging SQL", mlog.String("database", connType), mlog.String("dataSource", dataSource)) + // At this point, we have passed sql.Open, so we deliberately ignore any errors. + sanitized, _ := SanitizeDataSource(*settings.DriverName, dataSource) + mlog.Info("Pinging SQL", mlog.String("database", connType), mlog.String("dataSource", sanitized)) ctx, cancel := context.WithTimeout(context.Background(), DBPingTimeoutSecs*time.Second) defer cancel() err = db.PingContext(ctx) diff --git a/server/channels/store/sqlstore/utils.go b/server/channels/store/sqlstore/utils.go index 809439c820..5242f0c527 100644 --- a/server/channels/store/sqlstore/utils.go +++ b/server/channels/store/sqlstore/utils.go @@ -5,6 +5,7 @@ package sqlstore import ( "database/sql" + "errors" "io" "net/url" "strconv" @@ -206,3 +207,29 @@ func ResetReadTimeout(dataSource string) (string, error) { config.ReadTimeout = 0 return config.FormatDSN(), nil } + +func SanitizeDataSource(driverName, dataSource string) (string, error) { + switch driverName { + case model.DatabaseDriverPostgres: + u, err := url.Parse(dataSource) + if err != nil { + return "", err + } + u.User = url.UserPassword("****", "****") + params := u.Query() + params.Del("user") + params.Del("password") + u.RawQuery = params.Encode() + return u.String(), nil + case model.DatabaseDriverMysql: + cfg, err := mysql.ParseDSN(dataSource) + if err != nil { + return "", err + } + cfg.User = "****" + cfg.Passwd = "****" + return cfg.FormatDSN(), nil + default: + return "", errors.New("invalid drivername. Not postgres or mysql.") + } +} diff --git a/server/channels/store/sqlstore/utils_test.go b/server/channels/store/sqlstore/utils_test.go index 811ebf001a..96468323bb 100644 --- a/server/channels/store/sqlstore/utils_test.go +++ b/server/channels/store/sqlstore/utils_test.go @@ -6,6 +6,7 @@ package sqlstore import ( "testing" + "github.com/mattermost/mattermost-server/v6/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -160,3 +161,45 @@ func TestAppendMultipleStatementsFlag(t *testing.T) { }) } } + +func TestSanitizeDataSource(t *testing.T) { + t.Run(model.DatabaseDriverPostgres, func(t *testing.T) { + testCases := []struct { + Original string + Sanitized string + }{ + { + "postgres://mmuser:mostest@localhost/dummy?sslmode=disable", + "postgres://%2A%2A%2A%2A:%2A%2A%2A%2A@localhost/dummy?sslmode=disable", + }, + { + "postgres://localhost/dummy?sslmode=disable&user=mmuser&password=mostest", + "postgres://%2A%2A%2A%2A:%2A%2A%2A%2A@localhost/dummy?sslmode=disable", + }, + } + driver := model.DatabaseDriverPostgres + for _, tc := range testCases { + out, err := SanitizeDataSource(driver, tc.Original) + require.NoError(t, err) + assert.Equal(t, tc.Sanitized, out) + } + }) + + t.Run(model.DatabaseDriverMysql, func(t *testing.T) { + testCases := []struct { + Original string + Sanitized string + }{ + { + "mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s", + "****:****@tcp(localhost:3306)/mattermost_test?readTimeout=30s&writeTimeout=30s&charset=utf8mb4%2Cutf8", + }, + } + driver := model.DatabaseDriverMysql + for _, tc := range testCases { + out, err := SanitizeDataSource(driver, tc.Original) + require.NoError(t, err) + assert.Equal(t, tc.Sanitized, out) + } + }) +} diff --git a/server/channels/store/store.go b/server/channels/store/store.go index dec4fa0f89..e52c2037ab 100644 --- a/server/channels/store/store.go +++ b/server/channels/store/store.go @@ -985,11 +985,10 @@ type PostPriorityStore interface { } type DraftStore interface { - Save(d *model.Draft) (*model.Draft, error) + Upsert(d *model.Draft) (*model.Draft, error) Get(userID, channelID, rootID string, includeDeleted bool) (*model.Draft, error) Delete(userID, channelID, rootID string) error GetDraftsForUser(userID, teamID string) ([]*model.Draft, error) - Update(d *model.Draft) (*model.Draft, error) } type PostAcknowledgementStore interface { diff --git a/server/channels/store/storetest/draft_store.go b/server/channels/store/storetest/draft_store.go index 377f36fa60..45eb33cae9 100644 --- a/server/channels/store/storetest/draft_store.go +++ b/server/channels/store/storetest/draft_store.go @@ -68,17 +68,21 @@ func testSaveDraft(t *testing.T, ss store.Store) { } t.Run("save drafts", func(t *testing.T) { - draftResp, err := ss.Draft().Save(draft1) + draftResp, err := ss.Draft().Upsert(draft1) assert.NoError(t, err) assert.Equal(t, draft1.Message, draftResp.Message) assert.Equal(t, draft1.ChannelId, draftResp.ChannelId) - draftResp, err = ss.Draft().Save(draft2) + draftResp, err = ss.Draft().Upsert(draft2) assert.NoError(t, err) assert.Equal(t, draft2.Message, draftResp.Message) assert.Equal(t, draft2.ChannelId, draftResp.ChannelId) + + drafts, err := ss.Draft().GetDraftsForUser(user.Id, "") + assert.NoError(t, err) + assert.Len(t, drafts, 2) }) } @@ -90,56 +94,52 @@ func testUpdateDraft(t *testing.T, ss store.Store) { channel := &model.Channel{ Id: model.NewId(), } - channel2 := &model.Channel{ - Id: model.NewId(), - } - member1 := &model.ChannelMember{ + member := &model.ChannelMember{ ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps(), } - member2 := &model.ChannelMember{ - ChannelId: channel2.Id, - UserId: user.Id, - NotifyProps: model.GetDefaultChannelNotifyProps(), - } - - _, err := ss.Channel().SaveMember(member1) + _, err := ss.Channel().SaveMember(member) require.NoError(t, err) - _, err = ss.Channel().SaveMember(member2) - require.NoError(t, err) - - draft1 := &model.Draft{ - CreateAt: 00001, - UpdateAt: 00001, - UserId: user.Id, - ChannelId: channel.Id, - Message: "draft1", - } - - draft2 := &model.Draft{ - CreateAt: 00005, - UpdateAt: 00005, - UserId: user.Id, - ChannelId: channel2.Id, - Message: "draft2", - } - t.Run("update drafts", func(t *testing.T) { - draftResp, err := ss.Draft().Update(draft1) + draft := &model.Draft{ + UserId: user.Id, + ChannelId: channel.Id, + Message: "draft", + } + _, err := ss.Draft().Upsert(draft) assert.NoError(t, err) - assert.Equal(t, draft1.Message, draftResp.Message) - assert.Equal(t, draft1.ChannelId, draftResp.ChannelId) + drafts, err := ss.Draft().GetDraftsForUser(user.Id, "") + assert.NoError(t, err) + assert.Len(t, drafts, 1) + draft1 := drafts[0] - draftResp, err = ss.Draft().Update(draft2) + assert.Greater(t, draft1.CreateAt, int64(0)) + assert.Equal(t, draft1.UpdateAt, draft1.CreateAt) + assert.Equal(t, channel.Id, draft1.ChannelId) + assert.Equal(t, "draft", draft1.Message) + + updatedDraft := &model.Draft{ + UserId: user.Id, + ChannelId: channel.Id, + Message: "updatedDraft", + } + _, err = ss.Draft().Upsert(updatedDraft) assert.NoError(t, err) - assert.Equal(t, draft2.Message, draftResp.Message) - assert.Equal(t, draft2.ChannelId, draftResp.ChannelId) + drafts, err = ss.Draft().GetDraftsForUser(user.Id, "") + assert.NoError(t, err) + assert.Len(t, drafts, 1) + draft2 := drafts[0] + + assert.Greater(t, draft2.CreateAt, int64(0)) + assert.Equal(t, "updatedDraft", draft2.Message) + assert.Equal(t, channel.Id, draft2.ChannelId) + assert.Equal(t, draft1.CreateAt, draft2.CreateAt) }) } @@ -189,10 +189,10 @@ func testDeleteDraft(t *testing.T, ss store.Store) { Message: "draft2", } - _, err = ss.Draft().Save(draft1) + _, err = ss.Draft().Upsert(draft1) require.NoError(t, err) - _, err = ss.Draft().Save(draft2) + _, err = ss.Draft().Upsert(draft2) require.NoError(t, err) t.Run("delete drafts", func(t *testing.T) { @@ -258,10 +258,10 @@ func testGetDraft(t *testing.T, ss store.Store) { Message: "draft2", } - _, err = ss.Draft().Save(draft1) + _, err = ss.Draft().Upsert(draft1) require.NoError(t, err) - _, err = ss.Draft().Save(draft2) + _, err = ss.Draft().Upsert(draft2) require.NoError(t, err) t.Run("get drafts", func(t *testing.T) { @@ -326,35 +326,28 @@ func testGetDraftsForUser(t *testing.T, ss store.Store) { require.NoError(t, err) draft1 := &model.Draft{ - CreateAt: 00001, - UpdateAt: 00001, UserId: user.Id, ChannelId: channel.Id, Message: "draft1", } draft2 := &model.Draft{ - CreateAt: 00005, - UpdateAt: 00005, UserId: user.Id, ChannelId: channel2.Id, Message: "draft2", } - _, err = ss.Draft().Save(draft1) + _, err = ss.Draft().Upsert(draft1) require.NoError(t, err) - _, err = ss.Draft().Save(draft2) + _, err = ss.Draft().Upsert(draft2) require.NoError(t, err) t.Run("get drafts", func(t *testing.T) { draftResp, err := ss.Draft().GetDraftsForUser(user.Id, "") assert.NoError(t, err) + assert.Len(t, draftResp, 2) - assert.Equal(t, draft2.Message, draftResp[0].Message) - assert.Equal(t, draft2.ChannelId, draftResp[0].ChannelId) - - assert.Equal(t, draft1.Message, draftResp[1].Message) - assert.Equal(t, draft1.ChannelId, draftResp[1].ChannelId) + assert.ElementsMatch(t, []*model.Draft{draft1, draft2}, draftResp) }) } diff --git a/server/channels/store/storetest/mocks/DraftStore.go b/server/channels/store/storetest/mocks/DraftStore.go index 1eb7d5f17b..924bb49883 100644 --- a/server/channels/store/storetest/mocks/DraftStore.go +++ b/server/channels/store/storetest/mocks/DraftStore.go @@ -74,31 +74,8 @@ func (_m *DraftStore) GetDraftsForUser(userID string, teamID string) ([]*model.D return r0, r1 } -// Save provides a mock function with given fields: d -func (_m *DraftStore) Save(d *model.Draft) (*model.Draft, error) { - ret := _m.Called(d) - - var r0 *model.Draft - if rf, ok := ret.Get(0).(func(*model.Draft) *model.Draft); ok { - r0 = rf(d) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.Draft) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.Draft) error); ok { - r1 = rf(d) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Update provides a mock function with given fields: d -func (_m *DraftStore) Update(d *model.Draft) (*model.Draft, error) { +// Upsert provides a mock function with given fields: d +func (_m *DraftStore) Upsert(d *model.Draft) (*model.Draft, error) { ret := _m.Called(d) var r0 *model.Draft diff --git a/server/channels/store/timerlayer/timerlayer.go b/server/channels/store/timerlayer/timerlayer.go index 8199138ac2..2c156ccbe5 100644 --- a/server/channels/store/timerlayer/timerlayer.go +++ b/server/channels/store/timerlayer/timerlayer.go @@ -3038,10 +3038,10 @@ func (s *TimerLayerDraftStore) GetDraftsForUser(userID string, teamID string) ([ return result, err } -func (s *TimerLayerDraftStore) Save(d *model.Draft) (*model.Draft, error) { +func (s *TimerLayerDraftStore) Upsert(d *model.Draft) (*model.Draft, error) { start := time.Now() - result, err := s.DraftStore.Save(d) + result, err := s.DraftStore.Upsert(d) elapsed := float64(time.Since(start)) / float64(time.Second) if s.Root.Metrics != nil { @@ -3049,23 +3049,7 @@ func (s *TimerLayerDraftStore) Save(d *model.Draft) (*model.Draft, error) { if err == nil { success = "true" } - s.Root.Metrics.ObserveStoreMethodDuration("DraftStore.Save", success, elapsed) - } - return result, err -} - -func (s *TimerLayerDraftStore) Update(d *model.Draft) (*model.Draft, error) { - start := time.Now() - - result, err := s.DraftStore.Update(d) - - elapsed := float64(time.Since(start)) / float64(time.Second) - if s.Root.Metrics != nil { - success := "false" - if err == nil { - success = "true" - } - s.Root.Metrics.ObserveStoreMethodDuration("DraftStore.Update", success, elapsed) + s.Root.Metrics.ObserveStoreMethodDuration("DraftStore.Upsert", success, elapsed) } return result, err } diff --git a/server/config/database.go b/server/config/database.go index f2463e33b4..dd508d9c4f 100644 --- a/server/config/database.go +++ b/server/config/database.go @@ -407,7 +407,10 @@ func (ds *DatabaseStore) RemoveFile(name string) error { // String returns the path to the database backing the config, masking the password. func (ds *DatabaseStore) String() string { - return stripPassword(ds.originalDsn, ds.driverName) + // This is called during the running of MM, so we expect the parsing of DSN + // to be successful. + sanitized, _ := sqlstore.SanitizeDataSource(ds.driverName, ds.originalDsn) + return sanitized } // Close cleans up resources associated with the store. diff --git a/server/config/database_test.go b/server/config/database_test.go index 4eab71fc5c..6954461a08 100644 --- a/server/config/database_test.go +++ b/server/config/database_test.go @@ -1107,12 +1107,12 @@ func TestDatabaseStoreString(t *testing.T) { if *mainHelper.GetSQLSettings().DriverName == "postgres" { maskedDSN := ds.String() assert.True(t, strings.HasPrefix(maskedDSN, "postgres://")) - assert.True(t, strings.Contains(maskedDSN, "mmuser")) + assert.False(t, strings.Contains(maskedDSN, "mmuser")) assert.False(t, strings.Contains(maskedDSN, "mostest")) } else { maskedDSN := ds.String() - assert.True(t, strings.HasPrefix(maskedDSN, "mysql://")) - assert.True(t, strings.Contains(maskedDSN, "mmuser")) + assert.False(t, strings.HasPrefix(maskedDSN, "mysql://")) + assert.False(t, strings.Contains(maskedDSN, "mmuser")) assert.False(t, strings.Contains(maskedDSN, "mostest")) } } diff --git a/server/config/utils.go b/server/config/utils.go index b343106562..f247ea4a43 100644 --- a/server/config/utils.go +++ b/server/config/utils.go @@ -179,27 +179,6 @@ func IsDatabaseDSN(dsn string) bool { strings.HasPrefix(dsn, "postgresql://") } -// stripPassword remove the password from a given DSN -func stripPassword(dsn, schema string) string { - prefix := schema + "://" - dsn = strings.TrimPrefix(dsn, prefix) - - i := strings.Index(dsn, ":") - j := strings.LastIndex(dsn, "@") - - // Return error if no @ sign is found - if j < 0 { - return "(omitted due to error parsing the DSN)" - } - - // Return back the input if no password is found - if i < 0 || i > j { - return prefix + dsn - } - - return prefix + dsn[:i+1] + dsn[j:] -} - func isJSONMap(data string) bool { var m map[string]any return json.Unmarshal([]byte(data), &m) == nil diff --git a/server/config/utils_test.go b/server/config/utils_test.go index e3b903eeb1..9c8de20d23 100644 --- a/server/config/utils_test.go +++ b/server/config/utils_test.go @@ -197,61 +197,6 @@ func TestIsDatabaseDSN(t *testing.T) { } } -func TestStripPassword(t *testing.T) { - for name, test := range map[string]struct { - DSN string - Schema string - ExpectedOut string - }{ - "mysql": { - DSN: "mysql://mmuser:password@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - Schema: "mysql", - ExpectedOut: "mysql://mmuser:@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - }, - "mysql idempotent": { - DSN: "mysql://mmuser:@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - Schema: "mysql", - ExpectedOut: "mysql://mmuser:@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - }, - "mysql: password with : and @": { - DSN: "mysql://mmuser:p:assw@ord@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - Schema: "mysql", - ExpectedOut: "mysql://mmuser:@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - }, - "mysql: password with @ and :": { - DSN: "mysql://mmuser:pa@sswo:rd@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - Schema: "mysql", - ExpectedOut: "mysql://mmuser:@tcp(localhost:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s", - }, - "postgres": { - DSN: "postgres://mmuser:password@localhost:5432/mattermost?sslmode=disable&connect_timeout=10", - Schema: "postgres", - ExpectedOut: "postgres://mmuser:@localhost:5432/mattermost?sslmode=disable&connect_timeout=10", - }, - "pipe": { - DSN: "mysql://user@unix(/path/to/socket)/dbname", - Schema: "mysql", - ExpectedOut: "mysql://user@unix(/path/to/socket)/dbname", - }, - "malformed without :": { - DSN: "postgres://mmuserpassword@localhost:5432/mattermost?sslmode=disable&connect_timeout=10", - Schema: "postgres", - ExpectedOut: "postgres://mmuserpassword@localhost:5432/mattermost?sslmode=disable&connect_timeout=10", - }, - "malformed without @": { - DSN: "postgres://mmuser:passwordlocalhost:5432/mattermost?sslmode=disable&connect_timeout=10", - Schema: "postgres", - ExpectedOut: "(omitted due to error parsing the DSN)", - }, - } { - t.Run(name, func(t *testing.T) { - out := stripPassword(test.DSN, test.Schema) - - assert.Equal(t, test.ExpectedOut, out) - }) - } -} - func TestIsJSONMap(t *testing.T) { tests := []struct { name string diff --git a/server/i18n/de.json b/server/i18n/de.json index 7d7cefc91d..3e7aed88f5 100644 --- a/server/i18n/de.json +++ b/server/i18n/de.json @@ -9022,50 +9022,6 @@ "id": "api.custom_groups.count_err", "translation": "Fehler beim Zählen der Gruppen" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Erhöhe die Produktivität mit diesen tollen Funktionen" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Hi {{.Name}}, wir haben bemerkt, dass Dein Mattermost Server etwas Staub ansetzt, Schau mal auf die neuen Funktionen, die Dir helfen die Belastung Deines Team zu senken." - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Öffne Mattermost um Dein Team produktiver zu machen!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Verwalte Aufgaben mit " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Arbeitsablauf-Verwaltung mit " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Gastzugriff auf angegebene " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Komm und teste es!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Öffne Mattermost" - }, - { - "id": "Playbooks", - "translation": "Playbooks" - }, - { - "id": "Channels", - "translation": "Kanäle" - }, - { - "id": "Boards", - "translation": "Boards" - }, { "id": "app.job.get_all_jobs_by_type_and_status.app_error", "translation": "Kann nicht alle Jobs nach Typ und Status holen." @@ -9170,10 +9126,6 @@ "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Die Elasticsearch-Einstellungen haben nicht definierte Werte." }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Du hast diese einmalige E-Mail erhalten, weil dein Mattermost-Server für mehr als {{.Hours}} Stunden inaktiv war. Diese E-Mail wurde automatisch von deinem Mattermost-Server generiert." - }, { "id": "api.file.cloud_upload.app_error", "translation": "Hochladen über mmctl zu einer Cloud Instanz wird nicht unterstützt. Bitte prüfe die Dokumentation: https://docs.mattermost.com/manage/cloud-data-export.html." @@ -9510,10 +9462,6 @@ "id": "model.group.name.reserved_name.app_error", "translation": "Gruppenname existiert bereits als reservierter Name" }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "Plugin {{.Name}} kann im Produktmodus nicht aktiviert werden." - }, { "id": "api.team.invite_guests_to_channels.license.error", "translation": "Deine Lizenz unterstützt Gastkonten nicht" @@ -9586,10 +9534,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "Die Priorität der Nachricht kann nicht ermittelt werden" }, - { - "id": "app.draft.update.app_error", - "translation": "Die Aktualisierung des Entwurfs ist nicht möglich." - }, { "id": "app.draft.save.app_error", "translation": "Der Entwurf kann nicht gespeichert werden." @@ -9662,17 +9606,9 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "Du kannst eine Bestätigung in einem archivierten Kanal nicht entfernen." }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Chatten mit deinem Team in einem Feature-Release-Kanal, der sich problemlos mit deinen Boards, Playbooks und App-Bots verbinden lässt." - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Verwende unsere Vorlage für die Besprechungsagenda für wiederkehrende Besprechungen wie z. B. Standup-Meetings und unsere Projektaufgabentafel, um den Fortschritt der Aufgaben zu verwalten." - }, { "id": "worktemplate.category.product_teams", - "translation": "Produkt-Teams" + "translation": "Produkt" }, { "id": "model.draft.is_valid.priority.app_error", @@ -9686,13 +9622,9 @@ "id": "app.worktemplates.get_categories.app_error", "translation": "Arbeitsvorlagenkategorien können nicht abgerufen werden" }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "Erstelle transparente Arbeitsabläufe zwischen den Entwicklungsteams, um einen nahtlosen Entwicklungsprozess zu gewährleisten." - }, { "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Steigere die Produktivität in deinem Kanal durch die Integration eines Jira-Bots und eines Github-Bots. Diese werden für dich heruntergeladen." + "translation": "Steigere die Produktivität in deinem Kanal durch die Integration deiner am meistern verwendeten Tools, wie GitHub oder Jira. Diese werden für dich heruntergeladen." }, { "id": "api.templates.cloud_welcome_email.yearly_plan_button", @@ -9810,121 +9742,13 @@ "id": "app.worktemplates.execute_work_template.playbooks.find_channel_error", "translation": "Kanal, der mit einem Playbook verbunden ist, kann nicht gefunden werden." }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Verwende ein Projekt, um die Sprint-Planung zum Kinderspiel zu machen. Der Kanal sorgt dafür, dass die Unterhaltungen und Fragen fokussiert bleiben. Der Sprintplan hält alle Beteiligten auf dem Laufenden, und das Retrospektive Board bringt das Team zusammen, um sich kontinuierlich zu verbessern." - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Verwende ein Projekt, um die Sprint-Planung zum Kinderspiel zu machen. Der Kanal sorgt dafür, dass die Unterhaltungen und Fragen fokussiert bleiben. Der Sprintplan hält alle Beteiligten auf dem Laufenden, und das Retrospektive Board bringt das Team zusammen, um sich kontinuierlich zu verbessern." - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Verwende ein Projekt, um die Sprint-Planung zum Kinderspiel zu machen. Der Kanal sorgt dafür, dass die Unterhaltungen und Fragen fokussiert bleiben. Der Sprintplan hält alle Beteiligten auf dem Laufenden, und das Retrospektive Board bringt das Team zusammen, um sich kontinuierlich zu verbessern." - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Beschreibung, warum der/die Kanal/Kanäle benötigt werden" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Beschreibung, warum das/die Board(s) benötigt werden" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Organisiere dich und beseitige alle Bugs mit diesem Projekt! Bauen eine Dynamik auf und messe den Fortschritt mit dem zugeordneten Playbook, Board und Kanal." - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Organisiere dich und beseitige alle Bugs mit diesem Projekt! Bauen eine Dynamik auf und messe den Fortschritt mit dem zugeordneten Playbook, Board und Kanal." - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Organisiere dich und beseitige alle Bugs mit diesem Projekt! Bauen eine Dynamik auf und messe den Fortschritt mit dem zugeordneten Playbook, Board und Kanal." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Organisiere dich und beseitige alle Bugs mit diesem Projekt! Bauen eine Dynamik auf und messe den Fortschritt mit dem zugeordneten Playbook, Board und Kanal." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "Verpasse mit diesem Projekt keinen Schritt während einer Produktfreigabe. Weise Aufgaben aus der Playbook-Checkliste zu und setze Meilensteine mit dem Board. Verwende Kanäle, um alle auf dem gleichen Stand zu halten." - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "Verpasse mit diesem Projekt keinen Schritt während einer Produktfreigabe. Weise Aufgaben aus der Playbook-Checkliste zu und setze Meilensteine mit dem Board. Verwende Kanäle, um alle auf dem gleichen Stand zu halten." - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "Verpasse mit diesem Projekt keinen Schritt während einer Produktfreigabe. Weise Aufgaben aus der Playbook-Checkliste zu und setze Meilensteine mit dem Board. Verwende Kanäle, um alle auf dem gleichen Stand zu halten." - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "Wenn alles schief läuft, ist ein wiederholbarer Prozess der Schlüssel, um sicherzustellen, dass alles so schnell wie möglich in Ordnung gebracht wird. Dieses Projekt kombiniert alles, was Mattermost bietet, um sicherzustellen, dass die Probleme gelöst und die Beteiligten auf dem Weg dahin informiert werden." - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "Wenn alles schief läuft, ist ein wiederholbarer Prozess der Schlüssel, um sicherzustellen, dass alles so schnell wie möglich in Ordnung gebracht wird. Dieses Projekt kombiniert alles, was Mattermost bietet, um sicherzustellen, dass die Probleme gelöst und die Beteiligten auf dem Weg dahin informiert werden." - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "Wenn alles schief läuft, ist ein wiederholbarer Prozess der Schlüssel, um sicherzustellen, dass alles so schnell wie möglich in Ordnung gebracht wird. Dieses Projekt kombiniert alles, was Mattermost bietet, um sicherzustellen, dass die Probleme gelöst und die Beteiligten auf dem Weg dahin informiert werden." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Ein klarer Fokus ist für den Teamerfolg unerlässlich. Mit diesem Projekt kannst du die Ziele und OKRs des Teams dokumentieren und Aktualisierungen in den entsprechenden Kanal einstellen." - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "Plane eine Roadmap mit diesem Projekt-Board und arbeite gemeinsam in dem mit dieser Vorlage erstellten Kanal an einem Thema." - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "Plane eine Roadmap mit diesem Projekt-Board und arbeite im Team in dem mit dieser Vorlage erstellten Kanal an einem Thema." - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "Plane mit diesem Projektboard eine Roadmap und arbeite in dem mit dieser Vorlage erstellten Kanal an einem Thema." - }, { "id": "worktemplate.category.leadership", "translation": "Führung" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.companywide", @@ -10221,5 +10045,125 @@ { "id": "app.command.execute.error", "translation": "Kann Befehl nicht ausführen." + }, + { + "id": "api.license.request-trial.bad-request.business-email", + "translation": "Ungültige geschäftliche E-Mail für den Test" + }, + { + "id": "worktemplate.product_teams.sprint_planning.integration", + "translation": "Steigere die Produktivität deines Kanals, indem du die am häufigsten verwendeten Tools wie z. B. Zoom integrierst. Diese werden für dich heruntergeladen." + }, + { + "id": "worktemplate.product_teams.sprint_planning.channel", + "translation": "Chatte mit deinem Team in einem Kanal, der sich leicht mit deinen Boards und Integrationen verbinden lässt." + }, + { + "id": "worktemplate.product_teams.sprint_planning.board", + "translation": "Verfolge den Fortschritt deines Teams bei der Erreichung der wöchentlichen Ziele mit Sprintaufteilung, Priorisierung, Zuweisung von Verantwortlichen und Kommentaren." + }, + { + "id": "worktemplate.product_teams.product_roadmap.channel", + "translation": "Chatte mit deinem Team über das Feedback deiner Kunden, setze Prioritäten und stimmt euch gemeinsam über den Fortschritt ab." + }, + { + "id": "worktemplate.product_teams.product_roadmap.board", + "translation": "Verwende das Produkt-Roadmap-Board, um Benutzer-Feedback zu verwalten, Ressourcen zuzuweisen, Ergebnisse in einer Kalenderansicht anzuzeigen und Probleme nach Priorität zu ordnen." + }, + { + "id": "worktemplate.product_teams.goals_and_okrs.integration", + "translation": "Steigere die Produktivität in deinem Kanal, indem du die am häufigsten verwendeten Tools wie Zoom integrierst, um die Zusammenarbeit zu erleichtern. Diese werden für dich heruntergeladen." + }, + { + "id": "worktemplate.product_teams.goals_and_okrs.channel", + "translation": "Chatte mit deinem Team über Ziele und Fortschritte, asynchron oder in Echtzeit, und bleibe über Änderungen in einem einzigen Kanal auf dem Laufenden." + }, + { + "id": "worktemplate.product_teams.goals_and_okrs.board", + "translation": "Verfolge den Fortschritt deines Teams auf dem Weg zu den Unternehmenszielen mit dem Ziele und OKR Board. Halten Besprechungen mit der Besprechungsagenda auf Kurs." + }, + { + "id": "worktemplate.product_teams.feature_release.description.playbook", + "translation": "Fördere die funktionsübergreifende Zusammenarbeit im Team mit Aufgaben-Checklisten und Automatisierungen, die deinen Entwicklungsprozess unterstützen. Führe anschließend eine Retrospektive durch und nimm Verbesserungen für deine nächste Version vor." + }, + { + "id": "worktemplate.product_teams.feature_release.description.channel", + "translation": "Chatte mit deinem Team über alle Release-Blocker und Änderungen in einem Kanal, der sich leicht mit deinen Boards, Playbooks und anderen Integrationen verbinden lässt." + }, + { + "id": "worktemplate.product_teams.feature_release.description.board", + "translation": "Halte Besprechungen mit der Besprechungsagenda auf dem Laufenden. Verwalte dein Arbeitspensum mit dem Projektaufgabenboard." + }, + { + "id": "worktemplate.product_teams.bug_bash.playbook", + "translation": "Verwende Checklisten, um Testbereiche zuzuweisen, und automatisierte Aufgaben, um einen umfassenden Fehlerbehebungsprozess durchzuführen. Nutze eine Retrospektive, um deinen Prozess zu überprüfen und ihn für das nächste Mal zu verbessern." + }, + { + "id": "worktemplate.product_teams.bug_bash.integration", + "translation": "Steigere die Produktivität in deinem Kanal, indem du die am häufigsten verwendeten Tools, wie z. B. Jira, integrierst, um den Fortschritt bei der Fehlerbehebung zu verfolgen. Diese werden für dich heruntergeladen." + }, + { + "id": "worktemplate.product_teams.bug_bash.channel", + "translation": "Plane und verwalte Fehlerberichte und -behebungen in einem einzigen Kanal, der für dein Team und deine Organisation leicht zugänglich ist." + }, + { + "id": "worktemplate.leadership.goals_and_okrs.integration", + "translation": "Steigere die Produktivität in deinem Kanal, indem du die am häufigsten verwendeten Tools wie Zoom integrierst, um die Zusammenarbeit zu erleichtern. Diese werden für dich heruntergeladen." + }, + { + "id": "worktemplate.leadership.goals_and_okrs.channel", + "translation": "Chatte mit deinem Team über Ziele und Fortschritte, asynchron oder in Echtzeit, und bleibe über Änderungen in einem einzigen Kanal auf dem Laufenden." + }, + { + "id": "worktemplate.leadership.goals_and_okrs.board", + "translation": "Verfolge den Fortschritt deines Teams auf dem Weg zu den Unternehmenszielen mit dem Ziele und OKR Board. Halten Besprechungen mit der Besprechungsagenda auf Kurs." + }, + { + "id": "worktemplate.devops.product_release.playbook", + "translation": "Erstelle wiederholbare Arbeitsabläufe, die einfach zu befolgen und zu implementieren sind, damit die Produktveröffentlichungen zuverlässig und pünktlich erfolgen." + }, + { + "id": "worktemplate.devops.product_release.channel", + "translation": "Chatte einfach und schnell mit deinem Team über tägliche Meilensteine, eventuelle Hindernisse und Änderungen an den zu erbringenden Leistungen." + }, + { + "id": "worktemplate.devops.product_release.board", + "translation": "Verwende das Product Release Board, um den Zeitrahmen und den Prozess für die Freigabe zu unterstützen und sicherzustellen, dass jeder weiß, welche Aufgaben fällig sind." + }, + { + "id": "worktemplate.devops.incident_resolution.description.playbook", + "translation": "Nutze Checklisten und Automatisierungen, um wichtige Teammitglieder einzubeziehen, und teile mit, wie der Vorfall gelöst wird." + }, + { + "id": "worktemplate.devops.incident_resolution.description.channel", + "translation": "Chatte mit deinem Team über Prioritäten, füge Beteiligte hinzu, liefere Updates und arbeite an einer Lösung in einem einzigen Kanal." + }, + { + "id": "worktemplate.devops.incident_resolution.description.board", + "translation": "Verwende das Incident Resolution Board, um wiederholbare Prozesse zu unterstützen und definierte Aufgaben im Team zuzuweisen." + }, + { + "id": "worktemplate.companywide.goals_and_okrs.integration", + "translation": "Steigere die Produktivität in deinem Kanal, indem du die am häufigsten verwendeten Tools wie Zoom integrierst, um die Zusammenarbeit zu erleichtern. Diese werden für dich heruntergeladen." + }, + { + "id": "worktemplate.companywide.goals_and_okrs.channel", + "translation": "Chatte mit deinem Team über Ziele und Fortschritte, asynchron oder in Echtzeit, und bleibe über Änderungen in einem einzigen Kanal auf dem Laufenden." + }, + { + "id": "worktemplate.companywide.goals_and_okrs.board", + "translation": "Verfolge den Fortschritt deines Teams auf dem Weg zu den Unternehmenszielen mit dem Ziele und OKR Board. Halten Besprechungen mit der Besprechungsagenda auf Kurs." + }, + { + "id": "worktemplate.companywide.create_project.integration", + "translation": "Steigere die Produktivität deines Kanals durch die Integration am deiner am häufigsten verwendeten Tools. Diese werden für dich heruntergeladen." + }, + { + "id": "worktemplate.companywide.create_project.channel", + "translation": "Chatte mit deinem Team über ein neues Projekt und entscheide, wie es strukturiert werden soll, und zwar in einem Kanal zur Zusammenarbeit." + }, + { + "id": "worktemplate.companywide.create_project.board", + "translation": "Verwend eine Kanban-Board, um deine Projektaufgaben und -fortschritte zu definieren und zu verfolgen." } ] diff --git a/server/i18n/en.json b/server/i18n/en.json index 0994b13552..e73fa524c8 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -2591,6 +2591,10 @@ "id": "api.server.cws.needs_enterprise_edition", "translation": "Service only available in Mattermost Enterprise edition" }, + { + "id": "api.server.cws.subscribe_to_newsletter.app_error", + "translation": "CWS Server failed to subscribe to newsletter." + }, { "id": "api.server.hosted_signup_unavailable.error", "translation": "Portal unavailable for self-hosted signup." @@ -5027,10 +5031,6 @@ "id": "app.draft.save.app_error", "translation": "Unable to save the Draft." }, - { - "id": "app.draft.update.app_error", - "translation": "Unable to update the Draft." - }, { "id": "app.email.no_rate_limiter.app_error", "translation": "Rate limiter is not set up." @@ -10033,11 +10033,11 @@ }, { "id": "worktemplate.category.companywide", - "translation": "Company - Wide" + "translation": "Company-wide" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.leadership", @@ -10045,130 +10045,126 @@ }, { "id": "worktemplate.category.product_teams", - "translation": "Product Teams" + "translation": "Product" }, { "id": "worktemplate.companywide.create_project.board", - "translation": "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template." + "translation": "Use a Kanban board to define and track your project tasks and progress." }, { "id": "worktemplate.companywide.create_project.channel", - "translation": "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template." + "translation": "Chat with your team about your new project and decide how you’re going to structure it, in a collaborative channel." }, { "id": "worktemplate.companywide.create_project.integration", - "translation": "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template." + "translation": "Increase productivity in your channel by integrating your most commonly used tools. These will be downloaded for you." }, { "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board." }, { "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel." }, { "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you." }, { "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way." + "translation": "Use the Incident Resolution board to support repeatable processes and assign defined tasks across the team." }, { "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way." + "translation": "Chat with your team about priorities, add stakeholders, provide updates, and work toward a resolution in a single channel." }, { "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure the fires are put out and stakeholders informed along the way." + "translation": "Use checklists and automation to bring in key team members, and share how your incident is tracking toward resolution." }, { "id": "worktemplate.devops.product_release.board", - "translation": "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page." + "translation": "Use the Product Release board to support your release timeframe and process, ensuring everyone knows which tasks are due." }, { "id": "worktemplate.devops.product_release.channel", - "translation": "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page." + "translation": "Chat with your team about daily milestones, any blockers, and changes to deliverables, easily and quickly." }, { "id": "worktemplate.devops.product_release.playbook", - "translation": "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page." + "translation": "Create repeatable workflows that are easy to follow and implement so product releases are reliable and on time." }, { "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board." }, { "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel." }, { "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel." + "translation": "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you." }, { "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel." + "translation": "Plan and manage bug reports and resolutions in a single channel, that’s easily accessible to your team and organization." }, { "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel." + "translation": "Increase productivity in your channel by integrating your most commonly used tools, such as Jira, to track your bug bash progress. These will be downloaded for you." }, { "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Get organized and bash all the bugs with this project! Build momentum and measure progress using included Playbook, Board, and Channel." + "translation": "Use checklists to assign testing areas and automated tasks to run a comprehensive bug bash process. Use a retrospective to review your process and improve it for next time." }, { "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Use our Meeting Agenda board template for recurring meetings like standup and our Project Tasks board to manage the progress of tasks along the way." + "translation": "Keep meetings on track with the Meeting Agenda board. Manage your workload with the Project Tasks board." }, { "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Chat with your team in a Feature Release channel that connects easily with your boards, playbooks and app bots." + "translation": "Chat with your team about any release blockers and changes in a channel that connects easily with your boards, playbooks and other integrations." }, { "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Increase productivity in your channel by integrating a Jira bot and Github bot. These will be downloaded for you." + "translation": "Increase productivity in your channel by integrating your most commonly used tools to support your feature release, such as GitHub. These will be downloaded for you." }, { "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "Create transparent workflows across development teams to ensure your feature development process is seamless." + "translation": "Boost cross-functional team collaboration with task checklists and automation that support your feature development process. When you're done, run a retrospective and make improvements for your next release." }, { "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board." }, { "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Chat about your goals and progress with your team, async or real-time, and stay up to date with changes in a single channel." }, { "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKR’s as well as post updates in the dedicated channel." + "translation": "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom, to facilitate easy collaboration. These will be downloaded for you." }, { "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Description of why the board(s) are needed" + "translation": "Use the Product Roadmap board to manage user feedback, assign resources, view deliverables in a calendar view, and prioritize issues." }, { "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Description of why the channel(s) are needed" + "translation": "Chat with your team about your customers' feedback, prioritization, and get aligned on progress together." }, { "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve." + "translation": "Track your team's progress toward weekly goals with sprint breakdowns, prioritization, owner assignment, and comments." }, { "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve." + "translation": "Chat with your team in a channel that connects easily with your boards and integrations." }, { "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve." + "translation": "Increase productivity in your channel by integrating your most commonly used tools, such as Zoom. These will be downloaded for you." } ] diff --git a/server/i18n/en_AU.json b/server/i18n/en_AU.json index d5847594f7..8a71e91c0d 100644 --- a/server/i18n/en_AU.json +++ b/server/i18n/en_AU.json @@ -9026,50 +9026,6 @@ "id": "app.job.get_all_jobs_by_type_and_status.app_error", "translation": "Unable to get the all jobs by type and status." }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Unlock increased productivity with these awesome features" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Hey {{.Name}}, your Mattermost server has been a bit inactive. Would you like to take a look at some features that can help lighten your team's workload?" - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Open Mattermost to increase your team’s productivity!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Manage tasks using " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Workflow management with " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Guest Access to specified " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Come and check it out!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Open Mattermost" - }, - { - "id": "Playbooks", - "translation": "Playbooks" - }, - { - "id": "Channels", - "translation": "Channels" - }, - { - "id": "Boards", - "translation": "Boards" - }, { "id": "app.prepackged-plugin.invalid_version.app_error", "translation": "A prepackged plugin version could not be parsed." @@ -9166,10 +9122,6 @@ "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Elasticsearch settings has unset values." }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "You received this one-time email because your Mattermost server was inactive for more than {{.Hours}} hours. This email was automatically generated by your Mattermost server." - }, { "id": "app.recent_searches.app_error", "translation": "An error occurred while fetching recent searches" @@ -9518,10 +9470,6 @@ "id": "model.insights.get_start_of_day_for_time_range.time_range.app_error", "translation": "Invalid time range." }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "Plugin {{.Name}} cannot be enabled in product mode." - }, { "id": "api.team.invite_guests_to_channels.license.error", "translation": "Your workspace licence does not support guest accounts" @@ -9594,10 +9542,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "Unable to get post priority for post" }, - { - "id": "app.draft.update.app_error", - "translation": "Unable to update the draft." - }, { "id": "app.draft.save.app_error", "translation": "Unable to save the draft." @@ -9650,13 +9594,9 @@ "id": "worktemplate.product_teams.feature_release.description.channel", "translation": "Chat with your team in a Feature Release channel that connects easily with your boards, playbooks and app bots." }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Use the Meeting Agenda board template for recurring meetings like standup and the Project Tasks board to manage the progress of tasks along the way." - }, { "id": "worktemplate.category.product_teams", - "translation": "Product Teams" + "translation": "Product" }, { "id": "model.draft.is_valid.priority.app_error", @@ -9766,125 +9706,17 @@ "id": "api.acknowledgement.delete.deadline.app_error", "translation": "You cannot delete an acknowledgment after 5 minutes has elapsed." }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve." - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve." - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Use a Project to make sprint planning a breeze. The channel keeps the conversation and questions focused. The sprint plan keeps everyone on task for the week and the Retrospective board brings the team together to continuously improve." - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Description of why the channel(s) are necessary" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Description of why the board(s) are necessary" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Get organised and squash all the bugs with this project! Build momentum and measure progress using included Playbook, Board and Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Get organised and squash all the bugs with this project! Build momentum and measure progress using included Playbook, Board and Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Get organised and squash all the bugs with this project! Build momentum and measure progress using included Playbook, Board and Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Get organised and squash all the bugs with this project! Build momentum and measure progress using included Playbook, Board and Channel." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page." - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page." - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "Don’t miss a step during a product release with this Project. Assign tasks from the Playbook checklist and hit milestones with the Board. Use Channels to keep everyone on the same page." - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure issues are resolved and stakeholders informed along the way." - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure issues are resolved and stakeholders informed along the way." - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "When everything is going wrong, having a repeatable process is the key to making sure everything is made right as quickly as possible. This Project combines everything Mattermost offers to ensure issues are resolved and stakeholders informed along the way." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Clear focus is essential to team success and with this Project you can document the team’s goals and OKRs, as well as post updates in the dedicated channel." - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template." - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template." - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "Plan a Roadmap using this Project Board and collaborate on topic in the channel created with this template." - }, { "id": "worktemplate.category.leadership", "translation": "Leadership" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.companywide", - "translation": "Company - Wide" + "translation": "Company-wide" }, { "id": "app.worktemplates.execute_work_template.playbooks.find_channel_error", diff --git a/server/i18n/es.json b/server/i18n/es.json index 0e1791f125..6a8f3c43ce 100644 --- a/server/i18n/es.json +++ b/server/i18n/es.json @@ -8975,10 +8975,6 @@ "id": "model.config.is_valid.elastic_search.bulk_indexing_batch_size.app_error", "translation": "El tamaño del lote de indexación masiva de Elasticsearch debe ser al menos de {{.BatchSize}}." }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Recibiste este correo electrónico único porque tu servidor Mattermost estuvo inactivo durante más de {{.Hours}} horas. Este correo electrónico fue generado automáticamente por tu servidor Mattermost." - }, { "id": "api.custom_groups.no_remote_id", "translation": " " @@ -8987,10 +8983,6 @@ "id": "app.system.get_onboarding_request.app_error", "translation": "No se pudo obtener el estado de finalización de inducción." }, - { - "id": "Boards", - "translation": " " - }, { "id": "app.custom_group.unique_name", "translation": " " @@ -9059,26 +9051,6 @@ "id": "api.getThreadsForUser.bad_only_params", "translation": " " }, - { - "id": "api.templates.server_inactivity_button", - "translation": " " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "¡Ven y dale un vistazo!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": " " - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Hey {{.Name}}, notamos que tu servidor Mattermost está acumulando algo de polvo. Da un vistazo a algunas características que pueden aligerar la carga de trabajo de tu equipo." - }, - { - "id": "api.templates.server_inactivity_title", - "translation": " " - }, { "id": "app.job.get_all_jobs_by_type_and_status.app_error", "translation": "No se pudo obtener todos los jobs por tipo y estado." @@ -9115,14 +9087,6 @@ "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Los ajustes de Búsqueda Elástica tienen valores no establecidos." }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": " " - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "¡Ven y abre Mattermost para aumentar la productividad de tu equipo!" - }, { "id": "api.team.invite_members_to_team_and_channels.invalid_body.app_error", "translation": "Cuerpo de solicitud no válido." @@ -9207,10 +9171,6 @@ "id": "api.templates.invite_team_and_channel_body.title", "translation": "{{ .SenderName }} te invitó a unirte al Canal {{ .ChannelName }} en el Equipo {{ .TeamDisplayName}}" }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": " " - }, { "id": "api.templates.cloud_upgrade_confirmation.title", "translation": "¡Ahora estás actualizado!" @@ -9227,14 +9187,6 @@ "id": "api.team.invite_members.unable_to_send_email_with_defaults.app_error", "translation": " " }, - { - "id": "Playbooks", - "translation": " " - }, - { - "id": "Channels", - "translation": " " - }, { "id": "api.user.authorize_oauth_user.saml_response_too_long.app_error", "translation": " " @@ -9487,10 +9439,6 @@ "id": "app.post.get_top_dms_for_user_since.app_error", "translation": "No es posible obtener los DMs principales para el usuario." }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "El plugin {{.Name}} no se pudo activar en el modo producto." - }, { "id": "app.notify_admin.send_notification_post.app_error", "translation": "No es posible enviar la publicación de notificación." diff --git a/server/i18n/fr.json b/server/i18n/fr.json index 50b5d159c8..77720df20d 100644 --- a/server/i18n/fr.json +++ b/server/i18n/fr.json @@ -8571,34 +8571,6 @@ "id": "api.license.request_renewal_link.cannot_renew_on_cws", "translation": "Le renouvellement de cette licence sur le portail n'est pas possible" }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Bonjour {{.Name}}, nous avons remarqué que votre serveur Mattermost collecte un peu la poussière. Découvrez quelques fonctionnalités qui peuvent vous aider à alléger la charge de travail de votre équipe." - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Gérez des tâches en utilisant " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Venez et regardez ça !" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Ouvrir Mattermost" - }, - { - "id": "Playbooks", - "translation": "Playbooks" - }, - { - "id": "Channels", - "translation": "Channels" - }, - { - "id": "Boards", - "translation": "Boards" - }, { "id": "app.prepackged-plugin.invalid_version.app_error", "translation": "La version du plugin pré-packagé n'a pas pu être traitée." @@ -8755,26 +8727,6 @@ "id": "api.user.view_archived_channels.get_posts_for_channel.app_error", "translation": "Impossible de retrouver les messages pour un canal archivé" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Augmentez votre productivité en déverrouillant ces fonctionnalités géniales" - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Ouvrez Mattermost pour augmenter la productivité de votre équipe !" - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Gestion du déroulement des opérations avec " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Accès des invités à l'espace spécifié " - }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Vous avez reçu ce courriel unique parce que votre serveur Mattermost est inactif depuis plus de {{.Hours}} heures. Ce courriel a été généré automatiquement par votre serveur Mattermost." - }, { "id": "api.templates.invite_team_and_channels_subject", "translation": "[{{ .SiteName}}] {{ .SenderName }} vous a invité à rejoindre {{ .ChannelsLen }} canaux de l'équipe {{ .TeamDisplayName }}" diff --git a/server/i18n/hu.json b/server/i18n/hu.json index 11194850f6..e66cb9ae7f 100644 --- a/server/i18n/hu.json +++ b/server/i18n/hu.json @@ -9019,50 +9019,6 @@ "id": "app.member_count", "translation": "hiba a tagok számának lekérdezésében" }, - { - "id": "Boards", - "translation": "Táblák" - }, - { - "id": "Channels", - "translation": "Csatornák" - }, - { - "id": "Playbooks", - "translation": "Forgatókönyvek" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Mattermost megnyitása" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Helló {{.Name}}, észrevettük, hogy a Mattermost szervered egy kicsit porosodik. Vess egy pillantást néhány funkcióra, amelyek segíthetnek könnyíteni csapatod munkaterhét." - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Nyissa meg a Mattermostot, hogy növelje csapata termelékenységét!" - }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Növelje a termelékenységet ezekkel a fantasztikus funkciókkal" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Feladatok kezelése a " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Munkafolyamatok kezelése a " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Vendég hozzáférés a megadott " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Jöjjön és nézze meg!" - }, { "id": "app.job.get_all_jobs_by_type_and_status.app_error", "translation": "Nem lehet lekérni az összes munkát típus és státusz szerint." @@ -9163,10 +9119,6 @@ "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Az Elasticsearch beállításában nem mentett értékek vannak." }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Ezt az egyszeri e-mailt azért kapta, mert a Mattermost szervere több mint {{.Hours}} órán keresztül inaktív volt. Ezt az e-mailt a Mattermost szervere automatikusan generálta." - }, { "id": "api.file.cloud_upload.app_error", "translation": "Az mmctl segítségével történő feltöltés egy felhő alapú példányra nem támogatott. Kérjük, tekintse meg a dokumentációt itt: https://docs.mattermost.com/manage/cloud-data-export.html." diff --git a/server/i18n/it.json b/server/i18n/it.json index 14f201c891..0129b143eb 100644 --- a/server/i18n/it.json +++ b/server/i18n/it.json @@ -7359,14 +7359,6 @@ "id": "api.upgrade_to_enterprise.invalid-user.app_error", "translation": " " }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": " " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": " " - }, { "id": "api.templates.email_footer_v2", "translation": " " @@ -7479,10 +7471,6 @@ "id": "api.command_share.fetch_remote_status.error", "translation": " " }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": " " - }, { "id": "api.templates.invite_team_and_channels_body.title", "translation": " " @@ -7527,10 +7515,6 @@ "id": "api.command_remote.remote_add_remove.help", "translation": " " }, - { - "id": "api.templates.server_inactivity_title", - "translation": " " - }, { "id": "api.templates.invite_team_and_channels_subject", "translation": " " @@ -7863,10 +7847,6 @@ "id": "api.templates.invite_body_guest.subTitle", "translation": " " }, - { - "id": "Playbooks", - "translation": " " - }, { "id": "api.command_custom_status.hint", "translation": " " @@ -8199,10 +8179,6 @@ "id": "api.system.update_notices.clear_failed", "translation": " " }, - { - "id": "api.templates.server_inactivity_info", - "translation": " " - }, { "id": "app.system.complete_onboarding_request.no_first_user", "translation": " " @@ -8211,14 +8187,6 @@ "id": "app.user.get_unread_count.app_error", "translation": " " }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": " " - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": " " - }, { "id": "app.team.join_user_to_team.save_member.conflict.app_error", "translation": " " @@ -8419,18 +8387,10 @@ "id": "model.reaction.is_valid.update_at.app_error", "translation": " " }, - { - "id": "Boards", - "translation": " " - }, { "id": "app.notification.footer.info", "translation": " " }, - { - "id": "api.templates.server_inactivity_button", - "translation": " " - }, { "id": "api.command_share.remote_id_invalid.error", "translation": " " @@ -8499,10 +8459,6 @@ "id": "api.post.send_notification_and_forget.push_comment_on_crt_thread_dm", "translation": " " }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": " " - }, { "id": "import_process.worker.do_job.missing_jsonl", "translation": " " @@ -9231,10 +9187,6 @@ "id": "api.command_custom_status.clear.app_error", "translation": " " }, - { - "id": "Channels", - "translation": " " - }, { "id": "api.admin.add_certificate.parseform.app_error", "translation": " " diff --git a/server/i18n/ja.json b/server/i18n/ja.json index f42157caa1..58aa66d022 100644 --- a/server/i18n/ja.json +++ b/server/i18n/ja.json @@ -8983,18 +8983,6 @@ "id": "api.custom_groups.count_err", "translation": "グループのカウント中にエラーが発生しました" }, - { - "id": "Playbooks", - "translation": "Playbooks" - }, - { - "id": "Channels", - "translation": "チャンネル" - }, - { - "id": "Boards", - "translation": "Boards" - }, { "id": "model.emoji.system_emoji_name.app_error", "translation": "既存のシステム絵文字名と名前が競合しています。" @@ -9019,38 +9007,6 @@ "id": "app.custom_group.unique_name", "translation": "グループ名が重複しています" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "これらの機能による生産性の向上をアンロックする" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "{{.Name}} さん、あなたのMattermostサーバーが少し埃をかぶっていることに気づきました。チームの作業負荷を軽減するのに役立ついくつかの機能を確認してみてください。" - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Mattermostを開いてチームの生産性を向上しましょう!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "タスク管理のための " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "ワークフロー管理のための " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "ゲストアクセス可能な " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "ぜひご覧ください!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Mattermostを開く" - }, { "id": "api.license_error", "translation": "APIエンドポイントにはライセンスが必要です" @@ -9163,10 +9119,6 @@ "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Elasticsearchの設定に未設定の値があります。" }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "あなたの Mattermost サーバが {{.Hours}} 時間以上アクティブでなかったため、このワンタイム電子メールが送信されました。この電子メールは、Mattermost サーバによって自動的に生成されたものです。" - }, { "id": "app.recent_searches.app_error", "translation": "最近の検索履歴を取得する際にエラーが発生しました" @@ -9499,10 +9451,6 @@ "id": "model.group.name.reserved_name.app_error", "translation": "グループ名は予約語として既に登録されています" }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "プラグイン {{.Name}} はプロダクトモードでは有効化できません。" - }, { "id": "app.last_accessible_file.app_error", "translation": "最後にアクセスしたファイルの取得エラー" @@ -9571,10 +9519,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "投稿に対する優先度を取得できませんでした" }, - { - "id": "app.draft.update.app_error", - "translation": "下書きを更新できませんでした。" - }, { "id": "app.draft.save.app_error", "translation": "下書きを保存できませんでした。" @@ -9655,22 +9599,6 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "アーカイブされたチャンネルでは、確認応答を削除することはできません。" }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "開発チーム間で透明性の高いワークフローを作成し、機能開発プロセスをシームレスにすることができます。" - }, - { - "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Jira BotやGitHub Botと統合し、生産性を高めましょう。これらはあなたのためにダウンロードされます。" - }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Boards、Playbooks、Botと簡単に接続できる Feature Release チャンネルでチームとチャットできます。" - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "スタンドアップなどの定期的なミーティングには Meeting Agenda Boardテンプレート、タスクの進捗管理には Project Task Boardをご利用ください。" - }, { "id": "worktemplate.category.product_teams", "translation": "製品チーム" @@ -9747,121 +9675,13 @@ "id": "api.license.true_up_review.user_count_fail", "translation": "総アクティブユーザー数を取得できませんでした" }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "プロジェクトを使うことでスプリント計画が楽になります。チャンネルは会話と質問に集中することができます。スプリント計画では、全員がその週のタスクに集中し、レトロスペクティブBoardでチームが一丸となって継続的に改善することができます。" - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "プロジェクトを使うことでスプリント計画が楽になります。チャンネルは会話と質問に集中することができます。スプリント計画では、全員がその週のタスクに集中し、レトロスペクティブBoardでチームが一丸となって継続的に改善することができます。" - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "プロジェクトを使うことでスプリント計画が楽になります。チャンネルは会話と質問に集中することができます。スプリント計画では、全員がその週のタスクに集中し、レトロスペクティブBoardでチームが一丸となって継続的に改善することができます。" - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "チャンネルが必要な理由について" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Boardが必要な理由について" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "このプロジェクトで、すべてのバグに対応しましょう! Playbook、Board、Channelを使ってテンポ良く仕事を進めましょう。" - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "このプロジェクトで、すべてのバグに対応しましょう! Playbook、Board、Channelを使ってテンポ良く仕事を進めましょう。" - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "このプロジェクトで、すべてのバグに対応しましょう! Playbook、Board、Channelを使ってテンポ良く仕事を進めましょう。" - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "このプロジェクトで、すべてのバグに対応しましょう! Playbook、Board、Channelを使ってテンポ良く仕事を進めましょう。" - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "このプロジェクトで、製品リリース時の手順を見落とさないようにしましょう。Playbookのチェックリストからタスクを割り当て、Boardと共にマイルストーンを達成し、チャンネルを使用して全員が同じ情報を目にできるようにしましょう。" - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "このプロジェクトで、製品リリース時の手順を見落とさないようにしましょう。Playbookのチェックリストからタスクを割り当て、Boardと共にマイルストーンを達成し、チャンネルを使用して全員が同じ情報を目にできるようにしましょう。" - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "このプロジェクトで、製品リリース時の手順を見落とさないようにしましょう。Playbookのチェックリストからタスクを割り当て、Boardと共にマイルストーンを達成し、チャンネルを使用して全員が同じ情報を目にできるようにしましょう。" - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "すべてがうまくいかないとき、再現可能なプロセスを用意することは、正しい方向へ導くためのキーとなります。このプロジェクトは、Mattermostが提供するあらゆるものを組み合わせて、炎上を抑え、利害関係者に正しく情報を提供するものです。" - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "すべてがうまくいかないとき、再現可能なプロセスを用意することは、正しい方向へ導くためのキーとなります。このプロジェクトは、Mattermostが提供するあらゆるものを組み合わせて、炎上を抑え、利害関係者に正しく情報を提供するものです。" - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "すべてがうまくいかないとき、再現可能なプロセスを用意することは、正しい方向へ導くためのキーとなります。このプロジェクトは、Mattermostが提供するあらゆるものを組み合わせて、炎上を抑え、利害関係者に正しく情報を提供するものです。" - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "チームの成功には明確な焦点が必要です。このプロジェクトでは、専用のチャンネルに更新状況を投稿することで、チームの目標やOKRを文書化することができます。" - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "このProject Boardを使用してロードマップを計画し、このテンプレートで作成されたチャンネルでトピックについてコラボレーションを行うことができます。" - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "このProject Boardを使用してロードマップを計画し、このテンプレートで作成されたチャンネルでトピックについてコラボレーションを行うことができます。" - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "このProject Boardを使用してロードマップを計画し、このテンプレートで作成されたチャンネルでトピックについてコラボレーションを行うことができます。" - }, { "id": "worktemplate.category.leadership", "translation": "リーダーシップ" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.companywide", diff --git a/server/i18n/ko.json b/server/i18n/ko.json index 6e71b18d58..ddd30b9753 100644 --- a/server/i18n/ko.json +++ b/server/i18n/ko.json @@ -7859,10 +7859,6 @@ "id": "bleveengine.delete_post_files.error", "translation": "게시된 파일을 삭제하지 못했습니다." }, - { - "id": "Boards", - "translation": "보드" - }, { "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Elasticsearch 설정에 설정되지 않은 값이 있습니다." @@ -7887,14 +7883,6 @@ "id": "api.cloud.notify_admin_to_upgrade_error.already_notified", "translation": "이미 관리자에게 통지됨" }, - { - "id": "Playbooks", - "translation": "플레이북" - }, - { - "id": "Channels", - "translation": "채널" - }, { "id": "api.custom_groups.feature_disabled", "translation": "사용자 정의 그룹 기능은 비활성화되어 있습니다" @@ -8106,5 +8094,337 @@ { "id": "api.config.update_config.translations.app_error", "translation": "서버 번역 업데이트가 실패하였습니다." + }, + { + "id": "api.user.create_user.bad_token_email_data.app_error", + "translation": "토큰의 전자우편 주소가 사용자 데이터의 전자우편 주소와 일치하지 않습니다." + }, + { + "id": "api.user.authorize_oauth_user.saml_response_too_long.app_error", + "translation": "SAML 응답이 너무 깁니다" + }, + { + "id": "api.user.add_user_to_group_syncables.not_ldap_user.app_error", + "translation": "LDAP 사용자가 아님" + }, + { + "id": "api.upload.upload_data.multipart_error", + "translation": "멀티파트 데이터를 처리하지 못했습니다." + }, + { + "id": "api.upload.upload_data.invalid_content_type", + "translation": "멀티파트 업로드에 대한 Content-Type이 잘못되었습니다." + }, + { + "id": "api.upload.upload_data.invalid_content_length", + "translation": "유효하지 않은 Content-Length입니다." + }, + { + "id": "api.upload.get_upload.forbidden.app_error", + "translation": "업로드에 실패했습니다." + }, + { + "id": "api.upload.create.upload_too_large.app_error", + "translation": "파일을 업로드할 수 없습니다. 파일이 너무 큽니다." + }, + { + "id": "api.unable_to_read_file_from_backend", + "translation": "백엔드에서 파일 읽기 오류" + }, + { + "id": "api.templates.welcome_body.subTitle2", + "translation": "아래를 클릭하여 전자우편 주소를 인증하세요." + }, + { + "id": "api.templates.welcome_body.info1", + "translation": "수신자가 당신이 아닌 경우, 이 전자우편은 무시해도 됩니다." + }, + { + "id": "api.templates.verify_body.subTitle2", + "translation": "아래를 클릭하여 전자우편 주소를 인증하세요." + }, + { + "id": "api.templates.verify_body.subTitle1", + "translation": "참여해 주셔서 감사합니다 " + }, + { + "id": "api.templates.verify_body.serverURL", + "translation": "{{ .ServerURL }}." + }, + { + "id": "api.templates.verify_body.info1", + "translation": "수신자가 당신이 아닌 경우, 이 전자우편은 무시해도 됩니다." + }, + { + "id": "api.templates.reset_body.subTitle", + "translation": "비밀번호를 재설정하려면 아래 버튼을 클릭하세요. 요청하지 않은 경우 이 전자우편은 무시해도 됩니다." + }, + { + "id": "api.templates.reset_body.info", + "translation": "비밀번호 재설정 링크는 24시간 후에 만료됩니다." + }, + { + "id": "api.templates.questions_footer.title", + "translation": "질문이 있으신가요?" + }, + { + "id": "api.templates.questions_footer.info", + "translation": "도움이 필요하거나 질문이 있으신가요? 다음 주소로 전자우편을 보내주세요 " + }, + { + "id": "api.templates.payment_failed_no_card.title", + "translation": "Mattermost Cloud 청구서 마감일" + }, + { + "id": "api.templates.payment_failed_no_card.subject", + "translation": "Mattermost Cloud 구독에 대한 결제가 완료되었습니다" + }, + { + "id": "api.templates.payment_failed_no_card.info3", + "translation": "청구서를 검토하고 결제 방법을 추가하려면 지금 결제를 선택합니다." + }, + { + "id": "api.templates.payment_failed_no_card.info1", + "translation": "가장 최근 청구 기간에 대한 Mattermost Cloud 청구서가 처리되었습니다. 하지만 결제 세부 정보가 등록되어 있지 않습니다." + }, + { + "id": "api.templates.payment_failed_no_card.button", + "translation": "지금 결제하기" + }, + { + "id": "api.templates.payment_failed.title", + "translation": "결제가 성공하지 못했습니다" + }, + { + "id": "api.templates.payment_failed.info2", + "translation": "그들은 다음과 같은 이유를 제시했습니다:" + }, + { + "id": "api.templates.license_up_for_renewal_title", + "translation": "Mattermost 구독이 갱신될 예정입니다" + }, + { + "id": "api.templates.license_up_for_renewal_subtitle_two", + "translation": "갱신하려면 고객 계정으로 로그인하세요" + }, + { + "id": "api.templates.license_up_for_renewal_subject", + "translation": "라이선스 갱신 기간 만료" + }, + { + "id": "api.templates.license_up_for_renewal_contact_sales", + "translation": "영업팀에 문의" + }, + { + "id": "api.templates.invite_body_guest.subTitle", + "translation": "팀과의 공동 작업을 위해 게스트로 초대되었습니다" + }, + { + "id": "api.templates.invite_body_footer.info", + "translation": "Mattermost는 안전한 팀 협업을 지원하는 유연한 오픈소스 메시징 플랫폼입니다." + }, + { + "id": "api.templates.email_us_anytime_at", + "translation": "언제든지 다음 주소로 전자우편을 보내주세요 " + }, + { + "id": "api.templates.delinquency_90.title", + "translation": "Mattermost 워크스페이스가 다운그레이드되었습니다" + }, + { + "id": "api.templates.delinquency_90.subtitle2", + "translation": "또한 Cloud Free 제한으로 인해 데이터가 보관 처리되었을 수도 있습니다." + }, + { + "id": "api.templates.delinquency_90.subtitle3", + "translation": "데이터 보관을 해제하고 유료 기능을 계속 사용하려면 결제 정보를 업데이트하세요." + }, + { + "id": "api.templates.delinquency_90.subtitle1", + "translation": "중요한 비즈니스 운영에 Cloud Professional 또는 Enterprise 기능을 사용하는 경우 이러한 기능을 더 이상 사용할 수 없으며 성능이 저하됩니다." + }, + { + "id": "api.templates.delinquency_90.subject", + "translation": "Mattermost Cloud 워크스페이스가 다운그레이드되었습니다" + }, + { + "id": "api.templates.delinquency_90.secondary_action_button", + "translation": "플랜과 가격 보기" + }, + { + "id": "api.templates.delinquency_75.subtitle3", + "translation": "지금 결제 정보를 업데이트하거나 Cloud Free로 다운그레이드하세요." + }, + { + "id": "api.templates.delinquency_75.title", + "translation": "워크스페이스가 15일 후에 다운그레이드됩니다" + }, + { + "id": "api.templates.delinquency_90.button", + "translation": "결제 갱신" + }, + { + "id": "api.templates.delinquency_75.subject", + "translation": "Mattermost {{.Plan}} 플랜이 15일 후에 다운그레이드됩니다" + }, + { + "id": "api.templates.delinquency_75.downgrade_to_free", + "translation": "Cloud Free로 다운그레이드" + }, + { + "id": "api.templates.delinquency_75.button", + "translation": "결제 갱신" + }, + { + "id": "api.templates.delinquency_7.title", + "translation": "결제가 완료되지 않았습니다" + }, + { + "id": "api.templates.delinquency_7.subtitle1", + "translation": "가장 최근 결제를 처리하지 못했습니다." + }, + { + "id": "api.templates.delinquency_7.button", + "translation": "결제 갱신" + }, + { + "id": "api.templates.delinquency_60.title", + "translation": "Mattermost 워크스페이스가 30일 후에 다운그레이드됩니다" + }, + { + "id": "api.templates.delinquency_30.limits_documentation", + "translation": "모든 제한 문서 보기." + }, + { + "id": "api.templates.delinquency_30.button", + "translation": "결제 갱신" + }, + { + "id": "api.templates.delinquency_30.bullet.message_history", + "translation": "메시지 역사" + }, + { + "id": "api.templates.delinquency_30.bullet.files", + "translation": "파일" + }, + { + "id": "api.server.warn_metric.number_of_posts_2M.notification_title", + "translation": "성능 향상" + }, + { + "id": "api.server.warn_metric.number_of_teams_5.notification_title", + "translation": "고급 권한 사용 중" + }, + { + "id": "api.server.warn_metric.number_of_teams_5.start_trial_notification_success.message", + "translation": "엔터프라이즈 체험판이 활성화되었습니다. **시스템 콘솔 > 사용자 관리 > 권한** 에서 고급 권한 설정을 활성화하세요." + }, + { + "id": "api.system.logs.invalidFilter", + "translation": "유효하지 않은 로그 필터" + }, + { + "id": "api.team.add_team_member.invalid_body.app_error", + "translation": "요청 본문을 구문 분석할 수 없습니다." + }, + { + "id": "api.team.import_team.unknown_import_from.app_error", + "translation": "알 수 없는 들여오기 원본입니다." + }, + { + "id": "api.team.invite_guests_to_channels.disabled.error", + "translation": "게스트 계정이 비활성화되었습니다" + }, + { + "id": "api.team.invite_guests_to_channels.invalid_body.app_error", + "translation": "요청 본문이 유효하지 않거나 누락되었습니다." + }, + { + "id": "api.team.invite_guests_to_channels.license.error", + "translation": "게스트 계정을 지원하지 않는 라이선스입니다" + }, + { + "id": "api.team.invite_members.unable_to_send_email.app_error", + "translation": "전자우편을 보내는 중에 오류가 발생했습니다" + }, + { + "id": "api.team.invite_members.unable_to_send_email_with_defaults.app_error", + "translation": "시스템 콘솔에서 SMTP가 설정되지 않았습니다" + }, + { + "id": "api.team.invite_members_to_team_and_channels.invalid_body.app_error", + "translation": "유효하지 않은 요청 본문입니다." + }, + { + "id": "api.team.invite_members_to_team_and_channels.invalid_body_parsing.app_error", + "translation": "본문 데이터를 구문 분석하는 동안 오류가 발생했습니다." + }, + { + "id": "api.team.set_team_icon.check_image_limits.app_error", + "translation": "이미지 제한 확인에 실패했습니다. 해상도가 너무 높습니다." + }, + { + "id": "api.templates.cloud_upgrade_confirmation.subject", + "translation": "Mattermots 업그레이드 확인" + }, + { + "id": "api.templates.cloud_upgrade_confirmation.title", + "translation": "업그레이드 되었습니다!" + }, + { + "id": "api.templates.cloud_upgrade_confirmation_monthly.subtitle", + "translation": "{{.WorkspaceName}} 워크스페이스가 업그레이드 되었습니다. {{.Data}}에 결제가 됩니다" + }, + { + "id": "api.templates.cloud_upgrade_confirmation_yearly.subtitle", + "translation": "{{.WorkspaceName}} 워크스페이스가 업그레이드 되었습니다." + }, + { + "id": "api.templates.cloud_welcome_email.add_apps_info", + "translation": "워크스페이스에 앱들을 추가합니다" + }, + { + "id": "api.templates.cloud_welcome_email.app_market_place", + "translation": "앱 마켓플레이스." + }, + { + "id": "api.templates.cloud_welcome_email.button", + "translation": "매터모스트 열기" + }, + { + "id": "api.license.request-trial.can-start-trial.error", + "translation": "평가판을 시작할 수 있는지 확인할 수 없습니다" + }, + { + "id": "api.file.test_connection_s3_settings_nil.app_error", + "translation": "파일 저장소 설정에 설정되지 않은 값이 있습니다." + }, + { + "id": "api.error_set_first_admin_visit_marketplace_status", + "translation": "스토어에 초기 관리자 마켓플레이스 방문 상태를 저장하는 동안 오류가 발생했습니다." + }, + { + "id": "api.command_templates.unsupported.app_error", + "translation": "이 장치에서는 템플릿 명령이 지원되지 않습니다." + }, + { + "id": "api.command_templates.name", + "translation": "템플릿" + }, + { + "id": "api.command_templates.desc", + "translation": "템플릿에서 만들기 창을 엽니다" + }, + { + "id": "api.acknowledgement.save.archived_channel.app_error", + "translation": "보관된 채널에서는 승인할 수 없습니다." + }, + { + "id": "api.acknowledgement.delete.deadline.app_error", + "translation": "5분이 경과한 후에는 확인을 삭제할 수 없습니다." + }, + { + "id": "api.acknowledgement.delete.archived_channel.app_error", + "translation": "보관된 채널에서는 승인 내용을 삭제할 수 없습니다." } ] diff --git a/server/i18n/nl.json b/server/i18n/nl.json index a4e634b187..95097c7095 100644 --- a/server/i18n/nl.json +++ b/server/i18n/nl.json @@ -9026,50 +9026,6 @@ "id": "app.job.get_all_jobs_by_type_and_status.app_error", "translation": "Het lukt niet om alle jobs op type en status op te halen." }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Verhoog jouw productiviteit met deze geweldige functies" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Hey {{.Naam}}, we hebben gemerkt dat jouw Mattermost server een beetje stof aan het verzamelen is. Kijk eens naar enkele functies die kunnen helpen om de werklast van jouw team te verlichten." - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Open Mattermost om de productiviteit van je team te verhogen!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Beheer taken met " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Workflowbeheer met " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Gast-toegang tot gespecificeerd(e) " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Kom maar eens kijken!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Mattermost openen" - }, - { - "id": "Playbooks", - "translation": "Playbooks" - }, - { - "id": "Channels", - "translation": "Kanalen" - }, - { - "id": "Boards", - "translation": "Boards" - }, { "id": "api.team.invite_members.unable_to_send_email_with_defaults.app_error", "translation": "SMTP is niet geconfigureerd in Systeem Console" @@ -9162,10 +9118,6 @@ "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "De instellingen van Elasticsearch bevat niet-ingestelde waarden." }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Je hebt deze eenmalige email ontvangen omdat jouw Mattermost server inactief was voor meer dan {{.Hours}} uur. Deze e-mail werd automatisch aangemaakt door jouw Mattermost server." - }, { "id": "api.templates.cloud_upgrade_confirmation.title", "translation": "Je bent nu geüpgraded!" @@ -9502,10 +9454,6 @@ "id": "model.group.name.reserved_name.app_error", "translation": "groepsnaam bestaat al als een gereserveerde naam" }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "Plugin {{.Name}} kan niet worden ingeschakeld in de productmodus." - }, { "id": "app.last_accessible_file.app_error", "translation": "Fout bij het ophalen van het laatst toegankelijke bestand" @@ -9554,22 +9502,6 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "Je kan een bevestiging in een gearchiveerd kanaal niet verwijderen." }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "Creëer transparante workflows tussen ontwikkelingsteams om ervoor te zorgen dat jouw ontwikkelingsproces naadloos verloopt." - }, - { - "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Verhoog de productiviteit in je kanaal door een Jira bot en Github bot te integreren. Deze worden voor jou gedownload." - }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Chat met je team in een Feature Release-kanaal dat gemakkelijk verbinding maakt met je boards, playbooks en app bots." - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Gebruik ons vergaderagenda-bord voor terugkerende vergaderingen zoals stand-up en ons Projecttakenbord om de voortgang van taken onderweg te beheren." - }, { "id": "worktemplate.category.product_teams", "translation": "Productteams" @@ -9630,10 +9562,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "Kon geen berichtprioriteit ophalen voor bericht" }, - { - "id": "app.draft.update.app_error", - "translation": "Kan het concept niet bijwerken." - }, { "id": "app.draft.save.app_error", "translation": "Kan het concept niet opslaan." @@ -9774,73 +9702,13 @@ "id": "api.config.update_config.translations.app_error", "translation": "Fout bij het bijwerken van de serververtalingen." }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Organiseer en versla alle bugs met dit project! Bouw momentum op en meet de voortgang met behulp van het meegeleverde Playbook, Board en Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Organiseer en versla alle bugs met dit project! Bouw momentum op en meet de voortgang met behulp van het meegeleverde Playbook, Board en Channel." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "Mis geen stap tijdens een productrelease met dit Project. Wijs taken toe uit de Playbook-checklist en bereik mijlpalen met het Board. Gebruik Channels om iedereen op één lijn te houden." - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "Mis geen stap tijdens een productrelease met dit Project. Wijs taken toe uit de Playbook-checklist en bereik mijlpalen met het Board. Gebruik Channels om iedereen op één lijn te houden." - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "Mis geen stap tijdens een productrelease met dit Project. Wijs taken toe uit de Playbook-checklist en bereik mijlpalen met het Board. Gebruik Channels om iedereen op dezelfde lijn te houden." - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "Wanneer alles fout gaat, is een herhaalbaar proces de sleutel om ervoor te zorgen dat alles zo snel mogelijk in orde komt. Dit project combineert alles wat Mattermost biedt om ervoor te zorgen dat de brandjes worden geblust en de belanghebbenden onderweg worden geïnformeerd." - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "Wanneer alles fout gaat, is een herhaalbaar proces de sleutel om ervoor te zorgen dat alles zo snel mogelijk in orde komt. Dit project combineert alles wat Mattermost biedt om ervoor te zorgen dat de brandjes worden geblust en de belanghebbenden onderweg worden geïnformeerd." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "Plan een Roadmap met behulp van dit Project Board en werk op dit onderwerp samen in het kanaal dat met dit sjabloon is aangemaakt." - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "Plan een Roadmap met behulp van dit Project Board en werk op dit onderwerp samen in het kanaal dat met dit sjabloon is aangemaakt." - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "Plan een Roadmap met behulp van dit Project Board en werk op dit onderwerp samen in het kanaal dat met dit sjabloon is aangemaakt." - }, { "id": "worktemplate.category.leadership", "translation": "Leiderschap" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.companywide", @@ -9906,58 +9774,6 @@ "id": "api.work_templates.disabled", "translation": "Werksjablonen zijn uitgeschakeld." }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Organiseer en versla alle bugs met dit project! Bouw momentum op en meet de voortgang met behulp van het meegeleverde Playbook, Board en Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Organiseer en versla alle bugs met dit project! Bouw momentum op en meet de voortgang met behulp van het meegeleverde Playbook, Board en Channel." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "Wanneer alles fout gaat, is een herhaalbaar proces de sleutel om ervoor te zorgen dat alles zo snel mogelijk in orde komt. Dit project combineert alles wat Mattermost biedt om ervoor te zorgen dat de brandjes worden geblust en de belanghebbenden onderweg worden geïnformeerd." - }, - { - "id": "api.server.cws.needs_enterprise_edition", - "translation": "Alleen beschikbaar in Mattermost Enterprise editie" - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Gebruik een Project om sprintplanning een makkie te maken. Het kanaal houdt het gesprek en de vragen gefocust. Het sprintplan houdt iedereen op zijn taak voor de week en het Terugblikboard brengt het team samen om voortdurend te verbeteren." - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Gebruik een Project om sprintplanning een makkie te maken. Het kanaal houdt het gesprek en de vragen gefocust. Het sprintplan houdt iedereen op zijn taak voor de week en het Terugblikboard brengt het team samen om voortdurend te verbeteren." - }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Gebruik een Project om sprintplanning een makkie te maken. Het kanaal houdt het gesprek en de vragen gefocust. Het sprintplan houdt iedereen op zijn taak voor de week en het Terugblikboard brengt het team samen om voortdurend te verbeteren." - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Beschrijving van waarom het kanaal of de kanalen nodig zijn" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Beschrijving van waarom het bord of de borden nodig zijn" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit Project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Duidelijke focus is essentieel voor teamsucces en met dit Project kan je de doelstellingen en OKR's van het team documenteren en updates plaatsen in het toegewezen kanaal." - }, { "id": "api.command_templates.unsupported.app_error", "translation": "De sjablonenopdracht wordt niet ondersteund op jouw toestel." diff --git a/server/i18n/pl.json b/server/i18n/pl.json index 9049ed5e57..339c00b48b 100644 --- a/server/i18n/pl.json +++ b/server/i18n/pl.json @@ -9023,50 +9023,6 @@ "id": "api.custom_groups.count_err", "translation": "błąd liczenia grup" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Odblokuj zwiększoną produktywność dzięki tym wspaniałym funkcjom" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Hej {{.Name}}, zauważyliśmy, że twój serwer Mattermost zbiera trochę kurzu. Zapoznaj się z kilkoma funkcjami, które pomogą odciążyć Twój zespół." - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Otwórz Mattermost, aby zwiększyć produktywność swojego zespołu!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Zarządzaj zadaniami za pomocą " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Zarządzanie przepływem pracy z " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Dostęp gości do określenia " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Przyjdź i sprawdź to!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Otwórz Mattermost" - }, - { - "id": "Playbooks", - "translation": "Playbooki" - }, - { - "id": "Channels", - "translation": "Kanały" - }, - { - "id": "Boards", - "translation": "Tablice" - }, { "id": "model.oauth.is_valid.mattermost_app_id.app_error", "translation": "Maksymalna długość identyfikatora MattermostAppID wynosi 32 znaki." @@ -9171,10 +9127,6 @@ "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Ustawienia Elasticsearch mają nieustawione wartości." }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Otrzymałeś tę jednorazową wiadomość e-mail, ponieważ Twój serwer Mattermost był nieaktywny przez ponad {{.Hours}} godzin. Ta wiadomość e-mail została automatycznie wygenerowana przez serwer Mattermost." - }, { "id": "api.file.cloud_upload.app_error", "translation": "Przesyłanie danych do instancji Chmury za pomocą mmctl nie jest obsługiwane. Proszę sprawdzić dokumentację tutaj: https://docs.mattermost.com/manage/cloud-data-export.html." @@ -9511,10 +9463,6 @@ "id": "model.group.name.reserved_name.app_error", "translation": "nazwa grupy już istnieje jako nazwa zastrzeżona" }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "Wtyczka {{.Name}} nie może być włączona w trybie produktu." - }, { "id": "api.team.invite_guests_to_channels.license.error", "translation": "Twoja licencja nie wspiera kont gości" @@ -9591,10 +9539,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "Nie można uzyskać priorytetu dla posta" }, - { - "id": "app.draft.update.app_error", - "translation": "Nie można zaktualizować szkicu." - }, { "id": "app.draft.save.app_error", "translation": "Nie można zapisać Szkicu." @@ -9663,22 +9607,6 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "Nie można usunąć potwierdzenia w zarchiwizowanym kanale." }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "Twórz przejrzyste przepływy pracy pomiędzy zespołami programistów, aby zapewnić płynny proces rozwoju funkcji." - }, - { - "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Zwiększ wydajność na swoim kanale, integrując bota Jira i bota Github. Zostaną one pobrane za Ciebie." - }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Czatuj ze swoim zespołem na kanale Feature Release, który łatwo łączy się z tablicami, playbookami i botami aplikacji." - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Użyj naszego szablonu tablicy Meeting Agenda do powtarzających się spotkań, takich jak standup, oraz naszej tablicy Project Tasks do zarządzania postępem zadań w trakcie." - }, { "id": "worktemplate.category.product_teams", "translation": "Zespoły Produkcyjne" @@ -9775,81 +9703,13 @@ "id": "app.group.username_conflict", "translation": "użytkownik o nazwie \"{{.Username}}\" już istnieje." }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Zorganizuj się i pokonaj wszystkie błędy dzięki temu projektowi! Zbuduj tempo i mierz postępy dzięki dołączonemu Playbookowi, tablicy i kanałowi." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Zorganizuj się i pokonaj wszystkie błędy dzięki temu projektowi! Zbuduj tempo i mierz postępy dzięki dołączonemu Playbookowi, tablicy i kanałowi." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "Nie przegap żadnego kroku podczas wydania produktu dzięki temu projektowi. Przydziel zadania z listy kontrolnej Playbooka i uderzaj w kamienie milowe z tablicą. Użyj kanałów, aby wszyscy byli na tej samej stronie." - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "Nie przegap żadnego kroku podczas wydania produktu dzięki temu projektowi. Przydziel zadania z listy kontrolnej Playbooka i uderzaj w kamienie milowe z tablicą. Użyj kanałów, aby wszyscy byli na tej samej stronie." - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "Nie przegap żadnego kroku podczas wydania produktu dzięki temu projektowi. Przydziel zadania z listy kontrolnej Playbooka i uderzaj w kamienie milowe z tablicą. Użyj kanałów, aby wszyscy byli na tej samej stronie." - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "Kiedy wszystko idzie nie tak, posiadanie powtarzalnego procesu jest kluczem do zapewnienia, że wszystko zostanie naprawione tak szybko, jak to możliwe. Ten projekt łączy w sobie wszystko, co oferuje Mattermost, aby zapewnić gaszenie pożarów i informowanie zainteresowanych stron po drodze." - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "Kiedy wszystko idzie nie tak, posiadanie powtarzalnego procesu jest kluczem do zapewnienia, że wszystko zostanie naprawione tak szybko, jak to możliwe. Ten projekt łączy w sobie wszystko, co oferuje Mattermost, aby zapewnić gaszenie pożarów i informowanie zainteresowanych stron po drodze." - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "Kiedy wszystko idzie nie tak, posiadanie powtarzalnego procesu jest kluczem do zapewnienia, że wszystko zostanie naprawione tak szybko, jak to możliwe. Ten projekt łączy w sobie wszystko, co oferuje Mattermost, aby zapewnić gaszenie pożarów i informowanie zainteresowanych stron po drodze." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "Zaplanuj mapę drogową za pomocą tej tablicy projektów i współpracuj na temat w kanale utworzonym za pomocą tego szablonu." - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "Zaplanuj mapę drogową za pomocą tej tablicy projektów i współpracuj na temat w kanale utworzonym za pomocą tego szablonu." - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "Zaplanuj mapę drogową za pomocą tej tablicy projektów i współpracuj na temat w kanale utworzonym za pomocą tego szablonu." - }, { "id": "worktemplate.category.leadership", "translation": "Przywództwo" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.companywide", @@ -9911,46 +9771,6 @@ "id": "api.config.update_config.translations.app_error", "translation": "Nie udało się zaktualizować tłumaczeń serwera." }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Użyj Projektu, aby planowanie sprintu było proste. Kanał utrzymuje rozmowę i pytania w centrum uwagi. Plan sprintu utrzymuje wszystkich na zadaniach na dany tydzień, a tablica retrospektywna łączy zespół w celu ciągłego doskonalenia." - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Użyj Projektu, aby planowanie sprintu było proste. Kanał utrzymuje rozmowę i pytania w centrum uwagi. Plan sprintu utrzymuje wszystkich na zadaniach na dany tydzień, a tablica retrospektywna łączy zespół w celu ciągłego doskonalenia." - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Użyj Projektu, aby planowanie sprintu było proste. Kanał utrzymuje rozmowę i pytania w centrum uwagi. Plan sprintu utrzymuje wszystkich na zadaniach na dany tydzień, a tablica retrospektywna łączy zespół w celu ciągłego doskonalenia." - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Opis, dlaczego kanał(y) jest(są) potrzebny(e)" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Opis powodów, dla których tablica(e) jest(są) potrzebna(e)" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Jasna koncentracja jest niezbędna dla sukcesu zespołu, a dzięki temu projektowi możesz udokumentować cele zespołu i OKR, a także zamieszczać aktualizacje w dedykowanym kanale." - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Zorganizuj się i pokonaj wszystkie błędy dzięki temu projektowi! Zbuduj tempo i mierz postępy dzięki dołączonemu Playbookowi, tablicy i kanałowi." - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Zorganizuj się i pokonaj wszystkie błędy dzięki temu projektowi! Zbuduj tempo i mierz postępy dzięki dołączonemu Playbookowi, tablicy i kanałowi." - }, { "id": "app.import.validate_user_teams_import_data.invalid_auth_service.error", "translation": "Nieprawidłowy serwis autentykacji: {{.AuthService}}" @@ -10038,5 +9858,197 @@ { "id": "app.command.execute.error", "translation": "Nie można wykonać polecenia." + }, + { + "id": "app.user.digest.runs_in_progress.zero_in_progress", + "translation": "Obecnie masz 0 uruchomień w trakcie." + }, + { + "id": "app.user.digest.tasks.all_tasks_command", + "translation": "Użyj `/playbook todo`, aby zobaczyć wszystkie swoje zadania." + }, + { + "id": "app.user.digest.tasks.heading", + "translation": "Twoje przydzielone zadania" + }, + { + "id": "app.user.digest.tasks.zero_assigned", + "translation": "Masz 0 przydzielonych zadań." + }, + { + "id": "app.user.new_run.run_name", + "translation": "Nazwa uruchomienia" + }, + { + "id": "app.user.new_run.title", + "translation": "Uruchom playbook" + }, + { + "id": "app.user.run.add_checklist_item.description", + "translation": "Opis" + }, + { + "id": "app.user.run.add_checklist_item.name", + "translation": "Nazwa" + }, + { + "id": "app.user.run.add_checklist_item.submit_label", + "translation": "Dodaj zadanie" + }, + { + "id": "app.user.run.add_checklist_item.title", + "translation": "Dodaj nowe zadanie" + }, + { + "id": "app.user.run.add_to_timeline.playbook_run", + "translation": "Uruchomienie Playbooka" + }, + { + "id": "app.user.run.add_to_timeline.submit_label", + "translation": "Dodaj do osi czasu uruchomienia" + }, + { + "id": "app.user.run.add_to_timeline.summary", + "translation": "Podsumowanie" + }, + { + "id": "app.user.run.add_to_timeline.summary.help", + "translation": "Maksymalnie 64 znaki" + }, + { + "id": "app.user.run.add_to_timeline.summary.placeholder", + "translation": "Krótkie podsumowanie widoczne na osi czasu" + }, + { + "id": "app.user.run.update_status.finish_run", + "translation": "Zakończ uruchomienie" + }, + { + "id": "app.user.run.update_status.finish_run.placeholder", + "translation": "Oznacz również uruchomienie jako zakończone" + }, + { + "id": "app.user.run.update_status.reminder_for_next_update", + "translation": "Przypomnienie o następnej aktualizacji" + }, + { + "id": "app.user.run.update_status.submit_label", + "translation": "Status aktualizacji" + }, + { + "id": "app.user.digest.runs_in_progress.num_in_progress", + "translation": { + "few": "Masz {{.Count}} uruchomienia w toku:", + "many": "Masz {{.Count}} uruchomień w toku:", + "one": "Masz {{.Count}} uruchomienie w toku:" + } + }, + { + "id": "app.user.digest.tasks.due_in_x_days", + "translation": { + "few": "Termin płatności za {{.Count}} dni", + "many": "Termin płatności za {{.Count}} dni", + "one": "Termin płatności za {{.Count}} dzień" + } + }, + { + "id": "app.user.digest.tasks.due_after_today", + "translation": { + "few": "Masz **{{.Count}} przydzielone zadania, których termin wykonania wypada po dzisiejszym dniu**.", + "many": "Masz **{{.Count}} przydzielonych zadań, których termin wykonania wypada po dzisiejszym dniu**.", + "one": "Masz **{{.Count}} przydzielone zadanie, którego termin wykonania wypada po dzisiejszym dniu**." + } + }, + { + "id": "app.user.digest.tasks.due_today", + "translation": "Do zapłaty dzisiaj" + }, + { + "id": "app.user.digest.tasks.due_x_days_ago", + "translation": "Termin {{.Count}} dni temu" + }, + { + "id": "app.user.digest.tasks.num_assigned", + "translation": { + "few": "Masz {{.Count}} przydzielone zadania:", + "many": "Masz {{.Count}} przydzielonych zadań:", + "one": "Masz {{.Count}} przydzielone zadanie:" + } + }, + { + "id": "app.user.digest.tasks.num_assigned_due_until_today", + "translation": { + "few": "Masz {{.Count}} przydzielone zadania, których termin wykonania właśnie upływa:", + "many": "Masz {{.Count}} przydzielonych zadań, których termin wykonania właśnie upływa:", + "one": "Masz {{.Count}} przydzielone zadanie, którego termin wykonania właśnie upływa:" + } + }, + { + "id": "app.user.new_run.intro", + "translation": "**Właściciel** {{.Username}}" + }, + { + "id": "app.user.new_run.playbook", + "translation": "Playbook" + }, + { + "id": "app.user.new_run.submit_label", + "translation": "Rozpocznij uruchomienie" + }, + { + "id": "app.user.run.confirm_finish.num_outstanding", + "translation": { + "few": "Są **{.Count}} zaległe zadania**. Czy na pewno chcesz zakończyć uruchomienie *{.RunName}}* dla wszystkich uczestników?", + "many": "Jest **{.Count}} zaległych zadań**. Czy na pewno chcesz zakończyć uruchomienie *{.RunName}}* dla wszystkich uczestników?", + "one": "Jest **{.Count}} zaległe zadanie**. Czy na pewno chcesz zakończyć uruchomienie *{.RunName}}* dla wszystkich uczestników?" + } + }, + { + "id": "app.user.run.update_status.num_channel", + "translation": { + "few": "Przedstaw aktualizację dla interesariuszy. Ten post będzie transmitowany na {{.Count}} kanałach.", + "many": "Przedstaw aktualizację dla interesariuszy. Ten post będzie transmitowany na {{.Count}} kanałach.", + "one": "Przedstaw aktualizację dla interesariuszy. Ten post będzie transmitowany na {{.Count}} kanale." + } + }, + { + "id": "app.user.run.update_status.title", + "translation": "Aktualizacje statusu" + }, + { + "id": "app.user.digest.tasks.due_yesterday", + "translation": "Termin na wczoraj" + }, + { + "id": "app.user.run.status_disable", + "translation": "@{.Username}} wyłączył aktualizacje statusu dla [{{.RunName}}]({{.RunURL}})" + }, + { + "id": "app.user.run.status_enable", + "translation": "@{.Username}} włączył aktualizacje statusu dla [{{.RunName}}]({{.RunURL}})" + }, + { + "id": "app.user.run.update_status.change_since_last_update", + "translation": "Zmiana od ostatniej aktualizacji" + }, + { + "id": "app.user.run.confirm_finish.submit_label", + "translation": "Zakończ uruchomienie" + }, + { + "id": "app.user.run.confirm_finish.title", + "translation": "Potwierdź zakończenie uruchomienia" + }, + { + "id": "app.user.run.request_join_channel", + "translation": "@{{.Name}} jest uczestnikiem uruchomienia i chce dołączyć do tego kanału. Każdy członek kanału może go zaprosić.\n" + }, + { + "id": "app.user.run.request_update", + "translation": "@here - @{.Name}} zażądał aktualizacji statusu dla [{{.RunName}}]({{.RunURL}}). \n" + }, + { + "id": "app.user.run.add_to_timeline.title", + "translation": "Dodaj do osi czasu uruchomienia" } ] diff --git a/server/i18n/pt-BR.json b/server/i18n/pt-BR.json index 63dcbcc1ce..48f679a2be 100644 --- a/server/i18n/pt-BR.json +++ b/server/i18n/pt-BR.json @@ -8619,18 +8619,6 @@ "id": "api.cloud.notify_admin_to_upgrade_error.already_notified", "translation": "Administrador já notificado" }, - { - "id": "Channels", - "translation": "Canais" - }, - { - "id": "Boards", - "translation": "Quadros" - }, - { - "id": "Playbooks", - "translation": "Playbooks" - }, { "id": "api.cloud.delinquency_email.missing_email_to_trigger", "translation": "Campos faltando para envio de email." diff --git a/server/i18n/ru.json b/server/i18n/ru.json index d64ff35cfb..452811806c 100644 --- a/server/i18n/ru.json +++ b/server/i18n/ru.json @@ -8967,18 +8967,6 @@ "id": "api.custom_groups.feature_disabled", "translation": "функция пользовательских групп отключена" }, - { - "id": "Playbooks", - "translation": "Сценарии" - }, - { - "id": "Channels", - "translation": "Каналы" - }, - { - "id": "Boards", - "translation": "Доски" - }, { "id": "api.error_get_first_admin_complete_setup", "translation": "Ошибка при попытке получить первую завершенную настройку администратора из магазина." @@ -9035,34 +9023,6 @@ "id": "api.user.authorize_oauth_user.saml_response_too_long.app_error", "translation": "Ответ SAML слишком длинный" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Откройте для себя повышенную производительность с помощью этих замечательных функций" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Привет, {{.Name}}, мы заметили, что Ваш сервер Mattermost собирает немного пыли. Взгляните на некоторые функции, которые могут облегчить рабочую нагрузку в Вашей команде." - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Откройте все возможности Mattermost, чтобы повысить производительность вашей команды!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Управляйте задачами с помощью " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Управление рабочим процессом с " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Приходите и проверьте это!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Открыть Mattermost" - }, { "id": "api.team.invite_members.unable_to_send_email_with_defaults.app_error", "translation": "SMTP не настроен в Системной Консоли" @@ -9079,10 +9039,6 @@ "id": "api.error_set_first_admin_complete_setup", "translation": "Ошибка при попытке сохранить первую полную настройку администратора в магазине." }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Гостевой доступ к указанному " - }, { "id": "model.oauth.is_valid.mattermost_app_id.app_error", "translation": "Максимальная длина MattermostAppID — 32 символа." @@ -9307,10 +9263,6 @@ "id": "app.post.analytics_teams_count.app_error", "translation": "Не удалось получить сведения об использовании команд" }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "Плагин {{.Name}} не может быть включен в продуктовом режиме." - }, { "id": "app.notify_admin.send_notification_post.app_error", "translation": "Невозможно отправить сообщение с уведомлением." @@ -9371,10 +9323,6 @@ "id": "app.cloud.get_cloud_products.app_error", "translation": "Не удалось получить облачные продукты" }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Вы получили это одноразовое письмо, потому что ваш сервер Mattermost был неактивен более {{.Hours}} часов. Это письмо было автоматически сгенерировано вашим сервером Mattermost." - }, { "id": "api.templates.delinquency_90.title", "translation": "Ваше рабочее пространство Mattermost было понижено в статусе" @@ -9591,10 +9539,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "Невозможно получить приоритет для сообщения" }, - { - "id": "app.draft.update.app_error", - "translation": "Невозможно обновить черновик." - }, { "id": "app.draft.save.app_error", "translation": "Невозможно сохранить черновик." @@ -9663,22 +9607,6 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "Вы не можете удалить подтверждение в архивированном канале." }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "Создайте прозрачные рабочие процессы между командами разработчиков, чтобы обеспечить бесперебойный процесс разработки функций." - }, - { - "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Повысьте производительность вашего канала, интегрировав бота для Jira и бота для Github. Они будут загружены для вас." - }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Общайтесь со своей командой в канале Feature Release, который легко соединяется с вашими досками, сценариями и ботами приложений." - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Используйте наш шаблон доски \"Повестка дня совещания\" для повторяющихся совещаний, например, совещаний по подготовке к работе, и доску \"Задачи проекта\" для управления ходом выполнения задач." - }, { "id": "worktemplate.category.product_teams", "translation": "Продуктовые команды" @@ -9839,121 +9767,13 @@ "id": "app.import.validate_user_teams_import_data.invalid_auth_service.error", "translation": "Неверная служба авторизации: {{.AuthService}}" }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Используйте проект, чтобы сделать планирование спринта легким. Канал позволяет сфокусировать разговор и вопросы. План спринта помогает всем выполнить задачи на неделю, а доска ретроспектив объединяет команду для постоянного совершенствования." - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Используйте проект, чтобы сделать планирование спринта легким. Канал позволяет сфокусировать разговор и вопросы. План спринта помогает всем выполнить задачи на неделю, а доска ретроспектив объединяет команду для постоянного совершенствования." - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Используйте проект, чтобы сделать планирование спринта легким. Канал позволяет сфокусировать разговор и вопросы. План спринта помогает всем выполнить задачи на неделю, а доска ретроспектив объединяет команду для постоянного совершенствования." - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Описание того, зачем нужен канал(ы)" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Описание того, зачем нужна(ы) доска(и)" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Организуйтесь и уничтожьте все ошибки с помощью этого проекта! Создайте импульс и измерьте прогресс с помощью включенных в комплект Playbook, Board и Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Организуйтесь и уничтожьте все ошибки с помощью этого проекта! Создайте импульс и измерьте прогресс с помощью включенных в комплект Playbook, Board и Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Организуйтесь и уничтожьте все ошибки с помощью этого проекта! Создайте импульс и измерьте прогресс с помощью включенных в комплект Playbook, Board и Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Организуйтесь и уничтожьте все ошибки с помощью этого проекта! Создайте импульс и измерьте прогресс с помощью включенных в комплект Playbook, Board и Channel." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "С этим проектом вы не пропустите ни одного шага во время выпуска продукта. Назначайте задачи из контрольного списка Playbook и устанавливайте вехи на Досках. Используйте Каналы, чтобы все были вовлечены в процесс." - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "С этим проектом вы не пропустите ни одного шага во время выпуска продукта. Назначайте задачи из контрольного списка Playbook и устанавливайте вехи на Досках. Используйте Каналы, чтобы все были вовлечены в процесс." - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "С этим проектом вы не пропустите ни одного шага во время выпуска продукта. Назначайте задачи из контрольного списка Playbook и устанавливайте вехи на Досках. Используйте Каналы, чтобы все были вовлечены в процесс." - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "Когда все идет не так, как надо, наличие повторяющегося процесса является ключом к тому, чтобы все было исправлено как можно быстрее. Этот проект сочетает в себе все, что предлагает Mattermost, чтобы обеспечить разрешение трудностей и информирование заинтересованных сторон на этом пути." - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "Когда все идет не так, как надо, наличие повторяющегося процесса является ключом к тому, чтобы все было исправлено как можно быстрее. Этот проект сочетает в себе все, что предлагает Mattermost, чтобы обеспечить разрешение трудностей и информирование заинтересованных сторон на этом пути." - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "Когда все идет не так, как надо, наличие повторяющегося процесса является ключом к тому, чтобы все было исправлено как можно быстрее. Этот проект сочетает в себе все, что предлагает Mattermost, чтобы обеспечить разрешение трудностей и информирование заинтересованных сторон на этом пути." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Фокусировка необходима для успеха команды, и с помощью этого проекта вы можете документировать цели и OKR команды, а также публиковать обновления в специальном канале." - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "Планируйте дорожную карту с помощью этой проектной доски и сотрудничайте по теме в канале, созданном с помощью этого шаблона." - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "Планируйте дорожную карту с помощью этой проектной доски и сотрудничайте по теме в канале, созданном с помощью этого шаблона." - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "Планируйте дорожную карту с помощью этой проектной доски и сотрудничайте по теме в канале, созданном с помощью этого шаблона." - }, { "id": "worktemplate.category.leadership", "translation": "Лидерство" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "api.server.cws.needs_enterprise_edition", diff --git a/server/i18n/sv.json b/server/i18n/sv.json index e831a10a09..f63afba27a 100644 --- a/server/i18n/sv.json +++ b/server/i18n/sv.json @@ -8970,18 +8970,6 @@ "id": "app.system.complete_onboarding_request.app_error", "translation": "Misslyckades att tolka onboarding-anropet." }, - { - "id": "Playbooks", - "translation": "Playbooks" - }, - { - "id": "Channels", - "translation": "Kanaler" - }, - { - "id": "Boards", - "translation": "Boards" - }, { "id": "api.custom_groups.no_remote_id", "translation": "remote_id måste vara tomt för anpassade grupper" @@ -9070,42 +9058,6 @@ "id": "app.channel.get_file_count.app_error", "translation": "Det går inte att få fram antalet filer i kanalen" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Öka produktiviteten med dessa fantastiska funktioner" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Hej {{.Name}}, vi ser att din Mattermost-server samlat lite damm. Ta en titt på några funktioner som kan hjälpa till att lätta på arbetsbördan för ditt team." - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Öppna Mattermost för att öka teamets produktivitet!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Hantera uppgifter med hjälp av " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "Hantera arbetsflöden med " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Gäståtkomst till specificerade " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Kom och kolla in det!" - }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Du fick det här engångsmejlet eftersom din Mattermost-server har varit inaktiv i mer än {{.Hours}} timmar. Det här e-postmeddelandet genererades automatiskt av din Mattermost-server." - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Öppna Mattermost" - }, { "id": "api.templates.invite_team_and_channels_subject", "translation": "[{{ .SiteName }}] {{ .SenderName }} bjöd in dig att ansluta till {{ .ChannelsLen }} kanaler i teamet {{ .TeamDisplayName }}" @@ -9522,10 +9474,6 @@ "id": "model.insights.get_start_of_day_for_time_range.time_range.app_error", "translation": "Ogiltigt tidsintervall." }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "Plugin {{.Name}} kan inte aktiveras i produktionsläge." - }, { "id": "app.collection.add_collection.exists.app_error", "translation": "Samlingstypen finns redan." @@ -9582,22 +9530,6 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "Du kan inte ta bort en bekräftelse i en arkiverad kanal." }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "Skapa transparenta arbetsflöden mellan utvecklingsteamen för att säkerställa att utvecklingsprocessen för funktioner är smidig." - }, - { - "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Öka produktiviteten i kanalen genom att integrera en Jira-bot och en Github-bot. Dessa kommer att laddas ner åt dig." - }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Chatta med ditt team i kanalen för kommande leveranser, som enkelt kan anslutas till dina tavlor, playbooks och app-bottar." - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Använd vår mall för mötesagenda för återkommande möten exempelvis standup och vår Projekt-board för projektuppgifter och att hantera uppgifternas framskridande." - }, { "id": "worktemplate.category.product_teams", "translation": "Produkt-team" @@ -9654,10 +9586,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "Det går inte att få fram inläggets prioritet" }, - { - "id": "app.draft.update.app_error", - "translation": "Kunde inte uppdatera utkastet." - }, { "id": "app.draft.save.app_error", "translation": "Kunde inte spara utkastet." @@ -9822,121 +9750,13 @@ "id": "api.work_templates.disabled", "translation": "Arbetsmallar är inaktiverade." }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Använd ett projekt för att göra sprintplaneringen enkel. Kanalen håller konversationen och frågorna fokuserade. Sprintplanen håller alla på uppgiften för veckan och retrospektivtavlan samlar teamet för att kontinuerligt förbättra sig." - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Använd ett projekt för att göra sprintplaneringen enkel. Kanalen håller konversationen och frågorna fokuserade. Sprintplanen håller alla på uppgiften för veckan och retrospektivtavlan samlar teamet för att kontinuerligt förbättra sig." - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Använd ett projekt för att göra sprintplaneringen enkel. Kanalen håller konversationen och frågorna fokuserade. Sprintplanen håller alla på uppgiften för veckan och retrospektivtavlan samlar teamet för att kontinuerligt förbättra sig." - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Beskrivning av varför kanalen/kanalerna behövs" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Beskrivning av varför tavlan/tavlorna behövs" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Bli organiserad och kläm åt alla buggar med det här projektet! Skapa framdrift och mät framstegen med hjälp av den medföljande Playbook, Board och Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Bli organiserad och kläm åt alla buggar med det här projektet! Skapa framdrift och mät framstegen med hjälp av den medföljande Playbook, Board och Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Bli organiserad och kläm åt alla buggar med det här projektet! Skapa framdrift och mät framstegen med hjälp av den medföljande Playbook, Board och Channel." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Bli organiserad och kläm åt alla buggar med det här projektet! Skapa framdrift och mät framstegen med hjälp av den medföljande Playbook, Board och Channel." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "Med det här projektet kan du inte missa något steg under en produktlansering. Tilldela uppgifter från checklistan i Playbook och uppnå milstolpar med Board. Använd Channel för att se till att alla har samma information." - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "Med det här projektet kan du inte missa något steg under en produktlansering. Tilldela uppgifter från checklistan i Playbook och uppnå milstolpar med Board. Använd Channel för att se till att alla har samma information." - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "Med det här projektet kan du inte missa något steg under en produktlansering. Tilldela uppgifter från checklistan i Playbook och uppnå milstolpar med Board. Använd Channel för att se till att alla har samma information." - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "När saker är på väg att gå fel är en återupprepningsbar process en nyckel för att kunna rätta till så snabbt som möjligt. Det här projektet kombinerar allt som Mattermost erbjuder för att se till att bränderna släcks och att intressenterna informeras på vägen." - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "När saker är på väg att gå fel är en återupprepningsbar process en nyckel för att kunna rätta till så snabbt som möjligt. Det här projektet kombinerar allt som Mattermost erbjuder för att se till att bränderna släcks och att intressenterna informeras på vägen." - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "När saker är på väg att gå fel är en återupprepningsbar process en nyckel för att kunna rätta till så snabbt som möjligt. Det här projektet kombinerar allt som Mattermost erbjuder för att se till att bränderna släcks och att intressenterna informeras på vägen." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Tydligt fokus är viktigt för att teamet ska lyckas och med det här projektet kan du dokumentera teamets mål och OKR:er samt lägga upp uppdateringar i den särskilda kanalen." - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "Planera en roadmap med hjälp av projekttavlan och samarbeta i ämnet i kanalen som skapats från den här mallen." - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "Planera en roadmap med hjälp av projekttavlan och samarbeta i ämnet i kanalen som skapats från den här mallen." - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "Planera en roadmap med hjälp av projekttavlan och samarbeta i ämnet i kanalen som skapats från den här mallen." - }, { "id": "worktemplate.category.leadership", "translation": "Ledarskap" }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.companywide", @@ -10013,5 +9833,213 @@ { "id": "app.oauth.remove_auth_data_by_client_id.app_error", "translation": "Kunde inte rensa oauth-information." + }, + { + "id": "app.user.run.update_status.title", + "translation": "Statusuppdatering" + }, + { + "id": "app.user.run.update_status.submit_label", + "translation": "Uppdatera status" + }, + { + "id": "app.user.run.update_status.reminder_for_next_update", + "translation": "Påminnelse om nästa uppdatering" + }, + { + "id": "app.user.run.update_status.num_channel", + "translation": { + "one": "Ge en uppdatering till intressenterna. Detta inlägg kommer att publiceras i {{.Count}} kanal.", + "other": "Ge en uppdatering till intressenterna. Detta inlägg kommer att publiceras i {{.Count}} kanaler." + } + }, + { + "id": "app.user.run.update_status.finish_run.placeholder", + "translation": "Markera även körningen som avslutad" + }, + { + "id": "app.user.run.update_status.finish_run", + "translation": "Slutför körningen" + }, + { + "id": "app.user.run.update_status.change_since_last_update", + "translation": "Förändring sedan den senaste uppdateringen" + }, + { + "id": "app.user.run.status_enable", + "translation": "@{{.Username}} aktiverade statusuppdateringar för [{{.RunName}}]({{.RunURL}})" + }, + { + "id": "app.user.run.status_disable", + "translation": "@{{.Username}} inaktiverade statusuppdateringarna för [{{.RunName}}]({{.RunURL}})" + }, + { + "id": "app.user.run.request_update", + "translation": "@here - @{{.Name}} begärde en statusuppdatering för [{{.RunName}}]({{.RunURL}}). \n" + }, + { + "id": "app.user.run.request_join_channel", + "translation": "@{{.Name}} är en deltagare i en körning och vill gå med i den här kanalen. Alla medlemmar i kanalen kan bjuda in dem.\n" + }, + { + "id": "app.user.run.confirm_finish.title", + "translation": "Bekräfta att avsluta körningen" + }, + { + "id": "app.user.run.confirm_finish.submit_label", + "translation": "Slutför körningen" + }, + { + "id": "app.user.run.confirm_finish.num_outstanding", + "translation": { + "one": "Det finns **{{.Count}} utestående uppgift**. Är du säker på att du vill avsluta körningen *{{.RunName}}* för alla deltagare?", + "other": "Det finns **{{.Count}} utestående uppgifter**. Är du säker på att du vill avsluta körningen *{{.RunName}}* för alla deltagare?" + } + }, + { + "id": "app.user.run.add_to_timeline.title", + "translation": "Lägg till i tidslinjen för körning" + }, + { + "id": "app.user.run.add_to_timeline.summary.placeholder", + "translation": "Kort sammanfattning som visas i tidslinjen" + }, + { + "id": "app.user.run.add_to_timeline.summary.help", + "translation": "Max 64 tecken" + }, + { + "id": "app.user.run.add_to_timeline.summary", + "translation": "Sammanfattning" + }, + { + "id": "app.user.run.add_to_timeline.submit_label", + "translation": "Lägg till i tidslinjen för körning" + }, + { + "id": "app.user.run.add_to_timeline.playbook_run", + "translation": "Kör Playbook" + }, + { + "id": "app.user.run.add_checklist_item.title", + "translation": "Lägg till en ny uppgift" + }, + { + "id": "app.user.run.add_checklist_item.submit_label", + "translation": "Lägg till en uppgift" + }, + { + "id": "app.user.run.add_checklist_item.name", + "translation": "Namn" + }, + { + "id": "app.user.run.add_checklist_item.description", + "translation": "Beskrivning" + }, + { + "id": "app.user.new_run.title", + "translation": "Kör playbook" + }, + { + "id": "app.user.new_run.submit_label", + "translation": "Starta körning" + }, + { + "id": "app.user.new_run.run_name", + "translation": "Namn på körning" + }, + { + "id": "app.user.new_run.playbook", + "translation": "Playbook" + }, + { + "id": "app.user.new_run.intro", + "translation": "**Ägare** {{.Username}}" + }, + { + "id": "app.user.digest.tasks.zero_assigned", + "translation": "Du har 0 tilldelade uppgifter." + }, + { + "id": "app.user.digest.tasks.num_assigned_due_until_today", + "translation": { + "one": "Du har {{.Count}} tilldelad uppgift som nu är förfallen:", + "other": "Du har {{.Count}} tilldelade uppgifter som nu är förfallna:" + } + }, + { + "id": "app.user.digest.tasks.num_assigned", + "translation": { + "one": "Du har {{.Count}} tilldelad uppgift:", + "other": "Du har {{.Count}} tilldelade uppgifter:" + } + }, + { + "id": "app.user.digest.tasks.heading", + "translation": "Dina tilldelade uppgifter" + }, + { + "id": "app.user.digest.tasks.due_yesterday", + "translation": "Skulle utförts igår" + }, + { + "id": "app.user.digest.tasks.due_x_days_ago", + "translation": "Försenad {{.Count}} dagar" + }, + { + "id": "app.user.digest.tasks.due_in_x_days", + "translation": { + "one": "Ska utföras inom {{.Count}} dag", + "other": "Ska utföras inom {{.Count}} dagar" + } + }, + { + "id": "app.user.digest.tasks.due_today", + "translation": "Ska utföras idag" + }, + { + "id": "app.user.digest.tasks.due_after_today", + "translation": { + "one": "Du har **{{.Count}} tilldelad uppgift som ska utföras idag**.", + "other": "Du har **{{.Count}} tilldelade uppgifter som ska utföras idag**." + } + }, + { + "id": "app.user.digest.tasks.all_tasks_command", + "translation": "Använd `/playbook todo` för att se alla dina uppgifter." + }, + { + "id": "app.user.digest.runs_in_progress.zero_in_progress", + "translation": "Du har 0 pågående körningar." + }, + { + "id": "app.user.digest.runs_in_progress.num_in_progress", + "translation": { + "one": "Du har {{.Count}} körning som för närvarande pågår:", + "other": "Du har {{.Count}} körningar som för närvarande pågår:" + } + }, + { + "id": "app.user.digest.runs_in_progress.heading", + "translation": "Körningar som pågår" + }, + { + "id": "app.user.digest.overdue_status_updates.zero_overdue", + "translation": "Du har 0 försenade körningar." + }, + { + "id": "app.user.digest.overdue_status_updates.num_overdue", + "translation": { + "one": "Du har {{.Count}} körning som borde ha fått en statusuppdatering:", + "other": "Du har {{.Count}} körningar som borde ha fått en statusuppdatering:" + } + }, + { + "id": "app.user.digest.overdue_status_updates.heading", + "translation": "Status om förseningar" + }, + { + "id": "app.command.execute.error", + "translation": "Kunde inte utföra kommandot." } ] diff --git a/server/i18n/tr.json b/server/i18n/tr.json index 2f9c116dc4..218846ffc4 100644 --- a/server/i18n/tr.json +++ b/server/i18n/tr.json @@ -9022,50 +9022,6 @@ "id": "api.custom_groups.count_err", "translation": "gruplar sayılırken sorun çıktı" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "Bu harika özelliklerle üretkenliğinizi artırın" - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "Ekibinizin üretkenliğini arttırmak için Mattermost açın!" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "Merhaba {{.Name}}, Mattermost sunucunuzun biraz toz tuttuğunu fark ettik. Ekibinizin iş yükünü hafifletmeye yardımcı olabilecek bazı özelliklere göz atın." - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "Belirtilen konuk erişimi " - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "Görev yönetimi " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "İş akışı yönetimi " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "Gelin ve inceleyin!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "Mattermost uygulamasını aç" - }, - { - "id": "Playbooks", - "translation": "Senaryolar" - }, - { - "id": "Channels", - "translation": "Kanallar" - }, - { - "id": "Boards", - "translation": "Panolar" - }, { "id": "app.job.get_all_jobs_by_type_and_status.app_error", "translation": "Türe ve duruma göre tüm görevler alınamadı." @@ -9166,10 +9122,6 @@ "id": "api.templates.cloud_upgrade_confirmation.subject", "translation": "Mattermost üst tarifeye geçme onayı" }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "Bu bir kerelik e-posta Mattermost sunucunuz {{.Hours}} saatten uzun süredir etkin olmadığı için gönderildi. Bu e-posta Mattermost sunucunuz tarafından otomatik olarak oluşturuldu." - }, { "id": "api.elasticsearch.test_elasticsearch_settings_nil.app_error", "translation": "Elasticsearch yapılandırmasında ayarlanmamış değerler var." @@ -9510,10 +9462,6 @@ "id": "model.group.name.reserved_name.app_error", "translation": "aynı adlı bir grup zaten sistem kullanımına ayrılmış bir ad olarak var" }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "{{.Name}} uygulama eki ürün kipinde etkinleştirilemez." - }, { "id": "api.team.invite_guests_to_channels.license.error", "translation": "Lisansınız konuk hesaplarının kullanılmasını desteklemiyor" @@ -9558,22 +9506,6 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "Arşivlenmiş bir kanaldaki bir onayı kaldıramazsınız." }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "Özellik geliştirme sürecinizin sorunsuz olmasını sağlamak için geliştirme ekipleri arasında şeffaf iş akışları oluşturun." - }, - { - "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "Bir Jira botu ve Github botu ile bütünleştirerek kanalınızdaki üretkenliği artırın. Bu botlar sizin için indirilir." - }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Panolarınıza, senaryolarınıza ve uygulama botlarınıza kolayca bağlanan bir özellik yayını kanalında ekibinizle sohbet edin." - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "Ayaküstü gibi yinelenen toplantılar için toplantı gündemi panosu kalıbımızı ve yol boyunca görevlerin ilerleyişini yönetmek için proje görevleri panomuzu kullanın." - }, { "id": "worktemplate.category.product_teams", "translation": "Ürün takımları" @@ -9634,10 +9566,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "İletinin önceliği alınamadı" }, - { - "id": "app.draft.update.app_error", - "translation": "Taslak güncellenemedi." - }, { "id": "app.draft.get_drafts.app_error", "translation": "Kullanıcının taslakları alınamadı." @@ -9842,30 +9770,6 @@ "id": "worktemplate.category.devops", "translation": "Geliştirme işlemleri" }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "Bu proje panosunu kullanarak bir yol haritası planlayın ve bu kalıpla oluşturulan kanalda konu üzerine işbirliği yapın." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "Bu proje panosunu kullanarak bir yol haritası planlayın ve bu kalıpla oluşturulan kanalda konu üzerine işbirliği yapın." - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "Bu proje panosunu kullanarak bir yol haritası planlayın ve bu kalıpla oluşturulan kanalda konu üzerine işbirliği yapın." - }, { "id": "app.import.validate_user_teams_import_data.invalid_auth_service.error", "translation": "Kimlik doğrulama hizmeti geçersiz: {{.AuthService}}" @@ -9874,90 +9778,6 @@ "id": "api.server.cws.needs_enterprise_edition", "translation": "Bu hizmet yalnızca Mattermost Enterprise sürümüyle kullanılabilir" }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "Acil işlerin planlamasını kolaylaştırmak için bir proje kullanın. Kanal, konuşmanın ve soruların odaklanmasını sağlar. Acil iş planı herkesin hafta boyunca görevler üzerinde kalmasını sağlar ve geçmiş değerlendirmesi panosu takımı sürekli geliştirme için bir araya getirir." - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "Acil işlerin planlamasını kolaylaştırmak için bir proje kullanın. Kanal, konuşmanın ve soruların odaklanmasını sağlar. Acil iş planı herkesin hafta boyunca görevler üzerinde kalmasını sağlar ve geçmiş değerlendirmesi panosu takımı sürekli geliştirme için bir araya getirir." - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "Acil işlerin planlamasını kolaylaştırmak için bir proje kullanın. Kanal, konuşmanın ve soruların odaklanmasını sağlar. Acil iş planı herkesin hafta boyunca görevler üzerinde kalmasını sağlar ve geçmiş değerlendirmesi panosu takımı sürekli geliştirme için bir araya getirir." - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "Kanallara neden gerek duyulduğunun açıklaması" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "Panolara neden gerek duyulduğunun açıklaması" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "Bu proje ile organize olun ve tüm hatalardan kurtulun! Senaryo, Pano ve Kanal özelliklerini kullanarak çalışmaya başlayın ve ilerlemeyi ölçün." - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "Bu proje ile organize olun ve tüm hatalardan kurtulun! Senaryo, Pano ve Kanal özelliklerini kullanarak çalışmaya başlayın ve ilerlemeyi ölçün." - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "Bu proje ile organize olun ve tüm hatalardan kurtulun! Senaryo, Pano ve Kanal özelliklerini kullanarak çalışmaya başlayın ve ilerlemeyi ölçün." - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "Bu proje ile organize olun ve tüm hatalardan kurtulun! Senaryo, Pano ve Kanal özelliklerini kullanarak çalışmaya başlayın ve ilerlemeyi ölçün." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "Takım başarısı için net odaklanma şarttır ve bu proje ile ekibin hedefleri ile OKR kriterlerini belgeleyebilir ve güncellemeleri özel bir kanalda yayınlayabilirsiniz." - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "Bu proje ile bir ürün sürümü sırasında hiçbir adımı atlamayın. Senaryo kontrol listesinden görevler atayın ve Pano ile kilometre taşlarına ulaşın. Herkesin aynı şeyleri bilmesi için Kanalları kullanın." - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "Bu proje ile bir ürün sürümü sırasında hiçbir adımı atlamayın. Senaryo kontrol listesinden görevler atayın ve Pano ile kilometre taşlarına ulaşın. Herkesin aynı şeyleri bilmesi için Kanalları kullanın." - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "Bu proje ile bir ürün sürümü sırasında hiçbir adımı atlamayın. Senaryo kontrol listesinden görevler atayın ve Pano ile kilometre taşlarına ulaşın. Herkesin aynı şeyleri bilmesi için Kanalları kullanın." - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "İşler ters gittiğinde, yinelenebilir bir sürecinizin olması, her şeyin olabilecek en kısa sürede doğru olarak yapılmasını sağlar. Bu özellik projelerdeki yangınların söndürülmesini ve paydaşların yol boyunca bilgilendirilmesini sağlamak için Mattermost tarafından sunulan her şeyi bir araya getirir." - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "İşler ters gittiğinde, yinelenebilir bir sürecinizin olması, her şeyin olabilecek en kısa sürede doğru olarak yapılmasını sağlar. Bu özellik projelerdeki yangınların söndürülmesini ve paydaşların yol boyunca bilgilendirilmesini sağlamak için Mattermost tarafından sunulan her şeyi bir araya getirir." - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "İşler ters gittiğinde, yinelenebilir bir sürecinizin olması, her şeyin olabilecek en kısa sürede doğru olarak yapılmasını sağlar. Bu özellik projelerdeki yangınların söndürülmesini ve paydaşların yol boyunca bilgilendirilmesini sağlamak için Mattermost tarafından sunulan her şeyi bir araya getirir." - }, { "id": "app.worktemplates.execute_work_template.name_too_long", "translation": "Ad alanına en fazla 64 karakter yazılabilir." diff --git a/server/i18n/uk.json b/server/i18n/uk.json index bce734f746..b63779f26f 100644 --- a/server/i18n/uk.json +++ b/server/i18n/uk.json @@ -6847,10 +6847,6 @@ "id": "api.back_to_app", "translation": "Повернутися до {{.SiteName}}" }, - { - "id": "Channels", - "translation": "Канали" - }, { "id": "api.cloud.cws_webhook_event_missing_error", "translation": "Подія Webhook не оброблена. Або вона відсутня, або недійсна." @@ -6879,14 +6875,6 @@ "id": "api.acknowledgement.delete.archived_channel.app_error", "translation": "Ви не можете видалити підтвердження в заархівованому каналі." }, - { - "id": "Playbooks", - "translation": "Сценарії" - }, - { - "id": "Boards", - "translation": "Дошки" - }, { "id": "api.command_remote.invite.help", "translation": "Запросіть безпечне з'єднання" diff --git a/server/i18n/zh-CN.json b/server/i18n/zh-CN.json index 3cc21656bd..bbf95c2889 100644 --- a/server/i18n/zh-CN.json +++ b/server/i18n/zh-CN.json @@ -8999,38 +8999,6 @@ "id": "api.user.authorize_oauth_user.saml_response_too_long.app_error", "translation": "SAML的请求信息太长" }, - { - "id": "api.templates.server_inactivity_title", - "translation": "解锁这些令人敬佩的、提高生产力的功能" - }, - { - "id": "api.templates.server_inactivity_subtitle", - "translation": "嗨, {{.Name}}, 我们注意到您的 Mattermost 服务器集了一些“灰尘”(有一段时间没有维护了),快来看看可以帮助减轻团队工作量的一些功能." - }, - { - "id": "api.templates.server_inactivity_subject", - "translation": "快来打开 Mattermost 以提高您团队的生产力!" - }, - { - "id": "api.templates.server_inactivity_info_bullet2", - "translation": "管理任务由 " - }, - { - "id": "api.templates.server_inactivity_info_bullet1", - "translation": "工作流管理于 " - }, - { - "id": "api.templates.server_inactivity_info_bullet", - "translation": "指定客户专访 " - }, - { - "id": "api.templates.server_inactivity_info", - "translation": "快来看看吧!" - }, - { - "id": "api.templates.server_inactivity_button", - "translation": "打开 Mattermost" - }, { "id": "api.templates.invite_team_and_channels_subject", "translation": "[{{ .SiteName }}] {{ .SenderName }} 邀请您加入 {{ .TeamDisplayName }} 团队的 {{ .ChannelsLen }} 频道组" @@ -9127,18 +9095,6 @@ "id": "api.custom_groups.count_err", "translation": "统计“组”时出现错误" }, - { - "id": "Playbooks", - "translation": "规划书" - }, - { - "id": "Channels", - "translation": "频道" - }, - { - "id": "Boards", - "translation": "面板" - }, { "id": "model.oauth.is_valid.mattermost_app_id.app_error", "translation": "MattermostAppID 的最大长度为 32 个字符。" @@ -9171,10 +9127,6 @@ "id": "app.insights.feature_disabled", "translation": "Insights 功能已禁用。" }, - { - "id": "api.templates.server_inactivity_footer_disclaimer", - "translation": "您收到这封一次性电子邮件是因为您的 Mattermost 服务器处于非活动状态超过 {{.Hours}} 小时。 本电子邮件是由您的 Mattermost 服务器自动生成。" - }, { "id": "api.file.cloud_upload.app_error", "translation": "不支持通过 mmctl 上传到 Cloud 实例。 请在此处查看文档:https://docs.mattermost.com/manage/cloud-data-export.html。" @@ -9505,7 +9457,7 @@ }, { "id": "worktemplate.category.product_teams", - "translation": "产品团队" + "translation": "产品" }, { "id": "worktemplate.category.leadership", @@ -9513,7 +9465,7 @@ }, { "id": "worktemplate.category.devops", - "translation": "Dev Ops" + "translation": "DevOps" }, { "id": "worktemplate.category.companywide", @@ -9687,10 +9639,6 @@ "id": "app.post_prority.get_for_post.app_error", "translation": "无法取得消息的优先级" }, - { - "id": "app.plugin.product_mode.app_error", - "translation": "在生产模式中无法启用 {{.Name}} 插件。" - }, { "id": "app.notify_admin.send_notification_post.app_error", "translation": "无法发送通知消息。" @@ -9719,10 +9667,6 @@ "id": "app.file.cloud.get.app_error", "translation": "由于云订阅的限制,无法取得文件。" }, - { - "id": "app.draft.update.app_error", - "translation": "无法更新草稿。" - }, { "id": "app.draft.save.app_error", "translation": "无法保存草稿。" @@ -9859,130 +9803,6 @@ "id": "api.command_templates.unsupported.app_error", "translation": "您的设备不支持模板命令。" }, - { - "id": "worktemplate.product_teams.sprint_planning.integration", - "translation": "项目面板可以使迭代计划前所未来的容易。频道可以用来对话和保证问题的被关注。迭代计划面板可以使所以有本周对任务的关注,回顾面板让团队作为一个整体不断改进。" - }, - { - "id": "worktemplate.product_teams.sprint_planning.channel", - "translation": "项目面板可以使迭代计划前所未来的容易。频道可以用来对话和保证问题的被关注。迭代计划面板可以使所以有本周对任务的关注,回顾面板让团队作为一个整体不断改进。" - }, - { - "id": "worktemplate.product_teams.sprint_planning.board", - "translation": "项目面板可以使迭代计划前所未来的容易。频道可以用来对话和保证问题的被关注。迭代计划面板可以使所以有本周对任务的关注,回顾面板让团队作为一个整体不断改进。" - }, - { - "id": "worktemplate.product_teams.product_roadmap.channel", - "translation": "这里描述了为什么需要面板" - }, - { - "id": "worktemplate.product_teams.product_roadmap.board", - "translation": "这里描述了为什么需要面板" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.integration", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.channel", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.product_teams.goals_and_okrs.board", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.product_teams.feature_release.description.playbook", - "translation": "通过建立透明的跨越整个研发团队的工作流程确保你的功能开发过程完美流畅。" - }, - { - "id": "worktemplate.product_teams.feature_release.description.integration", - "translation": "在你的频道通过集成Jira和Gtihub机器人提高效率。这些会自动下载安装。" - }, - { - "id": "worktemplate.product_teams.feature_release.description.channel", - "translation": "Boards,Playbooks和应用Bot可以很容易地接入功能发布频道并且和你的团队进行相关互动和讨论。" - }, - { - "id": "worktemplate.product_teams.feature_release.description.board", - "translation": "使用我们的会议日程模板安排像站立会议这样的定期会议,使用我们的项目任务面板在一路上管理任务的进度。" - }, - { - "id": "worktemplate.product_teams.bug_bash.playbook", - "translation": "把事情安排好并且干掉此项目里的所有bug!用包含的Playbook, Board, and Channel推动项目并评估进度。" - }, - { - "id": "worktemplate.product_teams.bug_bash.integration", - "translation": "把事情安排好并且干掉此项目里的所有bug!用包含的Playbook, Board, and Channel推动项目并评估进度。" - }, - { - "id": "worktemplate.product_teams.bug_bash.channel", - "translation": "把事情安排好并且干掉此项目里的所有bug!用包含的Playbook, Board, and Channel推动项目并评估进度。" - }, - { - "id": "worktemplate.product_teams.bug_bash.board", - "translation": "把事情安排好并且干掉此项目里的所有bug!用包含的Playbook, Board, and Channel推动项目并评估进度。" - }, - { - "id": "worktemplate.leadership.goals_and_okrs.integration", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.leadership.goals_and_okrs.channel", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.leadership.goals_and_okrs.board", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.devops.product_release.playbook", - "translation": "不要丢失此项目的任何一个步骤。从Playbook的检验清单分离成任务部署并达到项目面板的里程碑。用频道来保持所有人对事情的理解一致。" - }, - { - "id": "worktemplate.devops.product_release.channel", - "translation": "不要丢失此项目的任何一个步骤。从Playbook的检验清单分离成任务部署并达到项目面板的里程碑。用频道来保持所有人对事情的理解一致。" - }, - { - "id": "worktemplate.devops.product_release.board", - "translation": "不要丢失此项目的任何一个步骤。从Playbook的检验清单分离成任务部署并达到项目面板的里程碑。用频道来保持所有人对事情的理解一致。" - }, - { - "id": "worktemplate.devops.incident_resolution.description.playbook", - "translation": "当到处都是问题的时候,有一个能够确保一切都尽快回归正确的可重复流程是关键。此项目使用Mattermost提供的一切功能保证火被一步步扑灭以及利益相关者被告知。" - }, - { - "id": "worktemplate.devops.incident_resolution.description.channel", - "translation": "当到处都是问题的时候,有一个能够确保一切都尽快回归正确的可重复流程是关键。此项目使用Mattermost提供的一切功能保证火被一步步扑灭以及利益相关者被告知。" - }, - { - "id": "worktemplate.devops.incident_resolution.description.board", - "translation": "当到处都是问题的时候,有一个能够确保一切都尽快回归正确的可重复流程是关键。此项目使用Mattermost提供的一切功能保证火被一步步扑灭以及利益相关者被告知。" - }, - { - "id": "worktemplate.companywide.goals_and_okrs.integration", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.companywide.goals_and_okrs.channel", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.companywide.goals_and_okrs.board", - "translation": "清晰的目标对团队的成功至关重要,在此项目里你可以在文档里写下团队的目标和OKR,并在相关的频道里会有消息提醒。" - }, - { - "id": "worktemplate.companywide.create_project.integration", - "translation": "使用此项目面板设计一个路线图,并在产生的频道里就相应话题进行探讨合作。" - }, - { - "id": "worktemplate.companywide.create_project.channel", - "translation": "使用此项目面板设计一个路线图,并在产生的频道里就相应话题进行探讨合作。" - }, - { - "id": "worktemplate.companywide.create_project.board", - "translation": "使用此项目面板设计一个路线图,并在产生的频道里就相应话题进行探讨合作。" - }, { "id": "app.user.run.update_status.title", "translation": "状态更新" @@ -10206,5 +10026,69 @@ { "id": "api.license.true_up_review.create_error", "translation": "无法创建真实的状态记录" + }, + { + "id": "api.license.request-trial.bad-request.business-email", + "translation": "无效的商务试用邮箱" + }, + { + "id": "worktemplate.product_teams.feature_release.description.integration", + "translation": "通过集成大多您使用过的工具(比如 GitHub)在您的频道里提高生产力,实现你的功能发布。这些工具将为你下载。" + }, + { + "id": "worktemplate.product_teams.feature_release.description.channel", + "translation": "在一个频道与你的团队讨论任何的发布障碍和变动,并很容易的与你的面板,Playbook或者其他的集成功能连接。" + }, + { + "id": "worktemplate.product_teams.feature_release.description.board", + "translation": "使用会议安排面板保持会议一起正常。使用项目任务面板管理你的工作量。" + }, + { + "id": "worktemplate.product_teams.bug_bash.integration", + "translation": "通过集成大多您使用过的工具(比如Jira)在您的频道里提高生产力,跟踪你的Bug解决过程。这些工具将为你下载。" + }, + { + "id": "worktemplate.leadership.goals_and_okrs.integration", + "translation": "通过集成大多您使用过的工具(比如 Zoom)在您的频道里提高生产力,实行更容易的协作。这些工具将为你下载。" + }, + { + "id": "worktemplate.leadership.goals_and_okrs.channel", + "translation": "和您的团队讨论目标和进度,以异步或者同步的方式,在同一个频道中保持跟上任何发生的变化。" + }, + { + "id": "worktemplate.leadership.goals_and_okrs.board", + "translation": "使用目标和OKR面板跟踪您的团队进度向组织目标推进。使用会议安排面板保持会议一起正常。" + }, + { + "id": "worktemplate.devops.incident_resolution.description.channel", + "translation": "在一单独频道与您的团队讨论优先级、添加利益相关者,提供更新,向解决的方向努力。" + }, + { + "id": "worktemplate.devops.incident_resolution.description.board", + "translation": "使用事故解决面板来实现重复性的流程和分配跨越整个团队的任务。" + }, + { + "id": "worktemplate.companywide.goals_and_okrs.integration", + "translation": "通过集成大多您使用过的工具(比如 Zoom)在您的频道里提高生产力,实行更容易的协作。这些工具将为你下载。" + }, + { + "id": "worktemplate.companywide.goals_and_okrs.channel", + "translation": "和您的团队讨论目标和进度,以异步或者同步的方式,在同一个频道中保持跟上任何发生的变化。" + }, + { + "id": "worktemplate.companywide.goals_and_okrs.board", + "translation": "使用目标和OKR面板跟踪您的团队进度向组织目标推进。使用会议安排面板保持会议一起正常。" + }, + { + "id": "worktemplate.companywide.create_project.integration", + "translation": "通过集成大多您使用过的工具在您的频道里提高生产力。这些工具将为你下载。" + }, + { + "id": "worktemplate.companywide.create_project.channel", + "translation": "与您的团队在一个协作频道里讨论新的项目并且决定如何你们将如此组织。" + }, + { + "id": "worktemplate.companywide.create_project.board", + "translation": "使用看板面板定义和跟踪您的项目任务列表和进度。" } ] diff --git a/server/i18n/zh-TW.json b/server/i18n/zh-TW.json index 780ee87973..e677cec709 100644 --- a/server/i18n/zh-TW.json +++ b/server/i18n/zh-TW.json @@ -7247,10 +7247,6 @@ "id": "api.back_to_app", "translation": "返回至 {{.SiteName}}" }, - { - "id": "Channels", - "translation": "頻道" - }, { "id": "api.cloud.app_error", "translation": "雲端 API 請求時發生內部錯誤。" diff --git a/webapp/channels/.eslintignore b/webapp/channels/.eslintignore index 9c3001349d..468bb89127 100644 --- a/webapp/channels/.eslintignore +++ b/webapp/channels/.eslintignore @@ -1,4 +1,3 @@ -e2e/results node_modules dist lib diff --git a/webapp/channels/.eslintrc.json b/webapp/channels/.eslintrc.json index 048bb4b85e..7a3865a6db 100644 --- a/webapp/channels/.eslintrc.json +++ b/webapp/channels/.eslintrc.json @@ -2,22 +2,19 @@ "root": true, "extends": [ "plugin:mattermost/react", - "plugin:cypress/recommended", "plugin:react-hooks/recommended" ], "plugins": [ "@babel/eslint-plugin", "mattermost", "import", - "cypress", "no-only-tests", "@typescript-eslint", "formatjs" ], "parser": "@typescript-eslint/parser", "env": { - "jest": true, - "cypress/globals": true + "jest": true }, "settings": { "import/resolver": "webpack", @@ -166,17 +163,6 @@ "no-process-env": 0, "prefer-arrow-callback": 0 } - }, - { - "files": ["e2e/**"], - "rules": { - "@babel/no-unused-expressions": 0, - "func-names": 0, - "import/no-unresolved": 0, - "max-nested-callbacks": 0, - "no-process-env": 0, - "no-unused-expressions": 0 - } } ] } diff --git a/webapp/channels/.gobom.json b/webapp/channels/.gobom.json index 3279174525..4ec47b22c6 100644 --- a/webapp/channels/.gobom.json +++ b/webapp/channels/.gobom.json @@ -3,7 +3,6 @@ "properties": { "GomodMainOnly": true, "GomodExcludes": "_tests/|[-_]generators?$|^plugin/checker$", - "NpmExcludes": "/e2e$", "GradlePath": "./gradlew:../gradlew" }, "filters": [ diff --git a/webapp/channels/Makefile b/webapp/channels/Makefile index 889907e624..aa20ad2ca2 100644 --- a/webapp/channels/Makefile +++ b/webapp/channels/Makefile @@ -6,58 +6,6 @@ export NODE_OPTIONS=--max-old-space-size=4096 i18n-extract: ## Extract strings for translation from the source code npm run i18n-extract -e2e/playwright/node_modules: - @echo Install Playwright and its dependencies - cd e2e/playwright && npm install - -.PHONY: e2e-test -e2e-test: - @echo E2E: Running mattermost-mysql-e2e - @if [ $(shell docker ps -a | grep -ci mattermost-mysql-e2e) -eq 0 ]; then \ - echo starting mattermost-mysql-e2e; \ - docker run --name mattermost-mysql-e2e -p 35476:3306 -e MYSQL_ROOT_PASSWORD=mostest \ - -e MYSQL_USER=mmuser -e MYSQL_PASSWORD=mostest -e MYSQL_DATABASE=mattermost_test -d mysql:5.7 > /dev/null; \ - elif [ $(shell docker ps | grep -ci mattermost-mysql-e2e) -eq 0 ]; then \ - echo restarting mattermost-mysql-e2e; \ - docker start mattermost-mysql-e2e > /dev/null; \ - fi - - cd $(BUILD_SERVER_DIR) && [[ -f config/config.json ]] && \ - cp config/config.json config/config-backup.json && make config-reset || \ - echo "config.json not found" && make config-reset - - @echo E2E: Starting the server - cd $(BUILD_SERVER_DIR) && $(MAKE) run - - @echo E2E: Generating test data - cd $(BUILD_SERVER_DIR) && $(MAKE) test-data - - @echo E2E: Running end-to-end testing - cd e2e && npm install && npm run cypress:run - - @echo E2E: Stoppping the server - cd $(BUILD_SERVER_DIR) && $(MAKE) stop - - @echo E2E: stopping mattermost-mysql-e2e - docker stop mattermost-mysql-e2e > /dev/null - - cd $(BUILD_SERVER_DIR) && [[ -f config/config-backup.json ]] && \ - cp config/config-backup.json config/config.json && echo "revert local config.json" || \ - echo "config-backup.json not found" && sed -i'' -e 's|"DataSource": ".*"|"DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s"|g' config/config.json - - @echo E2E: Tests completed - -.PHONY: clean-e2e -clean-e2e: - @if [ $(shell docker ps -a | grep -ci mattermost-mysql-e2e) -eq 1 ]; then \ - echo stopping mattermost-mysql-e2e; \ - docker stop mattermost-mysql-e2e > /dev/null; \ - fi - - cd $(BUILD_SERVER_DIR) && [[ -f config/config-backup.json ]] && \ - cp config/config-backup.json config/config.json && echo "revert local config.json" || \ - echo "config-backup.json not found" && sed -i'' -e 's|"DataSource": ".*"|"DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s"|g' config/config.json - .PHONY: emojis emojis: ## Creates emoji JSON, JSX and Go files and extracts emoji images from the system font SERVER_DIR=$(BUILD_SERVER_DIR) npm run make-emojis diff --git a/webapp/channels/jest.config.js b/webapp/channels/jest.config.js index 123f6d7e57..01d80ffe8d 100644 --- a/webapp/channels/jest.config.js +++ b/webapp/channels/jest.config.js @@ -5,7 +5,7 @@ const config = { snapshotSerializers: ['enzyme-to-json/serializer'], - testPathIgnorePatterns: ['/node_modules/', '/e2e/'], + testPathIgnorePatterns: ['/node_modules/'], clearMocks: true, collectCoverageFrom: [ 'actions/src/**/*.{js,jsx,ts,tsx}', @@ -17,7 +17,6 @@ const config = { 'selectors/src/**/*.{js,jsx,ts,tsx}', 'stores/src/**/*.{js,jsx,ts,tsx}', 'utils/src/**/*.{js,jsx,ts,tsx}', - '!e2e/**', ], coverageReporters: ['lcov', 'text-summary'], moduleNameMapper: { diff --git a/webapp/channels/package.json b/webapp/channels/package.json index 7f33c6b05f..830cbfae08 100644 --- a/webapp/channels/package.json +++ b/webapp/channels/package.json @@ -169,7 +169,6 @@ "enzyme-to-json": "3.6.2", "eslint": "7.32.0", "eslint-import-resolver-webpack": "0.13.2", - "eslint-plugin-cypress": "2.11.3", "eslint-plugin-formatjs": "4.3.4", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.23.4", diff --git a/webapp/channels/src/actions/command.ts b/webapp/channels/src/actions/command.ts index 2b89fae193..62195ac6dc 100644 --- a/webapp/channels/src/actions/command.ts +++ b/webapp/channels/src/actions/command.ts @@ -35,7 +35,7 @@ import KeyboardShortcutsModal from 'components/keyboard_shortcuts/keyboard_short import {GlobalState} from 'types/store'; import {t} from 'utils/i18n'; -import MarketplaceModal from 'components/plugin_marketplace'; +import MarketplaceModal from 'components/plugin_marketplace/marketplace_modal'; import WorkTemplateModal from 'components/work_templates'; import {haveICurrentTeamPermission} from 'mattermost-redux/selectors/entities/roles'; import {Permissions} from 'mattermost-redux/constants'; diff --git a/webapp/channels/src/components/__snapshots__/generic_modal.test.tsx.snap b/webapp/channels/src/components/__snapshots__/generic_modal.test.tsx.snap index 95b80cb2da..cf1462ee4a 100644 --- a/webapp/channels/src/components/__snapshots__/generic_modal.test.tsx.snap +++ b/webapp/channels/src/components/__snapshots__/generic_modal.test.tsx.snap @@ -59,7 +59,7 @@ exports[`components/GenericModal should match snapshot for base case 1`] = `
@@ -126,11 +126,12 @@ exports[`components/GenericModal should match snapshot with both buttons 1`] = `
{ className={'no-thanks-link style-link'} > diff --git a/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx b/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx index 4be3ffc4c6..8d963918b4 100644 --- a/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx +++ b/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx @@ -65,7 +65,7 @@ const useGetTaskDetails = () => { svg: Newspaper, message: formatMessage({ id: 'onboardingTask.checklist.task_create_from_work_template', - defaultMessage: 'Create from a template - set up a channel with linked boards and playbooks.', + defaultMessage: 'Create from a template', }), }, [OnboardingTasksName.CHANNELS_TOUR]: { diff --git a/webapp/channels/src/components/payment_form/stripe.ts b/webapp/channels/src/components/payment_form/stripe.ts index 9599ad5936..3ba494b3db 100644 --- a/webapp/channels/src/components/payment_form/stripe.ts +++ b/webapp/channels/src/components/payment_form/stripe.ts @@ -25,4 +25,5 @@ function devConfirmCardSetup(confirmCardSetup: ConfirmCardSetupType): ConfirmCar export const getConfirmCardSetup = (isDevMode?: boolean) => (isDevMode ? devConfirmCardSetup : prodConfirmCardSetup); export const STRIPE_CSS_SRC = 'https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,600i&display=swap'; -export const STRIPE_PUBLIC_KEY = 'pk_test_ttEpW6dCHksKyfAFzh6MvgBj'; +//eslint-disable-next-line no-process-env +export const STRIPE_PUBLIC_KEY = process.env.STRIPE_PUBLIC_KEY || 'pk_test_ttEpW6dCHksKyfAFzh6MvgBj'; diff --git a/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap b/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap index 480f4bda50..a280b3876e 100644 --- a/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap +++ b/webapp/channels/src/components/plugin_marketplace/__snapshots__/marketplace_modal.test.tsx.snap @@ -1,442 +1,816 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`components/marketplace/ AllListing should render with no plugins 1`] = ` - -`; - -exports[`components/marketplace/ AllListing should render with one plugin 1`] = ` - -`; - -exports[`components/marketplace/ AllListing should render with plugins 1`] = ` - -`; - -exports[`components/marketplace/ InstalledPlugins should render with multiple plugins 1`] = ` - -`; - -exports[`components/marketplace/ InstalledPlugins should render with no plugins 1`] = ` -
-
- -
- -
- -
-`; - -exports[`components/marketplace/ InstalledPlugins should render with one plugin 1`] = ` - -`; - -exports[`components/marketplace/ MarketplaceModal should render with error banner 1`] = ` - -
-
- +

+ App Marketplace +

+
+ } + inputSize="large" + name="searchMarketplaceTextbox" + onChange={[Function]} + onClear={[Function]} + placeholder="Search marketplace" + type="text" + useLegend={false} + value="" /> -
-
- - - + + `; -exports[`components/marketplace/ MarketplaceModal should render with no plugins installed 1`] = ` - - + +
+ + + + + + + + +
+ + + +
- - + + `; -exports[`components/marketplace/ MarketplaceModal should render with plugins installed 1`] = ` - - + + + + + - - + + +`; + +exports[`components/marketplace/ should render with plugins available 1`] = ` + + +
+ +
+

+ App Marketplace +

+
+ + } + inputSize="large" + name="searchMarketplaceTextbox" + onChange={[Function]} + onClear={[Function]} + placeholder="Search marketplace" + type="text" + useLegend={false} + value="" + /> +
+ +
+ + + + + + + + +
+
+ + + +
+
+
+`; + +exports[`components/marketplace/ should render with plugins installed 1`] = ` + + +
+ +
+

+ App Marketplace +

+
+ + } + inputSize="large" + name="searchMarketplaceTextbox" + onChange={[Function]} + onClear={[Function]} + placeholder="Search marketplace" + type="text" + useLegend={false} + value="" + /> +
+ +
+ + + + + + + + +
+
+ + + +
+
+
`; diff --git a/webapp/channels/src/components/plugin_marketplace/index.ts b/webapp/channels/src/components/plugin_marketplace/index.ts deleted file mode 100644 index b004efae32..0000000000 --- a/webapp/channels/src/components/plugin_marketplace/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {connect} from 'react-redux'; -import {bindActionCreators, Dispatch, ActionCreatorsMapObject} from 'redux'; - -import {GenericAction, ActionFunc} from 'mattermost-redux/types/actions'; - -import {GlobalState} from 'types/store'; -import {getListing, getInstalledListing} from 'selectors/views/marketplace'; -import {setFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/actions/general'; -import {getPluginStatuses} from 'mattermost-redux/actions/admin'; -import {getFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/selectors/entities/general'; - -import {makeAsyncComponent} from 'components/async_load'; - -import {isModalOpen} from 'selectors/views/modals'; -import {ModalIdentifiers} from 'utils/constants'; -import {getSiteURL} from 'utils/url'; - -import {closeModal} from 'actions/views/modals'; -import {fetchListing, filterListing} from 'actions/marketplace'; - -const MarketplaceModal = makeAsyncComponent('MarketplaceModal', React.lazy(() => import('./marketplace_modal'))); - -function mapStateToProps(state: GlobalState) { - return { - show: isModalOpen(state, ModalIdentifiers.PLUGIN_MARKETPLACE), - listing: getListing(state), - installedListing: getInstalledListing(state), - siteURL: getSiteURL(), - pluginStatuses: state.entities.admin.pluginStatuses, - firstAdminVisitMarketplaceStatus: getFirstAdminVisitMarketplaceStatus(state), - }; -} - -type Actions = { - closeModal(): void; - fetchListing(localOnly?: boolean): Promise<{error?: Error}>; - filterListing(filter: string): Promise<{error?: Error}>; - setFirstAdminVisitMarketplaceStatus(): Promise; - getPluginStatuses(): Promise; -} - -function mapDispatchToProps(dispatch: Dispatch) { - return { - actions: bindActionCreators, Actions>({ - closeModal: () => closeModal(ModalIdentifiers.PLUGIN_MARKETPLACE), - fetchListing, - filterListing, - setFirstAdminVisitMarketplaceStatus, - getPluginStatuses, - }, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(MarketplaceModal); diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_item/marketplace_item_app/__snapshots__/marketplace_item_app.test.tsx.snap b/webapp/channels/src/components/plugin_marketplace/marketplace_item/marketplace_item_app/__snapshots__/marketplace_item_app.test.tsx.snap index 1ec6de8134..7fb956f716 100644 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_item/marketplace_item_app/__snapshots__/marketplace_item_app.test.tsx.snap +++ b/webapp/channels/src/components/plugin_marketplace/marketplace_item/marketplace_item_app/__snapshots__/marketplace_item_app.test.tsx.snap @@ -11,7 +11,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render 1`] = ` } button={ + )} + + ) : ( +
+ {pageItems} +
+ )); +}; + +export default MarketplaceList; diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/__snapshots__/navigation_row.test.tsx.snap b/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/__snapshots__/navigation_row.test.tsx.snap deleted file mode 100644 index f08718159d..0000000000 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/__snapshots__/navigation_row.test.tsx.snap +++ /dev/null @@ -1,157 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/marketplace/navigation_row should not render any buttons 1`] = ` -
-
-
- -
-
-
-`; - -exports[`components/marketplace/navigation_row should render next and previous buttons 1`] = ` -
-
- -
-
- -
-
- -
-
-`; - -exports[`components/marketplace/navigation_row should render only next button 1`] = ` -
-
-
- -
-
- -
-
-`; - -exports[`components/marketplace/navigation_row should render only previous button 1`] = ` -
-
- -
-
- -
-
-
-`; diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/index.ts b/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/index.ts deleted file mode 100644 index 320c07dca9..0000000000 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {connect} from 'react-redux'; - -import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; -import {GlobalState} from '@mattermost/types/store'; - -import NavigationRow from './navigation_row'; - -function mapStateToProps(state: GlobalState) { - return { - theme: getTheme(state), - }; -} - -export default connect(mapStateToProps)(NavigationRow); diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_button.tsx b/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_button.tsx deleted file mode 100644 index 8433739030..0000000000 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_button.tsx +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {FormattedMessage} from 'react-intl'; - -type NavigationButtonProps = { - onClick: (event: React.MouseEvent) => void; - messageId: string; - defaultMessage: string; -}; - -export default class NavigationButton extends React.PureComponent { - onClick = (event: React.MouseEvent): void => { - event.preventDefault(); - this.props.onClick(event); - }; - - render(): JSX.Element { - const {onClick, messageId, defaultMessage} = this.props; - return ( - - ); - } -} diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_row.test.tsx b/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_row.test.tsx deleted file mode 100644 index 4633263c55..0000000000 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_row.test.tsx +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {shallow} from 'enzyme'; - -import {Theme} from 'mattermost-redux/selectors/entities/preferences'; - -import NavigationRow, {NavigationRowProps} from './navigation_row'; - -describe('components/marketplace/navigation_row', () => { - const baseProps: NavigationRowProps = { - page: 0, - total: 32, - maximumPerPage: 15, - onNextPageButtonClick: jest.fn(), - onPreviousPageButtonClick: jest.fn(), - theme: {centerChannelColor: '#fff'} as Theme, - }; - - it('should render only next button', () => { - const wrapper = shallow( - , - ); - - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('NavigationButton')).toHaveLength(1); - - wrapper.find('NavigationButton').simulate('click', {preventDefault: jest.fn}); - - expect(wrapper.instance().props.onNextPageButtonClick).toHaveBeenCalledTimes(1); - expect(wrapper.instance().props.onPreviousPageButtonClick).toHaveBeenCalledTimes(0); - }); - - it('should render next and previous buttons', () => { - const props = {...baseProps, page: 1}; - const wrapper = shallow( - , - ); - - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('NavigationButton')).toHaveLength(2); - - wrapper.find('NavigationButton').at(0).simulate('click', {preventDefault: jest.fn}); - wrapper.find('NavigationButton').at(1).simulate('click', {preventDefault: jest.fn}); - - expect(wrapper.instance().props.onNextPageButtonClick).toHaveBeenCalledTimes(1); - expect(wrapper.instance().props.onPreviousPageButtonClick).toHaveBeenCalledTimes(1); - }); - - it('should render only previous button', () => { - const props = {...baseProps, page: 2}; - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('NavigationButton')).toHaveLength(1); - - wrapper.find('NavigationButton').simulate('click', {preventDefault: jest.fn}); - - expect(wrapper.instance().props.onNextPageButtonClick).toHaveBeenCalledTimes(0); - expect(wrapper.instance().props.onPreviousPageButtonClick).toHaveBeenCalledTimes(1); - }); - - it('should not render any buttons', () => { - const props = {...baseProps, page: 0, total: 15}; - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('NavigationButton')).toHaveLength(0); - }); -}); diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_row.tsx b/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_row.tsx deleted file mode 100644 index 98cff683d0..0000000000 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_list/navigation_row/navigation_row.tsx +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {FormattedMessage} from 'react-intl'; - -import {Theme} from 'mattermost-redux/selectors/entities/preferences'; -import {changeOpacity, makeStyleFromTheme} from 'mattermost-redux/utils/theme_utils'; - -import NavigationButton from './navigation_button'; - -export type NavigationRowProps = { - page: number; - total: number; - maximumPerPage: number; - onNextPageButtonClick: (event: React.MouseEvent) => void; - onPreviousPageButtonClick: (event: React.MouseEvent) => void; - theme: Theme; -}; - -export default class NavigationRow extends React.PureComponent { - canShowNextButton = (): boolean => { - const {page, maximumPerPage, total} = this.props; - const totalPages = Math.trunc((total - 1) / maximumPerPage); - - return totalPages > page; - }; - - renderCount = (): JSX.Element => { - const {page, total, maximumPerPage} = this.props; - const startCount = page * maximumPerPage; - const endCount = Math.min(startCount + maximumPerPage, total); - - return ( - - ); - }; - - render(): JSX.Element { - const style = getStyle(this.props.theme); - - return ( -
-
- {(this.props.page > 0) && ( - - )} -
-
- {this.renderCount()} -
-
- {this.canShowNextButton() && ( - - )} -
-
- ); - } -} - -const getStyle = makeStyleFromTheme((theme) => { - return { - count: { - color: changeOpacity(theme.centerChannelColor, 0.6), - }, - }; -}); diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.scss b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.scss index f2f3dec629..5a0495c0fc 100644 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.scss +++ b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.scss @@ -1,21 +1,11 @@ @import 'utils/variables'; +@import 'utils/mixins'; -.modal-marketplace { - display: flex; - width: 100%; - height: 100%; - flex-direction: column; - align-items: center; - padding-top: 50px; - color: var(--center-channel-color); - font-size: 16px; - - @media (max-width: 768px) { - padding-right: 15px; - padding-left: 15px; - } +.marketplace-modal { + width: 800px; div.navigation-row { + overflow: unset; margin-top: 10px; div { @@ -29,7 +19,7 @@ } div.count { - padding-top: 8px; + padding: 9px 15px; } } @@ -45,33 +35,47 @@ } .nav-tabs { - font-size: 14px; + padding: 0 32px; + margin: 0 0 8px; - > li { - > a { - padding: 10px 16px; + li { + margin-right: 0; + + a { + padding: 13px 12px; + border: none; background: transparent; + color: rgba(var(--center-channel-color-rgb), 0.64); + font-size: 14px; + font-weight: 600; + line-height: 20px; transition: all 0.15s ease; &:hover, &:active, - &:focus { + &:focus, + &:focus-within { + border: none; background: transparent; + border-radius: none; color: var(--center-channel-color); } } - &.active > a { - color: var(--center-channel-color); + &.active { + border-bottom: 2px solid var(--denim-button-bg); + + a { + color: var(--denim-button-bg); + } + } + + &:not(:first-child) { + margin-left: 8px; } } } - h1 { - margin: 8px 0 24px; - font-size: 28px; - } - h2 { font-weight: 300; @@ -81,8 +85,14 @@ } .more-modal__list { + height: 390px; + margin: 0 6px 8px 12px; + overflow-y: scroll; + .more-modal__row { - align-items: normal; + min-height: 80px; + padding: 16px 20px; + border-bottom: none; .marketplace__tag { margin-left: 6px; @@ -94,25 +104,91 @@ margin: 10px 10px 0 0; font-size: 0.9em; } + + .more-modal__details { + padding-left: 16px; + + .more-modal__row--link { + color: var(--center-channel-color); + font-size: 16px; + font-weight: 600; + line-height: 24px; + } + + .more-modal__description { + margin: 2px 0 0; + color: rgba(var(--center-channel-color-rgb), 0.64); + font-size: 14px; + font-weight: 400; + line-height: 20px; + } + } + + .more-modal__actions { + padding-left: 16px; + margin: 0; + + .plugin-configure, + .app-installed { + @include secondary-button; + @include button-medium; + } + + .plugin-install, + .app-install { + @include primary-button; + @include button-medium; + } + + a { + &:hover, + &:focus { + text-decoration: none; + } + } + } + + &:hover, + &:focus, + &:focus-within { + background-color: rgba(var(--center-channel-color-rgb), 0.08); + } } - .more-modal__description { - margin: 2px 0 0; - font-size: 0.9em; - } + .icon__plugin { + display: flex; + height: 48px; + flex: 0 0 48px; + align-items: center; + justify-content: center; + background-color: $white; + border-radius: 50%; - padding-bottom: 80px; + svg, + img { + width: 48px; + height: 48px; + } + + svg { + fill: var(--button-bg); + } + + img { + border-radius: 4px; + } + } } - .search_input { - width: 720px; - height: 40px; - flex: 1; - margin-top: 28px; - margin-right: 16px; - margin-bottom: 20px; - margin-left: 16px; - box-shadow: none; + .marketplace-modal-search { + padding: 24px 0 0; + + .search_input { + width: 100%; + border: 0 !important; + border-radius: 0 !important; + box-shadow: none; + } } .btn { @@ -130,8 +206,9 @@ .tabs { display: flex; - width: 720px; + width: 100%; flex-direction: column; + margin-top: 12px; } .subtitle { @@ -160,67 +237,41 @@ } } - .icon__plugin { + .no_plugins { display: flex; - height: 42px; - flex: 0 0 42px; + height: 390px; + flex-flow: column; align-items: center; justify-content: center; - margin-right: 4px; - border-radius: 50%; + margin-bottom: 8px; - svg { - width: 32px; - height: 32px; - fill: var(--button-bg); + &__message { + margin-top: 20px; + color: var(--center-channel-color); + font-size: 20px; + font-weight: 600; + line-height: 28px; } - } - .icon__plugin--background { - padding: 6px; - background-color: $white; + &__action { + @include primary-button; + @include button-medium; - svg { - width: 24px; - height: 24px; + margin-top: 30px; } - } - .no_plugins_div { - text-align: center; + .icon__plugin { + svg { + fill: var(--button-bg); + } + } } .item_error { - background-color: rgba(var(--error-text-rgb), 0.08); + background-color: rgba(var(--error-text-color-rgb), 0.08); } - .error_text { - color: var(--error-text); - opacity: 1; - } -} - -.error-bar { - position: fixed; - z-index: 8; - top: 0; - overflow: hidden; - width: 100%; - min-height: $announcement-bar-height; - max-height: $announcement-bar-height; - padding: 5px 30px; - background-color: var(--center-channel-bg); - color: var(--error-text); - - .error-bar__content { - position: absolute; - top: 0; - left: 0; - display: flex; - width: 100%; - height: 100%; - align-items: center; - justify-content: center; - background-color: rgba(var(--error-text-rgb), 0.12); + .loading { + height: 390px; } } diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx index 3e2938603c..c4c2181182 100644 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx +++ b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.test.tsx @@ -5,19 +5,21 @@ import React from 'react'; import {shallow} from 'enzyme'; import {AuthorType, MarketplacePlugin, ReleaseStage} from '@mattermost/types/marketplace'; -import type {PluginStatusRedux} from '@mattermost/types/plugins'; -import {trackEvent} from 'actions/telemetry_actions.jsx'; +import {ActionFunc} from 'mattermost-redux/types/actions'; -import MarketplaceModal, {AllListing, InstalledListing, MarketplaceModalProps} from './marketplace_modal'; +import {GlobalState} from 'types/store'; +import {ModalIdentifiers} from 'utils/constants'; -jest.mock('actions/telemetry_actions.jsx', () => { - const original = jest.requireActual('actions/telemetry_actions.jsx'); - return { - ...original, - trackEvent: jest.fn(), - }; -}); +import MarketplaceModal, {OpenedFromType} from './marketplace_modal'; + +let mockState: GlobalState; + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux') as typeof import('react-redux'), + useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState), + useDispatch: jest.fn(() => (action: ActionFunc) => action), +})); describe('components/marketplace/', () => { const samplePlugin: MarketplacePlugin = { @@ -52,161 +54,106 @@ describe('components/marketplace/', () => { installed_version: '1.0.3', }; - describe('AllListing', () => { - it('should render with no plugins', () => { - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - }); + const defaultProps = { + openedFrom: 'actions_menu' as OpenedFromType, + }; - it('should render with one plugin', () => { - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - }); - - it('should render with plugins', () => { - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - }); - }); - - describe('InstalledPlugins', () => { - const baseProps = { - changeTab: jest.fn(), - }; - - it('should render with no plugins', () => { - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - }); - - it('should render with one plugin', () => { - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - }); - - it('should render with multiple plugins', () => { - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - }); - }); - - describe('MarketplaceModal', () => { - const baseProps: MarketplaceModalProps = { - show: true, - listing: [samplePlugin], - installedListing: [], - pluginStatuses: {}, - siteURL: 'http://example.com', - firstAdminVisitMarketplaceStatus: false, - openedFrom: 'actions_menu', - actions: { - closeModal: jest.fn(), - fetchListing: jest.fn(() => { - return Promise.resolve({}); - }), - filterListing: jest.fn(() => { - return Promise.resolve({}); - }), - setFirstAdminVisitMarketplaceStatus: jest.fn(), - getPluginStatuses: jest.fn(), + beforeEach(() => { + mockState = { + views: { + modals: { + modalState: { + [ModalIdentifiers.PLUGIN_MARKETPLACE]: { + open: true, + }, + }, + }, + marketplace: { + plugins: [], + apps: [], + }, }, - }; + entities: { + general: { + firstAdminCompleteSetup: false, + }, + admin: { + pluginStatuses: {}, + }, + }, + } as unknown as GlobalState; + }); - test('should render with no plugins installed', () => { - const wrapper = shallow( - , - ); - expect(wrapper).toMatchSnapshot(); - }); + test('should render default', () => { + const wrapper = shallow( + , + ); - test('should render with plugins installed', () => { - const props = { - ...baseProps, - plugins: [ - ...baseProps.listing, - sampleInstalledPlugin, - ], - installedListing: [ - sampleInstalledPlugin, - ], - }; + expect(wrapper.shallow()).toMatchSnapshot(); + }); - const wrapper = shallow( - , - ); + test('should render with no plugins available', () => { + const setState = jest.fn(); + const useStateSpy = jest.spyOn(React, 'useState'); + useStateSpy.mockImplementationOnce(() => [false, setState]); - expect(wrapper).toMatchSnapshot(); - }); + const wrapper = shallow( + , + ); - test('should fetch plugins when plugin status is changed', () => { - const fetchListing = baseProps.actions.fetchListing; - const wrapper = shallow(); + wrapper.update(); - expect(fetchListing).toBeCalledTimes(1); - wrapper.setProps({...baseProps}); - expect(fetchListing).toBeCalledTimes(1); + expect(wrapper.shallow()).toMatchSnapshot(); + }); - const status = { - id: 'test', - } as PluginStatusRedux; - wrapper.setProps({...baseProps, pluginStatuses: {test: status}}); - expect(fetchListing).toBeCalledTimes(2); - }); + test('should render with plugins available', () => { + const setState = jest.fn(); + const useStateSpy = jest.spyOn(React, 'useState'); + useStateSpy.mockImplementationOnce(() => [false, setState]); - test('should render with error banner', () => { - const wrapper = shallow( - , - ); + mockState.views.marketplace.plugins = [ + samplePlugin, + ]; - wrapper.setState({serverError: {name: 'some.error', message: 'Error test'}}); + const wrapper = shallow( + , + ); - expect(wrapper).toMatchSnapshot(); - }); + wrapper.update(); - test('Should call for track event when searching', () => { - const wrapper = shallow( - , - ); + expect(wrapper.shallow()).toMatchSnapshot(); + }); - wrapper.setState({filter: 'nps'}); - wrapper.instance().doSearch(); + test('should render with plugins installed', () => { + const setState = jest.fn(); + const useStateSpy = jest.spyOn(React, 'useState'); + useStateSpy.mockImplementationOnce(() => [false, setState]); - expect(trackEvent).toHaveBeenCalledWith('plugins', 'ui_marketplace_opened', {from: 'actions_menu'}); - expect(trackEvent).toHaveBeenCalledWith('plugins', 'ui_marketplace_search', {filter: 'nps'}); - }); + mockState.views.marketplace.plugins = [ + samplePlugin, + sampleInstalledPlugin, + ]; - test('Should call for opened track event on mount', () => { - const openedFrom = 'actions_menu'; + const wrapper = shallow( + , + ); - shallow( - , - ); + wrapper.update(); - expect(trackEvent).toHaveBeenCalledWith('plugins', 'ui_marketplace_opened', {from: openedFrom}); - }); + expect(wrapper.shallow()).toMatchSnapshot(); + }); + + test('should render with error banner', () => { + const setState = jest.fn(); + const useStateSpy = jest.spyOn(React, 'useState'); + useStateSpy.mockImplementation(() => [true, setState]); + + const wrapper = shallow( + , + ); + + wrapper.update(); + + expect(wrapper.shallow()).toMatchSnapshot(); }); }); diff --git a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx index 59ac7510ba..062727f9e1 100644 --- a/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx +++ b/webapp/channels/src/components/plugin_marketplace/marketplace_modal.tsx @@ -1,283 +1,269 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react'; -import {FormattedMessage} from 'react-intl'; -import debounce from 'lodash/debounce'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {Tabs, Tab, SelectCallback} from 'react-bootstrap'; +import {useIntl} from 'react-intl'; +import {useDispatch, useSelector} from 'react-redux'; +import {Link} from 'react-router-dom'; +import debounce from 'lodash/debounce'; -import {PluginStatusRedux} from '@mattermost/types/plugins'; -import type {MarketplaceApp, MarketplacePlugin} from '@mattermost/types/marketplace'; +import {MagnifyIcon} from '@mattermost/compass-icons/components'; -import FullScreenModal from 'components/widgets/modals/full_screen_modal'; -import RootPortal from 'components/root_portal'; -import QuickInput from 'components/quick_input'; -import LocalizedInput from 'components/localized_input/localized_input'; -import PluginIcon from 'components/widgets/icons/plugin_icon'; -import LoadingScreen from 'components/loading_screen'; -import FormattedMarkdownMessage from 'components/formatted_markdown_message'; +import {FooterPagination} from '@mattermost/components'; +import {getPluginStatuses} from 'mattermost-redux/actions/admin'; +import {setFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/actions/general'; +import {getFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/selectors/entities/general'; +import {ActionResult} from 'mattermost-redux/types/actions'; +import {fetchListing, filterListing} from 'actions/marketplace'; import {trackEvent} from 'actions/telemetry_actions.jsx'; -import {t} from 'utils/i18n'; -import {localizeMessage} from 'utils/utils'; +import {closeModal} from 'actions/views/modals'; + +import GenericModal from 'components/generic_modal'; +import LoadingScreen from 'components/loading_screen'; +import Input, {SIZE} from 'components/widgets/inputs/input/input'; + +import {getListing, getInstalledListing} from 'selectors/views/marketplace'; +import {isModalOpen} from 'selectors/views/modals'; +import {GlobalState} from 'types/store'; +import {ModalIdentifiers} from 'utils/constants'; import './marketplace_modal.scss'; -import MarketplaceList from './marketplace_list/marketplace_list'; + +import MarketplaceList, {ITEMS_PER_PAGE} from './marketplace_list/marketplace_list'; const MarketplaceTabs = { - ALL_LISTING: 'allListing', + ALL_LISTING: 'all', INSTALLED_LISTING: 'installed', }; const SEARCH_TIMEOUT_MILLISECONDS = 200; +const linkConsole = (msg: string) => ( + + {msg} + +); + export type OpenedFromType = 'actions_menu' | 'app_bar' | 'channel_header' | 'command' | 'open_plugin_install_post' | 'product_menu'; -type AllListingProps = { - listing: Array; -}; - -// AllListing renders the contents of the all listing tab. -export const AllListing = ({listing}: AllListingProps): JSX.Element => { - if (listing.length === 0) { - return ( -
-
- -
- -
-
- ); - } - - return ; -}; - -type InstalledListingProps = { - installedItems: Array; - changeTab: SelectCallback; -}; - -// InstalledListing renders the contents of the installed listing tab. -export const InstalledListing = ({installedItems, changeTab}: InstalledListingProps): JSX.Element => { - if (installedItems.length === 0) { - return ( -
-
- -
- -
- -
- ); - } - - return ; -}; - -export type MarketplaceModalProps = { - show: boolean; - listing: Array; - installedListing: Array; - siteURL: string; - pluginStatuses?: Record; - firstAdminVisitMarketplaceStatus: boolean; +type MarketplaceModalProps = { openedFrom: OpenedFromType; - actions: { - closeModal: () => void; - fetchListing(localOnly?: boolean): Promise<{error?: Error}>; - filterListing(filter: string): Promise<{error?: Error}>; - setFirstAdminVisitMarketplaceStatus(): Promise; - getPluginStatuses(): Promise; - }; -}; - -type MarketplaceModalState = { - tabKey: unknown; - loading: boolean; - serverError?: Error; - filter: string; -}; - -// MarketplaceModal is the marketplace modal. -export default class MarketplaceModal extends React.PureComponent { - private filterRef: React.RefObject; - - constructor(props: MarketplaceModalProps) { - super(props); - - this.state = { - tabKey: MarketplaceTabs.ALL_LISTING, - loading: true, - serverError: undefined, - filter: '', - }; - - this.filterRef = React.createRef(); - } - - componentDidMount(): void { - trackEvent('plugins', 'ui_marketplace_opened', {from: this.props.openedFrom}); - - this.fetchListing(); - this.props.actions.getPluginStatuses(); - if (!this.props.firstAdminVisitMarketplaceStatus) { - trackEvent('plugins', 'ui_first_admin_visit_marketplace_status'); - - this.props.actions.setFirstAdminVisitMarketplaceStatus(); - } - - this.filterRef.current?.focus(); - } - - componentDidUpdate(prevProps: MarketplaceModalProps): void { - // Automatically refresh the component when a plugin is installed or uninstalled. - if (this.props.pluginStatuses !== prevProps.pluginStatuses) { - this.fetchListing(); - } - } - - fetchListing = async (): Promise => { - const {error} = await this.props.actions.fetchListing(); - this.setState({loading: false, serverError: error}); - } - - close = (): void => { - trackEvent('plugins', 'ui_marketplace_closed'); - this.props.actions.closeModal(); - } - - changeTab: SelectCallback = (tabKey: any): void => { - this.setState({tabKey}); - } - - onInput = (): void => { - if (this.filterRef.current) { - this.setState({filter: this.filterRef.current.value}); - - this.debouncedSearch(); - } - } - - handleClearSearch = (): void => { - if (this.filterRef.current) { - this.filterRef.current.value = ''; - this.setState({filter: this.filterRef.current.value}, this.doSearch); - } - } - - doSearch = async (): Promise => { - trackEvent('plugins', 'ui_marketplace_search', {filter: this.state.filter}); - - const {error} = await this.props.actions.filterListing(this.state.filter); - - this.setState({serverError: error}); - } - - debouncedSearch = debounce(this.doSearch, SEARCH_TIMEOUT_MILLISECONDS); - - render(): JSX.Element { - const input = ( -
-
- -
-
- ); - - let errorBanner = null; - if (this.state.serverError) { - errorBanner = ( -
-
- -
-
- ); - } - - return ( - - - {errorBanner} - - - - ); - } } + +const MarketplaceModal = ({ + openedFrom, +}: MarketplaceModalProps) => { + const dispatch = useDispatch(); + const {formatMessage} = useIntl(); + const listRef = useRef(null); + + const show = useSelector((state: GlobalState) => isModalOpen(state, ModalIdentifiers.PLUGIN_MARKETPLACE)); + const listing = useSelector(getListing); + const installedListing = useSelector(getInstalledListing); + const pluginStatuses = useSelector((state: GlobalState) => state.entities.admin.pluginStatuses); + const hasFirstAdminVisitedMarketplace = useSelector(getFirstAdminVisitMarketplaceStatus); + + const [tabKey, setTabKey] = useState(MarketplaceTabs.ALL_LISTING); + const [filter, setFilter] = useState(''); + const [page, setPage] = useState(0); + const [hasLoaded, setHasLoaded] = useState(false); + const [loading, setLoading] = React.useState(true); + const [serverError, setServerError] = React.useState(false); + + const doFetchListing = useCallback(async () => { + const {error} = await dispatch(fetchListing()) as ActionResult; + + if (error) { + setServerError(true); + } + + setLoading(false); + }, []); + + const doSearch = useCallback(async () => { + trackEvent('plugins', 'ui_marketplace_search', {filter}); + + const {error} = await dispatch(filterListing(filter)) as ActionResult; + + if (error) { + setServerError(true); + } + }, [filter]); + + const debouncedSearch = debounce(doSearch, SEARCH_TIMEOUT_MILLISECONDS); + + useEffect(() => { + async function doFetch() { + await dispatch(getPluginStatuses()); + await doFetchListing(); + setHasLoaded(true); + } + + trackEvent('plugins', 'ui_marketplace_opened', {from: openedFrom}); + + if (!hasFirstAdminVisitedMarketplace) { + trackEvent('plugins', 'ui_first_admin_visit_marketplace_status'); + dispatch(setFirstAdminVisitMarketplaceStatus()); + } + + doFetch(); + }, []); + + useEffect(() => { + if (hasLoaded) { + doFetchListing(); + } + }, [pluginStatuses]); + + useEffect(() => { + if (hasLoaded) { + debouncedSearch(); + setPage(0); + } + }, [filter]); + + const scrollListToTop = useCallback(() => { + if (listRef.current) { + listRef.current.scrollTop = 0; + } + }, []); + + const handleOnClose = () => { + trackEvent('plugins', 'ui_marketplace_closed'); + dispatch(closeModal(ModalIdentifiers.PLUGIN_MARKETPLACE)); + }; + + const handleChangeTab: SelectCallback = useCallback((tabKey) => { + setTabKey(tabKey); + setPage(0); + scrollListToTop(); + }, [scrollListToTop]); + + const handleOnChange = useCallback((event: React.ChangeEvent) => { + setFilter(event.target.value); + }, []); + + const handleOnClear = useCallback(() => { + setFilter(''); + }, []); + + const handleOnNextPage = useCallback(() => { + setPage(page + 1); + scrollListToTop(); + }, [page, scrollListToTop]); + + const handleOnPreviousPage = useCallback(() => { + setPage(page - 1); + scrollListToTop(); + }, [page, scrollListToTop]); + + const handleNoResultsButtonClick = useCallback(() => { + handleChangeTab(MarketplaceTabs.ALL_LISTING); + }, [handleChangeTab]); + + const getHeaderInput = useCallback(() => ( + } + placeholder={formatMessage({id: 'marketplace_modal.search', defaultMessage: 'Search marketplace'})} + useLegend={false} + autoFocus={true} + clearable={true} + value={filter} + onChange={handleOnChange} + onClear={handleOnClear} + /> + ), [filter, handleOnChange, handleOnClear]); + + const getFooterContent = useCallback(() => ( + + ), [installedListing.length, listing.length, page, handleOnNextPage, handleOnPreviousPage, tabKey]); + + return ( + System Console.', + }, + {linkConsole}, + ) + ) : undefined} + show={show} + compassDesign={true} + bodyPadding={false} + footerDivider={true} + onExited={handleOnClose} + footerContent={getFooterContent()} + headerInput={getHeaderInput()} + > + + + {loading ? ( + + ) : ( + + )} + + + + + + + ); +}; + +export default MarketplaceModal; diff --git a/webapp/channels/src/components/product_notices_modal/__snapshots__/product_notices.test.tsx.snap b/webapp/channels/src/components/product_notices_modal/__snapshots__/product_notices.test.tsx.snap index abebd8539b..5252df431b 100644 --- a/webapp/channels/src/components/product_notices_modal/__snapshots__/product_notices.test.tsx.snap +++ b/webapp/channels/src/components/product_notices_modal/__snapshots__/product_notices.test.tsx.snap @@ -4,6 +4,7 @@ exports[`ProductNoticesModal Match snapshot for single notice 1`] = `
  • `; + +exports[`components/new_channel_modal should match snapshot when user has only join channel permissions 1`] = ` + +`; diff --git a/webapp/channels/src/components/sidebar/add_channel_dropdown.tsx b/webapp/channels/src/components/sidebar/add_channel_dropdown.tsx index b14ea97472..c31d2d0948 100644 --- a/webapp/channels/src/components/sidebar/add_channel_dropdown.tsx +++ b/webapp/channels/src/components/sidebar/add_channel_dropdown.tsx @@ -59,7 +59,7 @@ const AddChannelDropdown = ({ id='invitePeople' onClick={invitePeopleModal} icon={} - text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeople', defaultMessage: 'Invite People'})} + text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeople', defaultMessage: 'Invite people'})} extraText={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeopleExtraText', defaultMessage: 'Add people to the team'})} /> {showInviteTutorialTip && } @@ -74,7 +74,7 @@ const AddChannelDropdown = ({ modalId={ModalIdentifiers.WORK_TEMPLATE} dialogType={WorkTemplateModal} text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.work_template', defaultMessage: 'Create from a template'})} - extraText={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.work_template_extra', defaultMessage: 'Set up a channel with linked boards, and playbooks'})} + extraText={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.work_template_extra', defaultMessage: 'Link channels, boards, and playbooks together'})} icon={} className='work-template' /> @@ -88,7 +88,7 @@ const AddChannelDropdown = ({ id='showMoreChannels' onClick={showMoreChannelsModal} icon={} - text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.browseChannels', defaultMessage: 'Browse Channels'})} + text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.browseChannels', defaultMessage: 'Browse channels'})} /> ); } @@ -100,7 +100,7 @@ const AddChannelDropdown = ({ id='showNewChannel' onClick={showNewChannelModal} icon={} - text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createNewChannel', defaultMessage: 'Create New Channel'})} + text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createNewChannel', defaultMessage: 'Create new channel'})} /> ); } @@ -113,7 +113,7 @@ const AddChannelDropdown = ({ id='createCategory' onClick={showCreateCategoryModal} icon={} - text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createCategory', defaultMessage: 'Create New Category'})} + text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createCategory', defaultMessage: 'Create new category'})} /> ); } @@ -143,8 +143,8 @@ const AddChannelDropdown = ({ <> {workTemplate} - {joinPublicChannel} {createChannel} + {joinPublicChannel} {createDirectMessage} {showCreateTutorialTip && } {createUserGroup} diff --git a/webapp/channels/src/components/sidebar/add_channels_cta_button.test.tsx b/webapp/channels/src/components/sidebar/add_channels_cta_button.test.tsx index 6066f4dcd3..68ab7ed614 100644 --- a/webapp/channels/src/components/sidebar/add_channels_cta_button.test.tsx +++ b/webapp/channels/src/components/sidebar/add_channels_cta_button.test.tsx @@ -80,6 +80,12 @@ describe('components/new_channel_modal', () => { system_user: { permissions: [Permissions.JOIN_PUBLIC_CHANNELS, Permissions.CREATE_PRIVATE_CHANNEL, Permissions.CREATE_PUBLIC_CHANNEL], }, + system_user_join_permissions: { + permissions: [Permissions.JOIN_PUBLIC_CHANNELS], + }, + system_user_create_public_permissions: { + permissions: [Permissions.JOIN_PUBLIC_CHANNELS, Permissions.CREATE_PUBLIC_CHANNEL], + }, }, }, }, @@ -99,6 +105,25 @@ describe('components/new_channel_modal', () => { ).toMatchSnapshot(); }); + test('should match snapshot when user has only join channel permissions', () => { + const userWithJoinChannelsPermission = { + currentUserId: 'current_user_id', + profiles: { + current_user_id: { + id: 'current_user_id', + roles: 'system_user_join_permissions', + }, + }, + } as unknown as UsersState; + mockState = {...mockState, entities: {...mockState.entities, users: userWithJoinChannelsPermission}}; + + expect( + shallow( + , + ), + ).toMatchSnapshot(); + }); + test('should find the add channels button when user has permissions', () => { const wrapper = mountWithIntl( , @@ -145,4 +170,52 @@ describe('components/new_channel_modal', () => { expect(trackEvent).toHaveBeenCalledWith('ui', 'add_channels_cta_button_clicked'); }); + + test('should not display as a Cta Dropdown when user only has permissions to join channels ', () => { + const userWithJoinChannelsPermission = { + currentUserId: 'current_user_id', + profiles: { + current_user_id: { + id: 'current_user_id', + roles: 'system_user_join_permissions', + }, + }, + } as unknown as UsersState; + mockState = {...mockState, entities: {...mockState.entities, users: userWithJoinChannelsPermission}}; + + const wrapper = mountWithIntl( + , + ); + + // do not find the menu + expect(wrapper.find('.AddChannelsCtaDropdown').exists()).toBeFalsy(); + + // only find the button + const button = wrapper.find('button#addChannelsCta'); + expect(button.exists()).toBeTruthy(); + + button.simulate('click'); + + // when clicked show the browse channels modal + expect(trackEvent).toHaveBeenCalledWith('ui', 'browse_channels_button_is_clicked'); + }); + + test('should still display as a Cta Dropdown when user has permissions to create at least one form of channel', () => { + const userWithJoinChannelsPermission = { + currentUserId: 'current_user_id', + profiles: { + current_user_id: { + id: 'current_user_id', + roles: 'system_user_create_public_permissions', + }, + }, + } as unknown as UsersState; + mockState = {...mockState, entities: {...mockState.entities, users: userWithJoinChannelsPermission}}; + + const wrapper = mountWithIntl( + , + ); + + expect(wrapper.find('.AddChannelsCtaDropdown').exists()).toBeTruthy(); + }); }); diff --git a/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx b/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx index 07ed64b9e5..d3f1244edd 100644 --- a/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx +++ b/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx @@ -80,7 +80,7 @@ const AddChannelsCtaButton = (): JSX.Element | null => { ); } @@ -91,7 +91,7 @@ const AddChannelsCtaButton = (): JSX.Element | null => { ); } @@ -106,8 +106,28 @@ const AddChannelsCtaButton = (): JSX.Element | null => { ); }; - const trackOpen = (opened: boolean) => { - openAddChannelsCtaOpen(opened); + const addChannelsButton = (btnCallback?: () => void) => { + const handleClick = () => btnCallback?.(); + return ( + + ); + }; + + const storePreferencesAndTrackEvent = () => { trackEvent('ui', 'add_channels_cta_button_clicked'); if (!touchedAddChannelsCtaButton) { dispatch(savePreferences( @@ -122,26 +142,26 @@ const AddChannelsCtaButton = (): JSX.Element | null => { } }; + const trackOpen = (opened: boolean) => { + openAddChannelsCtaOpen(opened); + storePreferencesAndTrackEvent(); + }; + + if (!canCreateChannel) { + const browseChannelsAction = () => { + showMoreChannelsModal(); + storePreferencesAndTrackEvent(); + }; + return addChannelsButton(browseChannelsAction); + } + return ( - + {addChannelsButton()} +
    + + Interested in receiving Mattermost security updates via newsletter? + + + Sign up at + + https://mattermost.com/security-updates/ + + . + +
    +
    + + Interested in receiving Mattermost security updates via newsletter? + + + Sign up at + + https://mattermost.com/security-updates/ + + . + +
    ; let mockDispatch = jest.fn(); @@ -96,7 +98,7 @@ describe('components/signup/Signup', () => { beforeEach(() => { mockLocation = {pathname: '', search: '', hash: ''}; - mockLicense = {IsLicensed: 'true'}; + mockLicense = {IsLicensed: 'true', Cloud: 'false'}; mockState = { entities: { @@ -178,7 +180,7 @@ describe('components/signup/Signup', () => { }); it('should match snapshot for all signup options enabled with isLicensed disabled', () => { - mockLicense = {IsLicensed: 'false'}; + mockLicense = {IsLicensed: 'false', Cloud: 'false'}; const wrapper = shallow( , @@ -295,4 +297,45 @@ describe('components/signup/Signup', () => { expect(wrapper.find('.content-layout-column-title').text()).toEqual('This invite link is invalid'); }); }); + + it('should show newsletter check box opt-in for self-hosted non airgapped workspaces', async () => { + jest.spyOn(useCWSAvailabilityCheckAll, 'default').mockImplementation(() => true); + mockLicense = {IsLicensed: 'true', Cloud: 'false'}; + + const {container: signupContainer} = renderWithIntlAndStore( + + + , {}); + + screen.getByTestId('signup-body-card-form-check-newsletter'); + const checkInput = screen.getByTestId('signup-body-card-form-check-newsletter'); + expect(checkInput).toHaveAttribute('type', 'checkbox'); + + expect(signupContainer).toHaveTextContent(/I would like to receive Mattermost security updates via newsletter. Data Terms and Conditions apply/); + }); + + it('should NOT show newsletter check box opt-in for self-hosted AND airgapped workspaces', async () => { + jest.spyOn(useCWSAvailabilityCheckAll, 'default').mockImplementation(() => false); + mockLicense = {IsLicensed: 'true', Cloud: 'false'}; + + const {container: signupContainer} = renderWithIntlAndStore( + + + , {}); + + expect(() => screen.getByTestId('signup-body-card-form-check-newsletter')).toThrow(); + expect(signupContainer).toHaveTextContent('Interested in receiving Mattermost security updates via newsletter?Sign up at https://mattermost.com/security-updates/.'); + }); + + it('should not show any newsletter related opt-in or text for cloud', async () => { + jest.spyOn(useCWSAvailabilityCheckAll, 'default').mockImplementation(() => true); + mockLicense = {IsLicensed: 'true', Cloud: 'true'}; + + renderWithIntlAndStore( + + + , {}); + + expect(() => screen.getByTestId('signup-body-card-form-check-newsletter')).toThrow(); + }); }); diff --git a/webapp/channels/src/components/signup/signup.tsx b/webapp/channels/src/components/signup/signup.tsx index dde147597f..3f3a13d460 100644 --- a/webapp/channels/src/components/signup/signup.tsx +++ b/webapp/channels/src/components/signup/signup.tsx @@ -48,9 +48,12 @@ import LoginOpenIDIcon from 'components/widgets/icons/login_openid_icon'; import LoginOffice365Icon from 'components/widgets/icons/login_office_365_icon'; import Input, {CustomMessageInputType, SIZE} from 'components/widgets/inputs/input/input'; import PasswordInput from 'components/widgets/inputs/password_input/password_input'; +import CheckInput from 'components/widgets/inputs/check'; import SaveButton from 'components/save_button'; +import useCWSAvailabilityCheck from 'components/common/hooks/useCWSAvailabilityCheck'; +import ExternalLink from 'components/external_link'; -import {Constants, ItemStatus, ValidationErrors} from 'utils/constants'; +import {Constants, HostedCustomerLinks, ItemStatus, ValidationErrors} from 'utils/constants'; import {isValidUsername, isValidPassword, getPasswordConfig, getRoleFromTrackFlow, getMediumFromTrackFlow} from 'utils/utils'; import './signup.scss'; @@ -99,7 +102,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => { TermsOfServiceLink, PrivacyPolicyLink, } = config; - const {IsLicensed} = useSelector(getLicense); + const {IsLicensed, Cloud} = useSelector(getLicense); const loggedIn = Boolean(useSelector(getCurrentUserId)); const useCaseOnboarding = useSelector(getUseCaseOnboarding); const usedBefore = useSelector((state: GlobalState) => (!inviteId && !loggedIn && token ? getGlobalItem(state, token, null) : undefined)); @@ -110,6 +113,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => { const passwordInput = useRef(null); const isLicensed = IsLicensed === 'true'; + const isCloud = Cloud === 'true'; const enableOpenServer = EnableOpenServer === 'true'; const noAccounts = NoAccounts === 'true'; const enableSignUpWithEmail = EnableSignUpWithEmail === 'true'; @@ -136,12 +140,24 @@ const Signup = ({onCustomizeHeader}: SignupProps) => { const [teamName, setTeamName] = useState(parsedTeamName ?? ''); const [alertBanner, setAlertBanner] = useState(null); const [isMobileView, setIsMobileView] = useState(false); + const [subscribeToSecurityNewsletter, setSubscribeToSecurityNewsletter] = useState(false); + + const canReachCWS = useCWSAvailabilityCheck(); const enableExternalSignup = enableSignUpWithGitLab || enableSignUpWithOffice365 || enableSignUpWithGoogle || enableSignUpWithOpenId || enableLDAP || enableSAML; const hasError = Boolean(emailError || nameError || passwordError || serverError || alertBanner); const canSubmit = Boolean(email && name && password) && !hasError && !loading; const {error: passwordInfo} = isValidPassword('', getPasswordConfig(config), intl); + const subscribeToSecurityNewsletterFunc = () => { + try { + Client4.subscribeToNewsletter({email, subscribed_content: 'security_newsletter'}); + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } + }; + const getExternalSignupOptions = () => { const externalLoginOptions: ExternalLoginButtonType[] = []; @@ -564,6 +580,9 @@ const Signup = ({onCustomizeHeader}: SignupProps) => { } await handleSignupSuccess(user, data as UserProfile); + if (subscribeToSecurityNewsletter) { + subscribeToSecurityNewsletterFunc(); + } } else { setIsWaiting(false); } @@ -571,6 +590,60 @@ const Signup = ({onCustomizeHeader}: SignupProps) => { const handleReturnButtonOnClick = () => history.replace('/'); + const getNewsletterCheck = () => { + if (isCloud) { + return null; + } + + if (canReachCWS) { + return ( + setSubscribeToSecurityNewsletter(!subscribeToSecurityNewsletter)} + text={ + formatMessage( + {id: 'newsletter_optin.checkmark.text', defaultMessage: 'I would like to receive Mattermost security updates via newsletter. Data Terms and Conditions apply'}, + { + a: (chunks: React.ReactNode | React.ReactNodeArray) => ( + + {chunks} + + ), + }, + )} + checked={subscribeToSecurityNewsletter} + /> + ); + } + return ( +
    + + {formatMessage({id: 'newsletter_optin.title', defaultMessage: 'Interested in receiving Mattermost security updates via newsletter?'})} + + + {formatMessage( + {id: 'newsletter_optin.desc', defaultMessage: 'Sign up at {link}.'}, + { + link: HostedCustomerLinks.SECURITY_UPDATES, + a: (chunks: React.ReactNode | React.ReactNodeArray) => ( + + {chunks} + + ), + }, + )} + +
    + ); + }; + const handleOnBlur = (e: FocusEvent, inputId: string) => { const text = e.target.value; if (!text) { @@ -736,6 +809,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => { error={passwordError} onBlur={(e) => handleOnBlur(e, 'password')} /> + {getNewsletterCheck()} { const { show, @@ -131,6 +133,7 @@ export const ChannelsTourTip = ({ hideBackdrop={hideBackdrop} tippyBlueStyle={tippyBlueStyle} showOptOut={showOptOut} + interactivePunchOut={interactivePunchOut} /> ); }; diff --git a/webapp/channels/src/components/tours/crt_tour/crt_threads_pane_tutorial_tip.tsx b/webapp/channels/src/components/tours/crt_tour/crt_threads_pane_tutorial_tip.tsx index 4409a68c08..907966e23f 100644 --- a/webapp/channels/src/components/tours/crt_tour/crt_threads_pane_tutorial_tip.tsx +++ b/webapp/channels/src/components/tours/crt_tour/crt_threads_pane_tutorial_tip.tsx @@ -9,7 +9,7 @@ import {Constants, Preferences} from 'utils/constants'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/common'; import {savePreferences} from 'mattermost-redux/actions/preferences'; -import {TourTip, useMeasurePunchouts} from '@mattermost/components'; +import {TourTip, useFollowElementDimensions, useMeasurePunchouts} from '@mattermost/components'; const translate = {x: 2, y: 25}; @@ -17,6 +17,9 @@ const CRTThreadsPaneTutorialTip = () => { const dispatch = useDispatch(); const {formatMessage} = useIntl(); const currentUserId = useSelector(getCurrentUserId); + + const dimensions = useFollowElementDimensions('sidebar-right'); + const title = ( { dispatch(savePreferences(currentUserId, preferences)); }; - const overlayPunchOut = useMeasurePunchouts(['rhsContainer'], []); + const overlayPunchOut = useMeasurePunchouts(['rhsContainer'], [dimensions?.width]); return ( { const title = ( @@ -26,7 +26,7 @@ export const CreateAndJoinChannelsTour = () => {

    ); - const overlayPunchOut = useMeasurePunchouts(['showMoreChannels', 'invitePeople'], [], {y: -8, height: 16, x: 0, width: 0}); + const overlayPunchOut = useMeasurePunchouts(['showMoreChannels', 'showNewChannel'], [], {y: -8, height: 16, x: 0, width: 0}); return ( {

    ); - const overlayPunchOut = useMeasurePunchouts(['showMoreChannels', 'invitePeople'], [], {y: -8, height: 16, x: 0, width: 0}); + const overlayPunchOut = useMeasurePunchouts(['invitePeople'], [], {y: -8, height: 16, x: 0, width: 0}); return ( { const {formatMessage} = useIntl(); - const {playbooksCount, boardsCount, showBoardsTour} = useShowTourTip(); - const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], []); + const dimensions = useFollowElementDimensions('sidebar-right'); + const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], [dimensions?.width]); if (!showBoardsTour) { return null; @@ -60,6 +60,7 @@ export const BoardsTourTip = (): JSX.Element | null => { singleTip={playbooksCount === 0} placement='left-start' showOptOut={false} + interactivePunchOut={true} /> ); }; diff --git a/webapp/channels/src/components/tours/worktemplate_explore_tour/playbooks_tour_tip.tsx b/webapp/channels/src/components/tours/worktemplate_explore_tour/playbooks_tour_tip.tsx index d8dbe14116..0193222be4 100644 --- a/webapp/channels/src/components/tours/worktemplate_explore_tour/playbooks_tour_tip.tsx +++ b/webapp/channels/src/components/tours/worktemplate_explore_tour/playbooks_tour_tip.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {FormattedMessage, useIntl} from 'react-intl'; -import {useMeasurePunchouts} from '@mattermost/components'; +import {useFollowElementDimensions, useMeasurePunchouts} from '@mattermost/components'; import {useShowTourTip} from './useShowTourTip'; import OnboardingWorkTemplateTourTip from './worktemplate_explore_tour_tip'; @@ -12,7 +12,8 @@ import OnboardingWorkTemplateTourTip from './worktemplate_explore_tour_tip'; export const PlaybooksTourTip = (): JSX.Element | null => { const {formatMessage} = useIntl(); const {playbooksCount, boardsCount, showPlaybooksTour} = useShowTourTip(); - const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], []); + const dimensions = useFollowElementDimensions('sidebar-right'); + const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], [dimensions?.width]); if (!showPlaybooksTour) { return null; @@ -59,6 +60,7 @@ export const PlaybooksTourTip = (): JSX.Element | null => { overlayPunchOut={overlayPunchOut} placement='left-start' showOptOut={false} + interactivePunchOut={true} /> ); }; diff --git a/webapp/channels/src/components/widgets/inputs/check/check.scss b/webapp/channels/src/components/widgets/inputs/check/check.scss new file mode 100644 index 0000000000..e6e9809d39 --- /dev/null +++ b/webapp/channels/src/components/widgets/inputs/check/check.scss @@ -0,0 +1,19 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +.check-input { + display: flex; + align-items: flex-start; + margin-top: 24px; + margin-bottom: 32px; + + .text { + margin-left: 8px; + color: var(--center-channel-color-rgb); + font-family: 'Open Sans'; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + } +} diff --git a/webapp/channels/src/components/widgets/inputs/check/index.tsx b/webapp/channels/src/components/widgets/inputs/check/index.tsx new file mode 100644 index 0000000000..cc05728217 --- /dev/null +++ b/webapp/channels/src/components/widgets/inputs/check/index.tsx @@ -0,0 +1,28 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {ReactNode} from 'react'; +import './check.scss'; + +type Props = { + id: string; + name: string; + text: ReactNode; + onChange: () => void; + checked: boolean; +} + +function CheckInput(props: Props) { + return ( +
    + + {props.text} +
    + ); +} + +export default CheckInput; diff --git a/webapp/channels/src/components/widgets/inputs/input/__snapshots__/input.test.tsx.snap b/webapp/channels/src/components/widgets/inputs/input/__snapshots__/input.test.tsx.snap new file mode 100644 index 0000000000..701ba83d1c --- /dev/null +++ b/webapp/channels/src/components/widgets/inputs/input/__snapshots__/input.test.tsx.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/widgets/inputs/Input should match snapshot 1`] = ` +
    +
    + +
    + +
    +
    +
    +`; diff --git a/webapp/channels/src/components/widgets/inputs/input/input.scss b/webapp/channels/src/components/widgets/inputs/input/input.scss index 281c31175f..eacee3bfba 100644 --- a/webapp/channels/src/components/widgets/inputs/input/input.scss +++ b/webapp/channels/src/components/widgets/inputs/input/input.scss @@ -49,11 +49,16 @@ .Input_wrapper { display: flex; flex: 1; + align-items: center; padding: 0 16px; margin: 2px 0; color: rgba(var(--center-channel-color-rgb), 0.56); font-size: 14px; line-height: 20px; + + > :not(:first-child) { + margin-left: 8px; + } } .Input_limit-exceeded { @@ -196,4 +201,10 @@ background-color: rgba(var(--center-channel-color-rgb), 0.04); } } + + .Input__clear { + display: flex; + color: rgba(var(--center-channel-color), 0.68); + cursor: pointer; + } } diff --git a/webapp/channels/src/components/widgets/inputs/input/input.test.tsx b/webapp/channels/src/components/widgets/inputs/input/input.test.tsx new file mode 100644 index 0000000000..b77523ba2f --- /dev/null +++ b/webapp/channels/src/components/widgets/inputs/input/input.test.tsx @@ -0,0 +1,48 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {mount, shallow} from 'enzyme'; +import React from 'react'; + +import OverlayTrigger from 'components/overlay_trigger'; + +import Input from './input'; + +describe('components/widgets/inputs/Input', () => { + test('should match snapshot', () => { + const wrapper = shallow( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should render with clearable enabled', () => { + const value = 'value'; + const clearableTooltipText = 'tooltip text'; + const onClear = jest.fn(); + + const wrapper = shallow( + , + ); + + const clear = wrapper.find('.Input__clear'); + expect(clear.length).toEqual(1); + expect(wrapper.find('CloseCircleIcon').length).toEqual(1); + + const tooltip = wrapper.find(OverlayTrigger); + expect(tooltip.length).toEqual(1); + + const overlay = mount(tooltip.prop('overlay')); + expect(overlay.text()).toEqual(clearableTooltipText); + + clear.first().simulate('mousedown'); + + expect(onClear).toHaveBeenCalledTimes(1); + }); +}); diff --git a/webapp/channels/src/components/widgets/inputs/input/input.tsx b/webapp/channels/src/components/widgets/inputs/input/input.tsx index 1a55dbb9f4..bcc66e4a20 100644 --- a/webapp/channels/src/components/widgets/inputs/input/input.tsx +++ b/webapp/channels/src/components/widgets/inputs/input/input.tsx @@ -3,10 +3,14 @@ import React, {useState, useEffect} from 'react'; import {useIntl} from 'react-intl'; - import classNames from 'classnames'; -import {ItemStatus} from 'utils/constants'; +import {CloseCircleIcon} from '@mattermost/compass-icons/components'; + +import OverlayTrigger from 'components/overlay_trigger'; +import Tooltip from 'components/tooltip'; + +import Constants, {ItemStatus} from 'utils/constants'; import './input.scss'; @@ -32,6 +36,9 @@ interface InputProps extends React.InputHTMLAttributes { useLegend?: boolean; customMessage?: CustomMessageInputType; inputSize?: SIZE; + clearable?: boolean; + clearableTooltipText?: string; + onClear?: () => void; } const Input = React.forwardRef(( @@ -56,9 +63,12 @@ const Input = React.forwardRef(( maxLength, inputSize = SIZE.MEDIUM, disabled, + clearable, + clearableTooltipText, onFocus, onBlur, onChange, + onClear, ...otherProps }: InputProps, ref?: React.Ref, @@ -109,6 +119,12 @@ const Input = React.forwardRef(( } }; + const handleOnClear = () => { + if (onClear) { + onClear(); + } + }; + const validateInput = () => { if (!required || (value !== null && value !== '')) { return; @@ -121,6 +137,26 @@ const Input = React.forwardRef(( const error = customInputLabel?.type === 'error'; const limitExceeded = limit && value && !Array.isArray(value) ? value.toString().length - limit : 0; + const clearButton = value && clearable ? ( +
    + + {clearableTooltipText || formatMessage({id: 'widget.input.clear', defaultMessage: 'Clear'})} + + )} + > + + +
    + ) : null; + return (
    )} {inputSuffix} + {clearButton}
    {addon} diff --git a/webapp/channels/src/components/widgets/menu/menu.tsx b/webapp/channels/src/components/widgets/menu/menu.tsx index 21bac1e4e2..3e5712ee25 100644 --- a/webapp/channels/src/components/widgets/menu/menu.tsx +++ b/webapp/channels/src/components/widgets/menu/menu.tsx @@ -31,6 +31,9 @@ type Props = { listId?: string; } +/** + * @deprecated Use the "webapp/channels/src/components/menu" instead. + */ export default class Menu extends React.PureComponent { public static Header = MenuHeader public static Group = MenuGroup diff --git a/webapp/channels/src/components/widgets/menu/menu_group.tsx b/webapp/channels/src/components/widgets/menu/menu_group.tsx index e3fd39bb81..d0779e5661 100644 --- a/webapp/channels/src/components/widgets/menu/menu_group.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_group.tsx @@ -10,6 +10,9 @@ type Props = { children?: React.ReactNode; } +/** + * @deprecated Use the "webapp/channels/src/components/menu" instead. + */ export default class MenuGroup extends React.PureComponent { handleDividerClick = (e: React.MouseEvent): void => { e.preventDefault(); diff --git a/webapp/channels/src/components/widgets/menu/menu_header.tsx b/webapp/channels/src/components/widgets/menu/menu_header.tsx index d6933ae2c5..781eb5e41f 100644 --- a/webapp/channels/src/components/widgets/menu/menu_header.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_header.tsx @@ -11,6 +11,9 @@ type Props = { onClick?: () => void; } +/** + * @deprecated Use the "webapp/channels/src/components/menu" instead. + */ export default class MenuHeader extends React.PureComponent { public render() { const {children, onClick} = this.props; diff --git a/webapp/channels/src/components/widgets/menu/menu_items/menu_cloud_trial.test.tsx b/webapp/channels/src/components/widgets/menu/menu_items/menu_cloud_trial.test.tsx index 214b51d0f4..5ed6855015 100644 --- a/webapp/channels/src/components/widgets/menu/menu_items/menu_cloud_trial.test.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_items/menu_cloud_trial.test.tsx @@ -205,7 +205,6 @@ describe('components/widgets/menu/menu_items/menu_cloud_trial', () => { }; const store = mockStore(state); const wrapper = mountWithIntl(); - console.log(wrapper.debug()); expect(wrapper.find('.open-learn-more-trial-modal').exists()).toEqual(true); }); diff --git a/webapp/channels/src/components/widgets/menu/menu_items/menu_item.tsx b/webapp/channels/src/components/widgets/menu/menu_items/menu_item.tsx index 46618d19e5..8edc2328c5 100644 --- a/webapp/channels/src/components/widgets/menu/menu_items/menu_item.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_items/menu_item.tsx @@ -6,6 +6,9 @@ import classNames from 'classnames'; import './menu_item.scss'; +/** + * @deprecated Use the "webapp/channels/src/components/menu" instead. + */ export default function menuItem(Component: React.ComponentType) { type Props = { show: boolean; diff --git a/webapp/channels/src/components/widgets/menu/menu_items/submenu_item.tsx b/webapp/channels/src/components/widgets/menu/menu_items/submenu_item.tsx index a89226bc1d..b196de494d 100644 --- a/webapp/channels/src/components/widgets/menu/menu_items/submenu_item.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_items/submenu_item.tsx @@ -60,6 +60,9 @@ type State = { show: boolean; } +/** + * @deprecated Use the "webapp/channels/src/components/menu" instead. + */ export default class SubMenuItem extends React.PureComponent { private node: React.RefObject; diff --git a/webapp/channels/src/components/widgets/menu/menu_wrapper.tsx b/webapp/channels/src/components/widgets/menu/menu_wrapper.tsx index ca04bbc048..003c9bb791 100644 --- a/webapp/channels/src/components/widgets/menu/menu_wrapper.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_wrapper.tsx @@ -29,6 +29,9 @@ type State = { open: boolean; } +/** + * @deprecated Use the "webapp/channels/src/components/menu" instead. + */ export default class MenuWrapper extends React.PureComponent { private node: React.RefObject; diff --git a/webapp/channels/src/components/widgets/menu/menu_wrapper_animation.tsx b/webapp/channels/src/components/widgets/menu/menu_wrapper_animation.tsx index 4ac53e9682..950103ba00 100644 --- a/webapp/channels/src/components/widgets/menu/menu_wrapper_animation.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_wrapper_animation.tsx @@ -13,6 +13,9 @@ type Props = { show: boolean; } +/** + * @deprecated Use the "webapp/channels/src/components/menu" instead. + */ export default function MenuWrapperAnimation(props: Props) { if (isMobile()) { if (props.show) { diff --git a/webapp/channels/src/components/work_templates/index.tsx b/webapp/channels/src/components/work_templates/index.tsx index dc8e1a4a97..ed5eb0d6a6 100644 --- a/webapp/channels/src/components/work_templates/index.tsx +++ b/webapp/channels/src/components/work_templates/index.tsx @@ -298,7 +298,7 @@ const WorkTemplateModal = () => { let confirmButtonAction; switch (modalState) { case ModalState.Menu: - title = formatMessage({id: 'work_templates.menu.modal_title', defaultMessage: 'Start from a template'}); + title = formatMessage({id: 'work_templates.menu.modal_title', defaultMessage: 'Create from a template'}); break; case ModalState.Preview: title = formatMessage({id: 'work_templates.preview.modal_title', defaultMessage: 'Preview {useCase}'}, {useCase: selectedTemplate?.useCase}); diff --git a/webapp/channels/src/i18n/bg.json b/webapp/channels/src/i18n/bg.json index c5331d8e9c..f5f826761a 100644 --- a/webapp/channels/src/i18n/bg.json +++ b/webapp/channels/src/i18n/bg.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Корпоративна версия", "about.hash": "Хеш на компилация:", "about.hashee": "Хеш на EE компилация:", - "about.hashwebapp": "Webapp билд хеш:", "about.licensed": "Лицензиран на:", "about.notice": "Използването на Mattermost е възможно благодарение на софтуер с отворен код използван в нашите сървърно, настолно и мобилно приложения.", "about.privacy": "Политика за поверителност", @@ -2363,7 +2362,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} премахна целта на канала (беше: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} актуализира целта на канала от: {old} до: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} актуализира целта на канала до: {new}", - "app.plugin.marketplace_plugins.app_error": "Грешка при свързване със сървъра на пазара. Моля, проверете настройките си в [Системна конзола]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Грешка: {error}", "apps.error.command.field_missing": "Липсват задължителни полета: `{fieldName}`.", "apps.error.command.same_channel": "Повтаря се каналът с поле `{fieldName}`: `{option}`.", @@ -3415,7 +3413,6 @@ "login_mfa.token": "МФУ маркер", "manage_channel_groups_modal.search_placeholder": "Търсене на групи", "manage_team_groups_modal.search_placeholder": "Търсене на групи", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {приставка} other {приставки}} от {total, number} общо", "marketplace_modal.install_plugins": "Инсталирай приставки", "marketplace_modal.installing": "Инсталиране ...", "marketplace_modal.list.configure": "Конфигуриране", diff --git a/webapp/channels/src/i18n/de.json b/webapp/channels/src/i18n/de.json index 01990e3010..7d29a4fe4f 100644 --- a/webapp/channels/src/i18n/de.json +++ b/webapp/channels/src/i18n/de.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise Edition", "about.hash": "Build Hashwert:", "about.hashee": "EE Build Hashwert:", - "about.hashwebapp": "Webapp-Build-Hash:", "about.licensed": "Lizenziert für:", "about.notice": "Mattermost wird durch Open Source Software möglich gemacht, die in unseren Server-, Desktop- und mobilen Apps verwendet wird.", "about.privacy": "Datenschutzbedingungen", @@ -1338,8 +1337,9 @@ "admin.license.renewalCard.reviewNumbers": "Überprüfe deine Zahlen unten, um sicherzustellen, dass du für die richtige Anzahl von Benutzern erneuerst.", "admin.license.renewalCard.usersNumbers": "**Aktive Benutzer:** {activeUsersNum}", "admin.license.title": "Edition und Lizenz", - "admin.license.trial-request.accept-terms": "Indem ich auf Starte Testl klicke, stimme ich dem Mattermost Software und Services License Agreement, Datenschutz-Richtlinien und dem Erhalt von Produkt-E-Mails zu.", + "admin.license.trial-request.accept-terms": "Indem ich auf Starte Test klicke, stimme ich dem Mattermost Software und Services License Agreement, Datenschutz-Richtlinien und dem Erhalt von Produkt-E-Mails zu.", "admin.license.trial-request.embargoed": "Wir konnten die Anfrage auf Grund von Embargo-Beschränkungen für bestimmte Länder nicht verarbeiten. Lerne mehr in unserer Dokumentation, oder kontaktiere legal@mattermost.com für Fragen rund um Exportbeschränkungen.", + "admin.license.trial-request.embargoed.button": "Schliessen", "admin.license.trial-request.startTrial": "Starte Test", "admin.license.trial-request.title": "Teste Mattermost Enterprise Edition kostenlos für die nächsten 30 Tage. Keine Kaufverpflichtung oder Kreditkarte erforderlich. ", "admin.license.trialCard.contactSales": "Vertrieb kontaktieren", @@ -2563,6 +2563,9 @@ "admin.webserverModeUncompressed": "Unkomprimiert", "admin.webserverModeUncompressedDescription": "Der Mattermost-Server wird statische Dateien unkomprimiert bereitstellen.", "admin_settings.save_unsaved_changes": "Bitte speichere ungespeicherte Änderungen zuerst", + "air_gapped_modal.close": "Schliessen", + "air_gapped_modal.description": "Um deinen Test zu starten, besuche bitte {link} und fordere einen Testschlüssel an.", + "air_gapped_modal.title": "Testschlüssel anfordern", "alert_banner.tooltipCloseBtn": "Schliessen", "analytics.chart.loading": "Lade...", "analytics.chart.meaningful": "Nicht genügend Daten für eine aussagekräftige Darstellung.", @@ -2648,7 +2651,7 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} hat den Kanalzweck entfernt (war: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} hat den Kanalzweck aktualisiert von: {old} auf: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} hat den Kanalzweck geändert zu: {new}", - "app.plugin.marketplace_plugins.app_error": "Fehler beim Verbinden mit dem Marktplatz-Server. Bitte überprüfe deine Einstellungen in der [Systemkonsole]({siteURL}/admin_console/plugins/plugin_management).", + "app_bar.marketplace": "App-Marktplatz", "apps.error": "Fehler: {error}", "apps.error.command.field_missing": "Erforderliche Felder fehlen: `{fieldName}`.", "apps.error.command.same_channel": "Kanal wiederholt für Feld `{fieldName}`: `{option}`.", @@ -3437,6 +3440,9 @@ "filtered_user_list.userStatus": "Benutzerstatus:", "flag_post.flag": "Zur Nachverfolgung markieren", "flag_post.unflag": "Markierung entfernen", + "footer_pagination.count": "Zeige {startCount, number}-{endCount, number} von {total, number}", + "footer_pagination.next": "Weiter", + "footer_pagination.prev": "Zurück", "forward_post_button.label": "Weiterleiten", "forward_post_modal.button.cancel": "Abbrechen", "forward_post_modal.button.forward": "Weiterleiten", @@ -4081,7 +4087,7 @@ "mark_all_threads_as_read_modal.title": "Alle deine Unterhaltungen als gelesen markieren?", "marketplace_command.disabled": "Der Marktplatz ist deaktiviert. Bitte kontaktiere deinen Systemadmin für Details.", "marketplace_command.no_permission": "Du hast nicht die erforderlichen Berechtigungen um auf den Marktplatz zuzugreifen.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {Plugin} other {Plugins}} von insgesamt {total, number}", + "marketplace_modal.app_error": "Fehler beim Verbinden mit dem Marktplatz-Server. Bitte überprüfe deine Einstellungen in der Systemkonsole.", "marketplace_modal.install_plugins": "Plugins installieren", "marketplace_modal.installing": "Installiere...", "marketplace_modal.list.configure": "Konfigurieren", @@ -4099,12 +4105,13 @@ "marketplace_modal.list.update_confirmation.message.warning_major_version": "Dieses Update kann inkompatible Änderungen enthalten.", "marketplace_modal.list.update_confirmation.message.warning_major_version_with_release_notes": "Dieses Update kann inkompatible Änderungen enthalten. Prüfe die [Release Notes](!{releaseNotesUrl}) vor dem Upgrade.", "marketplace_modal.list.update_confirmation.title": "Plugin-Update bestätigen", - "marketplace_modal.no_plugins": "Zurzeit sind keine Plugins verfügbar.", - "marketplace_modal.no_plugins_installed": "Du hast keine Plugins installiert.", - "marketplace_modal.search": "Plugin-Marktplatz durchsuchen", + "marketplace_modal.no_plugins": "Keine Plugins gefunden", + "marketplace_modal.no_plugins_installed": "Keine installierten Plugins gefunden", + "marketplace_modal.search": "Marktplatz durchsuchen", "marketplace_modal.tabs.all_listing": "Alle", - "marketplace_modal.tabs.installed_listing": "Installierte", - "marketplace_modal.title": "Plugin-Marktplatz", + "marketplace_modal.tabs.installed_listing": "Installiert ({count})", + "marketplace_modal.title": "App-Marktplatz", + "marketplace_modal_list.no_plugins_filter": "Keine Ergebnisse für \"{filter}\"", "members_popover.button.message": "Nachricht", "menu.cloudFree.enterpriseTrialDescription": "Dein Test ist aktiv bis zum {trialEndDay}. Entdecke unsere besten Enterprise Funktionen. Erfahre mehr", "menu.cloudFree.enterpriseTrialTitle": "Enterprise Test", @@ -4156,22 +4163,13 @@ "modal.manual_status.title_offline": "Dein Status wurde auf \"Offline\" gesetzt", "modal.manual_status.title_ooo": "Dein Status ist auf \"Nicht im Büro\" gesetzt", "more.details": "Mehr Details", - "more_channels.channel_purpose": "Kanal-Informationen: Mitgliedschaftsindikator: Beigetreten, Mitglieder {memberCount}, Zweck: {channelPurpose}", - "more_channels.count": "{count} Ergebnisse", - "more_channels.count_one": "1 Ergebnis", - "more_channels.count_zero": "Keine Ergebnisse", "more_channels.create": "Kanal erstellen", - "more_channels.hide_joined": "Verbundene Kanäle ausblenden", - "more_channels.hide_joined_checked": "Kontrollkästchen Verbundene Kanäle ausblenden, aktiviert", - "more_channels.hide_joined_not_checked": "Kontrollkästchen Verbundene Kanäle ausblenden, deaktiviert", - "more_channels.joined": "Verknüpft", - "more_channels.membership_indicator": "Mitgliedschaftsindikator: Beigetreten", + "more_channels.createClick": "Klicke auf 'Neuen Kanal erstellen' um einen Neuen zu erzeugen", + "more_channels.join": "Beitreten", + "more_channels.joining": "Beitreten...", "more_channels.next": "Weiter", - "more_channels.noArchived": "Keine archivierten Kanäle", "more_channels.noMore": "Keine Ergebnisse für \"{text}\"", - "more_channels.noPublic": "Keine öffentlichen Kanäle", "more_channels.prev": "Zurück", - "more_channels.searchError": "Versuche, nach anderen Stichworten zu suchen, auf Tippfehlern zu prüfen oder die Filter anzupassen.", "more_channels.show_archived_channels": "Anzeigen: Archivierte Kanäle", "more_channels.show_public_channels": "Anzeigen: Öffentliche Kanäle", "more_channels.title": "Weitere Kanäle", @@ -4236,7 +4234,7 @@ "navbar_dropdown.logout": "Abmelden", "navbar_dropdown.manageGroups": "Gruppen verwalten", "navbar_dropdown.manageMembers": "Mitglieder verwalten", - "navbar_dropdown.marketplace": "Plugin-Marktplatz", + "navbar_dropdown.marketplace": "App-Marktplatz", "navbar_dropdown.menuAriaLabel": "Hauptmenü", "navbar_dropdown.nativeApps": "Apps herunterladen", "navbar_dropdown.profileSettings": "Profil", @@ -4294,9 +4292,10 @@ "onboardingTask.checklist.downloads": "Jetzt, da du alles konfiguriert hast, lade unsere Apps runter.", "onboardingTask.checklist.higher_security_features": "Interessiert an unseren Hochsicherheitsfunktionen?", "onboardingTask.checklist.main_subtitle": "Legen wir los.", + "onboardingTask.checklist.no_thanks": "Nein, danke", "onboardingTask.checklist.start_enterprise_now": "Starte jetzt Deinen kostenfreien Enterprise-Test!", "onboardingTask.checklist.task_complete_your_profile": "Vervollständige dein Profil.", - "onboardingTask.checklist.task_create_from_work_template": "Erstellen mit einer Vorlage - richte einen Kanal mit verknüpften Boards und Playbooks ein.", + "onboardingTask.checklist.task_create_from_work_template": "Erstellen aus einer Vorlage", "onboardingTask.checklist.task_download_mm_apps": "Lade die Desktop-, Tablet- und Handy-Apps runter.", "onboardingTask.checklist.task_explore_other_tools_in_platform": "Erkunde andere Tools auf der Plattform.", "onboardingTask.checklist.task_invite_team_members": "Lade Teammitglieder in den Arbeitsbereich ein.", @@ -4868,7 +4867,7 @@ "sidebar.directchannel.you": "{displayname} (Sie)", "sidebar.menu.item.notSelected": "nicht ausgewählt", "sidebar.menu.item.selected": "ausgewählt", - "sidebar.openDirectMessage": "Direktnachricht senden", + "sidebar.openDirectMessage": "Direktnachricht öffnen", "sidebar.show": "Anzeigen", "sidebar.sort": "Sortierung", "sidebar.sortedByRecencyLabel": "Letzte Aktivität", @@ -4883,6 +4882,8 @@ "sidebar.types.favorites": "FAVORITEN", "sidebar.types.unreads": "UNGELESENE", "sidebar.unreads": "Weitere Ungelesene", + "sidebar_left.addChannelsCta": "Kanäle hinzufügen", + "sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Kanal-Dropdown hinzufügen", "sidebar_left.add_channel_dropdown.browseChannels": "Kanäle durchsuchen", "sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Kanäle erstellen oder durchsuchen", "sidebar_left.add_channel_dropdown.createCategory": "Neue Kategorie erstellen", @@ -4891,7 +4892,7 @@ "sidebar_left.add_channel_dropdown.invitePeople": "Personen einladen", "sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Personen zum Team hinzufügen", "sidebar_left.add_channel_dropdown.work_template": "Aus einer Vorlage erstellen", - "sidebar_left.add_channel_dropdown.work_template_extra": "Richte einen Kanal mit verknüpften Boards und Playbooks ein", + "sidebar_left.add_channel_dropdown.work_template_extra": "Verknüpfe Kanäle, Boards und Playbooks miteinander", "sidebar_left.channel_filter.filterByUnread": "Gefiltert durch ungelesen", "sidebar_left.channel_filter.filterUnreadAria": "Ungelesen Filter", "sidebar_left.channel_filter.showAllChannels": "Alle Kanäle anzeigen", @@ -4931,6 +4932,7 @@ "sidebar_left.sidebar_channel_menu.unfavoriteChannel": "Nicht favorisieren", "sidebar_left.sidebar_channel_menu.unmuteChannel": "Stummschaltung aufheben", "sidebar_left.sidebar_channel_menu.unmuteConversation": "Stummschaltung aufheben", + "sidebar_left.sidebar_channel_navigator.addChannelsCta": "Kanäle hinzufügen", "sidebar_left.sidebar_channel_navigator.inviteUsers": "Benutzer einladen", "sidebar_right_menu.console": "Systemkonsole", "sidebar_right_menu.flagged": "Markierte Nachrichten", @@ -4979,13 +4981,21 @@ "start_trial.modal.gettingTrial": "Hole Test...", "start_trial.modal.loaded": "Geladen!", "start_trial.modal.loading": "Lade...", - "start_trial.modal_body": "Greife auf alle Plattform-Funktionen, inklusive erweiterter Sicherheit- und Unternehmens-Compliance, zu.", - "start_trial.modal_btn.nottnow": "Nicht jetzt", - "start_trial.modal_btn.start": "Starte dem freien 30-Tage Test", "start_trial.modal_btn.start_free_trial": "Kostenlose 30-Tage-Testversion starten", - "start_trial.modal_title": "Starte jetzt deine kostenlose Enterprise-Testversion", "start_trial.tutorialTip.desc": "Probiere unsere am meisten gefragten Premium Funktionen: Lege den Benutzerzugriff mit Gast-Konten fest, automatisiere Compliance Berichte und sende sichere, mobile Nur-ID Push-Nachrichten.", "start_trial.tutorialTip.title": "Teste kostenfrei Premium Funktionen", + "start_trial_form.company_name": "Firmenname", + "start_trial_form.company_size": "Größe des Unternehmens", + "start_trial_form.disclaimer": "Mit Auswählen von Starte Test, stimme ich dem Mattermost Software Evaluation Agreement und der Datenschutzrichtlinie zu, und erhalte Produktemails.", + "start_trial_form.email": "Geschäftliche E-Mail", + "start_trial_form.invalid_business_email": "Bitte gib eine gültige geschäftliche E-Mail-Adresse ein.", + "start_trial_form.modal_body": "Nur ein paar kurze Hinweise, die uns helfen, deine Testerfahrung individuell zu gestalten", + "start_trial_form.modal_btn.start": "Test starten", + "start_trial_form.modal_title": "Test starten", + "start_trial_form.name": "Name", + "start_trial_form_modal.failureModal.subtitle": "Es gab ein Problem bei der Bearbeitung deiner Testanfrage.", + "start_trial_form_modal.failureModal.subtitle2": "Bitte versuche es erneut oder kontaktiere den Support.", + "start_trial_form_modal.failureModal.title": "Bitte versuche es erneut", "status_dropdown.dnd_sub_menu_header": "Benachrichtigungen ausschalten bis:", "status_dropdown.dnd_sub_menu_item.custom": "Benutzerdefiniert", "status_dropdown.dnd_sub_menu_item.one_hour": "Eine Stunde", @@ -5669,6 +5679,7 @@ "welcome_post_renderer.user_message.first_paragraph": "Mattermost ist eine Open-Source-Plattform für sichere Kommunikation, Zusammenarbeit und Orchestrierung der Arbeit über Tools und Teams hinweg.", "welcome_post_renderer.user_message.second_paragraph": "Hier findest Du eine Liste von Befehlen, die Du verwenden kannst, um sich mit der Plattform vertraut zu machen.", "welcome_post_renderer.user_message.title": "Willkommen bei Mattermost! :rocket:", + "widget.input.clear": "Leeren", "widget.input.required": "Dieses Feld wird benötigt", "widget.passwordInput.createPassword": "Wähle ein Passwort", "widget.passwordInput.password": "Passwort", @@ -5687,7 +5698,7 @@ "work_templates.customize.name_label_channels_playbooks": "Benenne deinen Kanal und Playbook", "work_templates.customize.private_playbook_license_issue": "Für private Playbooks ist eine Enterprise-Lizenz erforderlich.", "work_templates.customize.visibility_title": "Wer soll Zugang dazu haben?", - "work_templates.menu.modal_title": "Beginne mit einer Vorlage", + "work_templates.menu.modal_title": "Erstellen aus einer Vorlage", "work_templates.menu.quick_use": "Schneller Einsatz", "work_templates.menu.template_title": "VORLAGE", "work_templates.menu.usecase_boards_count": "{boardsCount, plural, =1 {# Board} other {# Boards}}", diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index 93298430ab..55cd9e1e97 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -2653,7 +2653,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} removed the channel purpose (was: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} updated the channel purpose from: {old} to: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} updated the channel purpose to: {new}", - "app.plugin.marketplace_plugins.app_error": "Error connecting to the marketplace server. Please check your settings in the [System Console]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Error: {error}", "apps.error.command.field_missing": "Required fields missing: `{fieldName}`.", "apps.error.command.same_channel": "Channel repeated for field `{fieldName}`: `{option}`.", @@ -3442,6 +3441,9 @@ "filtered_user_list.userStatus": "User Status:", "flag_post.flag": "Save", "flag_post.unflag": "Remove from Saved", + "footer_pagination.count": "Showing {startCount, number}-{endCount, number} of {total, number}", + "footer_pagination.next": "Next", + "footer_pagination.prev": "Previous", "forward_post_button.label": "Forward", "forward_post_modal.button.cancel": "Cancel", "forward_post_modal.button.forward": "Forward", @@ -4086,8 +4088,9 @@ "mark_all_threads_as_read_modal.title": "Mark all your threads as read?", "marketplace_command.disabled": "The marketplace is disabled. Please contact your System Administrator for details.", "marketplace_command.no_permission": "You do not have the appropriate permissions to access the marketplace.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total", - "marketplace_modal.install_plugins": "Install Plugins", + "marketplace_modal_list.no_plugins_filter": "No results for \"{filter}\"", + "marketplace_modal.app_error": "Error connecting to the marketplace server. Please check your settings in the System Console.", + "marketplace_modal.install_plugins": "Install plugins", "marketplace_modal.installing": "Installing...", "marketplace_modal.list.configure": "Configure", "marketplace_modal.list.configure.plugin": "Configure {plugin}", @@ -4104,12 +4107,12 @@ "marketplace_modal.list.update_confirmation.message.warning_major_version": "This update may contain breaking changes.", "marketplace_modal.list.update_confirmation.message.warning_major_version_with_release_notes": "This update may contain breaking changes. Consult the [release notes](!{releaseNotesUrl}) before upgrading.", "marketplace_modal.list.update_confirmation.title": "Confirm Plugin Update", - "marketplace_modal.no_plugins": "There are no plugins available at this time.", - "marketplace_modal.no_plugins_installed": "You do not have any plugins installed.", - "marketplace_modal.search": "Search Marketplace", + "marketplace_modal.no_plugins": "No plugins found", + "marketplace_modal.no_plugins_installed": "No plugins installed found", + "marketplace_modal.search": "Search marketplace", "marketplace_modal.tabs.all_listing": "All", - "marketplace_modal.tabs.installed_listing": "Installed", - "marketplace_modal.title": "Marketplace", + "marketplace_modal.tabs.installed_listing": "Installed ({count})", + "marketplace_modal.title": "App Marketplace", "members_popover.button.message": "message", "menu.cloudFree.enterpriseTrialDescription": "Your trial is active until {trialEndDay}. Discover our top Enterprise features. Learn more", "menu.cloudFree.enterpriseTrialTitle": "Enterprise Trial", @@ -4252,6 +4255,9 @@ "navbar.viewPinnedPosts": "View Pinned Posts", "newChannelWithBoard.tutorialTip.description": "The board you just created can be quickly accessed by clicking on the Boards icon in the App bar. You can view the boards that are linked to this channel in the right-hand sidebar and open one in full view.", "newChannelWithBoard.tutorialTip.title": "Access linked boards from the App Bar", + "newsletter_optin.checkmark.text": "I would like to receive Mattermost security updates via newsletter. Data Terms and Conditions apply", + "newsletter_optin.desc": "Sign up at {link}.", + "newsletter_optin.title": "Interested in receiving Mattermost security updates via newsletter?", "next_steps_view.welcomeToMattermost": "Welcome to Mattermost", "no_results.channel_files_filtered.subtitle": "This channel doesn't contains any file with the selected file format.", "no_results.channel_files_filtered.title": "No files found", @@ -4309,10 +4315,10 @@ "onboardingTask.checklist.downloads": "Now that you’re all set up, download our apps.", "onboardingTask.checklist.higher_security_features": "Interested in our higher-security features?", "onboardingTask.checklist.main_subtitle": "Let's get up and running.", - "onboardingTask.checklist.no_tanks": "No, thanks", + "onboardingTask.checklist.no_thanks": "No, thanks", "onboardingTask.checklist.start_enterprise_now": "Start your free Enterprise trial now!", "onboardingTask.checklist.task_complete_your_profile": "Complete your profile.", - "onboardingTask.checklist.task_create_from_work_template": "Create from a template - set up a channel with linked boards and playbooks.", + "onboardingTask.checklist.task_create_from_work_template": "Create from a template", "onboardingTask.checklist.task_download_mm_apps": "Download the Desktop and Mobile Apps.", "onboardingTask.checklist.task_explore_other_tools_in_platform": "Explore other tools in the platform.", "onboardingTask.checklist.task_invite_team_members": "Invite team members to the workspace.", @@ -4880,15 +4886,15 @@ "shortcuts.team_nav.switcher": "Navigate to a specific team:\tCtrl|Alt|[1-9]", "shortcuts.team_nav.switcher.mac": "Navigate to a specific team:\t⌘|⌥|[1-9]", "sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Add Channel Dropdown", - "sidebar_left.add_channel_dropdown.browseChannels": "Browse Channels", + "sidebar_left.add_channel_dropdown.browseChannels": "Browse channels", "sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Browse or create channels", - "sidebar_left.add_channel_dropdown.createCategory": "Create New Category", - "sidebar_left.add_channel_dropdown.createNewChannel": "Create New Channel", + "sidebar_left.add_channel_dropdown.createCategory": "Create new category", + "sidebar_left.add_channel_dropdown.createNewChannel": "Create new channel", "sidebar_left.add_channel_dropdown.dropdownAriaLabel": "Add Channel Dropdown", - "sidebar_left.add_channel_dropdown.invitePeople": "Invite People", + "sidebar_left.add_channel_dropdown.invitePeople": "Invite people", "sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Add people to the team", "sidebar_left.add_channel_dropdown.work_template": "Create from a template", - "sidebar_left.add_channel_dropdown.work_template_extra": "Set up a channel with linked boards, and playbooks", + "sidebar_left.add_channel_dropdown.work_template_extra": "Link channels, boards, and playbooks together", "sidebar_left.addChannelsCta": "Add channels", "sidebar_left.channel_filter.filterByUnread": "Filter by unread", "sidebar_left.channel_filter.filterUnreadAria": "unreads filter", @@ -4940,7 +4946,7 @@ "sidebar.directchannel.you": "{displayname} (you)", "sidebar.menu.item.notSelected": "not selected", "sidebar.menu.item.selected": "selected", - "sidebar.openDirectMessage": "Open a Direct Message", + "sidebar.openDirectMessage": "Open a direct message", "sidebar.show": "Show", "sidebar.sort": "Sort", "sidebar.sortedByRecencyLabel": "Recent activity", @@ -5697,6 +5703,7 @@ "welcome_post_renderer.user_message.first_paragraph": "Mattermost is an open source platform for secure communication, collaboration, and orchestration of work across tools and teams.", "welcome_post_renderer.user_message.second_paragraph": "Here is a list of commands to use to try and get familiar with the platform.", "welcome_post_renderer.user_message.title": "Welcome to Mattermost! :rocket:", + "widget.input.clear": "Clear", "widget.input.required": "This field is required", "widget.passwordInput.createPassword": "Choose a Password", "widget.passwordInput.password": "Password", @@ -5715,7 +5722,7 @@ "work_templates.customize.name_label_channels_playbooks": "Name your channel and playbook", "work_templates.customize.private_playbook_license_issue": "Private playbooks requires an Enterprise license.", "work_templates.customize.visibility_title": "Who should have access to this?", - "work_templates.menu.modal_title": "Start from a template", + "work_templates.menu.modal_title": "Create from a template", "work_templates.menu.quick_use": "Quick use", "work_templates.menu.template_title": "TEMPLATE", "work_templates.menu.usecase_boards_count": "{boardsCount, plural, =1 {# board} other {# boards}}", diff --git a/webapp/channels/src/i18n/en_AU.json b/webapp/channels/src/i18n/en_AU.json index a8a38f34e8..628ed6d96c 100644 --- a/webapp/channels/src/i18n/en_AU.json +++ b/webapp/channels/src/i18n/en_AU.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise Edition", "about.hash": "Build Hash:", "about.hashee": "EE Build Hash:", - "about.hashwebapp": "Webapp Build Hash:", "about.licensed": "Licensed to:", "about.notice": "Mattermost is made possible by the open source software used in our server, desktop and mobile apps.", "about.privacy": "Privacy Policy", @@ -2644,7 +2643,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} removed the channel purpose (was: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} updated the channel purpose from: {old} to: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} updated the channel purpose to: {new}", - "app.plugin.marketplace_plugins.app_error": "Error connecting to the marketplace server. Please check your settings in the [System Console]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Error: {error}", "apps.error.command.field_missing": "Required fields missing: `{fieldName}`.", "apps.error.command.same_channel": "Channel repeated for field `{fieldName}`: `{option}`.", @@ -4077,7 +4075,6 @@ "mark_all_threads_as_read_modal.title": "Mark all your threads as read?", "marketplace_command.disabled": "The marketplace is disabled. Please contact your System Administrator for details.", "marketplace_command.no_permission": "You do not have the appropriate permissions to access the marketplace.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total", "marketplace_modal.install_plugins": "Install Plugins", "marketplace_modal.installing": "Installing...", "marketplace_modal.list.configure": "Configure", @@ -4280,7 +4277,7 @@ "onboardingTask.checklist.main_subtitle": "Let's get up and running.", "onboardingTask.checklist.start_enterprise_now": "Start your free Enterprise trial now!", "onboardingTask.checklist.task_complete_your_profile": "Complete your profile", - "onboardingTask.checklist.task_create_from_work_template": "Create from a template - set up a channel with linked boards and playbooks.", + "onboardingTask.checklist.task_create_from_work_template": "Create from a template", "onboardingTask.checklist.task_download_mm_apps": "Download the Desktop and Mobile Apps", "onboardingTask.checklist.task_explore_other_tools_in_platform": "Explore other tools in the platform.", "onboardingTask.checklist.task_invite_team_members": "Invite team members to the workspace", @@ -4831,7 +4828,7 @@ "sidebar.directchannel.you": "{displayname} (you)", "sidebar.menu.item.notSelected": "not selected", "sidebar.menu.item.selected": "selected", - "sidebar.openDirectMessage": "Open a Direct Message", + "sidebar.openDirectMessage": "Open a direct message", "sidebar.show": "Show", "sidebar.sort": "Sort", "sidebar.sortedByRecencyLabel": "Recent activity", @@ -4846,15 +4843,15 @@ "sidebar.types.favorites": "FAVOURITES", "sidebar.types.unreads": "UNREADS", "sidebar.unreads": "More unreads", - "sidebar_left.add_channel_dropdown.browseChannels": "Browse Channels", + "sidebar_left.add_channel_dropdown.browseChannels": "Browse channels", "sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Browse or create channels", - "sidebar_left.add_channel_dropdown.createCategory": "Create New Category", - "sidebar_left.add_channel_dropdown.createNewChannel": "Create New Channel", + "sidebar_left.add_channel_dropdown.createCategory": "Create new category", + "sidebar_left.add_channel_dropdown.createNewChannel": "Create new channel", "sidebar_left.add_channel_dropdown.dropdownAriaLabel": "Add Channel Dropdown", - "sidebar_left.add_channel_dropdown.invitePeople": "Invite People", + "sidebar_left.add_channel_dropdown.invitePeople": "Invite people", "sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Add people to the team", "sidebar_left.add_channel_dropdown.work_template": "Create from a template", - "sidebar_left.add_channel_dropdown.work_template_extra": "Set up a channel with linked boards, and playbooks", + "sidebar_left.add_channel_dropdown.work_template_extra": "Link channels, boards, and playbooks together", "sidebar_left.channel_filter.filterByUnread": "Filter by unread", "sidebar_left.channel_filter.filterUnreadAria": "unreads filter", "sidebar_left.channel_filter.showAllChannels": "Show all channels", @@ -4942,11 +4939,7 @@ "start_trial.modal.gettingTrial": "Getting Trial...", "start_trial.modal.loaded": "Loaded!", "start_trial.modal.loading": "Loading...", - "start_trial.modal_body": "Access all platform features including advanced security and enterprise compliance.", - "start_trial.modal_btn.nottnow": "Not now", - "start_trial.modal_btn.start": "Start a free 30-day trial", "start_trial.modal_btn.start_free_trial": "Start a free 30-day trial", - "start_trial.modal_title": "Start your free Enterprise trial now", "start_trial.tutorialTip.desc": "Explore our most requested premium features. Determine user access with Guest Accounts, automate compliance reports, and send secure ID-only mobile push notifications.", "start_trial.tutorialTip.title": "Try our premium features for free", "status_dropdown.dnd_sub_menu_header": "Disable notifications until:", @@ -5650,7 +5643,7 @@ "work_templates.customize.name_label_channels_playbooks": "Name your channel and playbook", "work_templates.customize.private_playbook_license_issue": "Private playbooks requires an Enterprise license.", "work_templates.customize.visibility_title": "Who should have access to this?", - "work_templates.menu.modal_title": "Start from a template", + "work_templates.menu.modal_title": "Create from a template", "work_templates.menu.quick_use": "Quick use", "work_templates.menu.template_title": "TEMPLATE", "work_templates.menu.usecase_boards_count": "{boardsCount, plural, =1 {# board} other {# boards}}", diff --git a/webapp/channels/src/i18n/es.json b/webapp/channels/src/i18n/es.json index 28de93f5e2..a29c97938c 100644 --- a/webapp/channels/src/i18n/es.json +++ b/webapp/channels/src/i18n/es.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Edición Empresarial E1", "about.hash": "Hash de compilación:", "about.hashee": "Hash de compilación de EE:", - "about.hashwebapp": "Hash de compilación de la App Web:", "about.licensed": "Licenciado a:", "about.notice": "Mattermost es hecho posible gracias a software de código libre utilizado en nuestro servidor, desktop y apps móviles.", "about.privacy": "Política de Privacidad", @@ -2567,7 +2566,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} eliminó el propósito del canal (era: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} ha actualizado el propósito del canal de: {old} a: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} ha actualizado el propósito del canal a: {new}", - "app.plugin.marketplace_plugins.app_error": "Error de conexión con el servidor del marketplace. Por favor revisa los ajustes en la [Consola del Sistema]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Error: {error}", "apps.error.command.field_missing": "Faltan campos obligatorios: `{fieldName}`.", "apps.error.command.same_channel": "Canal repetido para el campo `{fieldName}`: `{option}`.", @@ -3828,7 +3826,6 @@ "manage_channel_groups_modal.search_placeholder": "Buscar grupos", "manage_team_groups_modal.search_placeholder": "Buscar grupos", "mark_all_threads_as_read_modal.cancel": "Cancelar", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} de {total, number} total", "marketplace_modal.install_plugins": "Instalar Plugins", "marketplace_modal.installing": "Instalando...", "marketplace_modal.list.configure": "Configurar", @@ -4492,11 +4489,7 @@ "start_trial.modal.gettingTrial": "Obteniendo Prueba...", "start_trial.modal.loaded": "¡Cargado!", "start_trial.modal.loading": "Cargando...", - "start_trial.modal_body": "Accede a todas las características de la plataforma incluyendo seguridad avanzada y cumplimiento empresarial.", - "start_trial.modal_btn.nottnow": "Ahora no", - "start_trial.modal_btn.start": "¡Comienza la pruba gratuita de 30 días", "start_trial.modal_btn.start_free_trial": "Comienza tu prueba gratuita de 30 días", - "start_trial.modal_title": "Comienza tu prueba gratuita Enterprise ahora", "start_trial.tutorialTip.title": "Prueba nuestras características premium de forma gratuita", "status_dropdown.dnd_sub_menu_header": "Desactivar las notificaciones hasta:", "status_dropdown.dnd_sub_menu_item.custom": "Personalizado", @@ -5114,6 +5107,7 @@ "widgets.users_emails_input.loading": "Cargando", "widgets.users_emails_input.no_user_found_matching": "No se ha encontrado a nadie que coincida con **{text}**. Introduce su correo electrónico para invitarle.", "widgets.users_emails_input.valid_email": "Agregar **{email}**", + "work_templates.preview.section.included": "included sp", "workspace_limits.archived_file.archived_compact": "(archivado)", "workspace_limits.file_storage": "Almacenamiento de archivos", "workspace_limits.file_storage.short": "Archivos", diff --git a/webapp/channels/src/i18n/fa.json b/webapp/channels/src/i18n/fa.json index 90132c93a1..40a31e2d1a 100644 --- a/webapp/channels/src/i18n/fa.json +++ b/webapp/channels/src/i18n/fa.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "نسخه تجاری", "about.hash": "هش ساخت:", "about.hashee": "هش ساخت نسخه تجاری:", - "about.hashwebapp": "هش بیلد وب‌اپ:", "about.licensed": "مجوز داده شده به:", "about.notice": "Mattermost یک نرم افزار متن باز توسه یافته است . جهت بررسی نسخه ها در server و نسخه دسکتاپ در desktop و اپلیکشن موبایل در mobile اطلاع رسانی می شود.", "about.privacy": "سیاست حفظ حریم خصوصی", @@ -2404,7 +2403,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} هدف کانال را حذف کرد (بود: {قدیمی})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} هدف کانال را از: {old} به: {new} به‌روزرسانی کرد", "app.channel.post_update_channel_purpose_message.updated_to": "{username} هدف کانال را به‌روزرسانی کرد: {new}", - "app.plugin.marketplace_plugins.app_error": "خطا در اتصال به سرور بازار. لطفاً تنظیمات خود را در [System Console] ({siteURL}/admin_console/plugins/plugin_management) بررسی کنید.", "apps.error": "خطا: {error}", "apps.error.command.field_missing": "فیلدهای لازم وجود ندارد: `{fieldName}`.", "apps.error.command.same_channel": "تکرار کانال برای فیلد «{fieldName}»: «{option}».", @@ -3634,7 +3632,6 @@ "manage_channel_groups_modal.search_placeholder": "جستجو در گروه ها", "manage_team_groups_modal.search_placeholder": "جستجو در گروه ها", "mark_all_threads_as_read_modal.cancel": "لغو", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} از مجموع {total, number}", "marketplace_modal.install_plugins": "پلاگین ها را نصب کنید", "marketplace_modal.installing": "در حال نصب...", "marketplace_modal.list.configure": "پیکربندی کنید", @@ -4282,10 +4279,6 @@ "start_trial.modal.gettingTrial": "در حال دریافت دوره آزمایشی...", "start_trial.modal.loaded": "لود شده!", "start_trial.modal.loading": "بارگذاری...", - "start_trial.modal_body": "به تمام ویژگی های پلت فرم از جمله امنیت پیشرفته و انطباق سازمانی دسترسی داشته باشید.", - "start_trial.modal_btn.nottnow": "الان نه", - "start_trial.modal_btn.start": "شروع آزمایشی 30 روزه", - "start_trial.modal_title": "اکنون آزمایشی رایگان Enterprise خود را شروع کنید", "status_dropdown.dnd_sub_menu_header": "غیرفعال کردن اعلان ها تا زمانی که:", "status_dropdown.dnd_sub_menu_item.custom": "سفارشی", "status_dropdown.dnd_sub_menu_item.one_hour": "1 ساعت", diff --git a/webapp/channels/src/i18n/fr.json b/webapp/channels/src/i18n/fr.json index 0b4cc9471e..27f84b505c 100644 --- a/webapp/channels/src/i18n/fr.json +++ b/webapp/channels/src/i18n/fr.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Édition Entreprise", "about.hash": "Hash de version :", "about.hashee": "Hash de version EE :", - "about.hashwebapp": "Hash de la webapp :", "about.licensed": "Licence accordée à :", "about.notice": "Mattermost est rendu possible grâce aux logiciels open source utilisés dans notre serveur, applications de bureau et applications mobiles.", "about.privacy": "Politique de respect de la vie privée", @@ -2508,7 +2507,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} a supprimé la description du canal (précédemment : {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} a mis à jour la description du canal de : {old} en : {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} a mis à jour la description du canal en : {new}", - "app.plugin.marketplace_plugins.app_error": "Une erreur s'est produite lors de la connexion au serveur de place de marché. Veuillez vérifier vos paramètres dans la [Console du système]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Erreur : {error}", "apps.error.command.field_missing": "Champs obligatoires manquants : `{fieldName}`.", "apps.error.command.same_channel": "Canal répété pour le champ `{fieldName}` : `{option}`.", @@ -3066,7 +3064,7 @@ "edit_post.time_limit_modal.subscript": "Définit la durée durant laquelle les utilisateurs pourront modifier leur message après l'avoir envoyé.", "edit_post.time_limit_modal.title": "Configurer le délai global d'édition de messages", "email_verify.almost": "Vous avez pratiquement terminé !", - "email_verify.failed": " Échec de l'envoi de l'e-mail de vérification.", + "email_verify.failed": "Maxtrem271991@gmail.com", "email_verify.notVerifiedBody": "Veuillez vérifier votre adresse e-mail dans l'attente de la réception d'un e-mail de vérification.", "email_verify.resend": "Renvoyer l'e-mail", "email_verify.sent": " L'e-mail de vérification a été envoyé.", @@ -3667,7 +3665,6 @@ "login_mfa.token": "Jeton d'authentification multi-facteurs (MFA)", "manage_channel_groups_modal.search_placeholder": "Rechercher des groupes", "manage_team_groups_modal.search_placeholder": "Rechercher des groupes", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} sur un total de {total, number}", "marketplace_modal.install_plugins": "Installer des extensions", "marketplace_modal.installing": "Installation en cours...", "marketplace_modal.list.configure": "Configurer", @@ -4269,11 +4266,7 @@ "start_trial.modal.gettingTrial": "Obtention d'un essai...", "start_trial.modal.loaded": "Chargé !", "start_trial.modal.loading": "Chargement...", - "start_trial.modal_body": "Accédez à toutes les fonctionnalités de la plateforme, y compris la sécurité avancée et la conformité de l'entreprise.", - "start_trial.modal_btn.nottnow": "Pas maintenant", - "start_trial.modal_btn.start": "Commencer un essai gratuit de 30 jours", "start_trial.modal_btn.start_free_trial": "Commencez l'essai gratuit de 30 jours", - "start_trial.modal_title": "Commencez votre essai gratuit de la version Enterprise maintenant", "start_trial.tutorialTip.desc": "Explorez nos fonctionnalités premium les plus demandées. Déterminez l'accès des utilisateurs avec les comptes d'invités, automatisez les rapports de conformité et envoyez des notifications push mobiles sécurisées uniquement pour les identifiants.", "start_trial.tutorialTip.title": "Essayez gratuitement nos fonctionnalités premium", "status_dropdown.dnd_sub_menu_header": "Désactiver les notifications jusqu'à :", @@ -4471,7 +4464,13 @@ "user.settings.advance.sendDesc.mac": "Si activé, ⌘ + ENTRÉE envoie le message et ENTRÉE insère une nouvelle ligne.", "user.settings.advance.sendTitle": "Envoi des messages avec CTRL+ENTRÉE", "user.settings.advance.sendTitle.mac": "Envoi des messages avec ⌘+ENTRÉE", + "user.settings.advance.startFromLeftOff": "Reprendre là où je m'étais arrêté", + "user.settings.advance.startFromNewest": "Commencez par le message le plus récent", + "user.settings.advance.syncDrafts.Desc": "Lorsqu'ils sont activés, les brouillons de messages sont synchronisés avec le serveur afin qu'ils soient accessibles depuis n'importe quel appareil. Lorsqu'ils sont désactivés, les brouillons de message ne sont enregistrés que localement sur l'appareil sur lequel ils sont rédigés.", + "user.settings.advance.syncDrafts.Title": "Autoriser la synchronisation des messages en brouillon avec le serveur", "user.settings.advance.title": "Paramètres avancés", + "user.settings.advance.unreadScrollPositionDesc": "Choisissez le positionnement lors d'un canal non lu. Les canaux sont toujours marqués comme lus lorsqu'ils sont consultés.", + "user.settings.advance.unreadScrollPositionTitle": "Positionnement lors qu'un canal non lu", "user.settings.custom_theme.awayIndicator": "Indicateur « absent »", "user.settings.custom_theme.buttonBg": "Arrière-plan du bouton", "user.settings.custom_theme.buttonColor": "Texte de bouton", @@ -4875,6 +4874,8 @@ "web.header.back": "Précédent", "web.header.logout": "Se déconnecter", "web.root.signup_info": "Toute la communication de votre équipe au même endroit, accessible de partout", + "welcome_post_renderer.user_message.first_paragraph": "Mattermost is an open source platform for secure communication, collaboration, and orchestration of work across tools and teams.", + "welcome_post_renderer.user_message.title": "\t ", "widget.input.required": "Ce champ est requis", "widgets.channels_input.empty": "Aucun canal trouvé", "widgets.channels_input.loading": "Chargement", diff --git a/webapp/channels/src/i18n/hu.json b/webapp/channels/src/i18n/hu.json index cf69f79c4c..4402385b81 100644 --- a/webapp/channels/src/i18n/hu.json +++ b/webapp/channels/src/i18n/hu.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise Kiadás", "about.hash": "Kiadás ujjlenyomata:", "about.hashee": "EE kiadás ujjlenyomata:", - "about.hashwebapp": "Webapp kiadás ujjlenyomat:", "about.licensed": "Licenc tulajdonosa:", "about.notice": "A Mattermost rendszert a szerver, asztali és mobilalkalmazásainkban használt nyílt forráskódú szoftverek teszik lehetővé.", "about.privacy": "Adatvédelmi irányelvek", @@ -2544,7 +2543,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} eltávolította a csatorna célját (volt: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} frissítette a csatorna célját: {old} -> {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} frissítette a csatorna célját erre: {new}", - "app.plugin.marketplace_plugins.app_error": "Hiba történt a piactér szerveréhez való csatlakozáskor. Kérjük, ellenőrizze a beállításait a [Rendszerkonzolban]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Hiba: {error}", "apps.error.command.field_missing": "Hiányzó kötelező mezők: `{fieldName}`.", "apps.error.command.same_channel": "Beszélgetés ismételve lett a mezőhöz `{fieldName}`: `{option}`.", @@ -3846,7 +3844,6 @@ "login_mfa.token": "Többtényezős hitelesítés (MFA) token", "manage_channel_groups_modal.search_placeholder": "Csoportok keresése", "manage_team_groups_modal.search_placeholder": "Csoportok keresése", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} bővítmény / {total, number} összesen", "marketplace_modal.install_plugins": "Bővítmények telepítése", "marketplace_modal.installing": "Telepítés...", "marketplace_modal.list.configure": "Beállítás", @@ -4576,11 +4573,7 @@ "start_trial.modal.gettingTrial": "Próbaidőszak kezdése...", "start_trial.modal.loaded": "Betöltve!", "start_trial.modal.loading": "Betöltés...", - "start_trial.modal_body": "Férjen hozzá az összes platform szolgáltatáshoz beleértve a fejlett biztonságot és a vállalati megfelelősséget.", - "start_trial.modal_btn.nottnow": "Ne most", - "start_trial.modal_btn.start": "30 napos ingyenes próbaidőszak indítása", "start_trial.modal_btn.start_free_trial": "30 napos ingyenes próbaidőszak indítása", - "start_trial.modal_title": "Az ingyenes Enterprise kiadás próbaidőszak indítása", "start_trial.tutorialTip.desc": "Fedezze fel a legkeresettebb prémium funkcióinkat. Határozza meg a felhasználók hozzáférését a vendégfiókok segítségével, automatizálja a megfelelőségi jelentéseket, és küldjön biztonságos, csak azonosítóval ellátott mobil push-értesítéseket.", "start_trial.tutorialTip.title": "Próbálja ki prémium szolgáltatásainkat ingyen", "status_dropdown.dnd_sub_menu_header": "Értesítések kikapcsolása eddig:", diff --git a/webapp/channels/src/i18n/it.json b/webapp/channels/src/i18n/it.json index b64ecd9cd4..af4a0b1412 100644 --- a/webapp/channels/src/i18n/it.json +++ b/webapp/channels/src/i18n/it.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Edizione Enterprise", "about.hash": "Hash di compilazione:", "about.hashee": "Hash di compilazione EE:", - "about.hashwebapp": "Webapp Build Hash:", "about.licensed": "Licenza concessa a:", "about.notice": "Mattermost è reso possibile dal software open source utilizzato sul nostro server, desktop e mobile apps.", "about.privacy": "Politica sulla privacy", @@ -1970,7 +1969,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} ha cancellato lo scopo del canale (era: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} ha aggiornato lo scopo del canale da: {old} a: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} ha aggiornato lo scopo del canale a: {new}", - "app.plugin.marketplace_plugins.app_error": "Errore connessione al server marketplace. Controllare le impostazioni in [Console di Sistema]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Errore: {error}", "apps.error.command.field_missing": "Campo richiesto mancante: `{fieldName}`.", "apps.error.command.same_channel": "Canale ripetuto per il campo `{fieldName}`: `{option}`.", @@ -2932,7 +2930,6 @@ "login_mfa.token": "Token MFA", "manage_channel_groups_modal.search_placeholder": "Cerca gruppi", "manage_team_groups_modal.search_placeholder": "Cerca gruppi", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} di {total, number} totali", "marketplace_modal.install_plugins": "Installa Plugin", "marketplace_modal.installing": "Installazione...", "marketplace_modal.list.configure": "Configura", diff --git a/webapp/channels/src/i18n/ja.json b/webapp/channels/src/i18n/ja.json index fee7cc2e97..9ab8b3f551 100644 --- a/webapp/channels/src/i18n/ja.json +++ b/webapp/channels/src/i18n/ja.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise Edition", "about.hash": "ビルドハッシュ値:", "about.hashee": "EEビルドハッシュ値:", - "about.hashwebapp": "Webアプリのビルドハッシュ:", "about.licensed": "ライセンス供給先:", "about.notice": "Mattermostはserverdesktopmobileで使用されているオープンソースソフトウェアによって実現されています。", "about.privacy": "プライバシーポリシー", @@ -2648,7 +2647,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} がチャンネルの目的({old})を削除しました", "app.channel.post_update_channel_purpose_message.updated_from": "{username} がチャンネルの目的を {old} から {new} へ更新しました", "app.channel.post_update_channel_purpose_message.updated_to": "{username} がチャンネルの目的を {new} へ更新しました", - "app.plugin.marketplace_plugins.app_error": "マーケットプレースサーバーへ接続する際にエラーが発生しました。[システムコンソール]({siteURL}/admin_console/plugins/plugin_management)内の設定を確認してください。", "apps.error": "エラー: {error}", "apps.error.command.field_missing": "必須フィールドが存在しません: `{fieldName}`。", "apps.error.command.same_channel": "フィールド `{fieldName}` でチャンネルが繰り返し指定されました: `{option}`。", @@ -4081,7 +4079,6 @@ "mark_all_threads_as_read_modal.title": "すべてのスレッドを既読にしますか?", "marketplace_command.disabled": "マーケットプレイスが無効になっています。詳しくは、システム管理者に問い合わせてください。", "marketplace_command.no_permission": "マーケットプレイスにアクセスするための適切な権限を持っていません。", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} 全 {total, number} プラグイン中", "marketplace_modal.install_plugins": "プラグインをインストールする", "marketplace_modal.installing": "インストールしています...", "marketplace_modal.list.configure": "設定", @@ -4156,22 +4153,10 @@ "modal.manual_status.title_offline": "ステータスが \"オフライン\" になりました", "modal.manual_status.title_ooo": "ステータスが \"外出中\" になりました", "more.details": "もっと詳しく", - "more_channels.channel_purpose": "チャンネル情報: メンバーシップ状況: 加入済, メンバー数 {memberCount} , 目的: {channelPurpose}", - "more_channels.count": "{count}件", - "more_channels.count_one": "1件", - "more_channels.count_zero": "0件", "more_channels.create": "チャンネルを作成する", - "more_channels.hide_joined": "参加したことを表示しない", - "more_channels.hide_joined_checked": "チャンネルに参加したことを表示しないチェックボックスがチェック済です", - "more_channels.hide_joined_not_checked": "チャンネルに参加したことを表示しないチェックボックスがチェックされていません", - "more_channels.joined": "参加済", - "more_channels.membership_indicator": "メンバーシップ状況: 参加済", "more_channels.next": "次へ", - "more_channels.noArchived": "アーカイブされたチャンネルはありません", "more_channels.noMore": "\"{text}\"の結果はありません", - "more_channels.noPublic": "公開チャンネルはありません", "more_channels.prev": "前へ", - "more_channels.searchError": "違うキーワードで検索してみたり、入力ミスを確認したり、フィルター設定を変更して再度お試しください。", "more_channels.show_archived_channels": "表示: アーカイブチャンネル", "more_channels.show_public_channels": "表示: 公開チャンネル", "more_channels.title": "他のチャンネル", @@ -4979,11 +4964,7 @@ "start_trial.modal.gettingTrial": "トライアル開始中...", "start_trial.modal.loaded": "読み込みが完了しました!", "start_trial.modal.loading": "読み込み中です...", - "start_trial.modal_body": "高度なセキュリティとコンプライアンス機能を含むプラットフォームのすべての機能にアクセスできます。", - "start_trial.modal_btn.nottnow": "今はしない", - "start_trial.modal_btn.start": "30日間の無料トライアルを開始する", "start_trial.modal_btn.start_free_trial": "30日間の無料トライアルを開始する", - "start_trial.modal_title": "今すぐEnterprise版の無料トライアルを開始する", "start_trial.tutorialTip.desc": "最も要望の多いプレミアム機能を紹介します。ゲストアカウント、コンプライアンスレポートの自動化、IDのみを利用したセキュアなモバイルプッシュ通知。", "start_trial.tutorialTip.title": "プレミアム機能を無料でお試しいただけます", "status_dropdown.dnd_sub_menu_header": "通知を無効にする期間:", diff --git a/webapp/channels/src/i18n/ko.json b/webapp/channels/src/i18n/ko.json index a73022b548..7f6391fcd7 100644 --- a/webapp/channels/src/i18n/ko.json +++ b/webapp/channels/src/i18n/ko.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "엔터프라이즈 에디션", "about.hash": "빌드 해쉬:", "about.hashee": "EE 빌드 해쉬:", - "about.hashwebapp": "Webapp 빌드 해쉬:", "about.licensed": "다음 사용자에게 허가되었습니다:", "about.notice": "Mattermost는 서버, 데스크톱 그리고 모바일에서 오픈소스 소프트웨어로 이용 가능합니다.", "about.privacy": "개인정보처리방침", @@ -2834,7 +2833,6 @@ "login_mfa.token": "MFA 토큰", "manage_channel_groups_modal.search_placeholder": "그룹 찾기", "manage_team_groups_modal.search_placeholder": "그룹 찾기", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total", "marketplace_modal.install_plugins": "설치된 플러그인", "marketplace_modal.installing": "설치중...", "marketplace_modal.list.configure": "설정", @@ -3173,7 +3171,6 @@ "signup_user_completed.validEmail": "유효한 전자우편 주소를 입력해주세요", "someting.string": "기본문자열", "start_trial.modal.loading": "로딩중...", - "start_trial.modal_btn.start": "30일 평가판 시작", "status_dropdown.menuAriaLabel": "set status", "status_dropdown.set_away": "다른 용무 중", "status_dropdown.set_dnd": "방해 금지", diff --git a/webapp/channels/src/i18n/nl.json b/webapp/channels/src/i18n/nl.json index 2b0c2deccf..456a23818f 100644 --- a/webapp/channels/src/i18n/nl.json +++ b/webapp/channels/src/i18n/nl.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise-Editie", "about.hash": "Compilatiehash:", "about.hashee": "EE Compilatiehash:", - "about.hashwebapp": "Webapp Build Hash:", "about.licensed": "Licentie verleend aan:", "about.notice": "Mattermost wordt mogelijk gemaakt door de open source software die wordt gebruikt in onze apps server, desktop en mobiel apps.", "about.privacy": "Privacybeleid", @@ -2646,7 +2645,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} verwijderde het kanaal doel (was: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} heeft het doel van het kanaal bijgewerkt van: {old} naar: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} heeft het kanaaldoel bijgewerkt naar: {new}", - "app.plugin.marketplace_plugins.app_error": "Fout bij het maken van verbinding met de marktplaats server. Controleer uw instellingen in de [Systeemconsole] ({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Fout: {error}", "apps.error.command.field_missing": "Verplichte velden ontbreken: `{fieldName}`.", "apps.error.command.same_channel": "Kanaal herhaald voor veld `{fieldName}`: `{option}`.", @@ -4079,7 +4077,6 @@ "mark_all_threads_as_read_modal.title": "Al jouw draadjes als gelezen markeren?", "marketplace_command.disabled": "De marktplaats is uitgeschakeld. Neem contact op met jouw systeembeheerder voor meer informatie.", "marketplace_command.no_permission": "Je hebt niet de juiste rechten om toegang te krijgen tot de marktplaats.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} van {total, number} in het totaal", "marketplace_modal.install_plugins": "Installeer Plugins", "marketplace_modal.installing": "Bezig met Installeren...", "marketplace_modal.list.configure": "Configureren", @@ -4964,11 +4961,7 @@ "start_trial.modal.gettingTrial": "Proefversie ophalen...", "start_trial.modal.loaded": "Geladen!", "start_trial.modal.loading": "Laden...", - "start_trial.modal_body": "Toegang tot alle platformfuncties, inclusief geavanceerde beveiliging en de nalevingsregels met de onderneming.", - "start_trial.modal_btn.nottnow": "Niet nu", - "start_trial.modal_btn.start": "Start gratis je 30-dagen proefperiode", "start_trial.modal_btn.start_free_trial": "Start je gratis 30-dagen proefperiode", - "start_trial.modal_title": "Start nu je gratis Enterprise-proefperiode", "start_trial.tutorialTip.desc": "Ontdek onze meest gevraagde premium-functies. Bepaal gebruikerstoegang met gastaccounts, automatiseer nalevingsrapporten en verstuur veilige mobiele pushmeldingen met alleen ID's.", "start_trial.tutorialTip.title": "Probeer onze premium-functies gratis", "status_dropdown.dnd_sub_menu_header": "Meldingen uitschakelen tot:", diff --git a/webapp/channels/src/i18n/pl.json b/webapp/channels/src/i18n/pl.json index 96eda0f3b4..3e6612a6a6 100644 --- a/webapp/channels/src/i18n/pl.json +++ b/webapp/channels/src/i18n/pl.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Edycja Enterprise", "about.hash": "Hash Kompilacji:", "about.hashee": "Hash Kompilacji Enterprise:", - "about.hashwebapp": "Hash Kompilacji Aplikacji Webowej:", "about.licensed": "Licencjonowany dla:", "about.notice": "Mattermost jest został stworzony dzięki oprogramowaniu open source użytym na naszym server, desktop i mobile aplikacje.", "about.privacy": "Polityka prywatności", @@ -1340,6 +1339,7 @@ "admin.license.title": "Edycja i licencja", "admin.license.trial-request.accept-terms": "Klikając Rozpocznij wersję próbną, wyrażam zgodę na Mattermost Software and Services License Agreement, Privacy Policy oraz na otrzymywanie wiadomości e-mail dotyczących produktu.", "admin.license.trial-request.embargoed": "Nie mogliśmy przetworzyć tego żądania z powodu ograniczeń dotyczących krajów objętych embargiem. Dowiedz się więcej w naszej dokumentacji lub wyślij wiadomość na adres legal@mattermost.com, aby uzyskać odpowiedzi na pytania dotyczące ograniczeń eksportowych.", + "admin.license.trial-request.embargoed.button": "Zamknij", "admin.license.trial-request.startTrial": "Rozpoczęcie wersji trial", "admin.license.trial-request.title": "Korzystaj z Mattermost Enterprise Edition za darmo przez następne 30 dni. Nie ma obowiązku zakupu lub karty kredytowej. ", "admin.license.trialCard.contactSales": "Kontakt ze sprzedażą", @@ -2563,6 +2563,9 @@ "admin.webserverModeUncompressed": "Nieskompresowany", "admin.webserverModeUncompressedDescription": "Serwer Mattermost będzie serwował nieskompresowane statyczne pliki.", "admin_settings.save_unsaved_changes": "Należy najpierw zapisać niezapisane zmiany", + "air_gapped_modal.close": "Zamknij", + "air_gapped_modal.description": "Aby uruchomić wersję próbną, odwiedź stronę {link} i poproś o klucz próbny.", + "air_gapped_modal.title": "Poproś o klucz próbny", "alert_banner.tooltipCloseBtn": "Zamknij", "analytics.chart.loading": "Ładowanie...", "analytics.chart.meaningful": "Za mało danych dla sensownej reprezentacji.", @@ -2648,7 +2651,7 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} usunął cel kanału (było: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} zaktualizował cel kanału z: {old} na: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} zaktualizował cel kanału na: {new}", - "app.plugin.marketplace_plugins.app_error": "Błąd podczas łączenia z serwerem marketplace. Sprawdź ustawienia w [Konsoli systemowej] ({siteURL}/admin_console/plugins/plugin_management).", + "app_bar.marketplace": "Sklep z Aplikacjami", "apps.error": "Błąd: {error}", "apps.error.command.field_missing": "Brakujące wymagane pola: `{fieldName}`.", "apps.error.command.same_channel": "Kanał powtórzony dla pola `{fieldName}`: `{option}`.", @@ -3437,6 +3440,9 @@ "filtered_user_list.userStatus": "Status użytkownika:", "flag_post.flag": "Oznacz do obserwacji", "flag_post.unflag": "Usuń flagę", + "footer_pagination.count": "Pokazuje{startCount, number}-{endCount, number} z {total, number}", + "footer_pagination.next": "Dalej", + "footer_pagination.prev": "Wstecz", "forward_post_button.label": "Przekaż", "forward_post_modal.button.cancel": "Anuluj", "forward_post_modal.button.forward": "Przekaż", @@ -4081,8 +4087,8 @@ "mark_all_threads_as_read_modal.title": "Oznaczyć wszystkie twoje wątki jako przeczytane?", "marketplace_command.disabled": "Sklep jest wyłączony. W celu uzyskania szczegółowych informacji należy skontaktować się z administratorem systemu.", "marketplace_command.no_permission": "Nie masz odpowiednich uprawnień, aby uzyskać dostęp do sklepu.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {wtyczka} other {wtyczki}} z {total, number} wszystkich", - "marketplace_modal.install_plugins": "Zainstaluj wtyczki", + "marketplace_modal.app_error": "Błąd połączenia z serwerem marketplace. Proszę sprawdzić swoje ustawienia w Konsoli Systemowej .", + "marketplace_modal.install_plugins": "Zainstaluj Wtyczki", "marketplace_modal.installing": "Instalowanie...", "marketplace_modal.list.configure": "Konfiguruj", "marketplace_modal.list.configure.plugin": "Skonfiguruj {plugin}", @@ -4099,12 +4105,13 @@ "marketplace_modal.list.update_confirmation.message.warning_major_version": "Ta aktualizacja może zawierać duże zmiany.", "marketplace_modal.list.update_confirmation.message.warning_major_version_with_release_notes": "Ta aktualizacja może zawierać duże zmiany. Przejrzyj [release notes](!{releaseNotesUrl}) przed aktualizacją.", "marketplace_modal.list.update_confirmation.title": "Potwierdź aktualizację wtyczki", - "marketplace_modal.no_plugins": "Obecnie nie ma dostępnych wtyczek.", + "marketplace_modal.no_plugins": "Nie znaleziono żadnych wtyczek", "marketplace_modal.no_plugins_installed": "Nie masz zainstalowanych żadnych wtyczek.", - "marketplace_modal.search": "Szukaj w Sklepie", + "marketplace_modal.search": "Szukaj w sklepie", "marketplace_modal.tabs.all_listing": "Wszystko", - "marketplace_modal.tabs.installed_listing": "Zainstalowane", - "marketplace_modal.title": "Sklep", + "marketplace_modal.tabs.installed_listing": "Zainstalowane ({count})", + "marketplace_modal.title": "Sklep z Aplikacjami", + "marketplace_modal_list.no_plugins_filter": "Brak wyników dla \"{filter}\"", "members_popover.button.message": "wiadomość", "menu.cloudFree.enterpriseTrialDescription": "Twój okres próbny jest aktywny do {trialEndDay}. Poznaj nasze najważniejsze funkcje Enterprise. Dowiedz się więcej", "menu.cloudFree.enterpriseTrialTitle": "Enterprise Trial", @@ -4156,22 +4163,13 @@ "modal.manual_status.title_offline": "Twój status został ustawiony na \"Offline\"", "modal.manual_status.title_ooo": "Twój status został ustawiony na \"Poza biurem\"", "more.details": "Więcej informacji", - "more_channels.channel_purpose": "Informacje o kanale: Wskaźnik członkostwa: Dołączyło, liczba członków {memberCount}, Propozycje: {channelPurpose}", - "more_channels.count": "{count} Wyników", - "more_channels.count_one": "1 Wynik", - "more_channels.count_zero": "0 Wyników", "more_channels.create": "Stwórz kanał", - "more_channels.hide_joined": "Ukryj dołączonych", - "more_channels.hide_joined_checked": "Pole wyboru Ukryj połączone kanały, zaznaczone", - "more_channels.hide_joined_not_checked": "Pole wyboru Ukryj połączone kanały, nie zaznaczone", - "more_channels.joined": "Dołączył", - "more_channels.membership_indicator": "Wskaźnik członkostwa: Dołączył", + "more_channels.createClick": "Kliknij przycisk 'Utwórz nowy kanał', aby dodać nowy", + "more_channels.join": "Dołącz", + "more_channels.joining": "Dołączanie...", "more_channels.next": "Dalej", - "more_channels.noArchived": "Brak zarchiwizowanych kanałów", "more_channels.noMore": "Brak wyników dla \"{text}\"", - "more_channels.noPublic": "Brak kanałów publicznych", "more_channels.prev": "Wstecz", - "more_channels.searchError": "Spróbuj wyszukać inne słowa kluczowe, sprawdzić literówki lub dostosować filtry.", "more_channels.show_archived_channels": "Pokaż: Archiwizowane kanały", "more_channels.show_public_channels": "Pokaż: Publiczne kanały", "more_channels.title": "Więcej Kanałów", @@ -4236,7 +4234,7 @@ "navbar_dropdown.logout": "Wylogowanie", "navbar_dropdown.manageGroups": "Zarządzaj grupami", "navbar_dropdown.manageMembers": "Zarządzaj użytkownikami", - "navbar_dropdown.marketplace": "Sklep", + "navbar_dropdown.marketplace": "Sklep z Aplikacjami", "navbar_dropdown.menuAriaLabel": "menu główne", "navbar_dropdown.nativeApps": "Pobierz aplikacje", "navbar_dropdown.profileSettings": "Profil", @@ -4294,9 +4292,10 @@ "onboardingTask.checklist.downloads": "Teraz, gdy wszystko jest już gotowe, pobierz nasze aplikacje", "onboardingTask.checklist.higher_security_features": "Interesują Cię nasze funkcje o podwyższonym poziomie bezpieczeństwa?", "onboardingTask.checklist.main_subtitle": "Zaczynamy i działamy.", + "onboardingTask.checklist.no_thanks": "Nie, dzięki", "onboardingTask.checklist.start_enterprise_now": "Rozpocznij bezpłatny okres próbny Enterprise już teraz!", "onboardingTask.checklist.task_complete_your_profile": "Uzupełnij swój profil.", - "onboardingTask.checklist.task_create_from_work_template": "Utwórz z szablonu - ustaw kanał z połączonymi tablicami i playbookami.", + "onboardingTask.checklist.task_create_from_work_template": "Utwórz z szablonu", "onboardingTask.checklist.task_download_mm_apps": "Pobierz aplikację desktopową i mobilną.", "onboardingTask.checklist.task_explore_other_tools_in_platform": "Poznaj inne narzędzia w platformie.", "onboardingTask.checklist.task_invite_team_members": "Zaproś członków zespołu do obszaru roboczego.", @@ -4868,7 +4867,7 @@ "sidebar.directchannel.you": "{displayname} (ty)", "sidebar.menu.item.notSelected": "nie wybrany", "sidebar.menu.item.selected": "wybrane", - "sidebar.openDirectMessage": "Otwórz Wiadomość Bezpośrednią", + "sidebar.openDirectMessage": "Otwórz wiadomość bezpośrednią", "sidebar.show": "Pokaż", "sidebar.sort": "Sortuj", "sidebar.sortedByRecencyLabel": "Ostatnia aktywność", @@ -4883,15 +4882,17 @@ "sidebar.types.favorites": "ULUBIONE", "sidebar.types.unreads": "NIEPRZECZYTANE", "sidebar.unreads": "Więcej nieprzeczytanych", - "sidebar_left.add_channel_dropdown.browseChannels": "Przeglądaj Kanały", + "sidebar_left.addChannelsCta": "Dodaj kanały", + "sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Dodaj Menu Rozwijane Kanału", + "sidebar_left.add_channel_dropdown.browseChannels": "Przeglądaj kanały", "sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Przeglądaj lub twórz kanały", "sidebar_left.add_channel_dropdown.createCategory": "Utwórz nową kategorię", - "sidebar_left.add_channel_dropdown.createNewChannel": "Utwórz Nowy Kanał", + "sidebar_left.add_channel_dropdown.createNewChannel": "Utwórz nowy kanał", "sidebar_left.add_channel_dropdown.dropdownAriaLabel": "Dodaj Menu Rozwijane Kanału", - "sidebar_left.add_channel_dropdown.invitePeople": "Zaproś Ludzi", + "sidebar_left.add_channel_dropdown.invitePeople": "Zaproś osoby", "sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Dodaj ludzi do zespołu", "sidebar_left.add_channel_dropdown.work_template": "Utwórz z szablonu", - "sidebar_left.add_channel_dropdown.work_template_extra": "Skonfiguruj kanał z połączonymi tablicami i playbookami", + "sidebar_left.add_channel_dropdown.work_template_extra": "Połącz razem kanały, tablice i playbooki", "sidebar_left.channel_filter.filterByUnread": "Filtruj według nieprzeczytanych", "sidebar_left.channel_filter.filterUnreadAria": "filtr nieprzeczytanych", "sidebar_left.channel_filter.showAllChannels": "Pokaż wszystkie kanały", @@ -4931,6 +4932,7 @@ "sidebar_left.sidebar_channel_menu.unfavoriteChannel": "Cofnij ulubione", "sidebar_left.sidebar_channel_menu.unmuteChannel": "Wyłącz Wyciszenie Kanału", "sidebar_left.sidebar_channel_menu.unmuteConversation": "Wyłącz Wyciszenie Rozmowy", + "sidebar_left.sidebar_channel_navigator.addChannelsCta": "Dodaj kanały", "sidebar_left.sidebar_channel_navigator.inviteUsers": "Zaproś Użytkowników", "sidebar_right_menu.console": "Konsola systemu", "sidebar_right_menu.flagged": "Oznaczone Wiadmości", @@ -4979,11 +4981,7 @@ "start_trial.modal.gettingTrial": "Pobieranie wersji Testowej...", "start_trial.modal.loaded": "Załadowany!", "start_trial.modal.loading": "Ładowanie...", - "start_trial.modal_body": "Uzyskaj dostęp do wszystkich funkcji platformy, w tym zaawansowanych zabezpieczeń i zgodności korporacyjnej.", - "start_trial.modal_btn.nottnow": "Nie teraz", - "start_trial.modal_btn.start": "Rozpocznij bezpłatny 30-dniowy okres próbny", "start_trial.modal_btn.start_free_trial": "Rozpocznij bezpłatny 30-dniowy okres próbny", - "start_trial.modal_title": "Rozpocznij bezpłatny okres próbny Enterprise już teraz", "start_trial.tutorialTip.desc": "Zapoznaj się z naszymi najbardziej pożądanymi funkcjami premium. Określ dostęp użytkowników za pomocą kont gości, zautomatyzuj raporty zgodności i wysyłaj bezpieczne powiadomienia mobilne push z wykorzystaniem wyłącznie identyfikatorów.", "start_trial.tutorialTip.title": "Wypróbuj nasze funkcje premium za darmo", "status_dropdown.dnd_sub_menu_header": "Wyłącz powiadomienia do:", diff --git a/webapp/channels/src/i18n/pt-BR.json b/webapp/channels/src/i18n/pt-BR.json index 7c727fa2d3..e63417119f 100644 --- a/webapp/channels/src/i18n/pt-BR.json +++ b/webapp/channels/src/i18n/pt-BR.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise Edition", "about.hash": "Hash de Compilação:", "about.hashee": "Hash de Compilação EE:", - "about.hashwebapp": "Webapp Build Hash:", "about.licensed": "Licenciado para:", "about.notice": "Mattermost é possível graças ao software de código aberto usado em nosso servidor, desktop e aplicativos móveis.", "about.privacy": "Política de Privacidade", @@ -26,6 +25,8 @@ "accessibility.button.Info": "Informações", "accessibility.button.Search": "Procurar", "accessibility.button.attachment": "anexo", + "accessibility.button.bold": "negrito", + "accessibility.button.code": "código", "accessibility.button.dialog": "Diálogo {dialogName}", "accessibility.button.italic": "itálico", "accessibility.button.numbered_list": "lista numerada", @@ -248,6 +249,7 @@ "admin.billing.history.fractionalAndRatedUsers": "{fractionalUsers} usuários medidos, {fullUsers} usuários com taxa total, {partialUsers} usuários com cobranças parciais", "admin.billing.history.fractionalUsers": "{fractionalUsers} usuários", "admin.billing.history.noBillingHistory": "No futuro, é aqui que seu histórico de faturamento será exibido.", + "admin.billing.history.onPremUsers": "{num} usuários", "admin.billing.history.pageInfo": "{startRecord} - {endRecord} de {totalRecords}", "admin.billing.history.paid": "Pago", "admin.billing.history.paymentFailed": "Pagamento falhou", @@ -281,6 +283,7 @@ "admin.billing.subscription.cancelSubscriptionSection.contactUs": "Contate-Nos", "admin.billing.subscription.cancelSubscriptionSection.description": "No momento, a exclusão de um espaço de trabalho só pode ser feita com a ajuda de um representante de suporte ao cliente.", "admin.billing.subscription.cancelSubscriptionSection.title": "Cancelar sua assinatura", + "admin.billing.subscription.cloudMonthlyBadge": "Mensalmente", "admin.billing.subscription.creditCardExpired": "Seu cartão de crédito expirou. Atualize suas informações de pagamento para evitar interrupções.", "admin.billing.subscription.creditCardHasExpired": "Seu cartão de crédito expirou", "admin.billing.subscription.goBackTryAgain": "Volte e tente novamente", @@ -2192,7 +2195,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} removeu o propósito do canal (era: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} atualizou o propósito do canal de: {old} para: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} atualizou o propósito do canal para: {new}", - "app.plugin.marketplace_plugins.app_error": "Erro ao conectar-se ao servidor do marketplace. Por favor, verifique suas configurações no [Console do Sistema]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Erro: {error}", "apps.error.command.same_channel": "Canal repetido para o campo `{fieldName}`: `{option}`.", "apps.error.command.same_option": "Opção repetida para o campo `{fieldName}`: `{option}`.", @@ -3151,7 +3153,6 @@ "login_mfa.token": "Token MFA", "manage_channel_groups_modal.search_placeholder": "Buscar grupos", "manage_team_groups_modal.search_placeholder": "Buscar grupos", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} de {total, number} total", "marketplace_modal.install_plugins": "Instalar Plugins", "marketplace_modal.installing": "Instalando...", "marketplace_modal.list.configure": "Configurar", diff --git a/webapp/channels/src/i18n/ro.json b/webapp/channels/src/i18n/ro.json index 1f71d191a4..450f29be22 100644 --- a/webapp/channels/src/i18n/ro.json +++ b/webapp/channels/src/i18n/ro.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Ediția Enterprise", "about.hash": "Construcția Hash:", "about.hashee": "EE Construcția Hash:", - "about.hashwebapp": "Webapp Construcția Hash:", "about.licensed": "Licențiat la:", "about.notice": "Mattermost este făcut posibil de către software-ul open source utilizat în server, desktop și mobil aplicații.", "about.privacy": "Politica de confidentialitate", @@ -2254,7 +2253,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} a eliminat scopul canalului (a fost: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} a actualizat scopul canalului de la: {old} la: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} a actualizat scopul canalului la: {new}", - "app.plugin.marketplace_plugins.app_error": "Eroare la conectarea la serverul de pe piață. Verificați setările din [Consola de sistem]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Eroare: {error}", "apps.error.command.field_missing": "Câmpurile obligatorii lipsesc: `{fieldName}`.", "apps.error.command.unknown_channel": "Canal necunoscut pentru câmpul `{fieldName}`: `{option}`.", @@ -3237,7 +3235,6 @@ "login_mfa.token": "Eroare încearcă să se autentifice Mae token", "manage_channel_groups_modal.search_placeholder": "Căutați grupuri", "manage_team_groups_modal.search_placeholder": "Căutați grupuri", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {pluginuri}} din {total, number} total", "marketplace_modal.install_plugins": "Instalați pluginuri", "marketplace_modal.installing": "Se instalează ...", "marketplace_modal.list.configure": "Configurați", diff --git a/webapp/channels/src/i18n/ru.json b/webapp/channels/src/i18n/ru.json index bfca5442bf..228f9e88f4 100644 --- a/webapp/channels/src/i18n/ru.json +++ b/webapp/channels/src/i18n/ru.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Корпоративная редакция", "about.hash": "Хэш сборки:", "about.hashee": "Хэш сборки EE:", - "about.hashwebapp": "Хэш Веб-Приложения:", "about.licensed": "Лицензия зарегистрирована на:", "about.notice": "Mattermost стал возможен благодаря ПО с открытым исходным кодом, используемом в нашем сервере, настольном клиенте и мобильном приложениях.", "about.privacy": "Политика конфиденциальности", @@ -2127,7 +2126,7 @@ "admin.service.corsExposedHeadersTitle": "CORS открытые заголовки:", "admin.service.corsHeadersEx": "X-Мой-Заголовок", "admin.service.corsTitle": "Разрешить кроссдоменные запросы от:", - "admin.service.developerDesc": "При значении \"да\" на красной панели сверху будут показываться ошибки JavaScript. Не рекомендуется использовать на \"боевом\" сервере. ", + "admin.service.developerDesc": "При значении \"да\" на красной панели сверху будут показываться ошибки JavaScript. Не рекомендуется использовать на \"боевом\" сервере.", "admin.service.developerTitle": "Включить режим разработчика: ", "admin.service.disableBotOwnerDeactivatedTitle": "Отключить учетные записи ботов когда их владелец деактивирован:", "admin.service.disableBotWhenOwnerIsDeactivated": "Когда пользователь деактивирован, отключаются все учетные записи ботов, управляемых этим пользователем.Чтобы обратно включить учетные записи ботов, перейдите в [Интеграции > Учетные записи ботов]({siteURL}/_redirect/integrations/bots).", @@ -2648,7 +2647,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} удалил заголовок канала (было: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} сменил заголовок канала с {old} на {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} установил заголовок канала: {new}", - "app.plugin.marketplace_plugins.app_error": "Ошибка подключения к серверу магазина плагинов. Проверьте свои настройки в [Системной консоли]({siteURL}/admin_console/plugins/plugin_management).", "apps.error": "Ошибка: {error}", "apps.error.command.field_missing": "Обязательные поля отсутствуют: `{fieldName}`.", "apps.error.command.same_channel": "Канал повторяется для поля `{fieldName}`: `{option}`.", @@ -4081,7 +4079,6 @@ "mark_all_threads_as_read_modal.title": "Пометить все свои обсуждения как прочитанные?", "marketplace_command.disabled": "Магазин плагинов отключен. Для получения подробной информации обратитесь к системному администратору.", "marketplace_command.no_permission": "У вас нет соответствующих разрешений для доступа к магазину плагинов.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {плагин} few {плагина} other {плагинов}} из {total, number}", "marketplace_modal.install_plugins": "Установить плагины", "marketplace_modal.installing": "Устанавливается...", "marketplace_modal.list.configure": "Конфигурация", @@ -4158,7 +4155,7 @@ "more.details": "Подробнее", "more_channels.create": "Создать канал", "more_channels.next": "Далее", - "more_channels.noMore": "Доступных каналов не найдено", + "more_channels.noMore": "Нет результатов поиска для \"{text}\"", "more_channels.prev": "Предыдущая", "more_channels.show_archived_channels": "Показать: Архивированные каналы", "more_channels.show_public_channels": "Показать: Публичные каналы", @@ -4966,11 +4963,7 @@ "start_trial.modal.gettingTrial": "Получение пробной версии...", "start_trial.modal.loaded": "Загружено!", "start_trial.modal.loading": "Загрузка...", - "start_trial.modal_body": "Получите доступ ко всем функциям платформы, включая расширенную безопасность и соответствие требованиям предприятия.", - "start_trial.modal_btn.nottnow": "Не сейчас", - "start_trial.modal_btn.start": "Начать бесплатную 30-дневную пробную версию", "start_trial.modal_btn.start_free_trial": "Начать бесплатную 30-дневную пробную версию", - "start_trial.modal_title": "Начните бесплатную пробную версию Enterprise прямо сейчас", "start_trial.tutorialTip.desc": "Изучите наши самые востребованные премиум-функции. Определяйте доступ пользователей с помощью гостевых учетных записей, автоматизируйте отчеты о соответствии и отправляйте мобильные push-уведомления только с безопасным идентификатором.", "start_trial.tutorialTip.title": "Попробуйте наши премиум-функции бесплатно", "status_dropdown.dnd_sub_menu_header": "Отключить уведомления до:", @@ -5087,7 +5080,7 @@ "threadFromArchivedChannelMessage": "Вы просматриваете обсуждение в **архивированном канале**. На этом канале нельзя опубликовать новые сообщения.", "threading.filters.allThreads": "Все ваши треды", "threading.filters.unreads": "Непрочитанное", - "threading.following": "Отслеживание", + "threading.following": "Отслеживается", "threading.footer.lastReplyAt": "Последний ответ {formatted}", "threading.header.heading": "Тред", "threading.notFollowing": "Отслеживать", @@ -5105,7 +5098,7 @@ "threading.threadMenu.markUnread": "Пометить как непрочитанное", "threading.threadMenu.openInChannel": "Открыт в канале", "threading.threadMenu.save": "Сохранить", - "threading.threadMenu.unfollow": "Отключить отслеживание треда", + "threading.threadMenu.unfollow": "Прекратить отслеживание треда", "threading.threadMenu.unfollowExtra": "Вы не будете уведомлены об ответах", "threading.threadMenu.unfollowMessage": "Отписаться от сообщения", "threading.threadMenu.unsave": "Убрать из сохраненных", diff --git a/webapp/channels/src/i18n/sv.json b/webapp/channels/src/i18n/sv.json index 9ed2615d87..f459c90ff3 100644 --- a/webapp/channels/src/i18n/sv.json +++ b/webapp/channels/src/i18n/sv.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise-utgåva", "about.hash": "Bygg-hash:", "about.hashee": "EE bygg-hash:", - "about.hashwebapp": "Webapp bygg-hash:", "about.licensed": "Licensierad till:", "about.notice": "Mattermost är möjlig tack vare att både server, skrivbordsapp och mobilapp har öppen källkod.", "about.privacy": "Integritetspolicy", @@ -2127,7 +2126,7 @@ "admin.service.corsExposedHeadersTitle": "CORS Exposed Headers:", "admin.service.corsHeadersEx": "X-My-Header", "admin.service.corsTitle": "Tillåt cross-origin requests från:", - "admin.service.developerDesc": "När aktiverad kommer JavaScript-error visas i ett lilafärgat fält högst upp i användargränssnittet. Detta är inte rekommenderat i en produktionsmiljö. ", + "admin.service.developerDesc": "När aktiverad kommer JavaScript-error visas i ett lilafärgat fält högst upp i användargränssnittet. Detta är inte rekommenderat i en produktionsmiljö.", "admin.service.developerTitle": "Aktivera utvecklarläge: ", "admin.service.disableBotOwnerDeactivatedTitle": "Spärra bot-konton när dess ägare blir avaktiverad:", "admin.service.disableBotWhenOwnerIsDeactivated": "När en användare blir avaktiverad, spärras alla bot-konton som hanteras av användaren. För att aktivera bot-kontot, gå till [Integrations > Bot Accounts]({siteURL}/_redirect/integrations/bots).", @@ -2648,7 +2647,7 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} raderade kanalens syfte (var tidigare {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} uppdaterat kanal syfte från: {old} till: {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} ändrade kanalens rubrik till: {new}", - "app.plugin.marketplace_plugins.app_error": "Fel vid anslutning till marketplace server. Kontrollera inställningarna i [System Console]({siteURL}/admin_console/plugins/plugin_management).", + "app_bar.marketplace": "App Marketplace", "apps.error": "Fel: {error}", "apps.error.command.field_missing": "Nödvändiga fält saknas: `{fieldName}`.", "apps.error.command.same_channel": "Kanal upprepas i fältet `{fieldName}`: `{option}`.", @@ -4081,7 +4080,6 @@ "mark_all_threads_as_read_modal.title": "Markera alla dina trådar som lästa?", "marketplace_command.disabled": "Marknadsplatsen är inaktiverad. Kontakta din systemadministratör för mer information.", "marketplace_command.no_permission": "Du har inte rätt behörighet att komma åt marknadsplatsen.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} av totalt {total, number}", "marketplace_modal.install_plugins": "Installera plugins", "marketplace_modal.installing": "Installerar...", "marketplace_modal.list.configure": "Konfigurera", @@ -4157,8 +4155,11 @@ "modal.manual_status.title_ooo": "Din status är satt till \"Inte på kontoret\"", "more.details": "Mer information", "more_channels.create": "Skapa kanal", + "more_channels.createClick": "Klicka på 'Skapa ny kanal' för att skapa en ny", + "more_channels.join": "Gå med", + "more_channels.joining": "Går med...", "more_channels.next": "Nästa", - "more_channels.noMore": "Det finns inga fler kanaler att gå med i", + "more_channels.noMore": "Inget resultat för \"{text}\"", "more_channels.prev": "Föregående", "more_channels.show_archived_channels": "Visa: Arkiverade kanaler", "more_channels.show_public_channels": "Publika kanaler", @@ -4224,7 +4225,7 @@ "navbar_dropdown.logout": "Logga ut", "navbar_dropdown.manageGroups": "Hantera grupper", "navbar_dropdown.manageMembers": "Hantera medlemmar", - "navbar_dropdown.marketplace": "Marketplace", + "navbar_dropdown.marketplace": "App Marketplace", "navbar_dropdown.menuAriaLabel": "Huvudmeny", "navbar_dropdown.nativeApps": "Ladda ner app", "navbar_dropdown.profileSettings": "Profil", @@ -4369,6 +4370,7 @@ "payment_form.no_billing_address": "Ingen fakturaadress tillagd", "payment_form.no_credit_card": "Inget betalkort tillagt", "payment_form.saved_payment_method": "Spara betalmetod", + "payment_form.shipping_address": "Leveransadress", "payment_form.zipcode": "ZIP/Postnummer", "pending_post_actions.cancel": "Avbryt", "pending_post_actions.retry": "Försök igen", @@ -4870,6 +4872,8 @@ "sidebar.types.favorites": "FAVORITER", "sidebar.types.unreads": "OLÄSTA", "sidebar.unreads": "Fler olästa meddelanden", + "sidebar_left.addChannelsCta": "Lägg till kanaler", + "sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Lägg till en rullgardinsmeny för kanaler", "sidebar_left.add_channel_dropdown.browseChannels": "Bläddra bland kanalerna", "sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Bläddra eller skapa kanaler", "sidebar_left.add_channel_dropdown.createCategory": "Skapa ny kategori", @@ -4918,6 +4922,7 @@ "sidebar_left.sidebar_channel_menu.unfavoriteChannel": "Ta bort favorit", "sidebar_left.sidebar_channel_menu.unmuteChannel": "Ljud på i kanalen", "sidebar_left.sidebar_channel_menu.unmuteConversation": "Ljud på i konversationen", + "sidebar_left.sidebar_channel_navigator.addChannelsCta": "Lägg till kanaler", "sidebar_left.sidebar_channel_navigator.inviteUsers": "Bjud in användare", "sidebar_right_menu.console": "Systemkonsol", "sidebar_right_menu.flagged": "Markerade meddelanden", @@ -4966,11 +4971,7 @@ "start_trial.modal.gettingTrial": "Hämtar prova-på-period...", "start_trial.modal.loaded": "Laddad!", "start_trial.modal.loading": "Laddar...", - "start_trial.modal_body": "Få tillgång till alla funktioner inklusive avancerad säkerhet och efterlevnadskontroll för företags.", - "start_trial.modal_btn.nottnow": "Inte nu", - "start_trial.modal_btn.start": "Starta en kostnadsfri 30-dagars prova-på-period", "start_trial.modal_btn.start_free_trial": "Starta en kostnadsfri 30-dagars prova-på-period", - "start_trial.modal_title": "Starta din kostnadsfria prova-på-period för Enterprise nu", "start_trial.tutorialTip.desc": "Utforska våra mest efterfrågade premiumfunktioner. Bestäm användarnas åtkomst med gästkonton, automatisera rapporter om efterlevnad och skicka informationsskyddade notifieringar till mobilen.", "start_trial.tutorialTip.title": "Testa våra premiumfunktioner gratis", "status_dropdown.dnd_sub_menu_header": "Inaktivera notifieringar till:", diff --git a/webapp/channels/src/i18n/tr.json b/webapp/channels/src/i18n/tr.json index 4c4e6c1fed..366365b7a2 100644 --- a/webapp/channels/src/i18n/tr.json +++ b/webapp/channels/src/i18n/tr.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise paketi", "about.hash": "Yapım karması:", "about.hashee": "Enterprise paketi yapım karması:", - "about.hashwebapp": "Web uygulaması yapım karması:", "about.licensed": "Lisans sahibi:", "about.notice": "Kullandığımız açık kaynaklı sunucu, masaüstü ve mobil uygulamalari Mattermost tarafından sunulmaktadır.", "about.privacy": "Kişisel verilerin gizliliği ilkesi", @@ -2606,7 +2605,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} kanal amacını kaldırdı (önceki: {old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username}, {old} eski kanal amacını {new} olarak değiştirdi", "app.channel.post_update_channel_purpose_message.updated_to": "{username} kanal amacını {new} olarak güncelledi", - "app.plugin.marketplace_plugins.app_error": "Mağaza sunucusu ile bağlantı kurulurken sorun çıktı. Lütfen [Sistem panosu]({siteURL}/admin_console/plugins/plugin_management) üzerinden ayarlarınızı denetleyin.", "apps.error": "Hata: {error}", "apps.error.command.field_missing": "Zorunlu alan eksik: `{fieldName}`.", "apps.error.command.same_channel": "`{fieldName}`alanında kanal yineleniyor: `{option}`.", @@ -4019,7 +4017,6 @@ "mark_all_threads_as_read_modal.title": "Tüm konularınız okunmuş olarak işaretlensin mi?", "marketplace_command.disabled": "Mağaza devre dışı bırakılmış. Lütfen ayrıntılı bilgi almak için sistem yöneticiniz ile görüşün.", "marketplace_command.no_permission": "Mağazaya erişmek için yeterli izinleriniz yok.", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {uygulama eki} other {uygulama eki}} / {total, number}", "marketplace_modal.install_plugins": "Uygulama ekleri kur", "marketplace_modal.installing": "Kuruluyor...", "marketplace_modal.list.configure": "Yapılandır", @@ -4867,11 +4864,7 @@ "start_trial.modal.gettingTrial": "Deneme sürümü alınıyor...", "start_trial.modal.loaded": "Yüklendi!", "start_trial.modal.loading": "Yükleniyor...", - "start_trial.modal_body": "Gelişmiş güvenlik ve kurumsal uyumluluk ile tüm platform özelliklerine erişin.", - "start_trial.modal_btn.nottnow": "Şimdi değil", - "start_trial.modal_btn.start": "30 günlük ücretsiz denemeyi başlat", "start_trial.modal_btn.start_free_trial": "30 günlük ücretsiz denemeyi başlat", - "start_trial.modal_title": "Ücretsiz Enterprise tarifesi deneme sürenizi başlatın", "start_trial.tutorialTip.desc": "En çok istenilen ücretli özelliklerimizi keşfedin. Konuk hesapları ile kullanıcı erişimini sınırlayın, uyumluluk raporlarını otomatikleştirin ve yalnızca kimliğe özel güvenli mobil anında iletileri gönderin.", "start_trial.tutorialTip.title": "Ücretli özelliklerimizi ücretsiz olarak deneyin", "status_dropdown.dnd_sub_menu_header": "Bildirimler şu zamana kadar kapatılsın:", diff --git a/webapp/channels/src/i18n/uk.json b/webapp/channels/src/i18n/uk.json index 4afba9192e..20abccf1f8 100644 --- a/webapp/channels/src/i18n/uk.json +++ b/webapp/channels/src/i18n/uk.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "Enterprise Edition", "about.hash": "Хеш збірки:", "about.hashee": "Хеш збірки EE:", - "about.hashwebapp": "Хеш збірки Webapp:", "about.licensed": "Ліцензовано на:", "about.notice": "Mattermost це стало можливим за допомогою програмного забезпечення з відкритим кодом, яке використовується на нашому сервері , desktop та mobile додатків.", "about.privacy": "Політика конфіденційності", @@ -2193,7 +2192,6 @@ "login.verified": "Електронну пошту підтверджено", "login_mfa.submit": "Відправити", "login_mfa.token": "Токен MFA", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {count, plural, one {user} other {users}} of {total, number} total", "marketplace_modal.install_plugins": "Встановлені плагіни:", "marketplace_modal.list.try_again": "Спробуй ще раз ", "marketplace_modal.list.update": "Оновити", diff --git a/webapp/channels/src/i18n/zh-CN.json b/webapp/channels/src/i18n/zh-CN.json index 2db89a3255..01bf58512a 100644 --- a/webapp/channels/src/i18n/zh-CN.json +++ b/webapp/channels/src/i18n/zh-CN.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "企业版", "about.hash": "编译哈希:", "about.hashee": "企业版编译哈希:", - "about.hashwebapp": "网页应用编译哈系:", "about.licensed": "授权给:", "about.notice": "开源软件帮助 Mattermost 实现了我们的服务端桌面以及移动应用。", "about.privacy": "隐私政策", @@ -319,7 +318,16 @@ "admin.billing.subscription.creditCardExpired": "您的信用卡已过期。请更新您的付款信息,以免造成任何中断。", "admin.billing.subscription.creditCardHasExpired": "您的信用卡已过期", "admin.billing.subscription.creditCardHasExpired.description": "请更新您的 付款信息 以避免任何中断服务。", + "admin.billing.subscription.deleteWorkspaceModal.cancelButton": "保持订阅", + "admin.billing.subscription.deleteWorkspaceModal.deleteButton": "删除工作区", + "admin.billing.subscription.deleteWorkspaceModal.downgradeButton": "降级为免费", + "admin.billing.subscription.deleteWorkspaceModal.title": "您确定要删除吗?", + "admin.billing.subscription.deleteWorkspaceModal.usage": "作为你付费订阅Mattermost {sku} 的一部分,你目前创建了 ", "admin.billing.subscription.deleteWorkspaceModal.usageDetails": "{messageCount} 条消息和 {fileSize} 的文件", + "admin.billing.subscription.deleteWorkspaceModal.warning": "删除您的工作区是决定性的操作。在删除时,您将失去所有相关数据并且没有能力进行恢复。如果您降级到免费版,您将仍然能看到此信息。", + "admin.billing.subscription.deleteWorkspaceSection.delete": "删除工作区", + "admin.billing.subscription.deleteWorkspaceSection.description": "删除 {workspaceLink} 是最终决定并且以后不能恢复。", + "admin.billing.subscription.deleteWorkspaceSection.title": "删除你的工作区", "admin.billing.subscription.downgradedSuccess": "您现在订阅了 {productName}", "admin.billing.subscription.downgrading": "降级您的工作区", "admin.billing.subscription.featuresAvailable": "{productName} 功能现在已可以使用。", @@ -370,6 +378,7 @@ "admin.billing.subscription.planDetails.userCount": "{userCount} 位用户", "admin.billing.subscription.privateCloudCard.cloudEnterprise.description": "在 Mattermost,我们与您和您的团队合作,以满足您对整个产品的需求。如果您正在寻找年度折扣,请联系我们的销售团队。", "admin.billing.subscription.privateCloudCard.cloudEnterprise.title": "在寻找年度折扣吗? ", + "admin.billing.subscription.privateCloudCard.cloudFree.description": "使用访客账户,Office365套件集起,GitLab SSO和高级权限优化你的流程。", "admin.billing.subscription.privateCloudCard.cloudFree.title": "升级至 Cloud Professional 版", "admin.billing.subscription.privateCloudCard.cloudProfessional.description": "拥有高级支持的高级安全性和合规性功能。详细信息请参阅 {pricingLink}。", "admin.billing.subscription.privateCloudCard.cloudProfessional.title": "升级到企业级云", @@ -387,6 +396,7 @@ "admin.billing.subscription.providePaymentDetails": "提供您的付款明细", "admin.billing.subscription.returnToTeam": "返回到{team}", "admin.billing.subscription.stateprovince": "州/省", + "admin.billing.subscription.switchedToAnnual.title": "您现在的切换到了 {selectedProductName}", "admin.billing.subscription.title": "订阅", "admin.billing.subscription.updatePaymentInfo": "更新付款信息", "admin.billing.subscription.upgradedSuccess": "您现在已升级到{productName}", @@ -394,6 +404,8 @@ "admin.billing.subscription.userCount.tooltipTitle": "当前用户数", "admin.billing.subscription.verifyPaymentInformation": "验证您的付款信息中", "admin.billing.subscription.viewBilling": "查看账单", + "admin.billing.subscriptions.billing_summary.explore_enterprise": "浏览企业版功能", + "admin.billing.subscriptions.billing_summary.explore_enterprise.cta": "查看所有功能", "admin.billing.subscriptions.billing_summary.lastInvoice.failed": "失败", "admin.billing.subscriptions.billing_summary.lastInvoice.monthlyFlatFee": "每月固定费用", "admin.billing.subscriptions.billing_summary.lastInvoice.paid": "已支付", @@ -405,11 +417,26 @@ "admin.billing.subscriptions.billing_summary.lastInvoice.total": "总计", "admin.billing.subscriptions.billing_summary.lastInvoice.userCount": " x {users} 用户", "admin.billing.subscriptions.billing_summary.lastInvoice.userCountPartial": "{users} 用户", + "admin.billing.subscriptions.billing_summary.lastInvoice.viewInvoice": "查看发票", "admin.billing.subscriptions.billing_summary.lastInvoice.whatArePartialCharges": "什么是部分费用?", "admin.billing.subscriptions.billing_summary.lastInvoice.whatArePartialCharges.message": "在一个月的整个时间内未启用的用户将按比例按月计费。", "admin.billing.subscriptions.billing_summary.noBillingHistory.description": "将来,这是您最近的帐单摘要将显示的位置。", "admin.billing.subscriptions.billing_summary.noBillingHistory.link": "查看计费方式", "admin.billing.subscriptions.billing_summary.noBillingHistory.title": "尚无帐单记录", + "admin.billing.subscriptions.billing_summary.try_enterprise": "免费试用企业版功能", + "admin.billing.subscriptions.billing_summary.try_enterprise.cta": "免费试用{trialLength} 天", + "admin.billing.subscriptions.billing_summary.upcomingInvoice.has_more_line_items": "有{count}更多条目", + "admin.billing.subscriptions.billing_summary.upgrade_professional": "升级至专业套餐", + "admin.billing.subscriptions.billing_summary.upgrade_professional.cta": "升级", + "admin.billing.trueUpReview.button_download": "下载数据", + "admin.billing.trueUpReview.button_share": "分享到Mattermost", + "admin.billing.trueUpReview.docsLinkCTA": "了解更多关于true-up.", + "admin.billing.trueUpReview.due_date": "到期 ", + "admin.billing.trueUpReview.share_data_for_review": "与Mattermost分享您的系统统计数据以进行季度的True-Up审核。{link}", + "admin.billing.trueUpReview.submit.thanks_for_sharing": "谢谢您分享与True-Up审核相关的数据。", + "admin.billing.trueUpReview.submit_error": "发送您的TrueUp审核时发生错误。请重试。", + "admin.billing.trueUpReview.submit_success": "成功!", + "admin.billing.trueUpReview.title": "True Up审核", "admin.bleve.bulkIndexingTitle": "批量索引:", "admin.bleve.createJob.help": "数据库中所有的用户、频道以及消息将从旧到新顺序索引。Bleve 可以在索引过程中使用但搜索结果可能不完整。", "admin.bleve.createJob.title": "立刻索引", @@ -531,6 +558,7 @@ "admin.cluster.status_table.url": "Gossip 地址", "admin.cluster.status_table.version": "版本", "admin.cluster.unknown": "未知", + "admin.cluster.version_mismatch_warning": "警告:您的高可用集群检测出多个版本的Mattermost。如果您不是正在进行升级,请确认集群所有的节点使用相同的Mattermost版本,以防平台功能异常。", "admin.compliance.complianceMonitoring": "合规监视", "admin.compliance.directoryDescription": "用户保存守规报告。如果为空,将被设置为 ./data/。", "admin.compliance.directoryExample": "例如 \"./data/\"", @@ -604,6 +632,8 @@ "admin.connectionSecurityTlsDescription": "加密Mattermost和您的服务器之间的通信。", "admin.custom_terms_of_service_feature_discovery.copy": "创建您自己的服务条款,新用户在访问桌面、网页或移动设备上的 Mattermost 之前必须接受该条款。", "admin.custom_terms_of_service_feature_discovery.title": "使用 Mattermost Enterprise 创建自定义服务条款", + "admin.customization.allowSyncedDrafts": "开启服务端草稿消息同步:", + "admin.customization.allowSyncedDraftsDesc": "当开启,用户的消息草稿将同步至服务端,所有的设备都可以看到。用户可以在帐户设置里关闭这个功能。", "admin.customization.androidAppDownloadLinkDesc": "添加安卓应用下载链接。用移动设备访问的用户将看到应用下载提示页面。此栏留空将不显示。", "admin.customization.androidAppDownloadLinkTitle": "安卓应用下载链接:", "admin.customization.announcement.allowBannerDismissalDesc": "当设为是时,用户可以撤掉横幅直到下次更新。当设为否时,横幅将永久显示直到被系统管理员关闭。", @@ -633,6 +663,8 @@ "admin.customization.enableLinkPreviewsTitle": "启用网站链接预览:", "admin.customization.enablePermalinkPreviewsDesc": "启用后,指向 Mattermost 消息的链接将为有权访问原始消息的任何用户生成预览。请查看我们的文档了解详细信息。", "admin.customization.enablePermalinkPreviewsTitle": "开启消息链接预览:", + "admin.customization.enablePublicSharedBoardsDesc": "此项允许面板编辑使用链接分享面板给所有人访问。", + "admin.customization.enablePublicSharedBoardsTitle": "启用公共访问面板:", "admin.customization.enableSVGsDesc": "开启 SVG 附件预览并让它们显示在消息中。", "admin.customization.enableSVGsTitle": "开启 SVG:", "admin.customization.gfycatApiKey": "Gfycat API Key:", @@ -708,6 +740,12 @@ "admin.database.title": "数据库", "admin.developer.title": "开发人员设置", "admin.elasticsearch.bulkIndexingTitle": "批量索引:", + "admin.elasticsearch.caExample": "例:\"./elasticsearch/ca.pem\"", + "admin.elasticsearch.caTitle": "CA path:", + "admin.elasticsearch.clientCertExample": "例:\"./elasticsearch/client-cert.pem\"", + "admin.elasticsearch.clientCertTitle": "客户端证书路径:", + "admin.elasticsearch.clientKeyExample": "例:\"./elasticsearch/client-cert.pem\"", + "admin.elasticsearch.clientKeyTitle": "客户端证书钥匙路径:", "admin.elasticsearch.connectionUrlDescription": "Elasticsearch 服务器地址。{documentationLink}", "admin.elasticsearch.connectionUrlExample": "例如:\"https://elasticsearch.example.org:9200\"", "admin.elasticsearch.connectionUrlExample.documentationLinkText": "请见服务器架设说明文档。", @@ -798,6 +836,9 @@ "admin.environment.notifications.replyToAddress.label": "通知 Reply-To 地址:", "admin.environment.notifications.replyToAddress.placeholder": "例如:\"mattermost@yourcompany.com\", \"admin@yourcompany.com\"", "admin.environment.notifications.supportAddress.placeholder": "例:“support@yourcompany.com”,“admin@yourcompany.com”", + "admin.environment.notifications.supportEmail.help": "客户支持邮件里显示的电子邮件地址。", + "admin.environment.notifications.supportEmail.label": "客户支持邮件地址:", + "admin.environment.notifications.supportEmail.required": "需要“客户支持电子邮件地址”", "admin.environment.pushNotificationServer": "推送通知服务器", "admin.environment.smtp": "SMTP", "admin.environment.smtp.connectionSecurity.option.none": "无", @@ -812,6 +853,7 @@ "admin.environment.smtp.smtpAuth.description": "当设为是时,开启 SMTP 验证。", "admin.environment.smtp.smtpAuth.title": "开启 SMTP 验证:", "admin.environment.smtp.smtpFail": "连接失败:{error}", + "admin.environment.smtp.smtpFailure": "SMTP没有在在系统控制台里配置。可以在 这里配置。", "admin.environment.smtp.smtpPassword.description": "从邮件服务器管理员获得此凭据。", "admin.environment.smtp.smtpPassword.placeholder": "例如:\"yourpassword\"、\"jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY\"", "admin.environment.smtp.smtpPassword.title": "SMTP 服务器密码:", @@ -2427,7 +2469,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} 移除了频道作用 (曾是:{old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} 更新了频道作用从:{old} 到:{new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} 更新了频道作用到:{new}", - "app.plugin.marketplace_plugins.app_error": "连接到集市服务器出错。请检查[系统控制台]({siteURL}/admin_console/plugins/plugin_management)的设定。", "apps.error": "错误:{error}", "apps.error.command.field_missing": "缺少必填字段:`{fieldName}`。", "apps.error.command.same_channel": "频道重复,字段 `{fieldName}`:`{option}`。", @@ -3525,7 +3566,6 @@ "manage_channel_groups_modal.search_placeholder": "搜索组", "manage_team_groups_modal.search_placeholder": "搜索组", "mark_all_threads_as_read_modal.cancel": "取消", - "marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {count} 个,共 {total, number} 个", "marketplace_modal.install_plugins": "安装插件", "marketplace_modal.installing": "安装中...", "marketplace_modal.list.configure": "配置", @@ -4043,9 +4083,6 @@ "start_trial.modal.failed": "失败", "start_trial.modal.loaded": "已加载!", "start_trial.modal.loading": "加载中...", - "start_trial.modal_btn.nottnow": "现在不要", - "start_trial.modal_btn.start": "开始 30 天试用", - "start_trial.modal_title": "立即开始您的免费企业版试用", "status_dropdown.dnd_sub_menu_header": "禁用通知,直到:", "status_dropdown.dnd_sub_menu_item.custom": "自定义", "status_dropdown.dnd_sub_menu_item.one_hour": "1 小时", @@ -4127,7 +4164,7 @@ "team_settings_modal.title": "团队设置", "team_sidebar.join": "您可以加入的其他团队", "terms_of_service.agreeButton": "我同意", - "terms_of_service.api_error": "无法完成请求。如果此问题持续,请联系您的系统管理员。", + "terms_of_service.api_error": "无法完成请求。如果此问题仍然存在,请联系您的系统管理员。", "terms_of_service.disagreeButton": "我不同意", "test": "文本按钮", "textbox.bold": "**加粗**", diff --git a/webapp/channels/src/i18n/zh-TW.json b/webapp/channels/src/i18n/zh-TW.json index 63c968580a..423beece99 100644 --- a/webapp/channels/src/i18n/zh-TW.json +++ b/webapp/channels/src/i18n/zh-TW.json @@ -11,7 +11,6 @@ "about.enterpriseEditione1": "企業版", "about.hash": "編譯 Hash:", "about.hashee": "企業版編譯 Hash:", - "about.hashwebapp": "網頁程式建置雜湊:", "about.licensed": "授權給:", "about.notice": "藉由在伺服器桌面行動裝置上使用的開源軟體,才得以實現 Mattermost。", "about.privacy": "隱私政策", @@ -1929,7 +1928,6 @@ "app.channel.post_update_channel_purpose_message.removed": "{username} 已移除頻道用途(原為:{old})", "app.channel.post_update_channel_purpose_message.updated_from": "{username} 已更新頻道用途:從 {old} 改為 {new}", "app.channel.post_update_channel_purpose_message.updated_to": "{username} 已更新頻道用途為:{new}", - "app.plugin.marketplace_plugins.app_error": "連線至市集伺服器失敗。請檢查[系統控制台]({siteURL}/admin_console/plugins/plugin_management)的設定。", "apps.error": "錯誤:{error}", "apps.error.command.field_missing": "遺漏必填欄位:`{fieldName}`。", "apps.error.command.same_channel": "頻道重複的欄位 `{fieldName}`:`{option}`。", @@ -2848,7 +2846,6 @@ "login_mfa.token": "多重要素驗證 Token", "manage_channel_groups_modal.search_placeholder": "搜尋群組", "manage_team_groups_modal.search_placeholder": "搜尋群組", - "marketplace_list.count_total_page": "{total, number}位中{startCount, number} - {endCount, number}位使用者", "marketplace_modal.install_plugins": "安裝模組", "marketplace_modal.installing": "安裝中...", "marketplace_modal.list.configure": "設定", diff --git a/webapp/channels/src/images/worktemplates/boards/meeting_agenda.png b/webapp/channels/src/images/worktemplates/boards/meeting_agenda.png index 00bf8fb9e7..e840083276 100644 Binary files a/webapp/channels/src/images/worktemplates/boards/meeting_agenda.png and b/webapp/channels/src/images/worktemplates/boards/meeting_agenda.png differ diff --git a/webapp/channels/src/images/worktemplates/boards/project_tasks.png b/webapp/channels/src/images/worktemplates/boards/project_tasks.png index 157044839b..86ba744926 100644 Binary files a/webapp/channels/src/images/worktemplates/boards/project_tasks.png and b/webapp/channels/src/images/worktemplates/boards/project_tasks.png differ diff --git a/webapp/channels/src/images/worktemplates/boards/roadmap.png b/webapp/channels/src/images/worktemplates/boards/roadmap.png index 74fd691aca..d73ead57b1 100644 Binary files a/webapp/channels/src/images/worktemplates/boards/roadmap.png and b/webapp/channels/src/images/worktemplates/boards/roadmap.png differ diff --git a/webapp/channels/src/images/worktemplates/boards/sprint_planner.png b/webapp/channels/src/images/worktemplates/boards/sprint_planner.png index fc734715b2..d2e355a5ba 100644 Binary files a/webapp/channels/src/images/worktemplates/boards/sprint_planner.png and b/webapp/channels/src/images/worktemplates/boards/sprint_planner.png differ diff --git a/webapp/channels/src/images/worktemplates/boards/team_retrospective.png b/webapp/channels/src/images/worktemplates/boards/team_retrospective.png index 1e37fbaaa9..e65f448b2d 100644 Binary files a/webapp/channels/src/images/worktemplates/boards/team_retrospective.png and b/webapp/channels/src/images/worktemplates/boards/team_retrospective.png differ diff --git a/webapp/channels/src/images/worktemplates/companywide/create_project/channel.png b/webapp/channels/src/images/worktemplates/companywide/create_project/channel.png index f20df37c7a..73ccd4c4dc 100644 Binary files a/webapp/channels/src/images/worktemplates/companywide/create_project/channel.png and b/webapp/channels/src/images/worktemplates/companywide/create_project/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/companywide/create_project/create_project.png b/webapp/channels/src/images/worktemplates/companywide/create_project/create_project.png new file mode 100644 index 0000000000..ed1b49ff19 Binary files /dev/null and b/webapp/channels/src/images/worktemplates/companywide/create_project/create_project.png differ diff --git a/webapp/channels/src/images/worktemplates/companywide/create_project/create_project.svg b/webapp/channels/src/images/worktemplates/companywide/create_project/create_project.svg deleted file mode 100644 index 6ab29cc018..0000000000 --- a/webapp/channels/src/images/worktemplates/companywide/create_project/create_project.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/channel.png b/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/channel.png index f847b8ba4a..66ef1ec443 100644 Binary files a/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/channel.png and b/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/goals_and_okrs.png b/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/goals_and_okrs.png index 49f4808abc..ea2ebad29d 100644 Binary files a/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/goals_and_okrs.png and b/webapp/channels/src/images/worktemplates/companywide/goals_and_okrs/goals_and_okrs.png differ diff --git a/webapp/channels/src/images/worktemplates/devops/incident_resolution/channel.png b/webapp/channels/src/images/worktemplates/devops/incident_resolution/channel.png index 40b56f2ba8..e68fc26220 100644 Binary files a/webapp/channels/src/images/worktemplates/devops/incident_resolution/channel.png and b/webapp/channels/src/images/worktemplates/devops/incident_resolution/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/devops/incident_resolution/incident_resolution.png b/webapp/channels/src/images/worktemplates/devops/incident_resolution/incident_resolution.png index d2ee56b263..7f48afb9f7 100644 Binary files a/webapp/channels/src/images/worktemplates/devops/incident_resolution/incident_resolution.png and b/webapp/channels/src/images/worktemplates/devops/incident_resolution/incident_resolution.png differ diff --git a/webapp/channels/src/images/worktemplates/devops/product_release/channel.png b/webapp/channels/src/images/worktemplates/devops/product_release/channel.png index befad7acdf..79c613c5e2 100644 Binary files a/webapp/channels/src/images/worktemplates/devops/product_release/channel.png and b/webapp/channels/src/images/worktemplates/devops/product_release/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/devops/product_release/product_release.png b/webapp/channels/src/images/worktemplates/devops/product_release/product_release.png index 3250e85265..8b2c90788f 100644 Binary files a/webapp/channels/src/images/worktemplates/devops/product_release/product_release.png and b/webapp/channels/src/images/worktemplates/devops/product_release/product_release.png differ diff --git a/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/channel.png b/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/channel.png index f847b8ba4a..66ef1ec443 100644 Binary files a/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/channel.png and b/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/goals_and_okrs.png b/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/goals_and_okrs.png index 49f4808abc..ea2ebad29d 100644 Binary files a/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/goals_and_okrs.png and b/webapp/channels/src/images/worktemplates/leadership/goals_and_okrs/goals_and_okrs.png differ diff --git a/webapp/channels/src/images/worktemplates/playbooks/bug_bash.png b/webapp/channels/src/images/worktemplates/playbooks/bug_bash.png index 6e6a746411..67f1b4a79f 100644 Binary files a/webapp/channels/src/images/worktemplates/playbooks/bug_bash.png and b/webapp/channels/src/images/worktemplates/playbooks/bug_bash.png differ diff --git a/webapp/channels/src/images/worktemplates/playbooks/feature_lifecycle.png b/webapp/channels/src/images/worktemplates/playbooks/feature_lifecycle.png index 85dc522d03..f7cd6447ae 100644 Binary files a/webapp/channels/src/images/worktemplates/playbooks/feature_lifecycle.png and b/webapp/channels/src/images/worktemplates/playbooks/feature_lifecycle.png differ diff --git a/webapp/channels/src/images/worktemplates/playbooks/incident_resolution.png b/webapp/channels/src/images/worktemplates/playbooks/incident_resolution.png index a69308e750..d07a3c2592 100644 Binary files a/webapp/channels/src/images/worktemplates/playbooks/incident_resolution.png and b/webapp/channels/src/images/worktemplates/playbooks/incident_resolution.png differ diff --git a/webapp/channels/src/images/worktemplates/playbooks/product_release.png b/webapp/channels/src/images/worktemplates/playbooks/product_release.png index 8444c8c11a..df2fff7421 100644 Binary files a/webapp/channels/src/images/worktemplates/playbooks/product_release.png and b/webapp/channels/src/images/worktemplates/playbooks/product_release.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/bug_bash/bug_bash.png b/webapp/channels/src/images/worktemplates/product_teams/bug_bash/bug_bash.png index 6232b5e1f2..8ebf0f58f5 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/bug_bash/bug_bash.png and b/webapp/channels/src/images/worktemplates/product_teams/bug_bash/bug_bash.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/bug_bash/channel.png b/webapp/channels/src/images/worktemplates/product_teams/bug_bash/channel.png index 92355996f4..499917704a 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/bug_bash/channel.png and b/webapp/channels/src/images/worktemplates/product_teams/bug_bash/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/feature_release/channel.png b/webapp/channels/src/images/worktemplates/product_teams/feature_release/channel.png index ed7b54ff51..c488a16c08 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/feature_release/channel.png and b/webapp/channels/src/images/worktemplates/product_teams/feature_release/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/feature_release/feature_release.png b/webapp/channels/src/images/worktemplates/product_teams/feature_release/feature_release.png index 0426c85680..ae7090e2dd 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/feature_release/feature_release.png and b/webapp/channels/src/images/worktemplates/product_teams/feature_release/feature_release.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/feature_release/playbooks.png b/webapp/channels/src/images/worktemplates/product_teams/feature_release/playbooks.png new file mode 100644 index 0000000000..b7ed5bcad9 Binary files /dev/null and b/webapp/channels/src/images/worktemplates/product_teams/feature_release/playbooks.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/channel.png b/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/channel.png index f847b8ba4a..66ef1ec443 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/channel.png and b/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/goals_and_okrs.png b/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/goals_and_okrs.png index 49f4808abc..ea2ebad29d 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/goals_and_okrs.png and b/webapp/channels/src/images/worktemplates/product_teams/goals_and_okrs/goals_and_okrs.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/channel.png b/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/channel.png index f20df37c7a..3687297caa 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/channel.png and b/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/product_roadmap.png b/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/product_roadmap.png index ddbd32b31b..e45c56130c 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/product_roadmap.png and b/webapp/channels/src/images/worktemplates/product_teams/product_roadmap/product_roadmap.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/channel.png b/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/channel.png index 883d5f8304..0d087d0741 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/channel.png and b/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/channel.png differ diff --git a/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/sprint_planning.png b/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/sprint_planning.png index c82ecd7b6e..c3bcfe01da 100644 Binary files a/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/sprint_planning.png and b/webapp/channels/src/images/worktemplates/product_teams/sprint_planning/sprint_planning.png differ diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.test.ts index cb7eca1570..cf5e7e4665 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.test.ts @@ -732,3 +732,41 @@ describe('shouldShowUnreadsCategory', () => { expect(Selectors.shouldShowUnreadsCategory(state)).toBe(true); }); }); + +describe('get feature flags', () => { + test('should check the value of feature flag AppsSidebarCategory', () => { + let state = { + entities: { + general: { + config: {}, + }, + }, + } as GlobalState; + + expect(Selectors.appsSidebarCategoryEnabled(state)).toBe(false); + + state = { + entities: { + general: { + config: { + FeatureFlagAppsSidebarCategory: 'false', + }, + }, + }, + } as GlobalState; + + expect(Selectors.appsSidebarCategoryEnabled(state)).toBe(false); + + state = { + entities: { + general: { + config: { + FeatureFlagAppsSidebarCategory: 'true', + }, + }, + }, + } as GlobalState; + + expect(Selectors.appsSidebarCategoryEnabled(state)).toBe(true); + }); +}); diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts index cc3dc612cc..fa0149a93c 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/preferences.ts @@ -299,3 +299,7 @@ export function autoShowLinkedBoardFFEnabled(state: GlobalState): boolean { export function onboardingTourTipsEnabled(state: GlobalState): boolean { return getFeatureFlagValue(state, 'OnboardingTourTips') === 'true'; } + +export function appsSidebarCategoryEnabled(state: GlobalState): boolean { + return getFeatureFlagValue(state, 'AppsSidebarCategory') === 'true'; +} diff --git a/webapp/channels/src/sass/base/_structure.scss b/webapp/channels/src/sass/base/_structure.scss index 0ec379a86d..7171f80757 100644 --- a/webapp/channels/src/sass/base/_structure.scss +++ b/webapp/channels/src/sass/base/_structure.scss @@ -160,7 +160,6 @@ body.app__body #root { } #SidebarContainer.move--right { position: relative; - left: 65px; } } } diff --git a/webapp/channels/src/sass/layout/_sidebar-left.scss b/webapp/channels/src/sass/layout/_sidebar-left.scss index c670182893..2f5aa4759a 100644 --- a/webapp/channels/src/sass/layout/_sidebar-left.scss +++ b/webapp/channels/src/sass/layout/_sidebar-left.scss @@ -575,6 +575,18 @@ $sidebarOpacityAnimationDuration: 0.15s; } } + @media screen and (min-width: 768px) { + .SidebarNavContainer { + .scrollbar--view { + max-width: 240px; + } + } + + #SidebarContainer .SidebarChannelGroup .SidebarChannelGroupHeader { + max-width: 240px; + } + } + .SidebarCategory_newLabel { display: flex; width: 32px; @@ -741,6 +753,7 @@ $sidebarOpacityAnimationDuration: 0.15s; } .AddChannelsCtaDropdown .dropdown-menu { + min-width: 210px !important; margin-left: 20px; } @@ -748,20 +761,13 @@ $sidebarOpacityAnimationDuration: 0.15s; #addChannelsCta { display: flex; width: 100%; + margin-top: -6px; &:hover { background-color: var(--sidebar-text-hover-bg); } } - #AddChannelCtaDropdown { - position: fixed; - - ul { - min-width: 232px !important; - } - } - .SidebarChannelNavigator_inviteUsersSticky { position: absolute; z-index: 2; @@ -1021,7 +1027,6 @@ $sidebarOpacityAnimationDuration: 0.15s; /* Channels */ .SidebarChannel { display: flex; - overflow: hidden; height: 32px; /* height required for transition animation */ diff --git a/webapp/channels/src/sass/utils/_mixins.scss b/webapp/channels/src/sass/utils/_mixins.scss index 2d3f74df34..bb4ef5b0a0 100644 --- a/webapp/channels/src/sass/utils/_mixins.scss +++ b/webapp/channels/src/sass/utils/_mixins.scss @@ -114,9 +114,13 @@ display: flex; font-size: 18px; - &::before { + &:first-child::before { margin: 0 7px 0 0; } + + &:last-child::before { + margin: 0 0 0 7px; + } } } @@ -146,7 +150,7 @@ outline: none; } - &:disabled { + &:disabled:not(.always-show-enabled) { background: rgba(var(--center-channel-color-rgb), 0.08); color: rgba(var(--center-channel-color-rgb), 0.32); cursor: not-allowed; diff --git a/webapp/channels/src/selectors/work_template.ts b/webapp/channels/src/selectors/work_template.ts index 3d6e46235e..0d9b27cb5d 100644 --- a/webapp/channels/src/selectors/work_template.ts +++ b/webapp/channels/src/selectors/work_template.ts @@ -9,10 +9,8 @@ import {GlobalState} from 'types/store'; export const areWorkTemplatesEnabled = createSelector( 'areWorktemplatesEnabled', (state: GlobalState) => getFeatureFlagValue(state, 'WorkTemplate') === 'true', - (state: GlobalState) => getFeatureFlagValue(state, 'BoardsProduct') === 'true', - (state: GlobalState) => Boolean(state.plugins.plugins?.playbooks), - (workTemplateFF, boardProductEnabled, pluginPlaybooksInstalled) => { - return workTemplateFF && boardProductEnabled && pluginPlaybooksInstalled; + (workTemplateFF) => { + return workTemplateFF; }, ); diff --git a/webapp/channels/src/utils/constants.tsx b/webapp/channels/src/utils/constants.tsx index acbe965c4b..9c59ab386d 100644 --- a/webapp/channels/src/utils/constants.tsx +++ b/webapp/channels/src/utils/constants.tsx @@ -1080,6 +1080,7 @@ export const HostedCustomerLinks = { BILLING_DOCS: 'https://mattermost.com/pl/how-self-hosted-billing-works', SELF_HOSTED_BILLING: 'https://docs.mattermost.com/manage/self-hosted-billing.html', TERMS_AND_CONDITIONS: 'https://mattermost.com/enterprise-edition-terms/', + SECURITY_UPDATES: 'https://mattermost.com/security-updates/', }; export const DocLinks = { diff --git a/webapp/channels/tsconfig.json b/webapp/channels/tsconfig.json index f71364fdc0..85f5958590 100644 --- a/webapp/channels/tsconfig.json +++ b/webapp/channels/tsconfig.json @@ -28,8 +28,6 @@ "reselect": ["packages/reselect/src"], "@mui/styled-engine": ["./node_modules/@mui/styled-engine-sc"], "!!file-loader*": ["utils/empty-string"], - "@e2e-support/*": ["e2e/playwright/support/*"], - "@e2e-test.config": ["e2e/playwright/test.config.ts"] } }, "include": [ diff --git a/webapp/channels/webpack.config.js b/webapp/channels/webpack.config.js index 911550300f..2e7e3ab63c 100644 --- a/webapp/channels/webpack.config.js +++ b/webapp/channels/webpack.config.js @@ -441,7 +441,9 @@ if (DEV) { config.devtool = 'source-map'; } -const env = {}; +const env = { + STRIPE_PUBLIC_KEY: JSON.stringify(process.env.STRIPE_PUBLIC_KEY || ''), +}; if (DEV) { env.PUBLIC_PATH = JSON.stringify(publicPath); env.RUDDER_KEY = JSON.stringify(process.env.RUDDER_KEY || ''); diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 923fdfd0e4..987b8de866 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -3905,7 +3905,6 @@ "enzyme-to-json": "3.6.2", "eslint": "7.32.0", "eslint-import-resolver-webpack": "0.13.2", - "eslint-plugin-cypress": "2.11.3", "eslint-plugin-formatjs": "4.3.4", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.23.4", @@ -17819,18 +17818,6 @@ "eslint": ">=4.0.0" } }, - "node_modules/eslint-plugin-cypress": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.3.tgz", - "integrity": "sha512-hOoAid+XNFtpvOzZSNWP5LDrQBEJwbZwjib4XJ1KcRYKjeVj0mAmPmucG4Egli4j/aruv+Ow/acacoloWWCl9Q==", - "dev": true, - "dependencies": { - "globals": "^11.12.0" - }, - "peerDependencies": { - "eslint": ">= 3.2.1" - } - }, "node_modules/eslint-plugin-formatjs": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/eslint-plugin-formatjs/-/eslint-plugin-formatjs-4.3.4.tgz", @@ -50096,15 +50083,6 @@ "eslint-rule-composer": "^0.3.0" } }, - "eslint-plugin-cypress": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.3.tgz", - "integrity": "sha512-hOoAid+XNFtpvOzZSNWP5LDrQBEJwbZwjib4XJ1KcRYKjeVj0mAmPmucG4Egli4j/aruv+Ow/acacoloWWCl9Q==", - "dev": true, - "requires": { - "globals": "^11.12.0" - } - }, "eslint-plugin-formatjs": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/eslint-plugin-formatjs/-/eslint-plugin-formatjs-4.3.4.tgz", @@ -55129,7 +55107,6 @@ "enzyme-to-json": "3.6.2", "eslint": "7.32.0", "eslint-import-resolver-webpack": "0.13.2", - "eslint-plugin-cypress": "2.11.3", "eslint-plugin-formatjs": "4.3.4", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.23.4", diff --git a/webapp/platform/client/src/client4.ts b/webapp/platform/client/src/client4.ts index ce123c77d2..6d09c5cf68 100644 --- a/webapp/platform/client/src/client4.ts +++ b/webapp/platform/client/src/client4.ts @@ -26,6 +26,7 @@ import { CreateSubscriptionRequest, Feedback, WorkspaceDeletionRequest, + NewsletterRequestBody, } from '@mattermost/types/cloud'; import { SelfHostedSignupForm, @@ -3894,6 +3895,7 @@ export default class Client4 { ); }; + confirmSelfHostedExpansion = (setupIntentId: string, expandRequest: SelfHostedExpansionRequest) => { return this.doFetch( `${this.getHostedCustomerRoute()}/confirm?expand=true`, @@ -3901,6 +3903,13 @@ export default class Client4 { ); } + subscribeToNewsletter = (newletterRequestBody: NewsletterRequestBody) => { + return this.doFetch( + `${this.getHostedCustomerRoute()}/subscribe-newsletter`, + {method: 'post', body: JSON.stringify(newletterRequestBody)}, + ); + }; + createPaymentMethod = async () => { return this.doFetch( `${this.getCloudRoute()}/payment`, diff --git a/webapp/platform/components/src/common/hooks/useFollowElementDimensions.ts b/webapp/platform/components/src/common/hooks/useFollowElementDimensions.ts new file mode 100644 index 0000000000..beb3306fc9 --- /dev/null +++ b/webapp/platform/components/src/common/hooks/useFollowElementDimensions.ts @@ -0,0 +1,27 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {useEffect, useState} from 'react'; + +export const useFollowElementDimensions = (elementId: string): DOMRectReadOnly => { + const [dimensions, setDimensions] = useState(new DOMRect()); + + useEffect(() => { + const element = document.getElementById(elementId); + if (!element) { + return undefined; + } + const observer = new ResizeObserver((entries) => { + if (entries.length > 0) { + setDimensions(entries[0].contentRect); + } + }); + observer.observe(element); + + return () => { + observer.unobserve(element); + }; + }, [elementId]); + + return dimensions; +}; diff --git a/webapp/platform/components/src/generic_modal/footer_content/__snapshots__/footer_pagination.test.tsx.snap b/webapp/platform/components/src/generic_modal/footer_content/__snapshots__/footer_pagination.test.tsx.snap new file mode 100644 index 0000000000..29d49c0a2a --- /dev/null +++ b/webapp/platform/components/src/generic_modal/footer_content/__snapshots__/footer_pagination.test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/GenericModal/FooterPagination should render default 1`] = ` +
    +
    +
    + + +
    +
    +`; diff --git a/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.scss b/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.scss new file mode 100644 index 0000000000..35594f8bc5 --- /dev/null +++ b/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.scss @@ -0,0 +1,41 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +@import '../../../../../channels/src/sass/utils/mixins'; + +.footer-pagination { + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + + &__legend { + display: flex; + flex: 1; + align-items: center; + justify-content: flex-start; + color: rgba(var(--center-channel-color-rgb), 0.64); + font-size: 12px; + font-weight: 600; + line-height: 16px; + } + + &__button-container { + display: flex; + align-items: center; + justify-content: center; + + &__button { + @include tertiary-button; + @include button-small; + + &:not(:first-child) { + margin-left: 8px; + } + + > :not(:first-child) { + margin-left: 5px; + } + } + } +} diff --git a/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.test.tsx b/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.test.tsx new file mode 100644 index 0000000000..9b80d2a2fe --- /dev/null +++ b/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.test.tsx @@ -0,0 +1,88 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import {FooterPagination} from './'; + +describe('components/GenericModal/FooterPagination', () => { + const baseProps = { + page: 0, + total: 0, + itemsPerPage: 0, + onNextPage: jest.fn(), + onPreviousPage: jest.fn(), + }; + + test('should render default', () => { + const wrapper = shallow( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should render pagination legend', () => { + const wrapper = shallow( + , + ); + + const legend = wrapper.find('.footer-pagination__legend'); + + expect(legend.length).toEqual(1); + expect(legend.at(0).text()).toEqual('Showing 1-10 of 17'); + }); + + test('should render pagination buttons', () => { + const wrapper = shallow( + , + ); + + const buttons = wrapper.find('.footer-pagination__button-container__button'); + + expect(buttons.length).toEqual(2); + expect(buttons.at(0).text()).toEqual('Previous'); + expect(buttons.at(1).text()).toEqual('Next'); + }); + + test('should handle pagination buttons', async () => { + const onPreviousPage = jest.fn(); + const onNextPage = jest.fn(); + + const wrapper = shallow( + , + ); + + const buttons = wrapper.find('.footer-pagination__button-container__button'); + const prevButton = buttons.at(0); + const nextButton = buttons.at(1); + + expect(prevButton.hasClass('disabled')).toBeFalsy(); + expect(nextButton.hasClass('disabled')).toBeFalsy(); + + nextButton.simulate('click'); + + expect(onNextPage).toHaveBeenCalledTimes(1); + + prevButton.simulate('click'); + + expect(onPreviousPage).toHaveBeenCalledTimes(1); + }); +}); diff --git a/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.tsx b/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.tsx new file mode 100644 index 0000000000..3efd3c7a75 --- /dev/null +++ b/webapp/platform/components/src/generic_modal/footer_content/footer_pagination.tsx @@ -0,0 +1,93 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import classNames from 'classnames'; +import React from 'react'; +import {useIntl} from 'react-intl'; + +import {ChevronLeftIcon, ChevronRightIcon} from '@mattermost/compass-icons/components'; + +import './footer_pagination.scss'; + +const BUTTON_ICON_SIZE = 16; + +type FooterPaginationProps = { + page: number; + total: number; + itemsPerPage: number; + onNextPage: (event: React.MouseEvent) => void; + onPreviousPage: (event: React.MouseEvent) => void; +}; + +export const FooterPagination = ({ + page, + total, + itemsPerPage, + onNextPage, + onPreviousPage, +}: FooterPaginationProps) => { + const {formatMessage} = useIntl(); + + const startCount = page * itemsPerPage; + const endCount = Math.min(startCount + itemsPerPage, total); + const totalPages = Math.trunc((total - 1) / itemsPerPage); + + const prevDisabled = page <= 0; + const nextDisabled = page >= totalPages; + + return ( +
    +
    + {Boolean(total) && ( + formatMessage( + { + id: 'footer_pagination.count', + defaultMessage: 'Showing {startCount, number}-{endCount, number} of {total, number}', + }, + { + startCount: startCount + 1, + endCount, + total, + }, + ) + )} +
    +
    + + +
    +
    + ); +}; diff --git a/webapp/platform/components/src/generic_modal/footer_content/index.ts b/webapp/platform/components/src/generic_modal/footer_content/index.ts new file mode 100644 index 0000000000..6097345e39 --- /dev/null +++ b/webapp/platform/components/src/generic_modal/footer_content/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +export * from './footer_pagination'; diff --git a/webapp/platform/components/src/generic_modal/generic_modal.tsx b/webapp/platform/components/src/generic_modal/generic_modal.tsx index 9f5ea7a357..68272eb8f4 100644 --- a/webapp/platform/components/src/generic_modal/generic_modal.tsx +++ b/webapp/platform/components/src/generic_modal/generic_modal.tsx @@ -34,13 +34,17 @@ export type Props = { enforceFocus?: boolean; container?: React.ReactNode | React.ReactNodeArray; ariaLabel?: string; - errorText?: string; + errorText?: string | React.ReactNode; compassDesign?: boolean; backdrop?: boolean; backdropClassName?: string; tabIndex?: number; children: React.ReactNode; keyboardEscape?: boolean; + headerInput?: React.ReactNode; + bodyPadding?: boolean; + footerContent?: React.ReactNode; + footerDivider?: boolean; }; type State = { @@ -56,6 +60,7 @@ export class GenericModal extends React.PureComponent { autoCloseOnConfirmButton: true, enforceFocus: true, keyboardEscape: true, + bodyPadding: true, }; constructor(props: Props) { @@ -195,7 +200,12 @@ export class GenericModal extends React.PureComponent { className='GenericModal__wrapper-enter-key-press-catcher' > - {this.props.compassDesign && headerText} + {this.props.compassDesign && ( + <> + {headerText} + {this.props.headerInput} + + )} {this.props.compassDesign ? ( @@ -208,14 +218,22 @@ export class GenericModal extends React.PureComponent { ) : ( headerText )} -
    +
    {this.props.children}
    - {(cancelButton || confirmButton) && - {cancelButton} - {confirmButton} - } + {(cancelButton || confirmButton || this.props.footerContent) && ( + + {(cancelButton || confirmButton) ? ( + <> + {cancelButton} + {confirmButton} + + ) : ( + this.props.footerContent + )} + + )}
    diff --git a/webapp/platform/components/src/index.tsx b/webapp/platform/components/src/index.tsx index 4ea9461df8..2ead4f1994 100644 --- a/webapp/platform/components/src/index.tsx +++ b/webapp/platform/components/src/index.tsx @@ -7,6 +7,7 @@ export type {CircleSkeletonLoaderProps, RectangleSkeletonLoaderProps} from './sk export type {Props as FocusTrapProps} from './focus_trap'; // components +export * from './generic_modal/footer_content'; export {GenericModal} from './generic_modal/generic_modal'; export {CircleSkeletonLoader, RectangleSkeletonLoader} from './skeleton_loader'; export * from './tour_tip'; @@ -16,3 +17,4 @@ export {FocusTrap} from './focus_trap'; // hooks export * from './common/hooks/useMeasurePunchouts'; export {useElementAvailable} from './common/hooks/useElementAvailable'; +export {useFollowElementDimensions} from './common/hooks/useFollowElementDimensions'; diff --git a/webapp/platform/components/src/tour_tip/tour_tip.scss b/webapp/platform/components/src/tour_tip/tour_tip.scss index a4fedc2eb1..378ae208df 100644 --- a/webapp/platform/components/src/tour_tip/tour_tip.scss +++ b/webapp/platform/components/src/tour_tip/tour_tip.scss @@ -363,7 +363,7 @@ &__backdrop { position: absolute; - z-index: 999; + z-index: 100; top: 0; left: 0; width: 100%; diff --git a/webapp/platform/components/src/tour_tip/tour_tip.tsx b/webapp/platform/components/src/tour_tip/tour_tip.tsx index 7f2509bb15..90cdac6a8f 100644 --- a/webapp/platform/components/src/tour_tip/tour_tip.tsx +++ b/webapp/platform/components/src/tour_tip/tour_tip.tsx @@ -90,15 +90,10 @@ export const TourTip = ({ }: Props) => { const FIRST_STEP_INDEX = 0; const triggerRef = useRef(null); - const [useBackdrop, setUseBackdrop] = useState(!hideBackdrop); const onJump = (event: React.MouseEvent, jumpToStep: number) => { handleJump?.(event, jumpToStep); }; - useClickOutsideRef(triggerRef, () => { - setUseBackdrop(false); - }); - // This needs to be changed if root-portal node isn't available to maybe body const rootPortal = document.getElementById('root-portal'); @@ -214,7 +209,7 @@ export const TourTip = ({ >
    - {useBackdrop && } + /> {show && ( ); }; - diff --git a/webapp/platform/types/src/cloud.ts b/webapp/platform/types/src/cloud.ts index 36a586f7be..74b7195ab2 100644 --- a/webapp/platform/types/src/cloud.ts +++ b/webapp/platform/types/src/cloud.ts @@ -220,6 +220,11 @@ export interface CreateSubscriptionRequest { internal_purchase_order?: string; } +export interface NewsletterRequestBody { + email: string; + subscribed_content: string; +} + export const areShippingDetailsValid = (address: Address | null | undefined): boolean => { if (!address) { return false; diff --git a/webapp/platform/types/src/config.ts b/webapp/platform/types/src/config.ts index acb4047d45..a99f58ef4b 100644 --- a/webapp/platform/types/src/config.ts +++ b/webapp/platform/types/src/config.ts @@ -116,6 +116,7 @@ export type ClientConfig = { ExperimentalViewArchivedChannels: string; FileLevel: string; FeatureFlagAppsEnabled: string; + FeatureFlagAppsSidebarCategory: string; FeatureFlagBoardsProduct: string; FeatureFlagCallsEnabled: string; FeatureFlagGraphQL: string;