@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
3abstract class DifferentialController extends PhabricatorController {
4
5 private $packageChangesetMap;
6 private $pathPackageMap;
7 private $authorityPackages;
8
9 public function buildSideNavView($for_app = false) {
10 $viewer = $this->getRequest()->getUser();
11
12 $nav = new AphrontSideNavFilterView();
13 $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
14
15 id(new DifferentialRevisionSearchEngine())
16 ->setViewer($viewer)
17 ->addNavigationItems($nav->getMenu());
18
19 $nav->selectFilter(null);
20
21 return $nav;
22 }
23
24 public function buildApplicationMenu() {
25 return $this->buildSideNavView(true)->getMenu();
26 }
27
28 /**
29 * @param array<DifferentialChangeset> $changesets
30 */
31 protected function buildPackageMaps(array $changesets) {
32 assert_instances_of($changesets, DifferentialChangeset::class);
33
34 $this->packageChangesetMap = array();
35 $this->pathPackageMap = array();
36 $this->authorityPackages = array();
37
38 if (!$changesets) {
39 return;
40 }
41
42 $viewer = $this->getViewer();
43
44 $have_owners = PhabricatorApplication::isClassInstalledForViewer(
45 PhabricatorOwnersApplication::class,
46 $viewer);
47 if (!$have_owners) {
48 return;
49 }
50
51 $changeset = head($changesets);
52 $diff = $changeset->getDiff();
53 $repository_phid = $diff->getRepositoryPHID();
54 if (!$repository_phid) {
55 return;
56 }
57
58 if ($viewer->getPHID()) {
59 $packages = id(new PhabricatorOwnersPackageQuery())
60 ->setViewer($viewer)
61 ->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
62 ->withAuthorityPHIDs(array($viewer->getPHID()))
63 ->execute();
64 $this->authorityPackages = $packages;
65 }
66
67 $paths = mpull($changesets, 'getOwnersFilename');
68
69 $control_query = id(new PhabricatorOwnersPackageQuery())
70 ->setViewer($viewer)
71 ->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
72 ->withControl($repository_phid, $paths);
73 $control_query->execute();
74
75 foreach ($changesets as $changeset) {
76 $changeset_path = $changeset->getOwnersFilename();
77
78 $packages = $control_query->getControllingPackagesForPath(
79 $repository_phid,
80 $changeset_path);
81
82 // If this particular changeset is generated code and the package does
83 // not match generated code, remove it from the list.
84 if ($changeset->isGeneratedChangeset()) {
85 foreach ($packages as $key => $package) {
86 if ($package->getMustMatchUngeneratedPaths()) {
87 unset($packages[$key]);
88 }
89 }
90 }
91
92 $this->pathPackageMap[$changeset_path] = $packages;
93 foreach ($packages as $package) {
94 $this->packageChangesetMap[$package->getPHID()][] = $changeset;
95 }
96 }
97 }
98
99 protected function getAuthorityPackages() {
100 if ($this->authorityPackages === null) {
101 throw new PhutilInvalidStateException('buildPackageMaps');
102 }
103 return $this->authorityPackages;
104 }
105
106 protected function getChangesetPackages(DifferentialChangeset $changeset) {
107 if ($this->pathPackageMap === null) {
108 throw new PhutilInvalidStateException('buildPackageMaps');
109 }
110
111 $path = $changeset->getOwnersFilename();
112 return idx($this->pathPackageMap, $path, array());
113 }
114
115 protected function getPackageChangesets($package_phid) {
116 if ($this->packageChangesetMap === null) {
117 throw new PhutilInvalidStateException('buildPackageMaps');
118 }
119
120 return idx($this->packageChangesetMap, $package_phid, array());
121 }
122
123 protected function buildTableOfContents(
124 array $changesets,
125 array $visible_changesets,
126 array $coverage) {
127 $viewer = $this->getViewer();
128
129 $toc_view = id(new PHUIDiffTableOfContentsListView())
130 ->setViewer($viewer)
131 ->setBare(true)
132 ->setAuthorityPackages($this->getAuthorityPackages());
133
134 foreach ($changesets as $changeset_id => $changeset) {
135 $is_visible = isset($visible_changesets[$changeset_id]);
136 $anchor = $changeset->getAnchorName();
137
138 $filename = $changeset->getFilename();
139 $coverage_id = 'differential-mcoverage-'.md5($filename);
140
141 $item = id(new PHUIDiffTableOfContentsItemView())
142 ->setChangeset($changeset)
143 ->setIsVisible($is_visible)
144 ->setAnchor($anchor)
145 ->setCoverage(idx($coverage, $filename))
146 ->setCoverageID($coverage_id);
147
148 $packages = $this->getChangesetPackages($changeset);
149 $item->setPackages($packages);
150
151 $toc_view->addItem($item);
152 }
153
154 return $toc_view;
155 }
156
157 protected function loadDiffProperties(array $diffs) {
158 $diffs = mpull($diffs, null, 'getID');
159
160 $properties = id(new DifferentialDiffProperty())->loadAllWhere(
161 'diffID IN (%Ld)',
162 array_keys($diffs));
163 $properties = mgroup($properties, 'getDiffID');
164
165 foreach ($diffs as $id => $diff) {
166 $values = idx($properties, $id, array());
167 $values = mpull($values, 'getData', 'getName');
168 $diff->attachDiffProperties($values);
169 }
170 }
171
172
173 protected function loadHarbormasterData(array $diffs) {
174 $viewer = $this->getViewer();
175
176 $diffs = mpull($diffs, null, 'getPHID');
177
178 $buildables = id(new HarbormasterBuildableQuery())
179 ->setViewer($viewer)
180 ->withBuildablePHIDs(array_keys($diffs))
181 ->withManualBuildables(false)
182 ->needBuilds(true)
183 ->needTargets(true)
184 ->execute();
185
186 $buildables = mpull($buildables, null, 'getBuildablePHID');
187 foreach ($diffs as $phid => $diff) {
188 $diff->attachBuildable(idx($buildables, $phid));
189 }
190
191 $target_map = array();
192 foreach ($diffs as $phid => $diff) {
193 $target_map[$phid] = $diff->getBuildTargetPHIDs();
194 }
195 $all_target_phids = array_mergev($target_map);
196
197 if ($all_target_phids) {
198 $unit_messages = id(new HarbormasterBuildUnitMessageQuery())
199 ->setViewer($viewer)
200 ->withBuildTargetPHIDs($all_target_phids)
201 ->execute();
202 $unit_messages = mgroup($unit_messages, 'getBuildTargetPHID');
203 } else {
204 $unit_messages = array();
205 }
206
207 foreach ($diffs as $phid => $diff) {
208 $target_phids = idx($target_map, $phid, array());
209 $messages = array_select_keys($unit_messages, $target_phids);
210 $messages = array_mergev($messages);
211 $diff->attachUnitMessages($messages);
212 }
213
214 // For diffs with no messages, look for legacy unit messages stored on the
215 // diff itself.
216 foreach ($diffs as $phid => $diff) {
217 if ($diff->getUnitMessages()) {
218 continue;
219 }
220
221 if (!$diff->hasDiffProperty('arc:unit')) {
222 continue;
223 }
224
225 $legacy_messages = $diff->getProperty('arc:unit');
226 if (!$legacy_messages) {
227 continue;
228 }
229
230 // Show the top 100 legacy lint messages. Previously, we showed some
231 // by default and let the user toggle the rest. With modern messages,
232 // we can send the user to the Harbormaster detail page. Just show
233 // "a lot" of messages in legacy cases to try to strike a balance
234 // between implementation simplicity and compatibility.
235 $legacy_messages = array_slice($legacy_messages, 0, 100);
236
237 $messages = array();
238 foreach ($legacy_messages as $message) {
239 $messages[] = HarbormasterBuildUnitMessage::newFromDictionary(
240 new HarbormasterBuildTarget(),
241 $this->getModernUnitMessageDictionary($message));
242 }
243
244 $diff->attachUnitMessages($messages);
245 }
246 }
247
248 private function getModernUnitMessageDictionary(array $map) {
249 // Strip out `null` values to satisfy stricter typechecks.
250 foreach ($map as $key => $value) {
251 if ($value === null) {
252 unset($map[$key]);
253 }
254 }
255
256 // Cast duration to a float since it used to be a string in some
257 // cases.
258 if (isset($map['duration'])) {
259 $map['duration'] = (float)$map['duration'];
260 }
261
262 return $map;
263 }
264
265 protected function getDiffTabLabels(array $diffs) {
266 // Make sure we're only going to render unique diffs.
267 $diffs = mpull($diffs, null, 'getID');
268 $labels = array(pht('Left'), pht('Right'));
269
270 $results = array();
271
272 foreach ($diffs as $diff) {
273 if (count($diffs) == 2) {
274 $label = array_shift($labels);
275 $label = pht('%s (Diff %d)', $label, $diff->getID());
276 } else {
277 $label = pht('Diff %d', $diff->getID());
278 }
279
280 $results[] = array(
281 $label,
282 $diff,
283 );
284 }
285
286 return $results;
287 }
288
289
290}