[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DifferentialRevisionViewController extends DifferentialController { 4 5 private $revisionID; 6 7 public function shouldAllowPublic() { 8 return true; 9 } 10 11 public function willProcessRequest(array $data) { 12 $this->revisionID = $data['id']; 13 } 14 15 public function processRequest() { 16 17 $request = $this->getRequest(); 18 $user = $request->getUser(); 19 $viewer_is_anonymous = !$user->isLoggedIn(); 20 21 $revision = id(new DifferentialRevisionQuery()) 22 ->withIDs(array($this->revisionID)) 23 ->setViewer($request->getUser()) 24 ->needRelationships(true) 25 ->needReviewerStatus(true) 26 ->needReviewerAuthority(true) 27 ->executeOne(); 28 29 if (!$revision) { 30 return new Aphront404Response(); 31 } 32 33 $diffs = id(new DifferentialDiffQuery()) 34 ->setViewer($request->getUser()) 35 ->withRevisionIDs(array($this->revisionID)) 36 ->needArcanistProjects(true) 37 ->execute(); 38 $diffs = array_reverse($diffs, $preserve_keys = true); 39 40 if (!$diffs) { 41 throw new Exception( 42 'This revision has no diffs. Something has gone quite wrong.'); 43 } 44 45 $revision->attachActiveDiff(last($diffs)); 46 47 $diff_vs = $request->getInt('vs'); 48 49 $target_id = $request->getInt('id'); 50 $target = idx($diffs, $target_id, end($diffs)); 51 52 $target_manual = $target; 53 if (!$target_id) { 54 foreach ($diffs as $diff) { 55 if ($diff->getCreationMethod() != 'commit') { 56 $target_manual = $diff; 57 } 58 } 59 } 60 61 if (empty($diffs[$diff_vs])) { 62 $diff_vs = null; 63 } 64 65 $repository = null; 66 $repository_phid = $target->getRepositoryPHID(); 67 if ($repository_phid) { 68 if ($repository_phid == $revision->getRepositoryPHID()) { 69 $repository = $revision->getRepository(); 70 } else { 71 $repository = id(new PhabricatorRepositoryQuery()) 72 ->setViewer($user) 73 ->withPHIDs(array($repository_phid)) 74 ->executeOne(); 75 } 76 } 77 78 list($changesets, $vs_map, $vs_changesets, $rendering_references) = 79 $this->loadChangesetsAndVsMap( 80 $target, 81 idx($diffs, $diff_vs), 82 $repository); 83 84 if ($request->getExists('download')) { 85 return $this->buildRawDiffResponse( 86 $revision, 87 $changesets, 88 $vs_changesets, 89 $vs_map, 90 $repository); 91 } 92 93 $props = id(new DifferentialDiffProperty())->loadAllWhere( 94 'diffID = %d', 95 $target_manual->getID()); 96 $props = mpull($props, 'getData', 'getName'); 97 98 $all_changesets = $changesets; 99 $inlines = $this->loadInlineComments( 100 $revision, 101 $all_changesets); 102 103 $object_phids = array_merge( 104 $revision->getReviewers(), 105 $revision->getCCPHIDs(), 106 $revision->loadCommitPHIDs(), 107 array( 108 $revision->getAuthorPHID(), 109 $user->getPHID(), 110 )); 111 112 foreach ($revision->getAttached() as $type => $phids) { 113 foreach ($phids as $phid => $info) { 114 $object_phids[] = $phid; 115 } 116 } 117 118 $field_list = PhabricatorCustomField::getObjectFields( 119 $revision, 120 PhabricatorCustomField::ROLE_VIEW); 121 122 $field_list->setViewer($user); 123 $field_list->readFieldsFromStorage($revision); 124 125 $warning_handle_map = array(); 126 foreach ($field_list->getFields() as $key => $field) { 127 $req = $field->getRequiredHandlePHIDsForRevisionHeaderWarnings(); 128 foreach ($req as $phid) { 129 $warning_handle_map[$key][] = $phid; 130 $object_phids[] = $phid; 131 } 132 } 133 134 $handles = $this->loadViewerHandles($object_phids); 135 136 $request_uri = $request->getRequestURI(); 137 138 $limit = 100; 139 $large = $request->getStr('large'); 140 if (count($changesets) > $limit && !$large) { 141 $count = count($changesets); 142 $warning = new AphrontErrorView(); 143 $warning->setTitle('Very Large Diff'); 144 $warning->setSeverity(AphrontErrorView::SEVERITY_WARNING); 145 $warning->appendChild(hsprintf( 146 '%s <strong>%s</strong>', 147 pht( 148 'This diff is very large and affects %s files. '. 149 'You may load each file individually or ', 150 new PhutilNumber($count)), 151 phutil_tag( 152 'a', 153 array( 154 'class' => 'button grey', 155 'href' => $request_uri 156 ->alter('large', 'true') 157 ->setFragment('toc'), 158 ), 159 pht('Show All Files Inline')))); 160 $warning = $warning->render(); 161 162 $my_inlines = id(new DifferentialInlineCommentQuery()) 163 ->withDraftComments($user->getPHID(), $this->revisionID) 164 ->execute(); 165 166 $visible_changesets = array(); 167 foreach ($inlines + $my_inlines as $inline) { 168 $changeset_id = $inline->getChangesetID(); 169 if (isset($changesets[$changeset_id])) { 170 $visible_changesets[$changeset_id] = $changesets[$changeset_id]; 171 } 172 } 173 174 if (!empty($props['arc:lint'])) { 175 $changeset_paths = mpull($changesets, null, 'getFilename'); 176 foreach ($props['arc:lint'] as $lint) { 177 $changeset = idx($changeset_paths, $lint['path']); 178 if ($changeset) { 179 $visible_changesets[$changeset->getID()] = $changeset; 180 } 181 } 182 } 183 } else { 184 $warning = null; 185 $visible_changesets = $changesets; 186 } 187 188 189 // TODO: This should be in a DiffQuery or similar. 190 $need_props = array(); 191 foreach ($field_list->getFields() as $field) { 192 foreach ($field->getRequiredDiffPropertiesForRevisionView() as $prop) { 193 $need_props[$prop] = $prop; 194 } 195 } 196 197 if ($need_props) { 198 $prop_diff = $revision->getActiveDiff(); 199 $load_props = id(new DifferentialDiffProperty())->loadAllWhere( 200 'diffID = %d AND name IN (%Ls)', 201 $prop_diff->getID(), 202 $need_props); 203 $load_props = mpull($load_props, 'getData', 'getName'); 204 foreach ($need_props as $need) { 205 $prop_diff->attachProperty($need, idx($load_props, $need)); 206 } 207 } 208 209 $commit_hashes = mpull($diffs, 'getSourceControlBaseRevision'); 210 $local_commits = idx($props, 'local:commits', array()); 211 foreach ($local_commits as $local_commit) { 212 $commit_hashes[] = idx($local_commit, 'tree'); 213 $commit_hashes[] = idx($local_commit, 'local'); 214 } 215 $commit_hashes = array_unique(array_filter($commit_hashes)); 216 if ($commit_hashes) { 217 $commits_for_links = id(new DiffusionCommitQuery()) 218 ->setViewer($user) 219 ->withIdentifiers($commit_hashes) 220 ->execute(); 221 $commits_for_links = mpull( 222 $commits_for_links, 223 null, 224 'getCommitIdentifier'); 225 } else { 226 $commits_for_links = array(); 227 } 228 229 $revision_detail = id(new DifferentialRevisionDetailView()) 230 ->setUser($user) 231 ->setRevision($revision) 232 ->setDiff(end($diffs)) 233 ->setCustomFields($field_list) 234 ->setURI($request->getRequestURI()); 235 236 $actions = $this->getRevisionActions($revision); 237 238 $whitespace = $request->getStr( 239 'whitespace', 240 DifferentialChangesetParser::WHITESPACE_IGNORE_ALL); 241 242 $arc_project = $target->getArcanistProject(); 243 if ($arc_project) { 244 list($symbol_indexes, $project_phids) = $this->buildSymbolIndexes( 245 $arc_project, 246 $visible_changesets); 247 } else { 248 $symbol_indexes = array(); 249 $project_phids = null; 250 } 251 252 $revision_detail->setActions($actions); 253 $revision_detail->setUser($user); 254 255 $revision_detail_box = $revision_detail->render(); 256 257 $revision_warnings = $this->buildRevisionWarnings( 258 $revision, 259 $field_list, 260 $warning_handle_map, 261 $handles); 262 if ($revision_warnings) { 263 $revision_warnings = id(new AphrontErrorView()) 264 ->setSeverity(AphrontErrorView::SEVERITY_WARNING) 265 ->setErrors($revision_warnings); 266 $revision_detail_box->setErrorView($revision_warnings); 267 } 268 269 $comment_view = $this->buildTransactions( 270 $revision, 271 $diff_vs ? $diffs[$diff_vs] : $target, 272 $target, 273 $all_changesets); 274 275 if (!$viewer_is_anonymous) { 276 $comment_view->setQuoteRef('D'.$revision->getID()); 277 $comment_view->setQuoteTargetID('comment-content'); 278 } 279 280 $wrap_id = celerity_generate_unique_node_id(); 281 $comment_view = phutil_tag( 282 'div', 283 array( 284 'id' => $wrap_id, 285 ), 286 $comment_view); 287 288 if ($arc_project) { 289 Javelin::initBehavior( 290 'repository-crossreference', 291 array( 292 'section' => $wrap_id, 293 'projects' => $project_phids, 294 )); 295 } 296 297 $changeset_view = new DifferentialChangesetListView(); 298 $changeset_view->setChangesets($changesets); 299 $changeset_view->setVisibleChangesets($visible_changesets); 300 301 if (!$viewer_is_anonymous) { 302 $changeset_view->setInlineCommentControllerURI( 303 '/differential/comment/inline/edit/'.$revision->getID().'/'); 304 } 305 306 $changeset_view->setStandaloneURI('/differential/changeset/'); 307 $changeset_view->setRawFileURIs( 308 '/differential/changeset/?view=old', 309 '/differential/changeset/?view=new'); 310 311 $changeset_view->setUser($user); 312 $changeset_view->setDiff($target); 313 $changeset_view->setRenderingReferences($rendering_references); 314 $changeset_view->setVsMap($vs_map); 315 $changeset_view->setWhitespace($whitespace); 316 if ($repository) { 317 $changeset_view->setRepository($repository); 318 } 319 $changeset_view->setSymbolIndexes($symbol_indexes); 320 $changeset_view->setTitle('Diff '.$target->getID()); 321 322 $diff_history = id(new DifferentialRevisionUpdateHistoryView()) 323 ->setUser($user) 324 ->setDiffs($diffs) 325 ->setSelectedVersusDiffID($diff_vs) 326 ->setSelectedDiffID($target->getID()) 327 ->setSelectedWhitespace($whitespace) 328 ->setCommitsForLinks($commits_for_links); 329 330 $local_view = id(new DifferentialLocalCommitsView()) 331 ->setUser($user) 332 ->setLocalCommits(idx($props, 'local:commits')) 333 ->setCommitsForLinks($commits_for_links); 334 335 if ($repository) { 336 $other_revisions = $this->loadOtherRevisions( 337 $changesets, 338 $target, 339 $repository); 340 } else { 341 $other_revisions = array(); 342 } 343 344 $other_view = null; 345 if ($other_revisions) { 346 $other_view = $this->renderOtherRevisions($other_revisions); 347 } 348 349 $toc_view = new DifferentialDiffTableOfContentsView(); 350 $toc_view->setChangesets($changesets); 351 $toc_view->setVisibleChangesets($visible_changesets); 352 $toc_view->setRenderingReferences($rendering_references); 353 $toc_view->setUnitTestData(idx($props, 'arc:unit', array())); 354 if ($repository) { 355 $toc_view->setRepository($repository); 356 } 357 $toc_view->setDiff($target); 358 $toc_view->setUser($user); 359 $toc_view->setRevisionID($revision->getID()); 360 $toc_view->setWhitespace($whitespace); 361 362 $comment_form = null; 363 if (!$viewer_is_anonymous) { 364 $draft = id(new PhabricatorDraft())->loadOneWhere( 365 'authorPHID = %s AND draftKey = %s', 366 $user->getPHID(), 367 'differential-comment-'.$revision->getID()); 368 369 $reviewers = array(); 370 $ccs = array(); 371 if ($draft) { 372 $reviewers = idx($draft->getMetadata(), 'reviewers', array()); 373 $ccs = idx($draft->getMetadata(), 'ccs', array()); 374 if ($reviewers || $ccs) { 375 $handles = $this->loadViewerHandles(array_merge($reviewers, $ccs)); 376 $reviewers = array_select_keys($handles, $reviewers); 377 $ccs = array_select_keys($handles, $ccs); 378 } 379 } 380 381 $comment_form = new DifferentialAddCommentView(); 382 $comment_form->setRevision($revision); 383 384 $review_warnings = array(); 385 foreach ($field_list->getFields() as $field) { 386 $review_warnings[] = $field->getWarningsForDetailView(); 387 } 388 $review_warnings = array_mergev($review_warnings); 389 390 if ($review_warnings) { 391 $review_warnings_panel = id(new AphrontErrorView()) 392 ->setSeverity(AphrontErrorView::SEVERITY_WARNING) 393 ->setErrors($review_warnings); 394 $comment_form->setErrorView($review_warnings_panel); 395 } 396 397 $comment_form->setActions($this->getRevisionCommentActions($revision)); 398 $action_uri = $this->getApplicationURI( 399 'comment/save/'.$revision->getID().'/'); 400 401 $comment_form->setActionURI($action_uri); 402 $comment_form->setUser($user); 403 $comment_form->setDraft($draft); 404 $comment_form->setReviewers(mpull($reviewers, 'getFullName', 'getPHID')); 405 $comment_form->setCCs(mpull($ccs, 'getFullName', 'getPHID')); 406 407 // TODO: This just makes the "Z" key work. Generalize this and remove 408 // it at some point. 409 $comment_form = phutil_tag( 410 'div', 411 array( 412 'class' => 'differential-add-comment-panel', 413 ), 414 $comment_form); 415 } 416 417 $pane_id = celerity_generate_unique_node_id(); 418 Javelin::initBehavior( 419 'differential-keyboard-navigation', 420 array( 421 'haunt' => $pane_id, 422 )); 423 Javelin::initBehavior('differential-user-select'); 424 425 $page_pane = id(new DifferentialPrimaryPaneView()) 426 ->setID($pane_id) 427 ->appendChild(array( 428 $comment_view, 429 $diff_history, 430 $warning, 431 $local_view, 432 $toc_view, 433 $other_view, 434 $changeset_view, 435 )); 436 if ($comment_form) { 437 438 $page_pane->appendChild($comment_form); 439 } else { 440 // TODO: For now, just use this to get "Login to Comment". 441 $page_pane->appendChild( 442 id(new PhabricatorApplicationTransactionCommentView()) 443 ->setUser($user) 444 ->setRequestURI($request->getRequestURI())); 445 } 446 447 448 $object_id = 'D'.$revision->getID(); 449 450 $top_anchor = id(new PhabricatorAnchorView()) 451 ->setAnchorName('top') 452 ->setNavigationMarker(true); 453 454 $content = array( 455 $top_anchor, 456 $revision_detail_box, 457 $page_pane, 458 ); 459 460 $crumbs = $this->buildApplicationCrumbs(); 461 $crumbs->addTextCrumb($object_id, '/'.$object_id); 462 $crumbs->setActionList($revision_detail->getActionList()); 463 464 $prefs = $user->loadPreferences(); 465 466 $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE; 467 if ($prefs->getPreference($pref_filetree)) { 468 $collapsed = $prefs->getPreference( 469 PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED, 470 false); 471 472 $nav = id(new DifferentialChangesetFileTreeSideNavBuilder()) 473 ->setAnchorName('top') 474 ->setTitle('D'.$revision->getID()) 475 ->setBaseURI(new PhutilURI('/D'.$revision->getID())) 476 ->setCollapsed((bool)$collapsed) 477 ->build($changesets); 478 $nav->appendChild($content); 479 $nav->setCrumbs($crumbs); 480 $content = $nav; 481 } else { 482 array_unshift($content, $crumbs); 483 } 484 485 return $this->buildApplicationPage( 486 $content, 487 array( 488 'title' => $object_id.' '.$revision->getTitle(), 489 'pageObjects' => array($revision->getPHID()), 490 )); 491 } 492 493 private function getRevisionActions(DifferentialRevision $revision) { 494 $viewer = $this->getRequest()->getUser(); 495 $revision_id = $revision->getID(); 496 $revision_phid = $revision->getPHID(); 497 498 $can_edit = PhabricatorPolicyFilter::hasCapability( 499 $viewer, 500 $revision, 501 PhabricatorPolicyCapability::CAN_EDIT); 502 503 $actions = array(); 504 505 $actions[] = id(new PhabricatorActionView()) 506 ->setIcon('fa-pencil') 507 ->setHref("/differential/revision/edit/{$revision_id}/") 508 ->setName(pht('Edit Revision')) 509 ->setDisabled(!$can_edit) 510 ->setWorkflow(!$can_edit); 511 512 $this->requireResource('phabricator-object-selector-css'); 513 $this->requireResource('javelin-behavior-phabricator-object-selector'); 514 515 $actions[] = id(new PhabricatorActionView()) 516 ->setIcon('fa-link') 517 ->setName(pht('Edit Dependencies')) 518 ->setHref("/search/attach/{$revision_phid}/DREV/dependencies/") 519 ->setWorkflow(true) 520 ->setDisabled(!$can_edit); 521 522 $maniphest = 'PhabricatorManiphestApplication'; 523 if (PhabricatorApplication::isClassInstalled($maniphest)) { 524 $actions[] = id(new PhabricatorActionView()) 525 ->setIcon('fa-anchor') 526 ->setName(pht('Edit Maniphest Tasks')) 527 ->setHref("/search/attach/{$revision_phid}/TASK/") 528 ->setWorkflow(true) 529 ->setDisabled(!$can_edit); 530 } 531 532 $request_uri = $this->getRequest()->getRequestURI(); 533 $actions[] = id(new PhabricatorActionView()) 534 ->setIcon('fa-download') 535 ->setName(pht('Download Raw Diff')) 536 ->setHref($request_uri->alter('download', 'true')); 537 538 return $actions; 539 } 540 541 private function getRevisionCommentActions(DifferentialRevision $revision) { 542 $actions = array( 543 DifferentialAction::ACTION_COMMENT => true, 544 ); 545 546 $viewer = $this->getRequest()->getUser(); 547 $viewer_phid = $viewer->getPHID(); 548 $viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID()); 549 $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers()); 550 $status = $revision->getStatus(); 551 552 $viewer_has_accepted = false; 553 $viewer_has_rejected = false; 554 $status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED; 555 $status_rejected = DifferentialReviewerStatus::STATUS_REJECTED; 556 foreach ($revision->getReviewerStatus() as $reviewer) { 557 if ($reviewer->getReviewerPHID() == $viewer_phid) { 558 if ($reviewer->getStatus() == $status_accepted) { 559 $viewer_has_accepted = true; 560 } 561 if ($reviewer->getStatus() == $status_rejected) { 562 $viewer_has_rejected = true; 563 } 564 break; 565 } 566 } 567 568 $allow_self_accept = PhabricatorEnv::getEnvConfig( 569 'differential.allow-self-accept'); 570 $always_allow_abandon = PhabricatorEnv::getEnvConfig( 571 'differential.always-allow-abandon'); 572 $always_allow_close = PhabricatorEnv::getEnvConfig( 573 'differential.always-allow-close'); 574 $allow_reopen = PhabricatorEnv::getEnvConfig( 575 'differential.allow-reopen'); 576 577 if ($viewer_is_owner) { 578 switch ($status) { 579 case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: 580 $actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept; 581 $actions[DifferentialAction::ACTION_ABANDON] = true; 582 $actions[DifferentialAction::ACTION_RETHINK] = true; 583 break; 584 case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: 585 case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED: 586 $actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept; 587 $actions[DifferentialAction::ACTION_ABANDON] = true; 588 $actions[DifferentialAction::ACTION_REQUEST] = true; 589 break; 590 case ArcanistDifferentialRevisionStatus::ACCEPTED: 591 $actions[DifferentialAction::ACTION_ABANDON] = true; 592 $actions[DifferentialAction::ACTION_REQUEST] = true; 593 $actions[DifferentialAction::ACTION_RETHINK] = true; 594 $actions[DifferentialAction::ACTION_CLOSE] = true; 595 break; 596 case ArcanistDifferentialRevisionStatus::CLOSED: 597 break; 598 case ArcanistDifferentialRevisionStatus::ABANDONED: 599 $actions[DifferentialAction::ACTION_RECLAIM] = true; 600 break; 601 } 602 } else { 603 switch ($status) { 604 case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: 605 $actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon; 606 $actions[DifferentialAction::ACTION_ACCEPT] = true; 607 $actions[DifferentialAction::ACTION_REJECT] = true; 608 $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer; 609 break; 610 case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: 611 case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED: 612 $actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon; 613 $actions[DifferentialAction::ACTION_ACCEPT] = true; 614 $actions[DifferentialAction::ACTION_REJECT] = !$viewer_has_rejected; 615 $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer; 616 break; 617 case ArcanistDifferentialRevisionStatus::ACCEPTED: 618 $actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon; 619 $actions[DifferentialAction::ACTION_ACCEPT] = !$viewer_has_accepted; 620 $actions[DifferentialAction::ACTION_REJECT] = true; 621 $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer; 622 break; 623 case ArcanistDifferentialRevisionStatus::CLOSED: 624 case ArcanistDifferentialRevisionStatus::ABANDONED: 625 break; 626 } 627 if ($status != ArcanistDifferentialRevisionStatus::CLOSED) { 628 $actions[DifferentialAction::ACTION_CLAIM] = true; 629 $actions[DifferentialAction::ACTION_CLOSE] = $always_allow_close; 630 } 631 } 632 633 $actions[DifferentialAction::ACTION_ADDREVIEWERS] = true; 634 $actions[DifferentialAction::ACTION_ADDCCS] = true; 635 $actions[DifferentialAction::ACTION_REOPEN] = $allow_reopen && 636 ($status == ArcanistDifferentialRevisionStatus::CLOSED); 637 638 $actions = array_keys(array_filter($actions)); 639 $actions_dict = array(); 640 foreach ($actions as $action) { 641 $actions_dict[$action] = DifferentialAction::getActionVerb($action); 642 } 643 644 return $actions_dict; 645 } 646 647 private function loadInlineComments( 648 DifferentialRevision $revision, 649 array &$changesets) { 650 assert_instances_of($changesets, 'DifferentialChangeset'); 651 652 $inline_comments = array(); 653 654 $inline_comments = id(new DifferentialInlineCommentQuery()) 655 ->withRevisionIDs(array($revision->getID())) 656 ->withNotDraft(true) 657 ->execute(); 658 659 $load_changesets = array(); 660 foreach ($inline_comments as $inline) { 661 $changeset_id = $inline->getChangesetID(); 662 if (isset($changesets[$changeset_id])) { 663 continue; 664 } 665 $load_changesets[$changeset_id] = true; 666 } 667 668 $more_changesets = array(); 669 if ($load_changesets) { 670 $changeset_ids = array_keys($load_changesets); 671 $more_changesets += id(new DifferentialChangeset()) 672 ->loadAllWhere( 673 'id IN (%Ld)', 674 $changeset_ids); 675 } 676 677 if ($more_changesets) { 678 $changesets += $more_changesets; 679 $changesets = msort($changesets, 'getSortKey'); 680 } 681 682 return $inline_comments; 683 } 684 685 private function loadChangesetsAndVsMap( 686 DifferentialDiff $target, 687 DifferentialDiff $diff_vs = null, 688 PhabricatorRepository $repository = null) { 689 690 $load_diffs = array($target); 691 if ($diff_vs) { 692 $load_diffs[] = $diff_vs; 693 } 694 695 $raw_changesets = id(new DifferentialChangesetQuery()) 696 ->setViewer($this->getRequest()->getUser()) 697 ->withDiffs($load_diffs) 698 ->execute(); 699 $changeset_groups = mgroup($raw_changesets, 'getDiffID'); 700 701 $changesets = idx($changeset_groups, $target->getID(), array()); 702 $changesets = mpull($changesets, null, 'getID'); 703 704 $refs = array(); 705 $vs_map = array(); 706 $vs_changesets = array(); 707 if ($diff_vs) { 708 $vs_id = $diff_vs->getID(); 709 $vs_changesets_path_map = array(); 710 foreach (idx($changeset_groups, $vs_id, array()) as $changeset) { 711 $path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs); 712 $vs_changesets_path_map[$path] = $changeset; 713 $vs_changesets[$changeset->getID()] = $changeset; 714 } 715 foreach ($changesets as $key => $changeset) { 716 $path = $changeset->getAbsoluteRepositoryPath($repository, $target); 717 if (isset($vs_changesets_path_map[$path])) { 718 $vs_map[$changeset->getID()] = 719 $vs_changesets_path_map[$path]->getID(); 720 $refs[$changeset->getID()] = 721 $changeset->getID().'/'.$vs_changesets_path_map[$path]->getID(); 722 unset($vs_changesets_path_map[$path]); 723 } else { 724 $refs[$changeset->getID()] = $changeset->getID(); 725 } 726 } 727 foreach ($vs_changesets_path_map as $path => $changeset) { 728 $changesets[$changeset->getID()] = $changeset; 729 $vs_map[$changeset->getID()] = -1; 730 $refs[$changeset->getID()] = $changeset->getID().'/-1'; 731 } 732 } else { 733 foreach ($changesets as $changeset) { 734 $refs[$changeset->getID()] = $changeset->getID(); 735 } 736 } 737 738 $changesets = msort($changesets, 'getSortKey'); 739 740 return array($changesets, $vs_map, $vs_changesets, $refs); 741 } 742 743 private function buildSymbolIndexes( 744 PhabricatorRepositoryArcanistProject $arc_project, 745 array $visible_changesets) { 746 assert_instances_of($visible_changesets, 'DifferentialChangeset'); 747 748 $engine = PhabricatorSyntaxHighlighter::newEngine(); 749 750 $langs = $arc_project->getSymbolIndexLanguages(); 751 if (!$langs) { 752 return array(array(), array()); 753 } 754 755 $symbol_indexes = array(); 756 757 $project_phids = array_merge( 758 array($arc_project->getPHID()), 759 nonempty($arc_project->getSymbolIndexProjects(), array())); 760 761 $indexed_langs = array_fill_keys($langs, true); 762 foreach ($visible_changesets as $key => $changeset) { 763 $lang = $engine->getLanguageFromFilename($changeset->getFilename()); 764 if (isset($indexed_langs[$lang])) { 765 $symbol_indexes[$key] = array( 766 'lang' => $lang, 767 'projects' => $project_phids, 768 ); 769 } 770 } 771 772 return array($symbol_indexes, $project_phids); 773 } 774 775 private function loadOtherRevisions( 776 array $changesets, 777 DifferentialDiff $target, 778 PhabricatorRepository $repository) { 779 assert_instances_of($changesets, 'DifferentialChangeset'); 780 781 $paths = array(); 782 foreach ($changesets as $changeset) { 783 $paths[] = $changeset->getAbsoluteRepositoryPath( 784 $repository, 785 $target); 786 } 787 788 if (!$paths) { 789 return array(); 790 } 791 792 $path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs(); 793 794 if (!$path_map) { 795 return array(); 796 } 797 798 $query = id(new DifferentialRevisionQuery()) 799 ->setViewer($this->getRequest()->getUser()) 800 ->withStatus(DifferentialRevisionQuery::STATUS_OPEN) 801 ->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED) 802 ->setLimit(10) 803 ->needFlags(true) 804 ->needDrafts(true) 805 ->needRelationships(true); 806 807 foreach ($path_map as $path => $path_id) { 808 $query->withPath($repository->getID(), $path_id); 809 } 810 811 $results = $query->execute(); 812 813 // Strip out *this* revision. 814 foreach ($results as $key => $result) { 815 if ($result->getID() == $this->revisionID) { 816 unset($results[$key]); 817 } 818 } 819 820 return $results; 821 } 822 823 private function renderOtherRevisions(array $revisions) { 824 assert_instances_of($revisions, 'DifferentialRevision'); 825 826 $user = $this->getRequest()->getUser(); 827 828 $view = id(new DifferentialRevisionListView()) 829 ->setRevisions($revisions) 830 ->setUser($user); 831 832 $phids = $view->getRequiredHandlePHIDs(); 833 $handles = $this->loadViewerHandles($phids); 834 $view->setHandles($handles); 835 836 return id(new PHUIObjectBoxView()) 837 ->setHeaderText(pht('Open Revisions Affecting These Files')) 838 ->appendChild($view); 839 } 840 841 842 /** 843 * Note this code is somewhat similar to the buildPatch method in 844 * @{class:DifferentialReviewRequestMail}. 845 * 846 * @return @{class:AphrontRedirectResponse} 847 */ 848 private function buildRawDiffResponse( 849 DifferentialRevision $revision, 850 array $changesets, 851 array $vs_changesets, 852 array $vs_map, 853 PhabricatorRepository $repository = null) { 854 855 assert_instances_of($changesets, 'DifferentialChangeset'); 856 assert_instances_of($vs_changesets, 'DifferentialChangeset'); 857 858 $viewer = $this->getRequest()->getUser(); 859 860 id(new DifferentialHunkQuery()) 861 ->setViewer($viewer) 862 ->withChangesets($changesets) 863 ->needAttachToChangesets(true) 864 ->execute(); 865 866 $diff = new DifferentialDiff(); 867 $diff->attachChangesets($changesets); 868 $raw_changes = $diff->buildChangesList(); 869 $changes = array(); 870 foreach ($raw_changes as $changedict) { 871 $changes[] = ArcanistDiffChange::newFromDictionary($changedict); 872 } 873 874 $loader = id(new PhabricatorFileBundleLoader()) 875 ->setViewer($viewer); 876 877 $bundle = ArcanistBundle::newFromChanges($changes); 878 $bundle->setLoadFileDataCallback(array($loader, 'loadFileData')); 879 880 $vcs = $repository ? $repository->getVersionControlSystem() : null; 881 switch ($vcs) { 882 case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 883 case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 884 $raw_diff = $bundle->toGitPatch(); 885 break; 886 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 887 default: 888 $raw_diff = $bundle->toUnifiedDiff(); 889 break; 890 } 891 892 $request_uri = $this->getRequest()->getRequestURI(); 893 894 // this ends up being something like 895 // D123.diff 896 // or the verbose 897 // D123.vs123.id123.whitespaceignore-all.diff 898 // lame but nice to include these options 899 $file_name = ltrim($request_uri->getPath(), '/').'.'; 900 foreach ($request_uri->getQueryParams() as $key => $value) { 901 if ($key == 'download') { 902 continue; 903 } 904 $file_name .= $key.$value.'.'; 905 } 906 $file_name .= 'diff'; 907 908 $file = PhabricatorFile::buildFromFileDataOrHash( 909 $raw_diff, 910 array( 911 'name' => $file_name, 912 'ttl' => (60 * 60 * 24), 913 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 914 )); 915 916 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 917 $file->attachToObject($revision->getPHID()); 918 unset($unguarded); 919 920 return $file->getRedirectResponse(); 921 } 922 923 private function buildTransactions( 924 DifferentialRevision $revision, 925 DifferentialDiff $left_diff, 926 DifferentialDiff $right_diff, 927 array $changesets) { 928 929 $viewer = $this->getRequest()->getUser(); 930 931 $xactions = id(new DifferentialTransactionQuery()) 932 ->setViewer($viewer) 933 ->withObjectPHIDs(array($revision->getPHID())) 934 ->needComments(true) 935 ->execute(); 936 937 $timeline = id(new DifferentialTransactionView()) 938 ->setUser($viewer) 939 ->setObjectPHID($revision->getPHID()) 940 ->setChangesets($changesets) 941 ->setRevision($revision) 942 ->setLeftDiff($left_diff) 943 ->setRightDiff($right_diff) 944 ->setTransactions($xactions); 945 946 return $timeline; 947 } 948 949 private function buildRevisionWarnings( 950 DifferentialRevision $revision, 951 PhabricatorCustomFieldList $field_list, 952 array $warning_handle_map, 953 array $handles) { 954 955 $warnings = array(); 956 foreach ($field_list->getFields() as $key => $field) { 957 $phids = idx($warning_handle_map, $key, array()); 958 $field_handles = array_select_keys($handles, $phids); 959 $field_warnings = $field->getWarningsForRevisionHeader($field_handles); 960 foreach ($field_warnings as $warning) { 961 $warnings[] = $warning; 962 } 963 } 964 965 return $warnings; 966 } 967 968 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |