plan_my_stuff 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/CONFIGURATION.md +53 -1
- data/README.md +2 -30
- data/app/controllers/plan_my_stuff/comments_controller.rb +21 -10
- data/app/controllers/plan_my_stuff/issues/approvals_controller.rb +13 -4
- data/app/controllers/plan_my_stuff/issues/closures_controller.rb +12 -6
- data/app/controllers/plan_my_stuff/issues/links_controller.rb +14 -8
- data/app/controllers/plan_my_stuff/issues/takes_controller.rb +19 -17
- data/app/controllers/plan_my_stuff/issues/viewers_controller.rb +15 -9
- data/app/controllers/plan_my_stuff/issues/waitings_controller.rb +14 -8
- data/app/controllers/plan_my_stuff/issues_controller.rb +19 -5
- data/app/controllers/plan_my_stuff/labels_controller.rb +14 -8
- data/app/controllers/plan_my_stuff/project_items/assignments_controller.rb +6 -0
- data/app/controllers/plan_my_stuff/project_items/statuses_controller.rb +3 -0
- data/app/controllers/plan_my_stuff/project_items_controller.rb +11 -4
- data/app/controllers/plan_my_stuff/projects_controller.rb +14 -0
- data/app/controllers/plan_my_stuff/testing_project_items/results_controller.rb +3 -0
- data/app/controllers/plan_my_stuff/testing_project_items_controller.rb +5 -0
- data/app/controllers/plan_my_stuff/testing_projects_controller.rb +12 -0
- data/app/views/plan_my_stuff/issues/index.html.erb +1 -1
- data/app/views/plan_my_stuff/issues/partials/_approvals.html.erb +5 -5
- data/app/views/plan_my_stuff/issues/partials/_form.html.erb +1 -1
- data/app/views/plan_my_stuff/issues/partials/_links.html.erb +3 -3
- data/app/views/plan_my_stuff/issues/partials/_viewers.html.erb +2 -2
- data/app/views/plan_my_stuff/issues/show.html.erb +8 -8
- data/app/views/plan_my_stuff/projects/show.html.erb +3 -1
- data/app/views/plan_my_stuff/testing_projects/partials/_item.html.erb +1 -1
- data/lib/generators/plan_my_stuff/install/templates/initializer.rb +6 -0
- data/lib/plan_my_stuff/configuration.rb +61 -5
- data/lib/plan_my_stuff/issue.rb +91 -15
- data/lib/plan_my_stuff/repo.rb +28 -0
- data/lib/plan_my_stuff/version.rb +1 -1
- metadata +2 -2
|
@@ -7,40 +7,46 @@ module PlanMyStuff
|
|
|
7
7
|
labels = parse_labels(params[:label_name])
|
|
8
8
|
if labels.blank?
|
|
9
9
|
flash[:error] = 'Label name is required.'
|
|
10
|
-
redirect_to(plan_my_stuff.issue_path(params[:issue_id]
|
|
10
|
+
redirect_to(plan_my_stuff.issue_path(params[:issue_id]))
|
|
11
11
|
return
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
issue = PlanMyStuff::Issue.find(params[:issue_id]
|
|
14
|
+
issue = PlanMyStuff::Issue.find(params[:issue_id])
|
|
15
15
|
|
|
16
16
|
missing = labels.reject { |label| PlanMyStuff::Label.exists?(repo: issue.repo, name: label) }
|
|
17
17
|
if missing.any?
|
|
18
18
|
flash[:error] = "Label#{'s' if missing.size > 1} not found in repo: #{missing.join(', ')}"
|
|
19
|
-
redirect_to(plan_my_stuff.issue_path(issue
|
|
19
|
+
redirect_to(plan_my_stuff.issue_path(issue))
|
|
20
20
|
return
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
PlanMyStuff::Label.add!(issue: issue, labels: labels)
|
|
24
24
|
|
|
25
|
+
yield(issue) if block_given?
|
|
26
|
+
return if performed?
|
|
27
|
+
|
|
25
28
|
flash[:success] = 'Label was successfully added.'
|
|
26
|
-
redirect_to(plan_my_stuff.issue_path(issue
|
|
29
|
+
redirect_to(plan_my_stuff.issue_path(issue))
|
|
27
30
|
rescue PlanMyStuff::Error, Octokit::Error => e
|
|
28
31
|
pms_handle_rescue(e)
|
|
29
32
|
flash[:error] = e.message
|
|
30
|
-
redirect_to(plan_my_stuff.issue_path(params[:issue_id]
|
|
33
|
+
redirect_to(plan_my_stuff.issue_path(params[:issue_id]))
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
# DELETE /issues/:issue_id/labels/:id
|
|
34
37
|
def destroy
|
|
35
|
-
issue = PlanMyStuff::Issue.find(params[:issue_id]
|
|
38
|
+
issue = PlanMyStuff::Issue.find(params[:issue_id])
|
|
36
39
|
PlanMyStuff::Label.remove!(issue: issue, labels: [params[:id]])
|
|
37
40
|
|
|
41
|
+
yield(issue) if block_given?
|
|
42
|
+
return if performed?
|
|
43
|
+
|
|
38
44
|
flash[:success] = 'Label was successfully removed.'
|
|
39
|
-
redirect_to(plan_my_stuff.issue_path(issue
|
|
45
|
+
redirect_to(plan_my_stuff.issue_path(issue))
|
|
40
46
|
rescue PlanMyStuff::Error, Octokit::Error => e
|
|
41
47
|
pms_handle_rescue(e)
|
|
42
48
|
flash[:error] = e.message
|
|
43
|
-
redirect_to(plan_my_stuff.issue_path(params[:issue_id]
|
|
49
|
+
redirect_to(plan_my_stuff.issue_path(params[:issue_id]))
|
|
44
50
|
end
|
|
45
51
|
end
|
|
46
52
|
end
|
|
@@ -16,6 +16,9 @@ module PlanMyStuff
|
|
|
16
16
|
|
|
17
17
|
item.assign!(assignees)
|
|
18
18
|
|
|
19
|
+
yield(item) if block_given?
|
|
20
|
+
return if performed?
|
|
21
|
+
|
|
19
22
|
flash[:success] =
|
|
20
23
|
if assignees.present?
|
|
21
24
|
"Item assigned to #{assignees.join(', ')}."
|
|
@@ -43,6 +46,9 @@ module PlanMyStuff
|
|
|
43
46
|
|
|
44
47
|
item.assign!(remaining)
|
|
45
48
|
|
|
49
|
+
yield(item) if block_given?
|
|
50
|
+
return if performed?
|
|
51
|
+
|
|
46
52
|
flash[:success] = "#{params[:username]} unassigned."
|
|
47
53
|
redirect_to(plan_my_stuff.project_path(params[:project_id]))
|
|
48
54
|
rescue ArgumentError, PlanMyStuff::Error => e
|
|
@@ -14,6 +14,9 @@ module PlanMyStuff
|
|
|
14
14
|
|
|
15
15
|
item.move_to!(params[:status])
|
|
16
16
|
|
|
17
|
+
yield(item) if block_given?
|
|
18
|
+
return if performed?
|
|
19
|
+
|
|
17
20
|
flash[:success] = "Item moved to #{params[:status]}."
|
|
18
21
|
redirect_to(plan_my_stuff.project_path(params[:project_id]))
|
|
19
22
|
rescue ArgumentError, PlanMyStuff::Error => e
|
|
@@ -7,19 +7,23 @@ module PlanMyStuff
|
|
|
7
7
|
project_number = params[:project_id].to_i
|
|
8
8
|
|
|
9
9
|
if params[:draft] == '1'
|
|
10
|
-
PlanMyStuff::ProjectItem.create!(
|
|
10
|
+
item = PlanMyStuff::ProjectItem.create!(
|
|
11
11
|
params[:title],
|
|
12
12
|
draft: true,
|
|
13
13
|
body: params[:body],
|
|
14
14
|
project_number: project_number,
|
|
15
15
|
)
|
|
16
|
-
|
|
16
|
+
flash_message = 'Draft item created.'
|
|
17
17
|
else
|
|
18
18
|
issue = PlanMyStuff::Issue.find(params[:issue_number].to_i)
|
|
19
|
-
PlanMyStuff::ProjectItem.create!(issue, project_number: project_number)
|
|
20
|
-
|
|
19
|
+
item = PlanMyStuff::ProjectItem.create!(issue, project_number: project_number)
|
|
20
|
+
flash_message = "Issue ##{issue.number} added to project."
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
yield(item) if block_given?
|
|
24
|
+
return if performed?
|
|
25
|
+
|
|
26
|
+
flash[:success] = flash_message
|
|
23
27
|
redirect_to(plan_my_stuff.project_path(project_number))
|
|
24
28
|
rescue ArgumentError, PlanMyStuff::Error, Octokit::Error => e
|
|
25
29
|
pms_handle_rescue(e)
|
|
@@ -38,6 +42,9 @@ module PlanMyStuff
|
|
|
38
42
|
|
|
39
43
|
item.destroy!(user: pms_current_user)
|
|
40
44
|
|
|
45
|
+
yield(item) if block_given?
|
|
46
|
+
return if performed?
|
|
47
|
+
|
|
41
48
|
flash[:success] = 'Item removed from project.'
|
|
42
49
|
redirect_to(plan_my_stuff.project_path(project_number))
|
|
43
50
|
rescue ArgumentError, PlanMyStuff::Error, Octokit::Error => e
|
|
@@ -12,11 +12,15 @@ module PlanMyStuff
|
|
|
12
12
|
when 'regular' then all_projects.reject { |p| p.is_a?(PlanMyStuff::TestingProject) }
|
|
13
13
|
else all_projects
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
yield(@projects) if block_given?
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
# GET /projects/new
|
|
18
20
|
def new
|
|
19
21
|
@project = PlanMyStuff::Project.new
|
|
22
|
+
|
|
23
|
+
yield(@project) if block_given?
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
# POST /projects
|
|
@@ -28,6 +32,9 @@ module PlanMyStuff
|
|
|
28
32
|
user: pms_current_user,
|
|
29
33
|
)
|
|
30
34
|
|
|
35
|
+
yield(@project) if block_given?
|
|
36
|
+
return if performed?
|
|
37
|
+
|
|
31
38
|
flash[:success] = 'Project was successfully created.'
|
|
32
39
|
redirect_to(plan_my_stuff.project_path(@project.number))
|
|
33
40
|
rescue PlanMyStuff::ValidationError => e
|
|
@@ -46,11 +53,15 @@ module PlanMyStuff
|
|
|
46
53
|
@project = PlanMyStuff::Project.find(params[:id].to_i)
|
|
47
54
|
@statuses = @project.statuses.pluck(:name)
|
|
48
55
|
@items_by_status = @project.items.group_by(&:status)
|
|
56
|
+
|
|
57
|
+
yield(@project) if block_given?
|
|
49
58
|
end
|
|
50
59
|
|
|
51
60
|
# GET /projects/:id/edit
|
|
52
61
|
def edit
|
|
53
62
|
@project = PlanMyStuff::Project.find(params[:id].to_i)
|
|
63
|
+
|
|
64
|
+
yield(@project) if block_given?
|
|
54
65
|
end
|
|
55
66
|
|
|
56
67
|
# PATCH/PUT /projects/:id
|
|
@@ -63,6 +74,9 @@ module PlanMyStuff
|
|
|
63
74
|
description: project_params[:description],
|
|
64
75
|
)
|
|
65
76
|
|
|
77
|
+
yield(@project) if block_given?
|
|
78
|
+
return if performed?
|
|
79
|
+
|
|
66
80
|
flash[:success] = 'Project was successfully updated.'
|
|
67
81
|
redirect_to(plan_my_stuff.project_path(@project.number))
|
|
68
82
|
rescue PlanMyStuff::StaleObjectError => e
|
|
@@ -30,6 +30,9 @@ module PlanMyStuff
|
|
|
30
30
|
raise(ArgumentError, "Invalid result: #{params[:result].inspect}")
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
yield(item) if block_given?
|
|
34
|
+
return if performed?
|
|
35
|
+
|
|
33
36
|
redirect_to(plan_my_stuff.testing_project_path(project_number))
|
|
34
37
|
rescue PlanMyStuff::ValidationError => e
|
|
35
38
|
pms_handle_rescue(e)
|
|
@@ -5,6 +5,8 @@ module PlanMyStuff
|
|
|
5
5
|
# GET /testing_projects/:testing_project_id/items/new
|
|
6
6
|
def new
|
|
7
7
|
@project = PlanMyStuff::TestingProject.find(params[:testing_project_id].to_i)
|
|
8
|
+
|
|
9
|
+
yield(@project) if block_given?
|
|
8
10
|
rescue PlanMyStuff::Error, Octokit::Error => e
|
|
9
11
|
pms_handle_rescue(e)
|
|
10
12
|
flash[:error] = e.message
|
|
@@ -26,6 +28,9 @@ module PlanMyStuff
|
|
|
26
28
|
item.update_due_date!(Date.parse(item_params[:due_date])) if item_params[:due_date].present?
|
|
27
29
|
item.update_pass_mode!(item_params[:pass_mode]) if item_params[:pass_mode].present?
|
|
28
30
|
|
|
31
|
+
yield(item) if block_given?
|
|
32
|
+
return if performed?
|
|
33
|
+
|
|
29
34
|
flash[:success] = 'Item added.'
|
|
30
35
|
redirect_to(plan_my_stuff.testing_project_path(project_number))
|
|
31
36
|
rescue ArgumentError, PlanMyStuff::Error, Octokit::Error => e
|
|
@@ -6,6 +6,8 @@ module PlanMyStuff
|
|
|
6
6
|
def new
|
|
7
7
|
@project = PlanMyStuff::TestingProject.new
|
|
8
8
|
@project.metadata.subject_urls = [params[:subject_url]] if params[:subject_url].present?
|
|
9
|
+
|
|
10
|
+
yield(@project) if block_given?
|
|
9
11
|
end
|
|
10
12
|
|
|
11
13
|
# POST /testing_projects
|
|
@@ -19,6 +21,9 @@ module PlanMyStuff
|
|
|
19
21
|
user: pms_current_user,
|
|
20
22
|
)
|
|
21
23
|
|
|
24
|
+
yield(@project) if block_given?
|
|
25
|
+
return if performed?
|
|
26
|
+
|
|
22
27
|
flash[:success] = 'Testing project was successfully created.'
|
|
23
28
|
redirect_to(plan_my_stuff.testing_project_path(@project.number))
|
|
24
29
|
rescue PlanMyStuff::ValidationError => e
|
|
@@ -39,11 +44,15 @@ module PlanMyStuff
|
|
|
39
44
|
@project = PlanMyStuff::TestingProject.find(params[:id].to_i)
|
|
40
45
|
@statuses = @project.statuses.pluck(:name)
|
|
41
46
|
@items_by_status = @project.items.group_by(&:status)
|
|
47
|
+
|
|
48
|
+
yield(@project) if block_given?
|
|
42
49
|
end
|
|
43
50
|
|
|
44
51
|
# GET /testing_projects/:id/edit
|
|
45
52
|
def edit
|
|
46
53
|
@project = PlanMyStuff::TestingProject.find(params[:id].to_i)
|
|
54
|
+
|
|
55
|
+
yield(@project) if block_given?
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
# PATCH/PUT /testing_projects/:id
|
|
@@ -60,6 +69,9 @@ module PlanMyStuff
|
|
|
60
69
|
},
|
|
61
70
|
)
|
|
62
71
|
|
|
72
|
+
yield(@project) if block_given?
|
|
73
|
+
return if performed?
|
|
74
|
+
|
|
63
75
|
flash[:success] = 'Testing project was successfully updated.'
|
|
64
76
|
redirect_to(plan_my_stuff.testing_project_path(@project.number))
|
|
65
77
|
rescue PlanMyStuff::StaleObjectError => e
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<% @issues.each do |issue| %>
|
|
15
15
|
<tr>
|
|
16
16
|
<td><%= issue.number %></td>
|
|
17
|
-
<td><%= link_to(issue.title, plan_my_stuff.issue_path(issue
|
|
17
|
+
<td><%= link_to(issue.title, plan_my_stuff.issue_path(issue)) %></td>
|
|
18
18
|
<td><%= issue.state %></td>
|
|
19
19
|
<td><%= issue.labels.join(', ') %></td>
|
|
20
20
|
</tr>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
<%=
|
|
45
45
|
button_to(
|
|
46
46
|
'Approve',
|
|
47
|
-
plan_my_stuff.issue_approval_path(issue
|
|
47
|
+
plan_my_stuff.issue_approval_path(issue, approval.user_id),
|
|
48
48
|
method: :patch,
|
|
49
49
|
params: { approval: { status: 'approved' } },
|
|
50
50
|
form: { style: 'display:inline' },
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
<%=
|
|
54
54
|
button_to(
|
|
55
55
|
'Reject',
|
|
56
|
-
plan_my_stuff.issue_approval_path(issue
|
|
56
|
+
plan_my_stuff.issue_approval_path(issue, approval.user_id),
|
|
57
57
|
method: :patch,
|
|
58
58
|
params: { approval: { status: 'rejected' } },
|
|
59
59
|
form: { style: 'display:inline' },
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
<%=
|
|
66
66
|
button_to(
|
|
67
67
|
'Revoke',
|
|
68
|
-
plan_my_stuff.issue_approval_path(issue
|
|
68
|
+
plan_my_stuff.issue_approval_path(issue, approval.user_id),
|
|
69
69
|
method: :patch,
|
|
70
70
|
params: { approval: { status: 'pending' } },
|
|
71
71
|
form: { style: 'display:inline' },
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
<%=
|
|
78
78
|
button_to(
|
|
79
79
|
'Remove',
|
|
80
|
-
plan_my_stuff.issue_approval_path(issue
|
|
80
|
+
plan_my_stuff.issue_approval_path(issue, approval.user_id),
|
|
81
81
|
method: :delete,
|
|
82
82
|
form: { style: 'display:inline' },
|
|
83
83
|
)
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
<%=
|
|
95
95
|
form_with(
|
|
96
96
|
scope: :approval,
|
|
97
|
-
url: plan_my_stuff.issue_approvals_path(issue
|
|
97
|
+
url: plan_my_stuff.issue_approvals_path(issue),
|
|
98
98
|
method: :post,
|
|
99
99
|
local: true,
|
|
100
100
|
) do |form|
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
<%=
|
|
69
69
|
link_to(
|
|
70
70
|
"#{target.repo.full_name}##{target.number} - #{target.title}",
|
|
71
|
-
plan_my_stuff.issue_path(target
|
|
71
|
+
plan_my_stuff.issue_path(target),
|
|
72
72
|
)
|
|
73
73
|
%>
|
|
74
74
|
<% if section[:removable] %>
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
<%=
|
|
79
79
|
button_to(
|
|
80
80
|
'Remove',
|
|
81
|
-
plan_my_stuff.issue_link_path(issue
|
|
81
|
+
plan_my_stuff.issue_link_path(issue, link_id),
|
|
82
82
|
method: :delete,
|
|
83
83
|
form: { style: 'display:inline' },
|
|
84
84
|
)
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
<%=
|
|
96
96
|
form_with(
|
|
97
97
|
scope: :link,
|
|
98
|
-
url: plan_my_stuff.issue_links_path(issue
|
|
98
|
+
url: plan_my_stuff.issue_links_path(issue),
|
|
99
99
|
method: :post,
|
|
100
100
|
local: true,
|
|
101
101
|
) do |form|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<%=
|
|
11
11
|
button_to(
|
|
12
12
|
'Remove',
|
|
13
|
-
plan_my_stuff.issue_viewer_path(issue
|
|
13
|
+
plan_my_stuff.issue_viewer_path(issue, viewer_id),
|
|
14
14
|
method: :delete
|
|
15
15
|
)
|
|
16
16
|
%>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<p>No viewers added.</p>
|
|
22
22
|
<% end %>
|
|
23
23
|
|
|
24
|
-
<%= form_with(url: plan_my_stuff.issue_viewers_path(issue
|
|
24
|
+
<%= form_with(url: plan_my_stuff.issue_viewers_path(issue), method: :post) do |form| %>
|
|
25
25
|
<div>
|
|
26
26
|
<%= form.label(:viewer_ids, 'Add viewer IDs (comma-separated)') %>
|
|
27
27
|
<%= form.text_field(:viewer_ids) %>
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
<h1><%= @issue.title %> <small>#<%= @issue.number %></small></h1>
|
|
2
2
|
|
|
3
3
|
<p>
|
|
4
|
-
<%= link_to('Edit', plan_my_stuff.edit_issue_path(@issue
|
|
4
|
+
<%= link_to('Edit', plan_my_stuff.edit_issue_path(@issue)) %>
|
|
5
5
|
<% if @support_user && @issue.html_url.present? %>
|
|
6
6
|
<%= link_to('View on GitHub', @issue.html_url, target: '_blank', rel: 'noopener') %>
|
|
7
7
|
<% end %>
|
|
8
8
|
<% if @issue.state == 'open' %>
|
|
9
|
-
<%= button_to('Close Issue', plan_my_stuff.issue_closure_path(@issue
|
|
9
|
+
<%= button_to('Close Issue', plan_my_stuff.issue_closure_path(@issue), method: :post) %>
|
|
10
10
|
<% else %>
|
|
11
|
-
<%= button_to('Reopen Issue', plan_my_stuff.issue_closure_path(@issue
|
|
11
|
+
<%= button_to('Reopen Issue', plan_my_stuff.issue_closure_path(@issue), method: :delete) %>
|
|
12
12
|
<% end %>
|
|
13
13
|
<% if @support_user && @issue.state == 'open' %>
|
|
14
14
|
<% if @issue.metadata.waiting_on_user_at.present? %>
|
|
15
|
-
<%= button_to('Mark replied', plan_my_stuff.issue_waiting_path(@issue
|
|
15
|
+
<%= button_to('Mark replied', plan_my_stuff.issue_waiting_path(@issue), method: :delete) %>
|
|
16
16
|
<% else %>
|
|
17
|
-
<%= button_to('Mark waiting', plan_my_stuff.issue_waiting_path(@issue
|
|
17
|
+
<%= button_to('Mark waiting', plan_my_stuff.issue_waiting_path(@issue), method: :post) %>
|
|
18
18
|
<% end %>
|
|
19
19
|
<% end %>
|
|
20
20
|
<% if @support_user && @pipeline_enabled && @pipeline_item.nil? && @issue.assignees.blank? %>
|
|
21
|
-
<%= button_to('Take', plan_my_stuff.issue_take_path(@issue
|
|
21
|
+
<%= button_to('Take', plan_my_stuff.issue_take_path(@issue), method: :post) %>
|
|
22
22
|
<% end %>
|
|
23
23
|
<% if @support_user && @pipeline_enabled && @current_user_login.present? && @issue.assignees.include?(@current_user_login) %>
|
|
24
|
-
<%= button_to('Unassign', plan_my_stuff.issue_take_path(@issue
|
|
24
|
+
<%= button_to('Unassign', plan_my_stuff.issue_take_path(@issue), method: :delete) %>
|
|
25
25
|
<% end %>
|
|
26
26
|
<% if @support_user %>
|
|
27
27
|
<%= link_to('Start Testing Project', plan_my_stuff.new_testing_project_path(subject_url: @issue.html_url)) %>
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
<%= PlanMyStuff::Markdown.render(comment.body || '').html_safe %>
|
|
77
77
|
<% if comment.pms_comment? && (@support_user || comment.metadata.created_by == @current_user_id) %>
|
|
78
78
|
<p>
|
|
79
|
-
<%= link_to('Edit', plan_my_stuff.edit_issue_comment_path(@issue
|
|
79
|
+
<%= link_to('Edit', plan_my_stuff.edit_issue_comment_path(@issue, comment.id)) %>
|
|
80
80
|
</p>
|
|
81
81
|
<% end %>
|
|
82
82
|
<% if @support_user && comment.html_url.present? %>
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
<% items.each do |item| %>
|
|
25
25
|
<div style="border: 1px solid black; margin: 1em">
|
|
26
26
|
<% unless item.draft? %>
|
|
27
|
-
<strong
|
|
27
|
+
<strong>
|
|
28
|
+
<%= link_to(item.title, plan_my_stuff.issue_path(item.issue.to_param)) %>
|
|
29
|
+
</strong>
|
|
28
30
|
<% else %>
|
|
29
31
|
<strong><%= item.title %></strong>
|
|
30
32
|
<% end %>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<% if item.draft? %>
|
|
4
4
|
<strong><%= item.title %></strong>
|
|
5
5
|
<% else %>
|
|
6
|
-
<strong><%= link_to(item.title, plan_my_stuff.issue_path(item.
|
|
6
|
+
<strong><%= link_to(item.title, plan_my_stuff.issue_path(item.issue.to_param)) %></strong>
|
|
7
7
|
<small>#<%= item.number %></small>
|
|
8
8
|
<% end %>
|
|
9
9
|
|
|
@@ -24,6 +24,12 @@ PlanMyStuff.configure do |config|
|
|
|
24
24
|
# config.repos[:underwriter] = 'YourOrganization/Underwriter'
|
|
25
25
|
# config.default_repo = :element
|
|
26
26
|
|
|
27
|
+
# Human-readable prefix used as the `Issue#to_param` so URLs read
|
|
28
|
+
# `/issues/Element-1234` instead of `/issues/1234`. Missing keys fall back
|
|
29
|
+
# to `key.to_s.titleize`; only repos whose key doesn't titleize cleanly
|
|
30
|
+
# need an entry.
|
|
31
|
+
# config.repo_nicknames = { safety: 'Compliance' }
|
|
32
|
+
|
|
27
33
|
# --------------------------------------------------------------------------
|
|
28
34
|
# Projects
|
|
29
35
|
# --------------------------------------------------------------------------
|
|
@@ -348,6 +348,14 @@ module PlanMyStuff
|
|
|
348
348
|
#
|
|
349
349
|
attr_accessor :repos
|
|
350
350
|
|
|
351
|
+
# Human-readable repo names used as the +to_param+ prefix on +PlanMyStuff::Issue+ instances. Symbol-keyed
|
|
352
|
+
# against +repos+ -- missing keys fall back to +key.to_s.titleize+, so only entries that diverge from a simple
|
|
353
|
+
# +titleize+ of the key (e.g. +:safety+ -> +"Compliance"+) need to be listed.
|
|
354
|
+
#
|
|
355
|
+
# @return [Hash{Symbol => String}]
|
|
356
|
+
#
|
|
357
|
+
attr_accessor :repo_nicknames
|
|
358
|
+
|
|
351
359
|
# Bare repo name (under +config.organization+) that stores uploaded attachment binaries. Defaults to
|
|
352
360
|
# +'pms-attachments'+. The repo must exist; the uploader does not create it. Attachments commit onto
|
|
353
361
|
# +config.main_branch+ and live under +<repo_key_or_name>/issue-<number>/<uuid>.<ext>+.
|
|
@@ -368,6 +376,7 @@ module PlanMyStuff
|
|
|
368
376
|
# @return [Configuration]
|
|
369
377
|
def initialize
|
|
370
378
|
@repos = {}
|
|
379
|
+
@repo_nicknames = {}
|
|
371
380
|
@attachment_repo = 'pms-attachments'
|
|
372
381
|
@user_class = 'User'
|
|
373
382
|
@display_name_method = :to_s
|
|
@@ -435,12 +444,14 @@ module PlanMyStuff
|
|
|
435
444
|
missing << 'access_token' if access_token.nil? || access_token.to_s.strip.empty?
|
|
436
445
|
missing << 'organization' if organization.nil? || organization.to_s.strip.empty?
|
|
437
446
|
|
|
438
|
-
|
|
447
|
+
if missing.present?
|
|
448
|
+
raise(
|
|
449
|
+
PlanMyStuff::ConfigurationError,
|
|
450
|
+
"Missing required PlanMyStuff configuration: #{missing.join(', ')}",
|
|
451
|
+
)
|
|
452
|
+
end
|
|
439
453
|
|
|
440
|
-
|
|
441
|
-
PlanMyStuff::ConfigurationError,
|
|
442
|
-
"Missing required PlanMyStuff configuration: #{missing.join(', ')}",
|
|
443
|
-
)
|
|
454
|
+
validate_repo_nicknames!
|
|
444
455
|
end
|
|
445
456
|
|
|
446
457
|
# Returns the merged custom fields schema for the given context. Context-specific fields deep-merge on top of
|
|
@@ -477,6 +488,51 @@ module PlanMyStuff
|
|
|
477
488
|
path = controllers[key] || DEFAULT_CONTROLLERS.fetch(key)
|
|
478
489
|
path.start_with?('/') ? path : "/#{path}"
|
|
479
490
|
end
|
|
491
|
+
|
|
492
|
+
# Human-readable nickname for a repo key, used as the +to_param+ prefix on +PlanMyStuff::Issue+ instances. Falls
|
|
493
|
+
# back to +key.to_s.titleize+ when no explicit entry exists in +repo_nicknames+.
|
|
494
|
+
#
|
|
495
|
+
# @param key [Symbol, String]
|
|
496
|
+
#
|
|
497
|
+
# @return [String]
|
|
498
|
+
#
|
|
499
|
+
def repo_nickname_for(key)
|
|
500
|
+
repo_nicknames[key&.to_sym] || key.to_s.titleize
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
private
|
|
504
|
+
|
|
505
|
+
# Resolved nicknames feed directly into +Issue#to_param+ and route +:id+ tokens, so collisions or chars outside
|
|
506
|
+
# +[A-Za-z0-9_]+ (notably +-+, the nickname/number separator) break URL round-tripping.
|
|
507
|
+
#
|
|
508
|
+
# @raise [ConfigurationError] when any resolved nickname collides with another or contains non-token chars
|
|
509
|
+
#
|
|
510
|
+
# @return [void]
|
|
511
|
+
#
|
|
512
|
+
def validate_repo_nicknames!
|
|
513
|
+
resolved = repos.keys.index_with { |key| repo_nickname_for(key).to_s }
|
|
514
|
+
|
|
515
|
+
invalid = resolved.reject { |_key, nickname| nickname.match?(/\A[A-Za-z0-9_]+\z/) }
|
|
516
|
+
if invalid.present?
|
|
517
|
+
pairs = invalid.map { |key, nickname| "#{key.inspect} => #{nickname.inspect}" }
|
|
518
|
+
raise(
|
|
519
|
+
PlanMyStuff::ConfigurationError,
|
|
520
|
+
"Invalid repo nickname(s) (must match /\\A[A-Za-z0-9_]+\\z/): #{pairs.join(', ')}",
|
|
521
|
+
)
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
dupes = resolved.group_by { |_key, nickname| nickname }.select { |_n, entries| entries.size > 1 }
|
|
525
|
+
return if dupes.blank?
|
|
526
|
+
|
|
527
|
+
details = dupes.map do |nickname, entries|
|
|
528
|
+
keys = entries.map { |entry| entry.first.inspect }.join(', ')
|
|
529
|
+
"#{nickname.inspect} (#{keys})"
|
|
530
|
+
end
|
|
531
|
+
raise(
|
|
532
|
+
PlanMyStuff::ConfigurationError,
|
|
533
|
+
"Duplicate repo nickname(s): #{details.join('; ')}",
|
|
534
|
+
)
|
|
535
|
+
end
|
|
480
536
|
end
|
|
481
537
|
|
|
482
538
|
class ConfigurationError < StandardError
|