Skip to content

Commit 015902d

Browse files
authored
Merge pull request #22438 from opf/implementation/72816-limit-sprint-creation-based-on-project-s-sharing-settings
Implementation/72816 limit sprint creation based on project s sharing settings
2 parents d457547 + 80622cd commit 015902d

File tree

10 files changed

+244
-124
lines changed

10 files changed

+244
-124
lines changed

modules/backlogs/app/contracts/sprints/base_contract.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
module Sprints
3232
class BaseContract < ::ModelContract
33+
validate :user_authorized
34+
3335
def self.model
3436
Agile::Sprint
3537
end
@@ -38,5 +40,15 @@ def self.model
3840
attribute :project_id
3941
attribute :start_date
4042
attribute :finish_date
43+
44+
private
45+
46+
def user_authorized
47+
return unless model.project
48+
49+
unless user.allowed_in_project?(:create_sprints, model.project)
50+
errors.add :base, :error_unauthorized
51+
end
52+
end
4153
end
4254
end

modules/backlogs/app/contracts/sprints/create_contract.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,13 @@
3030

3131
module Sprints
3232
class CreateContract < BaseContract
33-
validate :user_allowed_to_create
33+
validate :no_receiving_project
3434

3535
private
3636

37-
def user_allowed_to_create
38-
return if model.project.nil?
39-
40-
unless user.allowed_in_project?(:create_sprints, model.project)
41-
errors.add :base, :error_unauthorized
37+
def no_receiving_project
38+
if model.project&.receive_shared_sprints?
39+
errors.add :project, :receiving_sprints
4240
end
4341
end
4442
end

modules/backlogs/app/contracts/sprints/update_contract.rb

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,5 @@
3030

3131
module Sprints
3232
class UpdateContract < BaseContract
33-
validate :user_allowed_to_update
34-
35-
private
36-
37-
def user_allowed_to_update
38-
return if model.project.nil?
39-
40-
unless user.allowed_in_project?(:create_sprints, model.project)
41-
errors.add :base, :error_unauthorized
42-
end
43-
end
4433
end
4534
end

modules/backlogs/app/helpers/rb_common_helper.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ def scrum_projects_enabled?
137137
end
138138

139139
def allow_sprint_creation?(project)
140-
scrum_projects_enabled? && current_user.allowed_in_project?(:create_sprints, project)
140+
scrum_projects_enabled? &&
141+
current_user.allowed_in_project?(:create_sprints, project) &&
142+
!project.receive_shared_sprints?
141143
end
142144

143145
private

