@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
fork

Configure Feed

Select the types of activity you want to include in your feed.

Build that thing someone posted a screenshot of on Facebook

Summary: Seemed kinda cool.

Test Plan: {F1707244}

Reviewers: chad

Reviewed By: chad

Differential Revision: https://secure.phabricator.com/D16210

+450 -225
+4
src/__phutil_library_map__.php
··· 556 556 'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php', 557 557 'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php', 558 558 'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php', 559 + 'DifferentialStackGraph' => 'applications/differential/edge/DifferentialStackGraph.php', 559 560 'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php', 560 561 'DifferentialSubscribersField' => 'applications/differential/customfield/DifferentialSubscribersField.php', 561 562 'DifferentialSummaryField' => 'applications/differential/customfield/DifferentialSummaryField.php', ··· 1599 1600 'PHUICurtainExtension' => 'view/extension/PHUICurtainExtension.php', 1600 1601 'PHUICurtainPanelView' => 'view/layout/PHUICurtainPanelView.php', 1601 1602 'PHUICurtainView' => 'view/layout/PHUICurtainView.php', 1603 + 'PHUIDiffGraphView' => 'infrastructure/diff/view/PHUIDiffGraphView.php', 1602 1604 'PHUIDiffInlineCommentDetailView' => 'infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php', 1603 1605 'PHUIDiffInlineCommentEditView' => 'infrastructure/diff/view/PHUIDiffInlineCommentEditView.php', 1604 1606 'PHUIDiffInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php', ··· 4928 4930 'DifferentialRevisionViewController' => 'DifferentialController', 4929 4931 'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec', 4930 4932 'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod', 4933 + 'DifferentialStackGraph' => 'AbstractDirectedGraph', 4931 4934 'DifferentialStoredCustomField' => 'DifferentialCustomField', 4932 4935 'DifferentialSubscribersField' => 'DifferentialCoreCustomField', 4933 4936 'DifferentialSummaryField' => 'DifferentialCoreCustomField', ··· 6132 6135 'PHUICurtainExtension' => 'Phobject', 6133 6136 'PHUICurtainPanelView' => 'AphrontTagView', 6134 6137 'PHUICurtainView' => 'AphrontTagView', 6138 + 'PHUIDiffGraphView' => 'Phobject', 6135 6139 'PHUIDiffInlineCommentDetailView' => 'PHUIDiffInlineCommentView', 6136 6140 'PHUIDiffInlineCommentEditView' => 'PHUIDiffInlineCommentView', 6137 6141 'PHUIDiffInlineCommentRowScaffold' => 'AphrontView',
+160
src/applications/differential/controller/DifferentialRevisionViewController.php
··· 341 341 ->setKey('commits') 342 342 ->appendChild($local_table)); 343 343 344 + $stack_graph = id(new DifferentialStackGraph()) 345 + ->setSeedRevision($revision) 346 + ->loadGraph(); 347 + if (!$stack_graph->isEmpty()) { 348 + $stack_view = $this->renderStackView($revision, $stack_graph); 349 + list($stack_name, $stack_color, $stack_table) = $stack_view; 350 + 351 + $tab_group->addTab( 352 + id(new PHUITabView()) 353 + ->setName($stack_name) 354 + ->setKey('stack') 355 + ->setColor($stack_color) 356 + ->appendChild($stack_table)); 357 + } 358 + 344 359 if ($other_view) { 345 360 $tab_group->addTab( 346 361 id(new PHUITabView()) ··· 1197 1212 ->setShowViewAll(true); 1198 1213 } 1199 1214 1215 + 1216 + private function renderStackView( 1217 + DifferentialRevision $current, 1218 + DifferentialStackGraph $graph) { 1219 + 1220 + $ancestry = $graph->getParentEdges(); 1221 + $viewer = $this->getViewer(); 1222 + 1223 + $revisions = id(new DifferentialRevisionQuery()) 1224 + ->setViewer($viewer) 1225 + ->withPHIDs(array_keys($ancestry)) 1226 + ->execute(); 1227 + $revisions = mpull($revisions, null, 'getPHID'); 1228 + 1229 + $order = id(new PhutilDirectedScalarGraph()) 1230 + ->addNodes($ancestry) 1231 + ->getTopographicallySortedNodes(); 1232 + 1233 + $ancestry = array_select_keys($ancestry, $order); 1234 + 1235 + $traces = id(new PHUIDiffGraphView()) 1236 + ->renderGraph($ancestry); 1237 + 1238 + // Load author handles, and also revision handles for any revisions which 1239 + // we failed to load (they might be policy restricted). 1240 + $handle_phids = mpull($revisions, 'getAuthorPHID'); 1241 + foreach ($order as $phid) { 1242 + if (empty($revisions[$phid])) { 1243 + $handle_phids[] = $phid; 1244 + } 1245 + } 1246 + $handles = $viewer->loadHandles($handle_phids); 1247 + 1248 + $rows = array(); 1249 + $rowc = array(); 1250 + 1251 + $ii = 0; 1252 + $seen = false; 1253 + foreach ($ancestry as $phid => $ignored) { 1254 + $revision = idx($revisions, $phid); 1255 + if ($revision) { 1256 + $status_icon = $revision->getStatusIcon(); 1257 + $status_name = $revision->getStatusDisplayName(); 1258 + 1259 + $status = array( 1260 + id(new PHUIIconView())->setIcon($status_icon), 1261 + ' ', 1262 + $status_name, 1263 + ); 1264 + 1265 + $author = $viewer->renderHandle($revision->getAuthorPHID()); 1266 + $title = phutil_tag( 1267 + 'a', 1268 + array( 1269 + 'href' => $revision->getURI(), 1270 + ), 1271 + array( 1272 + $revision->getMonogram(), 1273 + ' ', 1274 + $revision->getTitle(), 1275 + )); 1276 + } else { 1277 + $status = null; 1278 + $author = null; 1279 + $title = $viewer->renderHandle($phid); 1280 + } 1281 + 1282 + $rows[] = array( 1283 + $traces[$ii++], 1284 + $status, 1285 + $author, 1286 + $title, 1287 + ); 1288 + 1289 + if ($phid == $current->getPHID()) { 1290 + $rowc[] = 'highlighted'; 1291 + } else { 1292 + $rowc[] = null; 1293 + } 1294 + } 1295 + 1296 + $stack_table = id(new AphrontTableView($rows)) 1297 + ->setHeaders( 1298 + array( 1299 + null, 1300 + pht('Status'), 1301 + pht('Author'), 1302 + pht('Revision'), 1303 + )) 1304 + ->setRowClasses($rowc) 1305 + ->setColumnClasses( 1306 + array( 1307 + 'threads', 1308 + null, 1309 + null, 1310 + 'wide', 1311 + )); 1312 + 1313 + // Count how many revisions this one depends on that are not yet closed. 1314 + $seen = array(); 1315 + $look = array($current->getPHID()); 1316 + while ($look) { 1317 + $phid = array_pop($look); 1318 + 1319 + $parents = idx($ancestry, $phid, array()); 1320 + foreach ($parents as $parent) { 1321 + if (isset($seen[$parent])) { 1322 + continue; 1323 + } 1324 + 1325 + $seen[$parent] = $parent; 1326 + $look[] = $parent; 1327 + } 1328 + } 1329 + 1330 + $blocking_count = 0; 1331 + foreach ($seen as $parent) { 1332 + if ($parent == $current->getPHID()) { 1333 + continue; 1334 + } 1335 + 1336 + $revision = idx($revisions, $parent); 1337 + if (!$revision) { 1338 + continue; 1339 + } 1340 + 1341 + if ($revision->isClosed()) { 1342 + continue; 1343 + } 1344 + 1345 + $blocking_count++; 1346 + } 1347 + 1348 + if (!$blocking_count) { 1349 + $stack_name = pht('Stack'); 1350 + $stack_color = null; 1351 + } else { 1352 + $stack_name = pht( 1353 + 'Stack (%s Open)', 1354 + new PhutilNumber($blocking_count)); 1355 + $stack_color = PHUIListItemView::STATUS_FAIL; 1356 + } 1357 + 1358 + return array($stack_name, $stack_color, $stack_table); 1359 + } 1200 1360 1201 1361 }
-18
src/applications/differential/customfield/DifferentialChildRevisionsField.php
··· 19 19 return pht('Lists revisions this one is depended on by.'); 20 20 } 21 21 22 - public function shouldAppearInPropertyView() { 23 - return true; 24 - } 25 - 26 - public function renderPropertyViewLabel() { 27 - return $this->getFieldName(); 28 - } 29 - 30 - public function getRequiredHandlePHIDsForPropertyView() { 31 - return PhabricatorEdgeQuery::loadDestinationPHIDs( 32 - $this->getObject()->getPHID(), 33 - DifferentialRevisionDependedOnByRevisionEdgeType::EDGECONST); 34 - } 35 - 36 - public function renderPropertyViewValue(array $handles) { 37 - return $this->renderHandleList($handles); 38 - } 39 - 40 22 }
-18
src/applications/differential/customfield/DifferentialParentRevisionsField.php
··· 23 23 return pht('Lists revisions this one depends on.'); 24 24 } 25 25 26 - public function shouldAppearInPropertyView() { 27 - return true; 28 - } 29 - 30 - public function renderPropertyViewLabel() { 31 - return $this->getFieldName(); 32 - } 33 - 34 - public function getRequiredHandlePHIDsForPropertyView() { 35 - return PhabricatorEdgeQuery::loadDestinationPHIDs( 36 - $this->getObject()->getPHID(), 37 - DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST); 38 - } 39 - 40 - public function renderPropertyViewValue(array $handles) { 41 - return $this->renderHandleList($handles); 42 - } 43 - 44 26 public function getProTips() { 45 27 return array( 46 28 pht(
+58
src/applications/differential/edge/DifferentialStackGraph.php
··· 1 + <?php 2 + 3 + final class DifferentialStackGraph 4 + extends AbstractDirectedGraph { 5 + 6 + private $parentEdges = array(); 7 + private $childEdges = array(); 8 + 9 + public function setSeedRevision(DifferentialRevision $revision) { 10 + return $this->addNodes( 11 + array( 12 + '<seed>' => array($revision->getPHID()), 13 + )); 14 + } 15 + 16 + public function isEmpty() { 17 + return (count($this->getNodes()) <= 2); 18 + } 19 + 20 + public function getParentEdges() { 21 + return $this->parentEdges; 22 + } 23 + 24 + protected function loadEdges(array $nodes) { 25 + $query = id(new PhabricatorEdgeQuery()) 26 + ->withSourcePHIDs($nodes) 27 + ->withEdgeTypes( 28 + array( 29 + DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST, 30 + DifferentialRevisionDependedOnByRevisionEdgeType::EDGECONST, 31 + )); 32 + 33 + $query->execute(); 34 + 35 + $map = array(); 36 + foreach ($nodes as $node) { 37 + $parents = $query->getDestinationPHIDs( 38 + array($node), 39 + array( 40 + DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST, 41 + )); 42 + 43 + $children = $query->getDestinationPHIDs( 44 + array($node), 45 + array( 46 + DifferentialRevisionDependedOnByRevisionEdgeType::EDGECONST, 47 + )); 48 + 49 + $this->parentEdges[$node] = $parents; 50 + $this->childEdges[$node] = $children; 51 + 52 + $map[$node] = array_values(array_fuse($parents) + array_fuse($children)); 53 + } 54 + 55 + return $map; 56 + } 57 + 58 + }
+29
src/applications/differential/storage/DifferentialRevision.php
··· 136 136 return "D{$id}"; 137 137 } 138 138 139 + public function getURI() { 140 + return '/'.$this->getMonogram(); 141 + } 142 + 139 143 public function setTitle($title) { 140 144 $this->title = $title; 141 145 if (!$this->getID()) { ··· 424 428 425 429 public function isClosed() { 426 430 return DifferentialRevisionStatus::isClosedStatus($this->getStatus()); 431 + } 432 + 433 + public function getStatusIcon() { 434 + $map = array( 435 + ArcanistDifferentialRevisionStatus::NEEDS_REVIEW 436 + => 'fa-code grey', 437 + ArcanistDifferentialRevisionStatus::NEEDS_REVISION 438 + => 'fa-refresh red', 439 + ArcanistDifferentialRevisionStatus::CHANGES_PLANNED 440 + => 'fa-headphones red', 441 + ArcanistDifferentialRevisionStatus::ACCEPTED 442 + => 'fa-check green', 443 + ArcanistDifferentialRevisionStatus::CLOSED 444 + => 'fa-check-square-o black', 445 + ArcanistDifferentialRevisionStatus::ABANDONED 446 + => 'fa-plane black', 447 + ); 448 + 449 + return idx($map, $this->getStatus()); 450 + } 451 + 452 + public function getStatusDisplayName() { 453 + $status = $this->getStatus(); 454 + return ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( 455 + $status); 427 456 } 428 457 429 458 public function getFlag(PhabricatorUser $viewer) {
+6 -25
src/applications/differential/view/DifferentialRevisionListView.php
··· 104 104 105 105 $modified = $revision->getDateModified(); 106 106 107 - $status = $revision->getStatus(); 108 - $status_name = 109 - ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status); 110 - 111 107 if (isset($icons['flag'])) { 112 108 $item->addHeadIcon($icons['flag']); 113 109 } ··· 155 151 $item->addAttribute(pht('Reviewers: %s', $reviewers)); 156 152 $item->setEpoch($revision->getDateModified()); 157 153 158 - switch ($status) { 159 - case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: 160 - $item->setStatusIcon('fa-code grey', pht('Needs Review')); 161 - break; 162 - case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: 163 - $item->setStatusIcon('fa-refresh red', pht('Needs Revision')); 164 - break; 165 - case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED: 166 - $item->setStatusIcon('fa-headphones red', pht('Changes Planned')); 167 - break; 168 - case ArcanistDifferentialRevisionStatus::ACCEPTED: 169 - $item->setStatusIcon('fa-check green', pht('Accepted')); 170 - break; 171 - case ArcanistDifferentialRevisionStatus::CLOSED: 172 - $item->setDisabled(true); 173 - $item->setStatusIcon('fa-check-square-o black', pht('Closed')); 174 - break; 175 - case ArcanistDifferentialRevisionStatus::ABANDONED: 176 - $item->setDisabled(true); 177 - $item->setStatusIcon('fa-plane black', pht('Abandoned')); 178 - break; 154 + if ($revision->isClosed()) { 155 + $item->setDisabled(true); 179 156 } 157 + 158 + $item->setStatusIcon( 159 + $revision->getStatusIcon(), 160 + $revision->getStatusDisplayName()); 180 161 181 162 $list->addItem($item); 182 163 }
+4 -163
src/applications/diffusion/view/DiffusionHistoryTableView.php
··· 82 82 83 83 $graph = null; 84 84 if ($this->parents) { 85 - $graph = $this->renderGraph(); 85 + $graph = id(new PHUIDiffGraphView()) 86 + ->setIsHead($this->isHead) 87 + ->setIsTail($this->isTail) 88 + ->renderGraph($this->parents); 86 89 } 87 90 88 91 $show_builds = PhabricatorApplication::isClassInstalledForViewer( ··· 217 220 false, 218 221 )); 219 222 return $view->render(); 220 - } 221 - 222 - /** 223 - * Draw a merge/branch graph from the parent revision data. We're basically 224 - * building up a bunch of strings like this: 225 - * 226 - * ^ 227 - * |^ 228 - * o| 229 - * |o 230 - * o 231 - * 232 - * ...which form an ASCII representation of the graph we eventually want to 233 - * draw. 234 - * 235 - * NOTE: The actual implementation is black magic. 236 - */ 237 - private function renderGraph() { 238 - // This keeps our accumulated information about each line of the 239 - // merge/branch graph. 240 - $graph = array(); 241 - 242 - // This holds the next commit we're looking for in each column of the 243 - // graph. 244 - $threads = array(); 245 - 246 - // This is the largest number of columns any row has, i.e. the width of 247 - // the graph. 248 - $count = 0; 249 - 250 - foreach ($this->history as $key => $history) { 251 - $joins = array(); 252 - $splits = array(); 253 - 254 - $parent_list = $this->parents[$history->getCommitIdentifier()]; 255 - 256 - // Look for some thread which has this commit as the next commit. If 257 - // we find one, this commit goes on that thread. Otherwise, this commit 258 - // goes on a new thread. 259 - 260 - $line = ''; 261 - $found = false; 262 - $pos = count($threads); 263 - for ($n = 0; $n < $count; $n++) { 264 - if (empty($threads[$n])) { 265 - $line .= ' '; 266 - continue; 267 - } 268 - 269 - if ($threads[$n] == $history->getCommitIdentifier()) { 270 - if ($found) { 271 - $line .= ' '; 272 - $joins[] = $n; 273 - unset($threads[$n]); 274 - } else { 275 - $line .= 'o'; 276 - $found = true; 277 - $pos = $n; 278 - } 279 - } else { 280 - 281 - // We render a "|" for any threads which have a commit that we haven't 282 - // seen yet, this is later drawn as a vertical line. 283 - $line .= '|'; 284 - } 285 - } 286 - 287 - // If we didn't find the thread this commit goes on, start a new thread. 288 - // We use "o" to mark the commit for the rendering engine, or "^" to 289 - // indicate that there's nothing after it so the line from the commit 290 - // upward should not be drawn. 291 - 292 - if (!$found) { 293 - if ($this->isHead) { 294 - $line .= '^'; 295 - } else { 296 - $line .= 'o'; 297 - foreach ($graph as $k => $meta) { 298 - // Go back across all the lines we've already drawn and add a 299 - // "|" to the end, since this is connected to some future commit 300 - // we don't know about. 301 - for ($jj = strlen($meta['line']); $jj <= $count; $jj++) { 302 - $graph[$k]['line'] .= '|'; 303 - } 304 - } 305 - } 306 - } 307 - 308 - // Update the next commit on this thread to the commit's first parent. 309 - // This might have the effect of making a new thread. 310 - $threads[$pos] = head($parent_list); 311 - 312 - // If we made a new thread, increase the thread count. 313 - $count = max($pos + 1, $count); 314 - 315 - // Now, deal with splits (merges). I picked this terms opposite to the 316 - // underlying repository term to confuse you. 317 - foreach (array_slice($parent_list, 1) as $parent) { 318 - $found = false; 319 - 320 - // Try to find the other parent(s) in our existing threads. If we find 321 - // them, split to that thread. 322 - 323 - foreach ($threads as $idx => $thread_commit) { 324 - if ($thread_commit == $parent) { 325 - $found = true; 326 - $splits[] = $idx; 327 - } 328 - } 329 - 330 - // If we didn't find the parent, we don't know about it yet. Find the 331 - // first free thread and add it as the "next" commit in that thread. 332 - // This might create a new thread. 333 - 334 - if (!$found) { 335 - for ($n = 0; $n < $count; $n++) { 336 - if (empty($threads[$n])) { 337 - break; 338 - } 339 - } 340 - $threads[$n] = $parent; 341 - $splits[] = $n; 342 - $count = max($n + 1, $count); 343 - } 344 - } 345 - 346 - $graph[] = array( 347 - 'line' => $line, 348 - 'split' => $splits, 349 - 'join' => $joins, 350 - ); 351 - } 352 - 353 - // If this is the last page in history, replace the "o" with an "x" so we 354 - // do not draw a connecting line downward, and replace "^" with an "X" for 355 - // repositories with exactly one commit. 356 - if ($this->isTail && $graph) { 357 - $last = array_pop($graph); 358 - $last['line'] = str_replace('o', 'x', $last['line']); 359 - $last['line'] = str_replace('^', 'X', $last['line']); 360 - $graph[] = $last; 361 - } 362 - 363 - // Render into tags for the behavior. 364 - 365 - foreach ($graph as $k => $meta) { 366 - $graph[$k] = javelin_tag( 367 - 'div', 368 - array( 369 - 'sigil' => 'commit-graph', 370 - 'meta' => $meta, 371 - ), 372 - ''); 373 - } 374 - 375 - Javelin::initBehavior( 376 - 'diffusion-commit-graph', 377 - array( 378 - 'count' => $count, 379 - )); 380 - 381 - return $graph; 382 223 } 383 224 384 225 }
+171
src/infrastructure/diff/view/PHUIDiffGraphView.php
··· 1 + <?php 2 + 3 + final class PHUIDiffGraphView extends Phobject { 4 + 5 + private $isHead = true; 6 + private $isTail = true; 7 + 8 + public function setIsHead($is_head) { 9 + $this->isHead = $is_head; 10 + return $this; 11 + } 12 + 13 + public function getIsHead() { 14 + return $this->isHead; 15 + } 16 + 17 + public function setIsTail($is_tail) { 18 + $this->isTail = $is_tail; 19 + return $this; 20 + } 21 + 22 + public function getIsTail() { 23 + return $this->isTail; 24 + } 25 + 26 + public function renderGraph(array $parents) { 27 + // This keeps our accumulated information about each line of the 28 + // merge/branch graph. 29 + $graph = array(); 30 + 31 + // This holds the next commit we're looking for in each column of the 32 + // graph. 33 + $threads = array(); 34 + 35 + // This is the largest number of columns any row has, i.e. the width of 36 + // the graph. 37 + $count = 0; 38 + 39 + foreach ($parents as $cursor => $parent_list) { 40 + $joins = array(); 41 + $splits = array(); 42 + 43 + // Look for some thread which has this commit as the next commit. If 44 + // we find one, this commit goes on that thread. Otherwise, this commit 45 + // goes on a new thread. 46 + 47 + $line = ''; 48 + $found = false; 49 + $pos = count($threads); 50 + for ($n = 0; $n < $count; $n++) { 51 + if (empty($threads[$n])) { 52 + $line .= ' '; 53 + continue; 54 + } 55 + 56 + if ($threads[$n] == $cursor) { 57 + if ($found) { 58 + $line .= ' '; 59 + $joins[] = $n; 60 + unset($threads[$n]); 61 + } else { 62 + $line .= 'o'; 63 + $found = true; 64 + $pos = $n; 65 + } 66 + } else { 67 + 68 + // We render a "|" for any threads which have a commit that we haven't 69 + // seen yet, this is later drawn as a vertical line. 70 + $line .= '|'; 71 + } 72 + } 73 + 74 + // If we didn't find the thread this commit goes on, start a new thread. 75 + // We use "o" to mark the commit for the rendering engine, or "^" to 76 + // indicate that there's nothing after it so the line from the commit 77 + // upward should not be drawn. 78 + 79 + if (!$found) { 80 + if ($this->getIsHead()) { 81 + $line .= '^'; 82 + } else { 83 + $line .= 'o'; 84 + foreach ($graph as $k => $meta) { 85 + // Go back across all the lines we've already drawn and add a 86 + // "|" to the end, since this is connected to some future commit 87 + // we don't know about. 88 + for ($jj = strlen($meta['line']); $jj <= $count; $jj++) { 89 + $graph[$k]['line'] .= '|'; 90 + } 91 + } 92 + } 93 + } 94 + 95 + // Update the next commit on this thread to the commit's first parent. 96 + // This might have the effect of making a new thread. 97 + $threads[$pos] = head($parent_list); 98 + 99 + // If we made a new thread, increase the thread count. 100 + $count = max($pos + 1, $count); 101 + 102 + // Now, deal with splits (merges). I picked this terms opposite to the 103 + // underlying repository term to confuse you. 104 + foreach (array_slice($parent_list, 1) as $parent) { 105 + $found = false; 106 + 107 + // Try to find the other parent(s) in our existing threads. If we find 108 + // them, split to that thread. 109 + 110 + foreach ($threads as $idx => $thread_commit) { 111 + if ($thread_commit == $parent) { 112 + $found = true; 113 + $splits[] = $idx; 114 + } 115 + } 116 + 117 + // If we didn't find the parent, we don't know about it yet. Find the 118 + // first free thread and add it as the "next" commit in that thread. 119 + // This might create a new thread. 120 + 121 + if (!$found) { 122 + for ($n = 0; $n < $count; $n++) { 123 + if (empty($threads[$n])) { 124 + break; 125 + } 126 + } 127 + $threads[$n] = $parent; 128 + $splits[] = $n; 129 + $count = max($n + 1, $count); 130 + } 131 + } 132 + 133 + $graph[] = array( 134 + 'line' => $line, 135 + 'split' => $splits, 136 + 'join' => $joins, 137 + ); 138 + } 139 + 140 + // If this is the last page in history, replace the "o" with an "x" so we 141 + // do not draw a connecting line downward, and replace "^" with an "X" for 142 + // repositories with exactly one commit. 143 + if ($this->getIsTail() && $graph) { 144 + $last = array_pop($graph); 145 + $last['line'] = str_replace('o', 'x', $last['line']); 146 + $last['line'] = str_replace('^', 'X', $last['line']); 147 + $graph[] = $last; 148 + } 149 + 150 + // Render into tags for the behavior. 151 + 152 + foreach ($graph as $k => $meta) { 153 + $graph[$k] = javelin_tag( 154 + 'div', 155 + array( 156 + 'sigil' => 'commit-graph', 157 + 'meta' => $meta, 158 + ), 159 + ''); 160 + } 161 + 162 + Javelin::initBehavior( 163 + 'diffusion-commit-graph', 164 + array( 165 + 'count' => $count, 166 + )); 167 + 168 + return $graph; 169 + } 170 + 171 + }
+18 -1
src/view/phui/PHUITabView.php
··· 6 6 private $key; 7 7 private $keyLocked; 8 8 private $contentID; 9 + private $color; 9 10 10 11 public function setKey($key) { 11 12 if ($this->keyLocked) { ··· 58 59 return $this->contentID; 59 60 } 60 61 62 + public function setColor($color) { 63 + $this->color = $color; 64 + return $this; 65 + } 66 + 67 + public function getColor() { 68 + return $this->color; 69 + } 70 + 61 71 public function newMenuItem() { 62 - return id(new PHUIListItemView()) 72 + $item = id(new PHUIListItemView()) 63 73 ->setName($this->getName()) 64 74 ->setKey($this->getKey()) 65 75 ->setType(PHUIListItemView::TYPE_LINK) ··· 69 79 array( 70 80 'tabKey' => $this->getKey(), 71 81 )); 82 + 83 + $color = $this->getColor(); 84 + if ($color !== null) { 85 + $item->setStatusColor($color); 86 + } 87 + 88 + return $item; 72 89 } 73 90 74 91 }