@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorProjectSubprojectsController
4 extends PhabricatorProjectController {
5
6 public function shouldAllowPublic() {
7 return true;
8 }
9
10 public function handleRequest(AphrontRequest $request) {
11 $viewer = $request->getViewer();
12
13 $response = $this->loadProject();
14 if ($response) {
15 return $response;
16 }
17
18 $project = $this->getProject();
19 $id = $project->getID();
20
21 $can_edit = PhabricatorPolicyFilter::hasCapability(
22 $viewer,
23 $project,
24 PhabricatorPolicyCapability::CAN_EDIT);
25
26 $allows_subprojects = $project->supportsSubprojects();
27 $allows_milestones = $project->supportsMilestones();
28
29 $subproject_list = null;
30 $milestone_list = null;
31
32 if ($allows_subprojects) {
33 $subprojects = id(new PhabricatorProjectQuery())
34 ->setViewer($viewer)
35 ->withParentProjectPHIDs(array($project->getPHID()))
36 ->needImages(true)
37 ->withIsMilestone(false)
38 ->execute();
39
40 $subproject_list = id(new PHUIObjectBoxView())
41 ->setHeaderText(pht('%s Subprojects', $project->getName()))
42 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
43 ->setObjectList(
44 id(new PhabricatorProjectListView())
45 ->setViewer($viewer)
46 ->setProjects($subprojects)
47 ->setNoDataString(pht('This project has no subprojects.'))
48 ->renderList());
49 } else {
50 $subprojects = array();
51 }
52
53 if ($allows_milestones) {
54 $milestones = id(new PhabricatorProjectQuery())
55 ->setViewer($viewer)
56 ->withParentProjectPHIDs(array($project->getPHID()))
57 ->needImages(true)
58 ->withIsMilestone(true)
59 ->setOrderVector(array('milestoneNumber', 'id'))
60 ->execute();
61
62 $milestone_list = id(new PHUIObjectBoxView())
63 ->setHeaderText(pht('%s Milestones', $project->getName()))
64 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
65 ->setObjectList(
66 id(new PhabricatorProjectListView())
67 ->setViewer($viewer)
68 ->setProjects($milestones)
69 ->setNoDataString(pht('This project has no milestones.'))
70 ->renderList());
71 } else {
72 $milestones = array();
73 }
74
75 $curtain = $this->buildCurtainView(
76 $project,
77 $milestones,
78 $subprojects);
79
80 $nav = $this->newNavigation(
81 $project,
82 PhabricatorProject::ITEM_SUBPROJECTS);
83
84 $crumbs = $this->buildApplicationCrumbs();
85 $crumbs->addTextCrumb(pht('Subprojects'));
86 $crumbs->setBorder(true);
87
88 $header = id(new PHUIHeaderView())
89 ->setHeader(pht('Subprojects and Milestones'))
90 ->setHeaderIcon('fa-sitemap');
91
92 require_celerity_resource('project-view-css');
93
94 // This page isn't reachable via UI, but make it pretty anyways.
95 $info_view = null;
96 if (!$milestone_list && !$subproject_list) {
97 $info_view = id(new PHUIInfoView())
98 ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
99 ->appendChild(pht('Milestone projects do not support subprojects '.
100 'or milestones.'));
101 }
102
103 $view = id(new PHUITwoColumnView())
104 ->setHeader($header)
105 ->setCurtain($curtain)
106 ->addClass('project-view-home')
107 ->addClass('project-view-people-home')
108 ->setMainColumn(array(
109 $info_view,
110 $subproject_list,
111 $milestone_list,
112 ));
113
114 return $this->newPage()
115 ->setNavigation($nav)
116 ->setCrumbs($crumbs)
117 ->setTitle(array($project->getName(), pht('Subprojects')))
118 ->appendChild($view);
119 }
120
121 private function buildCurtainView(
122 PhabricatorProject $project,
123 array $milestones,
124 array $subprojects) {
125 $viewer = $this->getViewer();
126 $id = $project->getID();
127
128 $can_create = $this->hasApplicationCapability(
129 ProjectCreateProjectsCapability::CAPABILITY);
130
131 $can_edit = PhabricatorPolicyFilter::hasCapability(
132 $viewer,
133 $project,
134 PhabricatorPolicyCapability::CAN_EDIT);
135
136 $allows_subprojects = $project->supportsSubprojects();
137 $allows_milestones = $project->supportsMilestones();
138
139 $curtain = $this->newCurtainView();
140
141 $can_subproject = ($can_create && $can_edit && $allows_subprojects);
142
143 // If we're offering to create the first subproject, we're going to warn
144 // the user about the effects before moving forward.
145 if ($can_subproject && !$subprojects) {
146 $subproject_href = "/project/warning/{$id}/";
147 $subproject_disabled = false;
148 $subproject_workflow = true;
149 } else {
150 $subproject_href = "/project/edit/?parent={$id}";
151 $subproject_disabled = !$can_subproject;
152 $subproject_workflow = !$can_subproject;
153 }
154
155 $curtain->addAction(
156 id(new PhabricatorActionView())
157 ->setName(pht('Create Subproject'))
158 ->setIcon('fa-plus')
159 ->setHref($subproject_href)
160 ->setDisabled($subproject_disabled)
161 ->setWorkflow($subproject_workflow));
162
163 if ($allows_milestones && $milestones) {
164 $milestone_text = pht('Create Next Milestone');
165 } else {
166 $milestone_text = pht('Create Milestone');
167 }
168
169 $can_milestone = ($can_create && $can_edit && $allows_milestones);
170 $milestone_href = "/project/edit/?milestone={$id}";
171
172 $curtain->addAction(
173 id(new PhabricatorActionView())
174 ->setName($milestone_text)
175 ->setIcon('fa-plus')
176 ->setHref($milestone_href)
177 ->setDisabled(!$can_milestone)
178 ->setWorkflow(!$can_milestone));
179
180 if (!$project->supportsSubprojects()) {
181 $note = pht(
182 'This project is a milestone, and milestones may not have '.
183 'subprojects.');
184 } else {
185 if (!$subprojects) {
186 $note = pht('Subprojects can be created for this project.');
187 } else {
188 $note = pht('This project has subprojects.');
189 }
190 }
191
192 $curtain->newPanel()
193 ->setHeaderText(pht('Subprojects'))
194 ->appendChild($note);
195
196 if (!$project->supportsSubprojects()) {
197 $note = pht(
198 'This project is already a milestone, and milestones may not '.
199 'have their own milestones.');
200 } else {
201 if (!$milestones) {
202 $note = pht('Milestones can be created for this project.');
203 } else {
204 $note = pht('This project has milestones.');
205 }
206 }
207
208 $curtain->newPanel()
209 ->setHeaderText(pht('Milestones'))
210 ->appendChild($note);
211
212 return $curtain;
213 }
214
215}