modules/backlogs/app/models/agile/sprint.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ class Sprint < ApplicationRecord
6464
validates :start_date, :finish_date, presence: true, if: :active?
6565
validates :finish_date,
6666
comparison: { greater_than_or_equal_to: :start_date },
67-
if: -> { start_date? && finish_date? }
67+
if: :date_range_set?
68+
6869
validates :status,
6970
uniqueness: {
7071
scope: :project_id,

modules/backlogs/config/locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ en:
6060
only_one_active_sprint_allowed: "only one active sprint is allowed per project."
6161
models:
6262
project:
63+
receiving_sprints: "is receiving shared sprints. Own sprints cannot be created."
6364
attributes:
6465
sprint_sharing:
6566
share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects."

modules/backlogs/spec/contracts/sprints/create_contract_spec.rb

Lines changed: 6 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -30,109 +30,24 @@
3030

3131
require "spec_helper"
3232
require "contracts/shared/model_contract_shared_context"
33+
require_relative "shared_contract_examples"
3334

3435
RSpec.describe Sprints::CreateContract do
35-
include_context "ModelContract shared context"
36+
include_context "as sprint contract"
3637

37-
let(:project) { build_stubbed(:project) }
38-
let(:user) { build_stubbed(:user) }
3938
let(:sprint) do
4039
Agile::Sprint.new(name: sprint_name,
41-
project:,
40+
project: sprint_project,
4241
start_date: sprint_start_date,
4342
finish_date: sprint_finish_date,
4443
status: sprint_status)
4544
end
46-
let(:sprint_name) { "Sprint 1" }
47-
let(:sprint_start_date) { Time.zone.today }
48-
let(:sprint_finish_date) { Time.zone.today + 14.days }
49-
let(:sprint_status) { "in_planning" }
50-
let(:permissions) { [:create_sprints] }
51-
52-
subject(:contract) { described_class.new(sprint, user) }
53-
54-
before do
55-
mock_permissions_for(user) do |mock|
56-
mock.allow_in_project(*permissions, project:) if project
57-
end
58-
end
5945

6046
describe "validation" do
61-
context "with valid attributes and permissions" do
62-
it_behaves_like "contract is valid"
63-
end
64-
65-
context "when project is nil" do
66-
let(:project) { nil }
67-
68-
it_behaves_like "contract is invalid", project: :blank
69-
end
70-
71-
context "when user does not have create_sprints permission" do
72-
let(:permissions) { [:view_work_packages] }
73-
74-
it_behaves_like "contract is invalid", base: :error_unauthorized
75-
end
76-
77-
context "when user has no permissions in project" do
78-
let(:permissions) { [] }
79-
80-
it_behaves_like "contract is invalid", base: :error_unauthorized
81-
end
82-
83-
context "when name is blank" do
84-
let(:sprint_name) { "" }
85-
86-
it_behaves_like "contract is invalid", name: :blank
87-
end
88-
89-
context "when start_date is blank" do
90-
let(:sprint_start_date) { nil }
91-
92-
it_behaves_like "contract is valid"
93-
end
94-
95-
context "when finish_date is blank" do
96-
let(:sprint_finish_date) { nil }
97-
98-
it_behaves_like "contract is valid"
99-
end
100-
101-
context "when finish_date is before start_date" do
102-
let(:sprint_start_date) { Time.zone.today }
103-
let(:sprint_finish_date) { Time.zone.today - 1.day }
104-
105-
it_behaves_like "contract is invalid", finish_date: %i[greater_than_or_equal_to]
106-
end
107-
108-
context "when the sprint is active" do
109-
let(:sprint_status) { "active" }
110-
111-
context "when start_date is blank" do
112-
let(:sprint_start_date) { nil }
113-
114-
it_behaves_like "contract is invalid", start_date: :blank
115-
end
116-
117-
context "when finish_date is blank" do
118-
let(:sprint_finish_date) { nil }
119-
120-
it_behaves_like "contract is invalid", finish_date: :blank
121-
end
122-
123-
context "when finish_date is before start_date" do
124-
let(:sprint_start_date) { Time.zone.today }
125-
let(:sprint_finish_date) { Time.zone.today - 1.day }
126-
127-
it_behaves_like "contract is invalid", finish_date: %i[greater_than_or_equal_to]
128-
end
129-
end
130-
131-
context "when user is admin without project permission" do
132-
let(:user) { build_stubbed(:admin) }
133-
let(:permissions) { [] }
47+
context "when the project is configured to receive sprints" do
48+
let(:sprint_project) { build_stubbed(:project, sprint_sharing: Projects::SprintSharing::RECEIVE_SHARED) }
13449

135-
it_behaves_like "contract is valid"
50+
it_behaves_like "contract is invalid", project: :receiving_sprints
13651
end
13752
end
13853
end
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# frozen_string_literal: true
2+
3+
# -- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
# ++
30+
31+
require "contracts/shared/model_contract_shared_context"
32+
33+
RSpec.shared_context "as sprint contract" do
34+
include_context "ModelContract shared context"
35+
36+
let(:user) { build_stubbed(:user) }
37+
38+
let(:sprint_project) { build_stubbed(:project) }
39+
let(:sprint_name) { "Sprint 1" }
40+
let(:sprint_start_date) { Time.zone.today }
41+
let(:sprint_finish_date) { Time.zone.today + 14.days }
42+
let(:sprint_status) { "in_planning" }
43+
let(:permissions) { [:create_sprints] }
44+
45+
subject(:contract) { described_class.new(sprint, user) }
46+
47+
before do
48+
mock_permissions_for(user) do |mock|
49+
mock.allow_in_project(*permissions, project: sprint_project) if sprint_project
50+
end
51+
end
52+
53+
describe "validation" do
54+
context "with valid attributes and permissions" do
55+
it_behaves_like "contract is valid"
56+
end
57+
58+
context "when project is nil" do
59+
let(:sprint_project) { nil }
60+
61+
it_behaves_like "contract is invalid", project: :blank
62+
end
63+
64+
context "when user does not have create_sprints permission" do
65+
let(:permissions) { [:view_work_packages] }
66+
67+
it_behaves_like "contract is invalid", base: :error_unauthorized
68+
end
69+
70+
context "when user has no permissions in project" do
71+
let(:permissions) { [] }
72+
73+
it_behaves_like "contract is invalid", base: :error_unauthorized
74+
end
75+
76+
context "when name is blank" do
77+
let(:sprint_name) { "" }
78+
79+
it_behaves_like "contract is invalid", name: :blank
80+
end
81+
82+
context "when finish_date is before start_date" do
83+
let(:sprint_start_date) { Time.zone.today }
84+
let(:sprint_finish_date) { Time.zone.today - 1.day }
85+
86+
it_behaves_like "contract is invalid", finish_date: :greater_than_or_equal_to
87+
end
88+
89+
context "when finish_date is same as start_date" do
90+
let(:sprint_start_date) { Time.zone.today }
91+
let(:sprint_finish_date) { sprint_start_date }
92+
93+
it_behaves_like "contract is valid"
94+
end
95+
96+
context "when the sprint is active" do
97+
let(:sprint_status) { "active" }
98+
99+
context "when start_date is blank" do
100+
let(:sprint_start_date) { nil }
101+
102+
it_behaves_like "contract is invalid", start_date: :blank
103+
end
104+
105+
context "when finish_date is blank" do
106+
let(:sprint_finish_date) { nil }
107+
108+
it_behaves_like "contract is invalid", finish_date: :blank
109+
end
110+
end
111+
112+
context "when the sprint is in_planning" do
113+
context "when start_date is blank" do
114+
let(:sprint_start_date) { nil }
115+
116+
it_behaves_like "contract is valid"
117+
end
118+
119+
context "when finish_date is blank" do
120+
let(:sprint_finish_date) { nil }
121+
122+
it_behaves_like "contract is valid"
123+
end
124+
end
125+
126+
context "when user is admin without project permission" do
127+
let(:user) { build_stubbed(:admin) }
128+
let(:permissions) { [] }
129+
130+
it_behaves_like "contract is valid"
131+
end
132+
end
133+
end
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# frozen_string_literal: true
2+
3+
# -- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
# ++
30+
31+
require "spec_helper"
32+
require "contracts/shared/model_contract_shared_context"
33+
require_relative "shared_contract_examples"
34+
35+
RSpec.describe Sprints::UpdateContract do
36+
include_context "as sprint contract"
37+
38+
let(:sprint) do
39+
build_stubbed(:agile_sprint,
40+
name: sprint_name,
41+
project: sprint_project,
42+
start_date: sprint_start_date,
43+
finish_date: sprint_finish_date,
44+
status: sprint_status)
45+
end
46+
end

0 commit comments

Comments
 (0)