Skip to content

Commit 2b7e5a1

Browse files
committed
[#73188] Remove EE guards from boards
Make action boards available in Community Edition by removing board_view-based frontend gating and aligning boards feature coverage with the new default behavior. https://community.openproject.org/wp/73188
1 parent 772fae7 commit 2b7e5a1

File tree

8 files changed

+195
-292
lines changed

8 files changed

+195
-292
lines changed

frontend/src/app/core/setup/globals/onboarding/onboarding_tour.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,28 +92,18 @@ function workPackageFullViewTour() {
9292
});
9393
}
9494

95-
function ganttTour(configuration:ConfigurationService) {
95+
function ganttTour(_configuration:ConfigurationService) {
9696
initializeTour('ganttTourFinished');
9797

9898
const boardsDemoDataAvailable = getMetaContent('boards_demo_data_available') === 'true';
9999
const teamPlannerDemoDataAvailable = getMetaContent('demo_view_of_type_team_planner_seeded') === 'true';
100-
const eeTokenAvailable = configuration.availableFeatures.includes('board_view');
101100

102101
waitForElement('.work-package--results-tbody', '#content', () => {
103102
let steps:OnboardingStep[] = ganttOnboardingTourSteps();
104-
// Check for EE edition
105-
if (eeTokenAvailable) {
106-
// ... and available seed data of boards.
107-
// Then add boards to the tour, otherwise skip it.
108-
if (boardsDemoDataAvailable && moduleVisible('boards')) {
109-
steps = steps.concat(navigateToBoardStep('enterprise'));
110-
} else if (teamPlannerDemoDataAvailable && moduleVisible('team-planner-view')) {
111-
steps = steps.concat(navigateToTeamPlannerStep());
112-
} else {
113-
steps = steps.concat(menuTourSteps());
114-
}
115-
} else if (boardsDemoDataAvailable && moduleVisible('boards')) {
116-
steps = steps.concat(navigateToBoardStep('basic'));
103+
if (boardsDemoDataAvailable && moduleVisible('boards')) {
104+
steps = steps.concat(navigateToBoardStep('enterprise'));
105+
} else if (teamPlannerDemoDataAvailable && moduleVisible('team-planner-view')) {
106+
steps = steps.concat(navigateToTeamPlannerStep());
117107
} else {
118108
steps = steps.concat(menuTourSteps());
119109
}
@@ -122,14 +112,13 @@ function ganttTour(configuration:ConfigurationService) {
122112
});
123113
}
124114

125-
function boardTour(configuration:ConfigurationService) {
115+
function boardTour(_configuration:ConfigurationService) {
126116
initializeTour('boardsTourFinished');
127117

128118
const teamPlannerDemoDataAvailable = getMetaContent('demo_view_of_type_team_planner_seeded') === 'true';
129-
const eeTokenAvailable = configuration.availableFeatures.includes('board_view');
130119

131120
waitForElement('wp-single-card', '#content', () => {
132-
let steps:OnboardingStep[] = eeTokenAvailable ? boardTourSteps('enterprise') : boardTourSteps('basic');
121+
let steps:OnboardingStep[] = boardTourSteps('enterprise');
133122

134123
// Available seed data of team planner.
135124
// Then add Team planner to the tour, otherwise skip it.

frontend/src/app/features/boards/board/board-actions/board-actions-registry.service.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Injectable } from '@angular/core';
22
import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service';
3-
import { BannersService } from 'core-app/core/enterprise/banners.service';
43

54
export interface ITileViewEntry {
65
text:string;
@@ -13,10 +12,6 @@ export interface ITileViewEntry {
1312

1413
@Injectable({ providedIn: 'root' })
1514
export class BoardActionsRegistryService {
16-
constructor(
17-
private bannersService:BannersService,
18-
) {}
19-
2015
private mapping:Record<string, BoardActionService> = {};
2116

2217
public add(attribute:string, service:BoardActionService):void {
@@ -30,7 +25,6 @@ export class BoardActionsRegistryService {
3025
icon: '',
3126
description: '',
3227
image: '',
33-
disabled: !this.bannersService.allowsTo('board_view'),
3428
}));
3529
}
3630

Lines changed: 46 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,49 @@
11
@if ((board$ | async); as board) {
2-
@if (!board.isFree) {
3-
<op-enterprise-banner-frame class="boards-list--enterprise-banner"
4-
feature="board_view"
5-
[dismissable]="true"
6-
/>
7-
}
8-
@if (available) {
9-
<div
10-
class="boards-list--container"
11-
[ngClass]="{ '-free' : board.isFree }"
12-
#container
13-
cdkDropList
14-
[cdkDropListDisabled]="!board.editable"
15-
cdkDropListOrientation="horizontal"
16-
(cdkDropListDropped)="moveList(board, $event)"
17-
>
18-
@for (boardWidget of boardWidgets; track trackByQueryId($index, boardWidget)) {
19-
<div
20-
class="boards-list--item"
21-
cdkDrag
22-
[cdkDragData]="boardWidget"
23-
>
24-
@if (board.editable) {
25-
<span
26-
class="boards-list-item-handle icon icon-drag-handle"
27-
cdkDragHandle></span>
28-
}
29-
<board-list [resource]="boardWidget"
30-
[board]="board"
31-
(onRemove)="removeList(board, boardWidget)"
32-
(visibilityChange)="changeVisibilityOfList(board, boardWidget, $event)" />
2+
<div
3+
class="boards-list--container"
4+
[ngClass]="{ '-free' : board.isFree }"
5+
#container
6+
cdkDropList
7+
[cdkDropListDisabled]="!board.editable"
8+
cdkDropListOrientation="horizontal"
9+
(cdkDropListDropped)="moveList(board, $event)"
10+
>
11+
@for (boardWidget of boardWidgets; track trackByQueryId($index, boardWidget)) {
12+
<div
13+
class="boards-list--item"
14+
cdkDrag
15+
[cdkDragData]="boardWidget"
16+
>
17+
@if (board.editable) {
18+
<span
19+
class="boards-list-item-handle icon icon-drag-handle"
20+
cdkDragHandle></span>
21+
}
22+
<board-list [resource]="boardWidget"
23+
[board]="board"
24+
(onRemove)="removeList(board, boardWidget)"
25+
(visibilityChange)="changeVisibilityOfList(board, boardWidget, $event)" />
26+
</div>
27+
}
28+
@if (showHiddenListWarning) {
29+
<span
30+
class="boards-list--tooltip tooltip--right"
31+
[attr.data-tooltip]="text.hiddenListWarning">
32+
<i class="icon icon-attention"></i>
33+
</span>
34+
}
35+
@if (board.editable) {
36+
<div class="boards-list--add-item -no-text-select"
37+
role="button"
38+
tabindex="0"
39+
(click)="addList(board)"
40+
(keydown.enter)="addList(board)"
41+
(keydown.space)="addList(board)">
42+
<div class="boards-list--add-item-text">
43+
<op-icon icon-classes="icon-add icon-context" />
44+
<span [textContent]="text.addList"></span>
3345
</div>
34-
}
35-
@if (showHiddenListWarning) {
36-
<span
37-
class="boards-list--tooltip tooltip--right"
38-
[attr.data-tooltip]="text.hiddenListWarning">
39-
<i class="icon icon-attention"></i>
40-
</span>
41-
}
42-
@if (board.editable) {
43-
<div class="boards-list--add-item -no-text-select"
44-
role="button"
45-
tabindex="0"
46-
(click)="addList(board)"
47-
(keydown.enter)="addList(board)"
48-
(keydown.space)="addList(board)">
49-
<div class="boards-list--add-item-text">
50-
<op-icon icon-classes="icon-add icon-context" />
51-
<span [textContent]="text.addList"></span>
52-
</div>
53-
</div>
54-
}
55-
</div>
56-
}
46+
</div>
47+
}
48+
</div>
5749
}

frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { HalResourceNotificationService } from 'core-app/features/hal/services/h
1717
import { BoardListsService } from 'core-app/features/boards/board/board-list/board-lists.service';
1818
import { OpModalService } from 'core-app/shared/components/modal/modal.service';
1919
import { BoardService } from 'core-app/features/boards/board/board.service';
20-
import { BannersService } from 'core-app/core/enterprise/banners.service';
2120
import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service';
2221
import { QueryUpdatedService } from 'core-app/features/boards/board/query-updated/query-updated.service';
2322
import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin';
@@ -89,8 +88,6 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements
8988

9089
showHiddenListWarning = false;
9190

92-
available = this.Banner.allowsTo('board_view');
93-
9491
private currentQueryUpdatedMonitoring:Subscription;
9592

9693
constructor(
@@ -105,7 +102,6 @@ readonly I18n:I18nService,
105102
readonly injector:Injector,
106103
readonly apiV3Service:ApiV3Service,
107104
readonly Boards:BoardService,
108-
readonly Banner:BannersService,
109105
readonly boardListCrossSelectionService:BoardListCrossSelectionService,
110106
readonly wpStatesInitialization:WorkPackageStatesInitializationService,
111107
readonly Drag:DragAndDropService,
@@ -126,10 +122,6 @@ readonly I18n:I18nService,
126122
tap((board) => this.setupQueryUpdatedMonitoring(board)),
127123
);
128124

129-
this.board$.subscribe((board) => {
130-
this.available = this.Banner.allowsTo('board_view') || board.isFree;
131-
});
132-
133125
this.Boards.currentBoard$.next(id);
134126

135127
this.boardListCrossSelectionService

modules/boards/spec/features/board_enterprise_spec.rb

Lines changed: 19 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,10 @@
3232
require_relative "support/board_index_page"
3333
require_relative "support/board_page"
3434

35-
RSpec.describe "Boards enterprise spec", :js do
35+
RSpec.describe "Boards availability", :js do
3636
shared_let(:admin) { create(:admin) }
3737

3838
shared_let(:project) { create(:project, enabled_module_names: %i[work_package_tracking board_view]) }
39-
shared_let(:priority) { create(:default_priority) }
40-
shared_let(:status) { create(:default_status) }
41-
42-
let(:board_index) { Pages::BoardIndex.new(project) }
43-
4439
shared_let(:manual_board) { create(:board_grid_with_query, name: "My board", project:) }
4540
shared_let(:action_board) do
4641
create(:subproject_board,
@@ -49,61 +44,30 @@
4944
projects_columns: [])
5045
end
5146

52-
context "when EE inactive" do
53-
before do
54-
login_as(admin)
55-
board_index.visit!
56-
end
57-
58-
it "disabled all action boards" do
59-
page.find('[data-test-selector="add-board-button"]', text: "Board").click
60-
61-
expect(page).to have_css("#{test_selector('op-tile-block')}:not(.-disabled)", text: "Basic")
62-
expect(page).to have_css("#{test_selector('op-tile-block')}.-disabled", count: 5)
63-
end
64-
65-
it "shows a banner on the action board" do
66-
# Expect both existing boards to show
67-
expect(page).to have_content "My board"
68-
expect(page).to have_content "Subproject board"
69-
70-
board_page = board_index.open_board(manual_board)
71-
board_page.expect_query "My board"
72-
wait_for_network_idle # wait for the banner to be loaded
73-
expect(page).not_to have_enterprise_banner
47+
let(:board_index) { Pages::BoardIndex.new(project) }
7448

75-
board_index.visit!
76-
board_page = board_index.open_board(action_board)
77-
board_page.expect_query "Subproject board"
78-
expect(page).to have_enterprise_banner
79-
end
49+
before do
50+
login_as(admin)
51+
board_index.visit!
8052
end
8153

82-
context "when EE active", with_ee: %i[board_view] do
83-
before do
84-
login_as(admin)
85-
board_index.visit!
86-
end
54+
it "enables all board types" do
55+
page.find('[data-test-selector="add-board-button"]', text: "Board").click
8756

88-
it "enables all options" do
89-
page.find('[data-test-selector="add-board-button"]', text: "Board").click
90-
91-
expect(page).to have_css("#{test_selector('op-tile-block')}:not(.-disabled)", count: 6)
92-
end
57+
expect(page).to have_css("#{test_selector('op-tile-block')}:not(.-disabled)", count: 6)
58+
end
9359

94-
it "shows the action board" do
95-
# Expect both existing boards to show
96-
expect(page).to have_content "My board"
97-
expect(page).to have_content "Subproject board"
60+
it "renders action boards without an enterprise banner" do
61+
expect(page).to have_content "My board"
62+
expect(page).to have_content "Subproject board"
9863

99-
board_page = board_index.open_board(manual_board)
100-
board_page.expect_query "My board"
101-
expect(page).not_to have_enterprise_banner
64+
board_page = board_index.open_board(manual_board)
65+
board_page.expect_query "My board"
66+
expect(page).not_to have_enterprise_banner
10267

103-
board_index.visit!
104-
board_page = board_index.open_board(action_board)
105-
board_page.expect_query "Subproject board"
106-
expect(page).not_to have_enterprise_banner
107-
end
68+
board_index.visit!
69+
board_page = board_index.open_board(action_board)
70+
board_page.expect_query "Subproject board"
71+
expect(page).not_to have_enterprise_banner
10872
end
10973
end

0 commit comments

Comments
 (0)