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

DifferentialRevisionView

+2195 -19
+18 -2
src/__celerity_resource_map__.php
··· 82 82 ), 83 83 'differential-core-view-css' => 84 84 array( 85 - 'path' => '/res/f750b85d/rsrc/css/application/differential/core.css', 85 + 'path' => '/res/525d1a12/rsrc/css/application/differential/core.css', 86 + 'type' => 'css', 87 + 'requires' => 88 + array( 89 + ), 90 + ), 91 + 'differential-revision-detail-css' => 92 + array( 93 + 'path' => '/res/11a36dad/rsrc/css/application/differential/revision-detail.css', 94 + 'type' => 'css', 95 + 'requires' => 96 + array( 97 + ), 98 + ), 99 + 'differential-revision-history-css' => 100 + array( 101 + 'path' => '/res/755f3da3/rsrc/css/application/differential/revision-history.css', 86 102 'type' => 'css', 87 103 'requires' => 88 104 array( ··· 90 106 ), 91 107 'differential-table-of-contents-css' => 92 108 array( 93 - 'path' => '/res/ebf6641c/rsrc/css/application/differential/table-of-contents.css', 109 + 'path' => '/res/a4a7b2b5/rsrc/css/application/differential/table-of-contents.css', 94 110 'type' => 'css', 95 111 'requires' => 96 112 array(
+6
src/__phutil_library_map__.php
··· 86 86 'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest', 87 87 'DifferentialRevision' => 'applications/differential/storage/revision', 88 88 'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem', 89 + 'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail', 89 90 'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit', 90 91 'DifferentialRevisionEditor' => 'applications/differential/editor/revision', 91 92 'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist', 92 93 'DifferentialRevisionListData' => 'applications/differential/data/revisionlist', 93 94 'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus', 95 + 'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory', 96 + 'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview', 94 97 'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus', 95 98 'Javelin' => 'infratructure/javelin/api', 96 99 'LiskDAO' => 'storage/lisk/dao', ··· 232 235 'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail', 233 236 'DifferentialReviewRequestMail' => 'DifferentialMail', 234 237 'DifferentialRevision' => 'DifferentialDAO', 238 + 'DifferentialRevisionDetailView' => 'AphrontView', 235 239 'DifferentialRevisionEditController' => 'DifferentialController', 236 240 'DifferentialRevisionListController' => 'DifferentialController', 241 + 'DifferentialRevisionUpdateHistoryView' => 'AphrontView', 242 + 'DifferentialRevisionViewController' => 'DifferentialController', 237 243 'PhabricatorAuthController' => 'PhabricatorController', 238 244 'PhabricatorConduitAPIController' => 'PhabricatorConduitController', 239 245 'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
+2
src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
··· 77 77 ), 78 78 '/api/(?<method>[^/]+)$' => 'PhabricatorConduitAPIController', 79 79 80 + 81 + '/D(?<id>\d+)' => 'DifferentialRevisionViewController', 80 82 '/differential/' => array( 81 83 '$' => 'DifferentialRevisionListController', 82 84 'filter/(?<filter>\w+)/$' => 'DifferentialRevisionListController',
+1709
src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2011 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + class DifferentialRevisionViewController extends DifferentialController { 20 + 21 + private $revisionID; 22 + 23 + public function willProcessRequest(array $data) { 24 + $this->revisionID = $data['id']; 25 + } 26 + 27 + public function processRequest() { 28 + 29 + $request = $this->getRequest(); 30 + 31 + $revision = id(new DifferentialRevision())->load($this->revisionID); 32 + if (!$revision) { 33 + return new Aphront404Response(); 34 + } 35 + 36 + $revision->loadRelationships(); 37 + 38 + $diffs = $revision->loadDiffs(); 39 + 40 + $target = end($diffs); 41 + 42 + $changesets = $target->loadChangesets(); 43 + 44 + $object_phids = array_merge( 45 + $revision->getReviewers(), 46 + $revision->getCCPHIDs(), 47 + array( 48 + $revision->getOwnerPHID(), 49 + $request->getUser()->getPHID(), 50 + )); 51 + 52 + $handles = id(new PhabricatorObjectHandleData($object_phids)) 53 + ->loadHandles(); 54 + 55 + $diff_history = new DifferentialRevisionUpdateHistoryView(); 56 + $diff_history->setDiffs($diffs); 57 + 58 + $toc_view = new DifferentialDiffTableOfContentsView(); 59 + $toc_view->setChangesets($changesets); 60 + 61 + $changeset_view = new DifferentialChangesetListView(); 62 + $changeset_view->setChangesets($changesets); 63 + 64 + $revision_detail = new DifferentialRevisionDetailView(); 65 + $revision_detail->setRevision($revision); 66 + 67 + $properties = $this->getRevisionProperties($revision, $target, $handles); 68 + $revision_detail->setProperties($properties); 69 + 70 + $actions = $this->getRevisionActions($revision); 71 + $revision_detail->setActions($actions); 72 + 73 + return $this->buildStandardPageResponse( 74 + '<div class="differential-primary-pane">'. 75 + $revision_detail->render(). 76 + $diff_history->render(). 77 + $toc_view->render(). 78 + $changeset_view->render(). 79 + '</div>', 80 + array( 81 + 'title' => $revision->getTitle(), 82 + )); 83 + } 84 + 85 + private function getRevisionProperties( 86 + DifferentialRevision $revision, 87 + DifferentialDiff $diff, 88 + array $handles) { 89 + 90 + $properties = array(); 91 + 92 + $status = $revision->getStatus(); 93 + $status = DifferentialRevisionStatus::getNameForRevisionStatus($status); 94 + $properties['Revision Status'] = '<strong>'.$status.'</strong>'; 95 + 96 + $author = $handles[$revision->getOwnerPHID()]; 97 + $properties['Author'] = $author->renderLink(); 98 + 99 + $properties['Reviewers'] = $this->renderHandleLinkList( 100 + array_select_keys( 101 + $handles, 102 + $revision->getReviewers())); 103 + 104 + $properties['CCs'] = $this->renderHandleLinkList( 105 + array_select_keys( 106 + $handles, 107 + $revision->getCCPHIDs())); 108 + 109 + $path = $diff->getSourcePath(); 110 + if ($path) { 111 + $branch = $diff->getBranch() ? ' (' . $diff->getBranch() . ')' : ''; 112 + $host = $diff->getSourceMachine(); 113 + if ($host) { 114 + $host .= ':'; 115 + } 116 + $properties['Path'] = phutil_escape_html("{$host}{$path} {$branch}"); 117 + } 118 + 119 + 120 + $properties['Lint'] = 'TODO'; 121 + $properties['Unit'] = 'TODO'; 122 + 123 + return $properties; 124 + } 125 + 126 + private function getRevisionActions(DifferentialRevision $revision) { 127 + $viewer_phid = $this->getRequest()->getUser()->getPHID(); 128 + $viewer_is_owner = ($revision->getOwnerPHID() == $viewer_phid); 129 + $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers()); 130 + $viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs()); 131 + $status = $revision->getStatus(); 132 + $revision_id = $revision->getID(); 133 + $revision_phid = $revision->getPHID(); 134 + 135 + $links = array(); 136 + 137 + if ($viewer_is_owner) { 138 + $links[] = array( 139 + 'class' => 'revision-edit', 140 + 'href' => "/differential/revision/edit/{$revision_id}/", 141 + 'name' => 'Edit Revision', 142 + ); 143 + } 144 + 145 + if (!$viewer_is_owner && !$viewer_is_reviewer) { 146 + $action = $viewer_is_cc ? 'rem' : 'add'; 147 + $links[] = array( 148 + 'class' => $viewer_is_cc ? 'subscribe-rem' : 'subscribe-add', 149 + 'href' => "/differential/subscribe/{$action}/{$revision_id}/", 150 + 'name' => $viewer_is_cc ? 'Unsubscribe' : 'Subscribe', 151 + ); 152 + } else { 153 + $links[] = array( 154 + 'class' => 'subscribe-rem unavailable', 155 + 'name' => 'Automatically Subscribed', 156 + ); 157 + } 158 + 159 + $links[] = array( 160 + 'class' => 'transcripts-metamta', 161 + 'name' => 'MetaMTA Transcripts', 162 + 'href' => "/mail/?phid={$revision_phid}", 163 + ); 164 + 165 + return $links; 166 + } 167 + 168 + 169 + private function renderHandleLinkList(array $list) { 170 + if (empty($list)) { 171 + return '<em>None</em>'; 172 + } 173 + return implode(', ', mpull($list, 'renderLink')); 174 + } 175 + 176 + } 177 + /* 178 + 179 + 180 + $viewer_id = $this->getRequest()->getViewerContext()->getUserID(); 181 + $viewer_is_owner = ($viewer_id == $revision->getOwnerID()); 182 + $viewer_is_reviewer = 183 + ((array_search($viewer_id, $revision->getReviewers())) !== false); 184 + $viewer_is_cc = 185 + ((array_search($viewer_id, $revision->getCCFBIDs())) !== false); 186 + $status = $revision->getStatus(); 187 + 188 + $links = array(); 189 + 190 + if (!$viewer_is_owner && !$viewer_is_reviewer) { 191 + $action = $viewer_is_cc 192 + ? 'rem' 193 + : 'add'; 194 + $revision_id = $revision->getID(); 195 + $href = "/differential/subscribe/{$action}/{$revision_id}"; 196 + $links[] = array( 197 + $viewer_is_cc ? 'subscribe-disabled' : 'subscribe-enabled', 198 + <a href={$href}>{$viewer_is_cc ? 'Unsubscribe' : 'Subscribe'}</a>, 199 + ); 200 + } else { 201 + $links[] = array( 202 + 'subscribe-disabled unavailable', 203 + <a>Automatically Subscribed</a>, 204 + ); 205 + } 206 + 207 + $blast_uri = RedirectURI( 208 + '/intern/differential/?action=tasks&fbid='.$revision->getFBID()) 209 + ->setTier('intern'); 210 + $links[] = array( 211 + 'tasks', 212 + <a href={$blast_uri}>Edit Tasks</a>, 213 + ); 214 + 215 + $engineering_repository_id = RepositoryRef::getByCallsign('E')->getID(); 216 + $svn_revision = $revision->getSVNRevision(); 217 + if ($status == DifferentialConstants::COMMITTED && 218 + $svn_revision && 219 + $revision->getRepositoryID() == $engineering_repository_id) { 220 + $href = '/intern/push/request.php?rev='.$svn_revision; 221 + $href = RedirectURI($href)->setTier('intern'); 222 + $links[] = array( 223 + 'merge', 224 + <a href={$href} id="ask_for_merge_link">Ask for Merge</a>, 225 + ); 226 + } 227 + 228 + $links[] = array( 229 + 'herald-transcript', 230 + <a href={"/herald/transcript/?fbid=".$revision->getFBID()} 231 + >Herald Transcripts</a>, 232 + ); 233 + $links[] = array( 234 + 'metamta-transcript', 235 + <a href={"/mail/?view=all&fbid=".$revision->getFBID()} 236 + >MetaMTA Transcripts</a>, 237 + ); 238 + 239 + 240 + $list = <ul class="differential-actions" />; 241 + foreach ($links as $link) { 242 + list($class, $tag) = $link; 243 + $list->appendChild(<li class={$class}>{$tag}</li>); 244 + } 245 + 246 + return $list; 247 + 248 + 249 + 250 + /* 251 + // TODO 252 + // $sandcastle = $this->getSandcastleURI($diff); 253 + // if ($sandcastle) { 254 + // $fields['Sandcastle'] = <a href={$sandcastle}>{$sandcastle}</a>; 255 + // } 256 + 257 + $path = $diff->getSourcePath(); 258 + if ($path) { 259 + $host = $diff->getSourceMachine(); 260 + $branch = $diff->getGitBranch() ? ' (' . $diff->getGitBranch() . ')' : ''; 261 + 262 + if ($host) { 263 + // TODO 264 + // $user = $handles[$this->getRequest()->getViewerContext()->getUserID()] 265 + // ->getName(); 266 + $user = 'TODO'; 267 + $fields['Path'] = 268 + <x:frag> 269 + <a href={"ssh://{$user}@{$host}"}>{$host}</a>:{$path}{$branch} 270 + </x:frag>; 271 + } else { 272 + $fields['Path'] = $path; 273 + } 274 + } 275 + 276 + $reviewer_links = array(); 277 + foreach ($revision->getReviewers() as $reviewer) { 278 + $reviewer_links[] = <tools:handle handle={$handles[$reviewer]} 279 + link={true} />; 280 + } 281 + if ($reviewer_links) { 282 + $fields['Reviewers'] = array_implode(', ', $reviewer_links); 283 + } else { 284 + $fields['Reviewers'] = <em>None</em>; 285 + } 286 + 287 + $ccs = $revision->getCCFBIDs(); 288 + if ($ccs) { 289 + $links = array(); 290 + foreach ($ccs as $cc) { 291 + $links[] = <tools:handle handle={$handles[$cc]} 292 + link={true} />; 293 + } 294 + $fields['CCs'] = array_implode(', ', $links); 295 + } 296 + 297 + $blame_rev = $revision->getSvnBlameRevision(); 298 + if ($blame_rev) { 299 + if ($revision->getRepositoryRef() && is_numeric($blame_rev)) { 300 + $ref = new RevisionRef($revision->getRepositoryRef(), $blame_rev); 301 + $fields['Blame Revision'] = 302 + <a href={URI($ref->getDetailURL())}> 303 + {$ref->getName()} 304 + </a>; 305 + } else { 306 + $fields['Blame Revision'] = $blame_rev; 307 + } 308 + } 309 + 310 + $tasks = $revision->getTaskHandles(); 311 + 312 + if ($tasks) { 313 + $links = array(); 314 + foreach ($tasks as $task) { 315 + $links[] = <tools:handle handle={$task} link={true} />; 316 + } 317 + $fields['Tasks'] = array_implode(<br />, $links); 318 + } 319 + 320 + $bugzilla_id = $revision->getBugzillaID(); 321 + if ($bugzilla_id) { 322 + $href = 'http://bugs.developers.facebook.com/show_bug.cgi?id='. 323 + $bugzilla_id; 324 + $fields['Bugzilla'] = <a href={$href}>{'#'.$bugzilla_id}</a>; 325 + } 326 + 327 + $fields['Apply Patch'] = <tt>arc patch --revision {$revision->getID()}</tt>; 328 + 329 + if ($diff->getParentRevisionID()) { 330 + $parent = id(new DifferentialRevision())->load( 331 + $diff->getParentRevisionID()); 332 + if ($parent) { 333 + $fields['Depends On'] = 334 + <a href={$parent->getURI()}> 335 + D{$parent->getID()}: {$parent->getName()} 336 + </a>; 337 + } 338 + } 339 + 340 + $star = <span class="star">{"\xE2\x98\x85"}</span>; 341 + 342 + Javelin::initBehavior('differential-star-more'); 343 + 344 + switch ($diff->getLinted()) { 345 + case Diff::LINT_FAIL: 346 + $more = $this->renderDiffPropertyMoreLink($diff, 'lint'); 347 + $fields['Lint'] = 348 + <x:frag> 349 + <span class="star-warn">{$star} Lint Failures</span> 350 + {$more} 351 + </x:frag>; 352 + break; 353 + case Diff::LINT_WARNINGS: 354 + $more = $this->renderDiffPropertyMoreLink($diff, 'lint'); 355 + $fields['Lint'] = 356 + <x:frag> 357 + <span class="star-warn">{$star} Lint Warnings</span> 358 + {$more} 359 + </x:frag>; 360 + break; 361 + case Diff::LINT_OKAY: 362 + $fields['Lint'] = 363 + <span class="star-okay">{$star} Lint Free</span>; 364 + break; 365 + default: 366 + case Diff::LINT_NO: 367 + $fields['Lint'] = 368 + <span class="star-none">{$star} Not Linted</span>; 369 + break; 370 + } 371 + 372 + $unit_details = false; 373 + switch ($diff->getUnitTested()) { 374 + case Diff::UNIT_FAIL: 375 + $fields['Unit Tests'] = 376 + <span class="star-warn">{$star} Unit Test Failures</span>; 377 + $unit_details = true; 378 + break; 379 + case Diff::UNIT_WARN: 380 + $fields['Unit Tests'] = 381 + <span class="star-warn">{$star} Unit Test Warnings</span>; 382 + $unit_details = true; 383 + break; 384 + case Diff::UNIT_OKAY: 385 + $fields['Unit Tests'] = 386 + <span class="star-okay">{$star} Unit Tests Passed</span>; 387 + $unit_details = true; 388 + break; 389 + case Diff::UNIT_NO_TESTS: 390 + $fields['Unit Tests'] = 391 + <span class="star-none">{$star} No Test Coverage</span>; 392 + break; 393 + case Diff::UNIT_NO: 394 + default: 395 + $fields['Unit Tests'] = 396 + <span class="star-none">{$star} Not Unit Tested</span>; 397 + break; 398 + } 399 + 400 + if ($unit_details) { 401 + $fields['Unit Tests'] = 402 + <x:frag> 403 + {$fields['Unit Tests']} 404 + {$this->renderDiffPropertyMoreLink($diff, 'unit')} 405 + </x:frag>; 406 + } 407 + 408 + $platform_impact = $revision->getPlatformImpact(); 409 + if ($platform_impact) { 410 + $fields['Platform Impact'] = 411 + <text linebreaks="true">{$platform_impact}</text>; 412 + } 413 + 414 + return $fields; 415 + } 416 + 417 + 418 + } 419 + 420 + /* 421 + 422 + 423 + 424 + protected function getSandcastleURI(Diff $diff) { 425 + $uri = $this->getDiffProperty($diff, 'facebook:sandcastle_uri'); 426 + if (!$uri) { 427 + $uri = $diff->getSandboxURL(); 428 + } 429 + return $uri; 430 + } 431 + 432 + protected function getDiffProperty(Diff $diff, $property, $default = null) { 433 + $diff_id = $diff->getID(); 434 + if (empty($this->diffProperties[$diff_id])) { 435 + $props = id(new DifferentialDiffProperty()) 436 + ->loadAllWhere('diffID = %s', $diff_id); 437 + $dict = array_pull($props, 'getData', 'getName'); 438 + $this->diffProperties[$diff_id] = $dict; 439 + } 440 + return idx($this->diffProperties[$diff_id], $property, $default); 441 + } 442 + 443 + public function process() { 444 + $uri = $this->getRequest()->getPath(); 445 + if (starts_with($uri, '/d')) { 446 + return <alite:redirect uri={strtoupper($uri)}/>; 447 + } 448 + 449 + $revision = id(new DifferentialRevision())->load($this->revisionID); 450 + if (!$revision) { 451 + throw new Exception("Bad revision ID."); 452 + } 453 + 454 + $diffs = id(new Diff())->loadAllWhere( 455 + 'revisionID = %d', 456 + $revision->getID()); 457 + $diffs = array_psort($diffs, 'getID'); 458 + 459 + $request = $this->getRequest(); 460 + $new = $request->getInt('new'); 461 + $old = $request->getInt('old'); 462 + 463 + if (($new || $old) && $new <= $old) { 464 + throw new Exception( 465 + "You can only view the diff of an older update relative to a newer ". 466 + "update."); 467 + } 468 + 469 + if ($new && empty($diffs[$new])) { 470 + throw new Exception( 471 + "The 'new' diff does not exist."); 472 + } else if ($new) { 473 + $diff = $diffs[$new]; 474 + } else { 475 + $diff = end($diffs); 476 + if (!$diff) { 477 + throw new Exception("No diff attached to this revision?"); 478 + } 479 + $new = $diff->getID(); 480 + } 481 + 482 + $target_diff = $diff; 483 + 484 + if ($old && empty($diffs[$old])) { 485 + throw new Exception( 486 + "The 'old' diff does not exist."); 487 + } 488 + 489 + $rows = array(array('Base', '', true, false, null, 490 + $diff->getSourceControlBaseRevision() 491 + ? $diff->getSourceControlBaseRevision() 492 + : <em>Master</em>)); 493 + $idx = 0; 494 + foreach ($diffs as $cdiff) { 495 + $rows[] = array( 496 + 'Diff '.(++$idx), 497 + $cdiff->getID(), 498 + $cdiff->getID() != max(array_pull($diffs, 'getID')), 499 + true, 500 + $cdiff->getDateCreated(), 501 + $cdiff->getDescription() 502 + ? $cdiff->getDescription() 503 + : <em>No description available.</em>, 504 + $cdiff->getUnitTested(), 505 + $cdiff->getLinted()); 506 + } 507 + 508 + $diff_table = 509 + <table class="differential-diff-differ"> 510 + <tr> 511 + <th>Diff</th> 512 + <th>Diff ID</th> 513 + <th>Description</th> 514 + <th>Age</th> 515 + <th>Lint</th> 516 + <th>Unit</th> 517 + </tr> 518 + </table>; 519 + $ii = 0; 520 + 521 + $old_ids = array(); 522 + foreach ($rows as $row) { 523 + $xold = null; 524 + if ($row[2]) { 525 + $lradio = <input name="old" value={$row[1]} type="radio" 526 + disabled={$row[1] >= $new} 527 + checked={$old == $row[1]} />; 528 + if ($old == $row[1]) { 529 + $xold = 'old-now'; 530 + } 531 + $old_ids[] = $lradio->requireUniqueID(); 532 + } else { 533 + $lradio = null; 534 + } 535 + $xnew = null; 536 + if ($row[3]) { 537 + $rradio = <input name="new" value={$row[1]} type="radio" 538 + sigil="new-radio" 539 + checked={$new == $row[1]} />; 540 + if ($new == $row[1]) { 541 + $xnew = 'new-now'; 542 + } 543 + } else { 544 + $rradio = null; 545 + } 546 + 547 + if ($row[3]) { 548 + $unit_star = 'star-none'; 549 + switch ($row[6]) { 550 + case Diff::UNIT_FAIL: 551 + case Diff::UNIT_WARN: $unit_star = 'star-warn'; break; 552 + case Diff::UNIT_OKAY: $unit_star = 'star-okay'; break; 553 + } 554 + 555 + $lint_star = 'star-none'; 556 + switch ($row[7]) { 557 + case Diff::LINT_FAIL: 558 + case Diff::LINT_WARNINGS: $lint_star = 'star-warn'; break; 559 + case Diff::LINT_OKAY: $lint_star = 'star-okay'; break; 560 + } 561 + 562 + $star = "\xE2\x98\x85"; 563 + 564 + $unit_star = 565 + <span class={$unit_star}> 566 + <span class="star">{$star}</span> 567 + </span>; 568 + 569 + $lint_star = 570 + <span class={$lint_star}> 571 + <span class="star">{$star}</span> 572 + </span>; 573 + } else { 574 + $unit_star = null; 575 + $lint_star = null; 576 + } 577 + 578 + $diff_table->appendChild( 579 + <tr class={++$ii % 2 ? 'alt' : null}> 580 + <td class="name">{$row[0]}</td> 581 + <td class="diffid">{$row[1]}</td> 582 + <td class="desc">{$row[5]}</td> 583 + <td class="age">{$row[4] ? ago(time() - $row[4]) : null}</td> 584 + <td class="star">{$lint_star}</td> 585 + <td class="star">{$unit_star}</td> 586 + <td class={"old {$xold}"}>{$lradio}</td> 587 + <td class={"new {$xnew}"}>{$rradio}</td> 588 + </tr>); 589 + } 590 + 591 + Javelin::initBehavior('differential-diff-radios', array( 592 + 'radios' => $old_ids, 593 + )); 594 + 595 + $diff_table->appendChild( 596 + <tr> 597 + <td colspan="8" class="diff-differ-submit"> 598 + <label>Whitespace Changes:</label> 599 + {id(<select name="whitespace" />)->setOptions( 600 + array( 601 + 'ignore-all' => 'Ignore All', 602 + 'ignore-trailing' => 'Ignore Trailing', 603 + 'show-all' => 'Show All', 604 + ), $request->getStr('whitespace'))}{' '} 605 + <button type="submit">Show Diff</button> 606 + </td> 607 + </tr>); 608 + 609 + $diff_table = 610 + <div class="differential-table-of-contents"> 611 + <h1>Revision Update History</h1> 612 + <form action={URI::getRequestURI()} method="get"> 613 + {$diff_table} 614 + </form> 615 + </div>; 616 + 617 + 618 + $load_ids = array_filter(array($old, $diff->getID())); 619 + 620 + $viewer_id = $this->getRequest()->getViewerContext()->getUserID(); 621 + 622 + $raw_objects = queryfx_all( 623 + smc_get_db('cdb.differential', 'r'), 624 + 'SELECT * FROM changeset WHERE changeset.diffID IN (%Ld)', 625 + $load_ids); 626 + 627 + $raw_objects = array_group($raw_objects, 'diffID'); 628 + $objects = $raw_objects[$diff->getID()]; 629 + 630 + if (!$objects) { 631 + $changesets = array(); 632 + } else { 633 + $changesets = id(new DifferentialChangeset())->loadAllFromArray($objects); 634 + } 635 + 636 + $against_warn = null; 637 + $against_map = array(); 638 + $visible_changesets = array(); 639 + if ($old) { 640 + $old_diff = $diffs[$old]; 641 + $new_diff = $diff; 642 + $old_path = $old_diff->getSourcePath(); 643 + $new_path = $new_diff->getSourcePath(); 644 + 645 + $old_prefix = null; 646 + $new_prefix = null; 647 + if ((strlen($old_path) < strlen($new_path)) && 648 + (!strncmp($old_path, $new_path, strlen($old_path)))) { 649 + $old_prefix = substr($new_path, strlen($old_path)); 650 + } 651 + if ((strlen($new_path) < strlen($old_path)) && 652 + (!strncmp($old_path, $new_path, strlen($new_path)))) { 653 + $new_prefix = substr($old_path, strlen($new_path)); 654 + } 655 + 656 + $old_changesets = id(new DifferentialChangeset()) 657 + ->loadAllFromArray($raw_objects[$old]); 658 + $old_changesets = array_pull($old_changesets, null, 'getFilename'); 659 + if ($new_prefix) { 660 + $rekeyed_map = array(); 661 + foreach ($old_changesets as $key => $value) { 662 + $rekeyed_map[$new_prefix.$key] = $value; 663 + } 664 + $old_changesets = $rekeyed_map; 665 + } 666 + 667 + foreach ($changesets as $key => $changeset) { 668 + $file = $old_prefix.$changeset->getFilename(); 669 + if (isset($old_changesets[$file])) { 670 + $checksum = $changeset->getChecksum(); 671 + if ($checksum !== null && 672 + $checksum == $old_changesets[$file]->getChecksum()) { 673 + unset($changesets[$key]); 674 + unset($old_changesets[$file]); 675 + } else { 676 + $against_map[$changeset->getID()] = $old_changesets[$file]->getID(); 677 + unset($old_changesets[$file]); 678 + } 679 + } 680 + } 681 + 682 + foreach ($old_changesets as $changeset) { 683 + $changesets[$changeset->getID()] = $changeset; 684 + $against_map[$changeset->getID()] = -1; 685 + } 686 + 687 + $against_warn = 688 + <tools:notice title="NOTE - Diff of Diffs"> 689 + You are viewing a synthetic diff between two previous diffs in this 690 + revision. You can not add new inline comments (for now). 691 + </tools:notice>; 692 + } else { 693 + $visible_changesets = array_pull($changesets, 'getID'); 694 + } 695 + 696 + $changesets = array_psort($changesets, 'getSortKey'); 697 + $all_changesets = $changesets; 698 + 699 + $warning = null; 700 + $limit = 100; 701 + if (count($changesets) > $limit && !$this->getRequest()->getStr('large')) { 702 + $count = number_format(count($changesets)); 703 + $warning = 704 + <tools:notice title="Very Large Diff"> 705 + This diff is extremely large and affects {$count} files. Only the 706 + first {number_format($limit)} files are shown. 707 + <strong> 708 + <a href={$revision->getURI().'?large=true'}>Show All Files</a> 709 + </strong> 710 + </tools:notice>; 711 + $changesets = array_slice($changesets, 0, $limit); 712 + if (!$old) { 713 + $visible_changesets = array_pull($changesets, 'getID'); 714 + } 715 + } 716 + 717 + $detail_view = 718 + <differential:changeset-detail-view 719 + changesets={$changesets} 720 + revision={$revision} 721 + against={$against_map} 722 + edit={empty($against_map)} 723 + whitespace={$request->getStr('whitespace')} />; 724 + 725 + $table_of_contents = 726 + <differential:changeset-table-of-contents 727 + changesets={$all_changesets} />; 728 + 729 + $implied_feedback = array(); 730 + foreach (array( 731 + 'summarize' => $revision->getSummary(), 732 + 'testplan' => $revision->getTestPlan(), 733 + 'annotate' => $revision->getNotes(), 734 + ) as $type => $text) { 735 + if (!strlen($text)) { 736 + continue; 737 + } 738 + $implied_feedback[] = id(new DifferentialFeedback()) 739 + ->setUserID($revision->getOwnerID()) 740 + ->setAction($type) 741 + ->setDateCreated($revision->getDateCreated()) 742 + ->setContent($text); 743 + } 744 + 745 + $feedback = id(new DifferentialFeedback())->loadAllWithRevision($revision); 746 + $feedback = array_merge($implied_feedback, $feedback); 747 + 748 + $inline_comments = $this->loadInlineComments($feedback, $changesets); 749 + 750 + $diff_map = array(); 751 + $diffs = array_psort($diffs, 'getID'); 752 + foreach ($diffs as $diff) { 753 + $diff_map[$diff->getID()] = count($diff_map) + 1; 754 + } 755 + $visible_changesets = array_fill_keys($visible_changesets, true); 756 + $hidden_changesets = array(); 757 + foreach ($changesets as $changeset) { 758 + $id = $changeset->getID(); 759 + if (isset($visible_changesets[$id])) { 760 + continue; 761 + } 762 + $hidden_changesets[$id] = $diff_map[$changeset->getDiffID()]; 763 + } 764 + 765 + $revision->loadRelationships(); 766 + $ccs = $revision->getCCFBIDs(); 767 + $reviewers = $revision->getReviewers(); 768 + 769 + $actors = array_pull($feedback, 'getUserID'); 770 + $actors[] = $revision->getOwnerID(); 771 + 772 + $tasks = array(); 773 + assoc_get_by_type( 774 + $revision->getFBID(), 775 + 22284182462, // TODO: include issue, DIFFCAMP_TASK_ASSOC 776 + $start = null, 777 + $limit = null, 778 + $pending = true, 779 + $tasks); 780 + memcache_dispatch(); 781 + $tasks = array_keys($tasks); 782 + 783 + $preparer = new Preparer(); 784 + $fbids = array_merge_fast( 785 + array($actors, array($viewer_id), $reviewers, $ccs, $tasks), 786 + true); 787 + $handles = array(); 788 + $handle_data = id(new ToolsHandleData($fbids, $handles)) 789 + ->needNames() 790 + ->needAlternateNames() 791 + ->needAlternateIDs() 792 + ->needThumbnails(); 793 + $preparer->waitFor($handle_data); 794 + $preparer->go(); 795 + 796 + $revision->attachTaskHandles(array_select_keys($handles, $tasks)); 797 + 798 + $inline_comments = array_group($inline_comments, 'getFeedbackID'); 799 + 800 + $engine = new RemarkupEngine(); 801 + $engine->enableFeature(RemarkupEngine::FEATURE_GUESS_IMAGES); 802 + $engine->enableFeature(RemarkupEngine::FEATURE_YOUTUBE); 803 + $engine->setCurrentSandcastle($this->getSandcastleURI($target_diff)); 804 + $feed = array(); 805 + foreach ($feedback as $comment) { 806 + $inlines = null; 807 + if (isset($inline_comments[$comment->getID()])) { 808 + $inlines = $inline_comments[$comment->getID()]; 809 + } 810 + $feed[] = 811 + <differential:feedback 812 + feedback={$comment} 813 + handle={$handles[$comment->getUserID()]} 814 + engine={$engine} 815 + inline={$inlines} 816 + changesets={$changesets} 817 + hidden={$hidden_changesets} />; 818 + } 819 + 820 + $feed = $this->renderFeedbackList($feed, $feedback, $viewer_id); 821 + 822 + $fields = $this->getDetailFields($revision, $diff, $handles); 823 + $table = <table class="differential-revision-properties" />; 824 + foreach ($fields as $key => $value) { 825 + $table->appendChild( 826 + <tr> 827 + <th>{$key}:</th><td>{$value}</td> 828 + </tr>); 829 + } 830 + 831 + $quick_links = $this->getQuickLinks($revision); 832 + 833 + $edit_link = null; 834 + if ($revision->getOwnerID() == $viewer_id) { 835 + $edit_link = '/differential/revision/edit/'.$revision->getID().'/'; 836 + $edit_link = 837 + <x:frag> 838 + {' '}(<a href={$edit_link}>Edit Revision</a>) 839 + </x:frag>; 840 + } 841 + 842 + $info = 843 + <div class="differential-revision-information"> 844 + <div class="differential-revision-actions"> 845 + {$quick_links} 846 + </div> 847 + <div class="differential-revision-detail"> 848 + <h1>{$revision->getName()}{$edit_link}</h1> 849 + {$table} 850 + </div> 851 + </div>; 852 + 853 + $actions = $this->getRevisionActions($revision); 854 + $revision_id = $revision->getID(); 855 + 856 + Javelin::initBehavior( 857 + 'differential-feedback-preview', 858 + array( 859 + 'uri' => '/differential/preview/'.$revision->getFBID().'/', 860 + 'preview' => 'overall-feedback-preview', 861 + 'action' => 'feedback-action', 862 + 'content' => 'feedback-content', 863 + )); 864 + 865 + Javelin::initBehavior( 866 + 'differential-inline-comment-preview', 867 + array( 868 + 'uri' => '/differential/inline-preview/'.$revision_id.'/'.$new.'/', 869 + 'preview' => 'inline-comment-preview', 870 + )); 871 + 872 + $content = SavedCopy::loadData( 873 + $viewer_id, 874 + SavedCopy::Type_DifferentialRevisionFeedback, 875 + $revision->getFBID()); 876 + 877 + 878 + $inline_comment_container = 879 + <div id="inline-comment-preview"><p>Loading...</p></div>; 880 + 881 + $feedback = id(new DifferentialFeedback()) 882 + ->setAction('none') 883 + ->setUserID($viewer_id) 884 + ->setContent($content); 885 + 886 + $preview = 887 + <div class="differential-feedback differential-feedback-preview"> 888 + <div id="overall-feedback-preview"> 889 + <differential:feedback 890 + feedback={$feedback} 891 + engine={$engine} 892 + preview={true} 893 + handle={$handles[$viewer_id]} /> 894 + </div> 895 + {$inline_comment_container} 896 + </div>; 897 + 898 + $syntax_link = 899 + <a href={'http://www.intern.facebook.com/intern/wiki/index.php' . 900 + '/Articles/Remarkup_Syntax_Reference'} 901 + target="_blank" 902 + tabindex="4">Remarkup Reference</a>; 903 + 904 + Javelin::initBehavior( 905 + 'differential-add-reviewers', 906 + array( 907 + 'src' => redirect_str('/datasource/employee/', 'tools'), 908 + 'tokenizer' => 'reviewer-tokenizer', 909 + 'select' => 'feedback-action', 910 + 'row' => 'reviewer-tokenizer-row', 911 + )); 912 + 913 + $feedback_form = 914 + <x:frag> 915 + <div class="differential-feedback-form"> 916 + <tools:form 917 + method="post" 918 + action={"/differential/revision/feedback/{$revision_id}/"}> 919 + <h1>Provide Feedback</h1> 920 + <tools:fieldset> 921 + <tools:control type="select" label="Action"> 922 + {id(<select name="action" id="feedback-action" 923 + tabindex="1" />) 924 + ->setOptions($actions)} 925 + </tools:control> 926 + <tools:control type="text" label="Reviewers" 927 + style="display: none;" 928 + id="reviewer-tokenizer-row"> 929 + <javelin:tokenizer-template 930 + id="reviewer-tokenizer" 931 + name="reviewers" /> 932 + </tools:control> 933 + <tools:control type="textarea" label="Feedback" 934 + caption={$syntax_link}> 935 + <tools:droppable-textarea id="feedback-content" name="feedback" 936 + tabindex="2"> 937 + {$content} 938 + </tools:droppable-textarea> 939 + </tools:control> 940 + <tools:control type="submit"> 941 + <button type="submit" 942 + tabindex="3">Clowncopterize</button> 943 + </tools:control> 944 + </tools:fieldset> 945 + </tools:form> 946 + </div> 947 + {$preview} 948 + </x:frag>; 949 + 950 + $notice = null; 951 + if ($this->getRequest()->getBool('diff_changed')) { 952 + $notice = 953 + <tools:notice title="Revision Updated Recently"> 954 + This revision was updated with a <strong>new diff</strong> while you 955 + were providing feedback. Your inline comments appear on the 956 + <strong>old diff</strong>. 957 + </tools:notice>; 958 + } 959 + 960 + return 961 + <differential:standard-page title={$revision->getName()}> 962 + <div class="differential-primary-pane"> 963 + {$warning} 964 + {$notice} 965 + {$info} 966 + <div class="differential-feedback"> 967 + {$feed} 968 + </div> 969 + {$diff_table} 970 + {$table_of_contents} 971 + {$against_warn} 972 + {$detail_view} 973 + {$feedback_form} 974 + </div> 975 + </differential:standard-page>; 976 + } 977 + 978 + protected function getQuickLinks(DifferentialRevision $revision) { 979 + 980 + $viewer_id = $this->getRequest()->getViewerContext()->getUserID(); 981 + $viewer_is_owner = ($viewer_id == $revision->getOwnerID()); 982 + $viewer_is_reviewer = 983 + ((array_search($viewer_id, $revision->getReviewers())) !== false); 984 + $viewer_is_cc = 985 + ((array_search($viewer_id, $revision->getCCFBIDs())) !== false); 986 + $status = $revision->getStatus(); 987 + 988 + $links = array(); 989 + 990 + if (!$viewer_is_owner && !$viewer_is_reviewer) { 991 + $action = $viewer_is_cc 992 + ? 'rem' 993 + : 'add'; 994 + $revision_id = $revision->getID(); 995 + $href = "/differential/subscribe/{$action}/{$revision_id}"; 996 + $links[] = array( 997 + $viewer_is_cc ? 'subscribe-disabled' : 'subscribe-enabled', 998 + <a href={$href}>{$viewer_is_cc ? 'Unsubscribe' : 'Subscribe'}</a>, 999 + ); 1000 + } else { 1001 + $links[] = array( 1002 + 'subscribe-disabled unavailable', 1003 + <a>Automatically Subscribed</a>, 1004 + ); 1005 + } 1006 + 1007 + $blast_uri = RedirectURI( 1008 + '/intern/differential/?action=blast&fbid='.$revision->getFBID()) 1009 + ->setTier('intern'); 1010 + $links[] = array( 1011 + 'blast', 1012 + <a href={$blast_uri}>Blast Revision</a>, 1013 + ); 1014 + 1015 + $blast_uri = RedirectURI( 1016 + '/intern/differential/?action=tasks&fbid='.$revision->getFBID()) 1017 + ->setTier('intern'); 1018 + $links[] = array( 1019 + 'tasks', 1020 + <a href={$blast_uri}>Edit Tasks</a>, 1021 + ); 1022 + 1023 + if ($viewer_is_owner && false) { 1024 + $perflab_uri = RedirectURI( 1025 + '/intern/differential/?action=perflab&fbid='.$revision->getFBID()) 1026 + ->setTier('intern'); 1027 + $links[] = array( 1028 + 'perflab', 1029 + <a href={$perflab_uri}>Run in Perflab</a>, 1030 + ); 1031 + } 1032 + 1033 + $engineering_repository_id = RepositoryRef::getByCallsign('E')->getID(); 1034 + $svn_revision = $revision->getSVNRevision(); 1035 + if ($status == DifferentialConstants::COMMITTED && 1036 + $svn_revision && 1037 + $revision->getRepositoryID() == $engineering_repository_id) { 1038 + $href = '/intern/push/request.php?rev='.$svn_revision; 1039 + $href = RedirectURI($href)->setTier('intern'); 1040 + $links[] = array( 1041 + 'merge', 1042 + <a href={$href} id="ask_for_merge_link">Ask for Merge</a>, 1043 + ); 1044 + } 1045 + 1046 + $links[] = array( 1047 + 'herald-transcript', 1048 + <a href={"/herald/transcript/?fbid=".$revision->getFBID()} 1049 + >Herald Transcripts</a>, 1050 + ); 1051 + $links[] = array( 1052 + 'metamta-transcript', 1053 + <a href={"/mail/?view=all&fbid=".$revision->getFBID()} 1054 + >MetaMTA Transcripts</a>, 1055 + ); 1056 + 1057 + 1058 + $list = <ul class="differential-actions" />; 1059 + foreach ($links as $link) { 1060 + list($class, $tag) = $link; 1061 + $list->appendChild(<li class={$class}>{$tag}</li>); 1062 + } 1063 + 1064 + return $list; 1065 + } 1066 + 1067 + protected function getDetailFields( 1068 + DifferentialRevision $revision, 1069 + Diff $diff, 1070 + array $handles) { 1071 + 1072 + $fields = array(); 1073 + $fields['Revision Status'] = $this->getRevisionStatusDisplay($revision); 1074 + 1075 + $author = $revision->getOwnerID(); 1076 + $fields['Author'] = <tools:handle handle={$handles[$author]} 1077 + link={true} />; 1078 + 1079 + $sandcastle = $this->getSandcastleURI($diff); 1080 + if ($sandcastle) { 1081 + $fields['Sandcastle'] = <a href={$sandcastle}>{$sandcastle}</a>; 1082 + } 1083 + 1084 + $path = $diff->getSourcePath(); 1085 + if ($path) { 1086 + $host = $diff->getSourceMachine(); 1087 + $branch = $diff->getGitBranch() ? ' (' . $diff->getGitBranch() . ')' : ''; 1088 + 1089 + if ($host) { 1090 + $user = $handles[$this->getRequest()->getViewerContext()->getUserID()] 1091 + ->getName(); 1092 + $fields['Path'] = 1093 + <x:frag> 1094 + <a href={"ssh://{$user}@{$host}"}>{$host}</a>:{$path}{$branch} 1095 + </x:frag>; 1096 + } else { 1097 + $fields['Path'] = $path; 1098 + } 1099 + } 1100 + 1101 + $reviewer_links = array(); 1102 + foreach ($revision->getReviewers() as $reviewer) { 1103 + $reviewer_links[] = <tools:handle handle={$handles[$reviewer]} 1104 + link={true} />; 1105 + } 1106 + if ($reviewer_links) { 1107 + $fields['Reviewers'] = array_implode(', ', $reviewer_links); 1108 + } else { 1109 + $fields['Reviewers'] = <em>None</em>; 1110 + } 1111 + 1112 + $ccs = $revision->getCCFBIDs(); 1113 + if ($ccs) { 1114 + $links = array(); 1115 + foreach ($ccs as $cc) { 1116 + $links[] = <tools:handle handle={$handles[$cc]} 1117 + link={true} />; 1118 + } 1119 + $fields['CCs'] = array_implode(', ', $links); 1120 + } 1121 + 1122 + $blame_rev = $revision->getSvnBlameRevision(); 1123 + if ($blame_rev) { 1124 + if ($revision->getRepositoryRef() && is_numeric($blame_rev)) { 1125 + $ref = new RevisionRef($revision->getRepositoryRef(), $blame_rev); 1126 + $fields['Blame Revision'] = 1127 + <a href={URI($ref->getDetailURL())}> 1128 + {$ref->getName()} 1129 + </a>; 1130 + } else { 1131 + $fields['Blame Revision'] = $blame_rev; 1132 + } 1133 + } 1134 + 1135 + $tasks = $revision->getTaskHandles(); 1136 + 1137 + if ($tasks) { 1138 + $links = array(); 1139 + foreach ($tasks as $task) { 1140 + $links[] = <tools:handle handle={$task} link={true} />; 1141 + } 1142 + $fields['Tasks'] = array_implode(<br />, $links); 1143 + } 1144 + 1145 + $bugzilla_id = $revision->getBugzillaID(); 1146 + if ($bugzilla_id) { 1147 + $href = 'http://bugs.developers.facebook.com/show_bug.cgi?id='. 1148 + $bugzilla_id; 1149 + $fields['Bugzilla'] = <a href={$href}>{'#'.$bugzilla_id}</a>; 1150 + } 1151 + 1152 + $fields['Apply Patch'] = <tt>arc patch --revision {$revision->getID()}</tt>; 1153 + 1154 + if ($diff->getParentRevisionID()) { 1155 + $parent = id(new DifferentialRevision())->load( 1156 + $diff->getParentRevisionID()); 1157 + if ($parent) { 1158 + $fields['Depends On'] = 1159 + <a href={$parent->getURI()}> 1160 + D{$parent->getID()}: {$parent->getName()} 1161 + </a>; 1162 + } 1163 + } 1164 + 1165 + $star = <span class="star">{"\xE2\x98\x85"}</span>; 1166 + 1167 + Javelin::initBehavior('differential-star-more'); 1168 + 1169 + switch ($diff->getLinted()) { 1170 + case Diff::LINT_FAIL: 1171 + $more = $this->renderDiffPropertyMoreLink($diff, 'lint'); 1172 + $fields['Lint'] = 1173 + <x:frag> 1174 + <span class="star-warn">{$star} Lint Failures</span> 1175 + {$more} 1176 + </x:frag>; 1177 + break; 1178 + case Diff::LINT_WARNINGS: 1179 + $more = $this->renderDiffPropertyMoreLink($diff, 'lint'); 1180 + $fields['Lint'] = 1181 + <x:frag> 1182 + <span class="star-warn">{$star} Lint Warnings</span> 1183 + {$more} 1184 + </x:frag>; 1185 + break; 1186 + case Diff::LINT_OKAY: 1187 + $fields['Lint'] = 1188 + <span class="star-okay">{$star} Lint Free</span>; 1189 + break; 1190 + default: 1191 + case Diff::LINT_NO: 1192 + $fields['Lint'] = 1193 + <span class="star-none">{$star} Not Linted</span>; 1194 + break; 1195 + } 1196 + 1197 + $unit_details = false; 1198 + switch ($diff->getUnitTested()) { 1199 + case Diff::UNIT_FAIL: 1200 + $fields['Unit Tests'] = 1201 + <span class="star-warn">{$star} Unit Test Failures</span>; 1202 + $unit_details = true; 1203 + break; 1204 + case Diff::UNIT_WARN: 1205 + $fields['Unit Tests'] = 1206 + <span class="star-warn">{$star} Unit Test Warnings</span>; 1207 + $unit_details = true; 1208 + break; 1209 + case Diff::UNIT_OKAY: 1210 + $fields['Unit Tests'] = 1211 + <span class="star-okay">{$star} Unit Tests Passed</span>; 1212 + $unit_details = true; 1213 + break; 1214 + case Diff::UNIT_NO_TESTS: 1215 + $fields['Unit Tests'] = 1216 + <span class="star-none">{$star} No Test Coverage</span>; 1217 + break; 1218 + case Diff::UNIT_NO: 1219 + default: 1220 + $fields['Unit Tests'] = 1221 + <span class="star-none">{$star} Not Unit Tested</span>; 1222 + break; 1223 + } 1224 + 1225 + if ($unit_details) { 1226 + $fields['Unit Tests'] = 1227 + <x:frag> 1228 + {$fields['Unit Tests']} 1229 + {$this->renderDiffPropertyMoreLink($diff, 'unit')} 1230 + </x:frag>; 1231 + } 1232 + 1233 + $platform_impact = $revision->getPlatformImpact(); 1234 + if ($platform_impact) { 1235 + $fields['Platform Impact'] = 1236 + <text linebreaks="true">{$platform_impact}</text>; 1237 + } 1238 + 1239 + return $fields; 1240 + } 1241 + 1242 + protected function renderDiffPropertyMoreLink(Diff $diff, $name) { 1243 + $target = <div class="star-more" 1244 + style="display: none;"> 1245 + <div class="star-loading">Loading...</div> 1246 + </div>; 1247 + $meta = array( 1248 + 'target' => $target->requireUniqueID(), 1249 + 'uri' => '/differential/diffprop/'.$diff->getID().'/'.$name.'/', 1250 + ); 1251 + $more = 1252 + <span sigil="star-link-container"> 1253 + &middot; 1254 + <a mustcapture="true" 1255 + sigil="star-more" 1256 + href="#" 1257 + meta={$meta}>Show Details</a> 1258 + </span>; 1259 + return <x:frag>{$more}{$target}</x:frag>; 1260 + } 1261 + 1262 + 1263 + 1264 + protected function loadInlineComments(array $feedback, array &$changesets) { 1265 + 1266 + $inline_comments = array(); 1267 + $feedback_ids = array_filter(array_pull($feedback, 'getID')); 1268 + if (!$feedback_ids) { 1269 + return $inline_comments; 1270 + } 1271 + 1272 + $inline_comments = id(new DifferentialInlineComment()) 1273 + ->loadAllWhere('feedbackID in (%Ld)', $feedback_ids); 1274 + 1275 + $load_changesets = array(); 1276 + $load_hunks = array(); 1277 + foreach ($inline_comments as $inline) { 1278 + $changeset_id = $inline->getChangesetID(); 1279 + if (isset($changesets[$changeset_id])) { 1280 + continue; 1281 + } 1282 + $load_changesets[$changeset_id] = true; 1283 + } 1284 + 1285 + $more_changesets = array(); 1286 + if ($load_changesets) { 1287 + $changeset_ids = array_keys($load_changesets); 1288 + $more_changesets += id(new DifferentialChangeset()) 1289 + ->loadAllWithIDs($changeset_ids); 1290 + } 1291 + 1292 + if ($more_changesets) { 1293 + $changesets += $more_changesets; 1294 + $changesets = array_psort($changesets, 'getSortKey'); 1295 + } 1296 + 1297 + return $inline_comments; 1298 + } 1299 + 1300 + protected function getRevisionActions(DifferentialRevision $revision) { 1301 + $actions = array( 1302 + 'none' => true, 1303 + ); 1304 + 1305 + $viewer = $this->getRequest()->getViewerContext(); 1306 + 1307 + $viewer_is_owner = ($viewer->getUserID() == $revision->getOwnerID()); 1308 + if ($viewer_is_owner) { 1309 + switch ($revision->getStatus()) { 1310 + case DifferentialConstants::NEEDS_REVIEW: 1311 + $actions['abandon'] = true; 1312 + break; 1313 + case DifferentialConstants::NEEDS_REVISION: 1314 + $actions['abandon'] = true; 1315 + $actions['request_review'] = true; 1316 + break; 1317 + case DifferentialConstants::ACCEPTED: 1318 + $actions['abandon'] = true; 1319 + $actions['request_review'] = true; 1320 + break; 1321 + case DifferentialConstants::COMMITTED: 1322 + break; 1323 + case DifferentialConstants::ABANDONED: 1324 + $actions['reclaim'] = true; 1325 + break; 1326 + default: 1327 + throw new Exception('Unknown DifferentialRevision status.'); 1328 + } 1329 + } else { 1330 + switch ($revision->getStatus()) { 1331 + case DifferentialConstants::NEEDS_REVIEW: 1332 + $actions['accept'] = true; 1333 + $actions['reject'] = true; 1334 + break; 1335 + case DifferentialConstants::NEEDS_REVISION: 1336 + $actions['accept'] = true; 1337 + break; 1338 + case DifferentialConstants::ACCEPTED: 1339 + $actions['reject'] = true; 1340 + break; 1341 + case DifferentialConstants::COMMITTED: 1342 + break; 1343 + case DifferentialConstants::ABANDONED: 1344 + break; 1345 + default: 1346 + throw new Exception('Unknown DifferentialRevision status.'); 1347 + } 1348 + 1349 + if (in_array($viewer->getUserID(), $revision->getReviewers())) { 1350 + $actions['resign'] = true; 1351 + } 1352 + } 1353 + 1354 + // Put add reviewers at the bottom since it's rare relative to other 1355 + // actions, notably accept and reject 1356 + $actions['add_reviewers'] = true; 1357 + 1358 + static $action_names = array( 1359 + 'none' => 'Comment', 1360 + 'abandon' => 'Abandon Revision', 1361 + 'request_review' => 'Request Review', 1362 + 'reclaim' => 'Reclaim Revision', 1363 + 'accept' => "Accept Revision \xE2\x9C\x94", 1364 + 'reject' => "Request Changes \xE2\x9C\x98", 1365 + 'resign' => "Resign as Reviewer", 1366 + 'add_reviewers' => "Add Reviewers", 1367 + ); 1368 + 1369 + foreach ($actions as $key => $value) { 1370 + $actions[$key] = $action_names[$key]; 1371 + } 1372 + 1373 + return $actions; 1374 + } 1375 + 1376 + protected function getRevisionStatusDisplay(DifferentialRevision $revision) { 1377 + $viewer_id = $this->getRequest()->getViewerContext()->getUserID(); 1378 + $viewer_is_owner = ($viewer_id == $revision->getOwnerID()); 1379 + $status = $revision->getStatus(); 1380 + 1381 + $more = null; 1382 + switch ($status) { 1383 + case DifferentialConstants::NEEDS_REVIEW: 1384 + $message = 'Pending Review'; 1385 + break; 1386 + case DifferentialConstants::NEEDS_REVISION: 1387 + $message = 'Awaiting Revision'; 1388 + if ($viewer_is_owner) { 1389 + $more = 'Make the requested changes and update the revision.'; 1390 + } 1391 + break; 1392 + case DifferentialConstants::ACCEPTED: 1393 + $message = 'Ready for Commit'; 1394 + if ($viewer_is_owner) { 1395 + $more = 1396 + <x:frag> 1397 + Run <tt>arc commit</tt> (svn) or <tt>arc amend</tt> (git) to 1398 + proceed. 1399 + </x:frag>; 1400 + } 1401 + break; 1402 + case DifferentialConstants::COMMITTED: 1403 + $message = 'Committed'; 1404 + $ref = $revision->getRevisionRef(); 1405 + $more = $ref 1406 + ? (<a href={URI($ref->getDetailURL())}> 1407 + {$ref->getName()} 1408 + </a>) 1409 + : null; 1410 + 1411 + $engineering_repository_id = RepositoryRef::getByCallsign('E')->getID(); 1412 + if ($revision->getSVNRevision() && 1413 + $revision->getRepositoryID() == $engineering_repository_id) { 1414 + Javelin::initBehavior( 1415 + 'differential-revtracker-status', 1416 + array( 1417 + 'uri' => '/differential/revtracker/'.$revision->getID().'/', 1418 + 'statusId' => 'revtracker_status', 1419 + 'mergeLinkId' => 'ask_for_merge_link', 1420 + )); 1421 + } 1422 + break; 1423 + case DifferentialConstants::ABANDONED: 1424 + $message = 'Abandoned'; 1425 + break; 1426 + default: 1427 + throw new Exception("Unknown revision status."); 1428 + } 1429 + 1430 + if ($more) { 1431 + $message = 1432 + <x:frag> 1433 + <strong id="revtracker_status">{$message}</strong> 1434 + &middot; {$more} 1435 + </x:frag>; 1436 + } else { 1437 + $message = <strong id="revtracker_status">{$message}</strong>; 1438 + } 1439 + 1440 + return $message; 1441 + } 1442 + 1443 + protected function renderFeedbackList(array $xhp, array $obj, $viewer_id) { 1444 + 1445 + // Use magical heuristics to try to hide older comments. 1446 + 1447 + $obj = array_reverse($obj); 1448 + $obj = array_values($obj); 1449 + $xhp = array_reverse($xhp); 1450 + $xhp = array_values($xhp); 1451 + 1452 + $last_comment = null; 1453 + foreach ($obj as $position => $feedback) { 1454 + if ($feedback->getUserID() == $viewer_id) { 1455 + if ($last_comment === null) { 1456 + $last_comment = $position; 1457 + } else if ($last_comment == $position - 1) { 1458 + // If you made consecuitive comments, show them all. This is a spaz 1459 + // rule for epriestley comments. 1460 + $last_comment = $position; 1461 + } 1462 + } 1463 + } 1464 + 1465 + $header = array(); 1466 + 1467 + $hide = array(); 1468 + if ($last_comment !== null) { 1469 + foreach ($obj as $position => $feedback) { 1470 + $action = $feedback->getAction(); 1471 + if ($action == 'testplan' || $action == 'summarize') { 1472 + // Always show summary and test plan. 1473 + $header[] = $xhp[$position]; 1474 + unset($xhp[$position]); 1475 + continue; 1476 + } 1477 + 1478 + if ($position <= $last_comment) { 1479 + // Always show comments after your last comment. 1480 + continue; 1481 + } 1482 + 1483 + if ($position < 3) { 1484 + // Always show the most recent 3 comments. 1485 + continue; 1486 + } 1487 + 1488 + // Hide everything else. 1489 + $hide[] = $position; 1490 + } 1491 + } 1492 + 1493 + if (count($hide) <= 3) { 1494 + // Don't hide if there's not much to hide. 1495 + $hide = array(); 1496 + } 1497 + 1498 + $header = array_reverse($header); 1499 + 1500 + $hidden = array_select_keys($xhp, $hide); 1501 + $visible = array_diff_key($xhp, $hidden); 1502 + 1503 + $visible = array_reverse($visible); 1504 + $hidden = array_reverse($hidden); 1505 + 1506 + if ($hidden) { 1507 + Javelin::initBehavior( 1508 + 'differential-show-all-feedback', 1509 + array( 1510 + 'markup' => id(<x:frag>{$hidden}</x:frag>)->toString(), 1511 + )); 1512 + $hidden = 1513 + <div sigil="all-feedback-container"> 1514 + <div class="older-replies-are-hidden"> 1515 + {number_format(count($hidden))} older replies are hidden. 1516 + <a href="#" sigil="show-all-feedback" 1517 + mustcapture="true">Show all feedback.</a> 1518 + </div> 1519 + </div>; 1520 + } else { 1521 + $hidden = null; 1522 + } 1523 + 1524 + return 1525 + <x:frag> 1526 + {$header} 1527 + {$hidden} 1528 + {$visible} 1529 + </x:frag>; 1530 + } 1531 + 1532 + } 1533 + protected function getDetailFields( 1534 + DifferentialRevision $revision, 1535 + Diff $diff, 1536 + array $handles) { 1537 + 1538 + $fields = array(); 1539 + $fields['Revision Status'] = $this->getRevisionStatusDisplay($revision); 1540 + 1541 + $author = $revision->getOwnerID(); 1542 + $fields['Author'] = <tools:handle handle={$handles[$author]} 1543 + link={true} />; 1544 + 1545 + $sandcastle = $this->getSandcastleURI($diff); 1546 + if ($sandcastle) { 1547 + $fields['Sandcastle'] = <a href={$sandcastle}>{$sandcastle}</a>; 1548 + } 1549 + 1550 + $path = $diff->getSourcePath(); 1551 + if ($path) { 1552 + $host = $diff->getSourceMachine(); 1553 + $branch = $diff->getGitBranch() ? ' (' . $diff->getGitBranch() . ')' : ''; 1554 + 1555 + if ($host) { 1556 + $user = $handles[$this->getRequest()->getViewerContext()->getUserID()] 1557 + ->getName(); 1558 + $fields['Path'] = 1559 + <x:frag> 1560 + <a href={"ssh://{$user}@{$host}"}>{$host}</a>:{$path}{$branch} 1561 + </x:frag>; 1562 + } else { 1563 + $fields['Path'] = $path; 1564 + } 1565 + } 1566 + 1567 + $reviewer_links = array(); 1568 + foreach ($revision->getReviewers() as $reviewer) { 1569 + $reviewer_links[] = <tools:handle handle={$handles[$reviewer]} 1570 + link={true} />; 1571 + } 1572 + if ($reviewer_links) { 1573 + $fields['Reviewers'] = array_implode(', ', $reviewer_links); 1574 + } else { 1575 + $fields['Reviewers'] = <em>None</em>; 1576 + } 1577 + 1578 + $ccs = $revision->getCCFBIDs(); 1579 + if ($ccs) { 1580 + $links = array(); 1581 + foreach ($ccs as $cc) { 1582 + $links[] = <tools:handle handle={$handles[$cc]} 1583 + link={true} />; 1584 + } 1585 + $fields['CCs'] = array_implode(', ', $links); 1586 + } 1587 + 1588 + $blame_rev = $revision->getSvnBlameRevision(); 1589 + if ($blame_rev) { 1590 + if ($revision->getRepositoryRef() && is_numeric($blame_rev)) { 1591 + $ref = new RevisionRef($revision->getRepositoryRef(), $blame_rev); 1592 + $fields['Blame Revision'] = 1593 + <a href={URI($ref->getDetailURL())}> 1594 + {$ref->getName()} 1595 + </a>; 1596 + } else { 1597 + $fields['Blame Revision'] = $blame_rev; 1598 + } 1599 + } 1600 + 1601 + $tasks = $revision->getTaskHandles(); 1602 + 1603 + if ($tasks) { 1604 + $links = array(); 1605 + foreach ($tasks as $task) { 1606 + $links[] = <tools:handle handle={$task} link={true} />; 1607 + } 1608 + $fields['Tasks'] = array_implode(<br />, $links); 1609 + } 1610 + 1611 + $bugzilla_id = $revision->getBugzillaID(); 1612 + if ($bugzilla_id) { 1613 + $href = 'http://bugs.developers.facebook.com/show_bug.cgi?id='. 1614 + $bugzilla_id; 1615 + $fields['Bugzilla'] = <a href={$href}>{'#'.$bugzilla_id}</a>; 1616 + } 1617 + 1618 + $fields['Apply Patch'] = <tt>arc patch --revision {$revision->getID()}</tt>; 1619 + 1620 + if ($diff->getParentRevisionID()) { 1621 + $parent = id(new DifferentialRevision())->load( 1622 + $diff->getParentRevisionID()); 1623 + if ($parent) { 1624 + $fields['Depends On'] = 1625 + <a href={$parent->getURI()}> 1626 + D{$parent->getID()}: {$parent->getName()} 1627 + </a>; 1628 + } 1629 + } 1630 + 1631 + $star = <span class="star">{"\xE2\x98\x85"}</span>; 1632 + 1633 + Javelin::initBehavior('differential-star-more'); 1634 + 1635 + switch ($diff->getLinted()) { 1636 + case Diff::LINT_FAIL: 1637 + $more = $this->renderDiffPropertyMoreLink($diff, 'lint'); 1638 + $fields['Lint'] = 1639 + <x:frag> 1640 + <span class="star-warn">{$star} Lint Failures</span> 1641 + {$more} 1642 + </x:frag>; 1643 + break; 1644 + case Diff::LINT_WARNINGS: 1645 + $more = $this->renderDiffPropertyMoreLink($diff, 'lint'); 1646 + $fields['Lint'] = 1647 + <x:frag> 1648 + <span class="star-warn">{$star} Lint Warnings</span> 1649 + {$more} 1650 + </x:frag>; 1651 + break; 1652 + case Diff::LINT_OKAY: 1653 + $fields['Lint'] = 1654 + <span class="star-okay">{$star} Lint Free</span>; 1655 + break; 1656 + default: 1657 + case Diff::LINT_NO: 1658 + $fields['Lint'] = 1659 + <span class="star-none">{$star} Not Linted</span>; 1660 + break; 1661 + } 1662 + 1663 + $unit_details = false; 1664 + switch ($diff->getUnitTested()) { 1665 + case Diff::UNIT_FAIL: 1666 + $fields['Unit Tests'] = 1667 + <span class="star-warn">{$star} Unit Test Failures</span>; 1668 + $unit_details = true; 1669 + break; 1670 + case Diff::UNIT_WARN: 1671 + $fields['Unit Tests'] = 1672 + <span class="star-warn">{$star} Unit Test Warnings</span>; 1673 + $unit_details = true; 1674 + break; 1675 + case Diff::UNIT_OKAY: 1676 + $fields['Unit Tests'] = 1677 + <span class="star-okay">{$star} Unit Tests Passed</span>; 1678 + $unit_details = true; 1679 + break; 1680 + case Diff::UNIT_NO_TESTS: 1681 + $fields['Unit Tests'] = 1682 + <span class="star-none">{$star} No Test Coverage</span>; 1683 + break; 1684 + case Diff::UNIT_NO: 1685 + default: 1686 + $fields['Unit Tests'] = 1687 + <span class="star-none">{$star} Not Unit Tested</span>; 1688 + break; 1689 + } 1690 + 1691 + if ($unit_details) { 1692 + $fields['Unit Tests'] = 1693 + <x:frag> 1694 + {$fields['Unit Tests']} 1695 + {$this->renderDiffPropertyMoreLink($diff, 'unit')} 1696 + </x:frag>; 1697 + } 1698 + 1699 + $platform_impact = $revision->getPlatformImpact(); 1700 + if ($platform_impact) { 1701 + $fields['Platform Impact'] = 1702 + <text linebreaks="true">{$platform_impact}</text>; 1703 + } 1704 + 1705 + return $fields; 1706 + } 1707 + 1708 + 1709 + */
+23
src/applications/differential/controller/revisionview/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'aphront/response/404'); 10 + phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus'); 11 + phutil_require_module('phabricator', 'applications/differential/controller/base'); 12 + phutil_require_module('phabricator', 'applications/differential/storage/revision'); 13 + phutil_require_module('phabricator', 'applications/differential/view/changesetlistview'); 14 + phutil_require_module('phabricator', 'applications/differential/view/difftableofcontents'); 15 + phutil_require_module('phabricator', 'applications/differential/view/revisiondetail'); 16 + phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory'); 17 + phutil_require_module('phabricator', 'applications/phid/handle/data'); 18 + 19 + phutil_require_module('phutil', 'markup'); 20 + phutil_require_module('phutil', 'utils'); 21 + 22 + 23 + phutil_require_source('DifferentialRevisionViewController.php');
+9
src/applications/differential/storage/revision/DifferentialRevision.php
··· 51 51 return PhabricatorPHID::generateNewPHID('DREV'); 52 52 } 53 53 54 + public function loadDiffs() { 55 + if (!$this->getID()) { 56 + return array(); 57 + } 58 + return id(new DifferentialDiff())->loadAllWhere( 59 + 'revisionID = %d', 60 + $this->getID()); 61 + } 62 + 54 63 public function loadRelationships() { 55 64 if (!$this->getID()) { 56 65 $this->relationships = array();
+1
src/applications/differential/storage/revision/__init__.php
··· 7 7 8 8 9 9 phutil_require_module('phabricator', 'applications/differential/storage/base'); 10 + phutil_require_module('phabricator', 'applications/differential/storage/diff'); 10 11 phutil_require_module('phabricator', 'applications/phid/storage/phid'); 11 12 phutil_require_module('phabricator', 'storage/qsprintf'); 12 13 phutil_require_module('phabricator', 'storage/queryfx');
+3 -1
src/applications/differential/view/difftableofcontents/DifferentialDiffTableOfContentsView.php
··· 26 26 } 27 27 28 28 public function render() { 29 + 30 + require_celerity_resource('differential-core-view-css'); 29 31 require_celerity_resource('differential-table-of-contents-css'); 30 32 31 33 $rows = array(); ··· 112 114 } 113 115 114 116 return 115 - '<div class="differential-toc">'. 117 + '<div class="differential-toc differential-panel">'. 116 118 '<h1>Table of Contents</h1>'. 117 119 '<table>'. 118 120 implode("\n", $rows).
+90
src/applications/differential/view/revisiondetail/DifferentialRevisionDetailView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2011 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class DifferentialRevisionDetailView extends AphrontView { 20 + 21 + private $revision; 22 + private $properties; 23 + private $actions; 24 + 25 + public function setRevision($revision) { 26 + $this->revision = $revision; 27 + return $this; 28 + } 29 + 30 + public function setProperties(array $properties) { 31 + $this->properties = $properties; 32 + return $this; 33 + } 34 + 35 + public function setActions(array $actions) { 36 + $this->actions = $actions; 37 + return $this; 38 + } 39 + 40 + public function render() { 41 + 42 + require_celerity_resource('differential-core-view-css'); 43 + require_celerity_resource('differential-revision-detail-css'); 44 + 45 + $revision = $this->revision; 46 + 47 + $rows = array(); 48 + foreach ($this->properties as $key => $field) { 49 + $rows[] = 50 + '<tr>'. 51 + '<th>'.phutil_escape_html($key).':</th>'. 52 + '<td>'.$field.'</td>'. 53 + '</tr>'; 54 + } 55 + 56 + $properties = 57 + '<table class="differential-revision-properties">'. 58 + implode("\n", $rows). 59 + '</table>'; 60 + 61 + $actions = array(); 62 + foreach ($this->actions as $action) { 63 + if (empty($action['href'])) { 64 + $tag = 'span'; 65 + } else { 66 + $tag = 'a'; 67 + } 68 + $actions[] = phutil_render_tag( 69 + $tag, 70 + array( 71 + 'href' => idx($action, 'href'), 72 + 'class' => idx($action, 'class'), 73 + ), 74 + phutil_escape_html($action['name'])); 75 + } 76 + $actions = implode("\n", $actions); 77 + 78 + return 79 + '<div class="differential-revision-detail differential-panel">'. 80 + '<div class="differential-revision-actions">'. 81 + $actions. 82 + '</div>'. 83 + '<div class="differential-revision-detail-core">'. 84 + '<h1>'.phutil_escape_html($revision->getTitle()).'</h1>'. 85 + $properties. 86 + '</div>'. 87 + '<div style="clear: both;"></div>'. 88 + '</div>'; 89 + } 90 + }
+16
src/applications/differential/view/revisiondetail/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infratructure/celerity/api'); 10 + phutil_require_module('phabricator', 'view/base'); 11 + 12 + phutil_require_module('phutil', 'markup'); 13 + phutil_require_module('phutil', 'utils'); 14 + 15 + 16 + phutil_require_source('DifferentialRevisionDetailView.php');
+121
src/applications/differential/view/revisionupdatehistory/DifferentialRevisionUpdateHistoryView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2011 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class DifferentialRevisionUpdateHistoryView extends AphrontView { 20 + 21 + private $diffs = array(); 22 + 23 + public function setDiffs($diffs) { 24 + $this->diffs = $diffs; 25 + return $this; 26 + } 27 + 28 + public function render() { 29 + 30 + require_celerity_resource('differential-core-view-css'); 31 + require_celerity_resource('differential-revision-history-css'); 32 + 33 + $data = array( 34 + array( 35 + 'name' => 'Base', 36 + 'id' => null, 37 + 'desc' => 'Base', 38 + 'old' => false, 39 + 'new' => false, 40 + 'age' => null, 41 + 'lint' => null, 42 + 'unit' => null, 43 + ), 44 + ); 45 + 46 + $seq = 0; 47 + foreach ($this->diffs as $diff) { 48 + $data[] = array( 49 + 'name' => 'Diff '.(++$seq), 50 + 'id' => $diff->getID(), 51 + 'desc' => 'TODO',//$diff->getDescription(), 52 + 'old' => false, 53 + 'new' => false, 54 + 'age' => $diff->getDateCreated(), 55 + 'lint' => $diff->getLintStatus(), 56 + 'unit' => $diff->getUnitStatus(), 57 + ); 58 + } 59 + 60 + $idx = 0; 61 + $rows = array(); 62 + foreach ($data as $row) { 63 + 64 + $name = phutil_escape_html($row['name']); 65 + $id = phutil_escape_html($row['id']); 66 + 67 + $lint = '*'; 68 + $unit = '*'; 69 + $old = '<input type="radio" name="old" />'; 70 + $new = '<input type="radio" name="new" />'; 71 + 72 + $desc = 'TODO'; 73 + $age = '-'; 74 + 75 + if (++$idx % 2) { 76 + $class = ' class="alt"'; 77 + } else { 78 + $class = null; 79 + } 80 + 81 + $rows[] = 82 + '<tr'.$class.'>'. 83 + '<td class="revhistory-name">'.$name.'</td>'. 84 + '<td class="revhistory-id">'.$id.'</td>'. 85 + '<td class="revhistory-desc">'.$desc.'</td>'. 86 + '<td class="revhistory-age">'.$age.'</td>'. 87 + '<td class="revhistory-star">'.$lint.'</td>'. 88 + '<td class="revhistory-star">'.$unit.'</td>'. 89 + '<td class="revhistory-old">'.$old.'</td>'. 90 + '<td class="revhistory-new">'.$new.'</td>'. 91 + '</tr>'; 92 + } 93 + 94 + $select = '<select><option>Ignore All</option></select>'; 95 + 96 + return 97 + '<div class="differential-revision-history differential-panel">'. 98 + '<h1>Revision Update History</h1>'. 99 + '<form>'. 100 + '<table class="differential-revision-history-table">'. 101 + '<tr>'. 102 + '<th>Diff</th>'. 103 + '<th>ID</th>'. 104 + '<th>Description</th>'. 105 + '<th>Age</th>'. 106 + '<th>Lint</th>'. 107 + '<th>Unit</th>'. 108 + '</tr>'. 109 + implode("\n", $rows). 110 + '<tr>'. 111 + '<td colspan="8" class="diff-differ-submit">'. 112 + '<label>Whitespace Changes: '.$select.'</label>'. 113 + '<button class="disabled" 114 + disabled="disabled">Show Diff</button>'. 115 + '</td>'. 116 + '</tr>'. 117 + '</table>'. 118 + '</form>'. 119 + '</div>'; 120 + } 121 + }
+15
src/applications/differential/view/revisionupdatehistory/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infratructure/celerity/api'); 10 + phutil_require_module('phabricator', 'view/base'); 11 + 12 + phutil_require_module('phutil', 'markup'); 13 + 14 + 15 + phutil_require_source('DifferentialRevisionUpdateHistoryView.php');
+8 -1
src/applications/phid/handle/PhabricatorObjectHandle.php
··· 69 69 return $this->email; 70 70 } 71 71 72 - 72 + public function renderLink() { 73 + return phutil_render_tag( 74 + 'a', 75 + array( 76 + 'href' => $this->getURI(), 77 + ), 78 + phutil_escape_html($this->getName())); 79 + } 73 80 74 81 }
+2
src/applications/phid/handle/__init__.php
··· 6 6 7 7 8 8 9 + phutil_require_module('phutil', 'markup'); 10 + 9 11 10 12 phutil_require_source('PhabricatorObjectHandle.php');
+14
webroot/rsrc/css/application/differential/core.css
··· 7 7 max-width: 1162px; 8 8 } 9 9 10 + .differential-panel { 11 + margin: 25px 0; 12 + max-width: 1118px; 13 + border: 1px solid #666622; 14 + background: #efefdf; 15 + padding: 15px 20px; 16 + font-size: 13px; 17 + } 18 + 19 + .differential-panel h1 { 20 + border-bottom: 1px solid #aaaa99; 21 + padding-bottom: 8px; 22 + margin-bottom: 8px; 23 + }
+68
webroot/rsrc/css/application/differential/revision-detail.css
··· 1 + /** 2 + * @provides differential-revision-detail-css 3 + */ 4 + 5 + .differential-revision-properties { 6 + font-size: 12px; 7 + width: 100%; 8 + } 9 + 10 + .differential-revision-properties tt { 11 + letter-spacing: 1.1px; 12 + } 13 + 14 + .differential-revision-properties th { 15 + font-weight: bold; 16 + width: 100px; 17 + text-align: right; 18 + padding: 3px 4px 3px 3px; 19 + color: #333333; 20 + white-space: nowrap; 21 + } 22 + 23 + .differential-revision-properties td { 24 + padding: 3px 2px; 25 + } 26 + 27 + .differential-revision-actions { 28 + float: right; 29 + width: 250px; 30 + background: #cfcfbf; 31 + border: 1px solid #666622; 32 + border-width: 0px 0px 1px 1px; 33 + margin: -15px -20px 1em 0; 34 + font-size: 11px; 35 + } 36 + 37 + .differential-revision-detail-core { 38 + margin-right: 265px; 39 + } 40 + 41 + .differential-revision-actions a, 42 + .differential-revision-actions span { 43 + background-position: 8px center; 44 + background-repeat: no-repeat; 45 + display: block; 46 + padding: 4px 4px 4px 32px; 47 + } 48 + 49 + .differential-revision-actions span.unavailable { 50 + color: #666666; 51 + font-style: italic; 52 + } 53 + 54 + .differential-revision-actions .subscribe-rem { 55 + background-image: url(/rsrc/image/icon/unsubscribe.png); 56 + } 57 + 58 + .differential-revision-actions .revision-edit { 59 + background-image: url(/rsrc/image/icon/tango/edit.png); 60 + } 61 + 62 + .differential-revision-actions .revision-edit { 63 + background-image: url(/rsrc/image/icon/tango/edit.png); 64 + } 65 + 66 + .differential-revision-actions .transcripts-metamta { 67 + background-image: url(/rsrc/image/icon/tango/log.png); 68 + }
+87
webroot/rsrc/css/application/differential/revision-history.css
··· 1 + /** 2 + * @provides differential-revision-history-css 3 + */ 4 + 5 + .differential-revision-history-table { 6 + width: 100%; 7 + border-collapse: separate; 8 + border-spacing: 1px; 9 + } 10 + 11 + .differential-revision-history-table th { 12 + padding: 0 .5em; 13 + color: #666666; 14 + } 15 + 16 + .differential-revision-history-table td { 17 + padding: 4px; 18 + } 19 + 20 + .differential-revision-history-table td { 21 + white-space: nowrap; 22 + } 23 + 24 + .differential-revision-history-table tr.alt { 25 + background: #ddddcc; 26 + } 27 + 28 + 29 + .differential-revision-history-table td.revhistory-desc { 30 + width: 100%; 31 + white-space: normal; 32 + padding-left: .5em; 33 + } 34 + 35 + .differential-revision-history-table td.revhistory-name { 36 + font-weight: bold; 37 + padding-right: .5em; 38 + padding-left: .5em; 39 + } 40 + 41 + .differential-revision-history-table td.revhistory-age { 42 + text-align: right; 43 + } 44 + 45 + .differential-revision-history-table td.revhistory-old, 46 + .differential-revision-history-table td.revhistory-new { 47 + padding: 0em 1.5em; 48 + text-align: center; 49 + } 50 + 51 + .differential-revision-history-table td.revhistory-old { 52 + background: #f9d0d0; 53 + } 54 + 55 + .differential-revision-history-table td.revhistory-old-now { 56 + background: #ffaaaa; 57 + } 58 + 59 + .differential-revision-history-table td.revhistory-new { 60 + background: #d0ffd0; 61 + } 62 + 63 + .differential-revision-history-table td.revhistory-new-now { 64 + background: #aaffaa; 65 + } 66 + 67 + .differential-revision-history-table td.revhistory-star { 68 + text-align: center; 69 + } 70 + 71 + 72 + 73 + 74 + .differential-revision-history-table td.diff-differ-submit { 75 + text-align: right; 76 + border-bottom: none; 77 + } 78 + 79 + .differential-revision-history-table td.diff-differ-submit button { 80 + margin-left: 1em; 81 + } 82 + 83 + .differential-revision-history-table td.diff-differ-submit label { 84 + font-weight: bold; 85 + padding-right: .25em; 86 + color: #444444; 87 + }
-15
webroot/rsrc/css/application/differential/table-of-contents.css
··· 2 2 * @provides differential-table-of-contents-css 3 3 */ 4 4 5 - .differential-toc { 6 - margin: 25px 0; 7 - max-width: 1118px; 8 - border: 1px solid #666622; 9 - background: #efefdf; 10 - padding: 15px 20px; 11 - font-size: 13px; 12 - } 13 - 14 5 .differential-toc-meta { 15 6 color: #666666; 16 7 padding-left: 1em; ··· 32 23 .differential-toc-file { 33 24 color: #444444; 34 25 } 35 - 36 - .differential-toc h1 { 37 - border-bottom: 1px solid #aaaa99; 38 - padding-bottom: 8px; 39 - margin-bottom: 8px; 40 - }
webroot/rsrc/image/icon/subscribe.png

This is a binary file and will not be displayed.

+3
webroot/rsrc/image/icon/tango/README
··· 1 + These icons come from the Tango Desktop Project: 2 + 3 + http://tango.freedesktop.org/
webroot/rsrc/image/icon/tango/edit.png

This is a binary file and will not be displayed.

webroot/rsrc/image/icon/tango/log.png

This is a binary file and will not be displayed.

webroot/rsrc/image/icon/unsubscribe.png

This is a binary file and will not be displayed.