diff --git a/server/channels/app/worktemplates/templates.yaml b/server/channels/app/worktemplates/templates.yaml index d935f9308c..351e034d15 100644 --- a/server/channels/app/worktemplates/templates.yaml +++ b/server/channels/app/worktemplates/templates.yaml @@ -42,7 +42,7 @@ content: template: "Product Release" name: "Feature release" id: product-release-playbook - illustration: "/static/worktemplates/product_teams/feature_release/playbook.png" + illustration: "/static/worktemplates/playbooks/product_release.png" - integration: id: jira - integration: @@ -61,7 +61,7 @@ description: board: id: worktemplate.product_teams.goals_and_okrs.board defaultMessage: >- - Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. + 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: >- @@ -134,7 +134,7 @@ description: board: id: worktemplate.product_teams.sprint_planning.board defaultMessage: >- - Track your team's progress toward weekly goals with sprint breakdowns, prioritization, owner assignment, and comments. + 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: >- @@ -263,7 +263,7 @@ description: board: id: worktemplate.companywide.goals_and_okrs.board defaultMessage: >- - Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. + 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: >- @@ -337,7 +337,7 @@ description: board: id: worktemplate.leadership.goals_and_okrs.board defaultMessage: >- - Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. + 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: >- diff --git a/server/channels/app/worktemplates/worktemplate_generated.go b/server/channels/app/worktemplates/worktemplate_generated.go index 95e77edee5..a201d7c5c3 100644 --- a/server/channels/app/worktemplates/worktemplate_generated.go +++ b/server/channels/app/worktemplates/worktemplate_generated.go @@ -143,7 +143,7 @@ var wt00a1b44a5831c0a3acb14787b3fdd352 = &WorkTemplate{ Template: "Product Release", Name: "Feature release", ID: "product-release-playbook", - Illustration: "/static/worktemplates/product_teams/feature_release/playbook.png", + Illustration: "/static/worktemplates/playbooks/product_release.png", }, }, { @@ -174,7 +174,7 @@ var wt5baa68055bf9ea423273662e01ccc575 = &WorkTemplate{ }, Board: &TranslatableString{ ID: "worktemplate.product_teams.goals_and_okrs.board", - DefaultMessage: "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. ", + 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: "", }, @@ -286,7 +286,7 @@ var wt8d2ef53deac5517eb349dc5de6150196 = &WorkTemplate{ }, Board: &TranslatableString{ ID: "worktemplate.product_teams.sprint_planning.board", - DefaultMessage: "Track your team's progress toward weekly goals with sprint breakdowns, prioritization, owner assignment, and comments. ", + DefaultMessage: "Track your team's progress toward weekly goals with sprint breakdowns, prioritization, owner assignment, and comments.", Illustration: "", }, @@ -487,7 +487,7 @@ var wtf7b846d35810f8272eeb9a1a562025b5 = &WorkTemplate{ }, Board: &TranslatableString{ ID: "worktemplate.companywide.goals_and_okrs.board", - DefaultMessage: "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. ", + 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: "", }, @@ -601,7 +601,7 @@ var wt32ab773bfe021e3d4913931041552559 = &WorkTemplate{ }, Board: &TranslatableString{ ID: "worktemplate.leadership.goals_and_okrs.board", - DefaultMessage: "Track your team's progress toward organizational goals with the Goals and OKR board. Keep meetings on track with the Meeting Agenda board. ", + 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: "", }, diff --git a/webapp/channels/src/components/work_templates/components/customize.tsx b/webapp/channels/src/components/work_templates/components/customize.tsx index f2014c5617..6d5639308c 100644 --- a/webapp/channels/src/components/work_templates/components/customize.tsx +++ b/webapp/channels/src/components/work_templates/components/customize.tsx @@ -11,9 +11,12 @@ import {trackEvent} from 'actions/telemetry_actions'; import Constants, {TELEMETRY_CATEGORIES} from 'utils/constants'; import {isEnterpriseOrE20License} from 'utils/license_utils'; import {getLicense} from 'mattermost-redux/selectors/entities/general'; - +import {haveICurrentTeamPermission} from 'mattermost-redux/selectors/entities/roles'; +import {Permissions} from 'mattermost-redux/constants'; +import {GlobalState} from 'types/store'; import {Visibility, WorkTemplate} from '@mattermost/types/work_templates'; import {ChannelType} from '@mattermost/types/channels'; + export interface CustomizeProps { className?: string; name: string; @@ -38,6 +41,10 @@ const Customize = ({ const templateHasChannels = template.content.findIndex((item) => item.channel) !== -1; const templateHasBoards = template.content.findIndex((item) => item.board) !== -1; const templateHasPlaybooks = template.content.findIndex((item) => item.playbook) !== -1; + const canCreatePublicChannel = useSelector((state: GlobalState) => haveICurrentTeamPermission(state, Permissions.CREATE_PUBLIC_CHANNEL)); + const canCreatePrivateChannel = useSelector((state: GlobalState) => haveICurrentTeamPermission(state, Permissions.CREATE_PRIVATE_CHANNEL)); + const canCreatePublicPlaybook = useSelector((state: GlobalState) => haveICurrentTeamPermission(state, Permissions.PLAYBOOK_PUBLIC_CREATE)); + const canCreatePrivatePlaybook = useSelector((state: GlobalState) => haveICurrentTeamPermission(state, Permissions.PLAYBOOK_PRIVATE_CREATE)); useEffect(() => { trackEvent(TELEMETRY_CATEGORIES.WORK_TEMPLATES, 'pageview_customize'); @@ -49,10 +56,39 @@ const Customize = ({ }; let privateButtonProps = {}; - if (templateHasPlaybooks && !licenseIsEnterprise) { - if (visibility === Visibility.Private) { - onVisibilityChanged(Visibility.Public); + let publicButtonProps = {}; + if (templateHasPlaybooks) { + if (!canCreatePublicPlaybook) { + publicButtonProps = { + tooltip: formatMessage({id: 'work_templates.customize.public_playbook_permission_issue', defaultMessage: 'You do not have permission to create public playbooks.'}), + disabled: true, + }; } + if (!canCreatePrivatePlaybook) { + privateButtonProps = { + tooltip: formatMessage({id: 'work_templates.customize.private_playbook_permission_issue', defaultMessage: 'You do not have permission to create private playbooks.'}), + disabled: true, + }; + } + } + + if (templateHasChannels) { + if (!canCreatePublicChannel) { + publicButtonProps = { + tooltip: formatMessage({id: 'work_templates.customize.public_channel_permission_issue', defaultMessage: 'You do not have permission to create public channels.'}), + disabled: true, + }; + } + if (!canCreatePrivateChannel) { + privateButtonProps = { + tooltip: formatMessage({id: 'work_templates.customize.private_channel_permission_issue', defaultMessage: 'You do not have permission to create private channels.'}), + disabled: true, + }; + } + } + + // leave this rule last as it has priority + if (templateHasPlaybooks && !licenseIsEnterprise) { privateButtonProps = { tooltip: formatMessage({id: 'work_templates.customize.private_playbook_license_issue', defaultMessage: 'Private playbooks requires an Enterprise license.'}), locked: true, @@ -98,6 +134,7 @@ const Customize = ({ selected={privacySelectorValue} onChange={onPrivacySelectorChanged} privateButtonProps={privateButtonProps} + publicButtonProps={publicButtonProps} /> @@ -146,4 +183,3 @@ const StyledCustomized = styled(Customize)` `; export default StyledCustomized; - diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index 522d68ae64..46bfe63691 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -5699,7 +5699,11 @@ "work_templates.customize.name_label_all": "Name your channel, board, and playbook", "work_templates.customize.name_label_channels_boards": "Name your channel and board", "work_templates.customize.name_label_channels_playbooks": "Name your channel and playbook", + "work_templates.customize.private_channel_permission_issue": "You do not have permission to create private channels.", "work_templates.customize.private_playbook_license_issue": "Private playbooks requires an Enterprise license.", + "work_templates.customize.private_playbook_permission_issue": "You do not have permission to create private playbooks.", + "work_templates.customize.public_channel_permission_issue": "You do not have permission to create public channels.", + "work_templates.customize.public_playbook_permission_issue": "You do not have permission to create public playbooks.", "work_templates.customize.visibility_title": "Who should have access to this?", "work_templates.menu.modal_title": "Create from a template", "work_templates.menu.quick_use": "Quick use", diff --git a/webapp/channels/src/images/worktemplates/playbooks/feature_lifecycle.png b/webapp/channels/src/images/worktemplates/playbooks/feature_lifecycle.png index f7cd6447ae..b7ed5bcad9 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/product_teams/feature_release/playbooks.png b/webapp/channels/src/images/worktemplates/product_teams/feature_release/playbooks.png deleted file mode 100644 index b7ed5bcad9..0000000000 Binary files a/webapp/channels/src/images/worktemplates/product_teams/feature_release/playbooks.png and /dev/null differ diff --git a/webapp/channels/src/selectors/work_template.ts b/webapp/channels/src/selectors/work_template.ts index 0d9b27cb5d..1f9774b47d 100644 --- a/webapp/channels/src/selectors/work_template.ts +++ b/webapp/channels/src/selectors/work_template.ts @@ -3,14 +3,23 @@ import {createSelector} from 'reselect'; -import {getFeatureFlagValue} from 'mattermost-redux/selectors/entities/general'; +import {getFeatureFlagValue, getLicense} from 'mattermost-redux/selectors/entities/general'; import {GlobalState} from 'types/store'; +import {haveICurrentTeamPermission} from 'mattermost-redux/selectors/entities/roles'; +import {Permissions} from 'mattermost-redux/constants'; +import {isEnterpriseOrE20License} from 'utils/license_utils'; export const areWorkTemplatesEnabled = createSelector( 'areWorktemplatesEnabled', (state: GlobalState) => getFeatureFlagValue(state, 'WorkTemplate') === 'true', - (workTemplateFF) => { - return workTemplateFF; + (state: GlobalState) => getLicense(state), + (state: GlobalState) => haveICurrentTeamPermission(state, Permissions.CREATE_PUBLIC_CHANNEL) || haveICurrentTeamPermission(state, Permissions.CREATE_PRIVATE_CHANNEL), + (state: GlobalState) => haveICurrentTeamPermission(state, Permissions.PLAYBOOK_PUBLIC_CREATE), + (state: GlobalState) => haveICurrentTeamPermission(state, Permissions.PLAYBOOK_PRIVATE_CREATE), + (workTemplateFF, license, canCreateChannel, canCreatePublicPlaybook, canCreatePrivatePlaybook) => { + const licenseIsEnterprise = isEnterpriseOrE20License(license); + const canCreatePlaybook = canCreatePublicPlaybook || (canCreatePrivatePlaybook && licenseIsEnterprise); + return workTemplateFF && canCreateChannel && canCreatePlaybook; }, );