[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DiffusionBrowseFileController extends DiffusionBrowseController { 4 5 private $lintCommit; 6 private $lintMessages; 7 private $coverage; 8 9 public function processRequest() { 10 $request = $this->getRequest(); 11 $drequest = $this->getDiffusionRequest(); 12 $viewer = $request->getUser(); 13 14 $before = $request->getStr('before'); 15 if ($before) { 16 return $this->buildBeforeResponse($before); 17 } 18 19 $path = $drequest->getPath(); 20 21 $preferences = $viewer->loadPreferences(); 22 23 $show_blame = $request->getBool( 24 'blame', 25 $preferences->getPreference( 26 PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME, 27 false)); 28 $show_color = $request->getBool( 29 'color', 30 $preferences->getPreference( 31 PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR, 32 true)); 33 34 $view = $request->getStr('view'); 35 if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) { 36 $preferences->setPreference( 37 PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME, 38 $show_blame); 39 $preferences->setPreference( 40 PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR, 41 $show_color); 42 $preferences->save(); 43 44 $uri = $request->getRequestURI() 45 ->alter('blame', null) 46 ->alter('color', null); 47 48 return id(new AphrontRedirectResponse())->setURI($uri); 49 } 50 51 // We need the blame information if blame is on and we're building plain 52 // text, or blame is on and this is an Ajax request. If blame is on and 53 // this is a colorized request, we don't show blame at first (we ajax it 54 // in afterward) so we don't need to query for it. 55 $needs_blame = ($show_blame && !$show_color) || 56 ($show_blame && $request->isAjax()); 57 58 $file_content = DiffusionFileContent::newFromConduit( 59 $this->callConduitWithDiffusionRequest( 60 'diffusion.filecontentquery', 61 array( 62 'commit' => $drequest->getCommit(), 63 'path' => $drequest->getPath(), 64 'needsBlame' => $needs_blame, 65 ))); 66 $data = $file_content->getCorpus(); 67 68 if ($view === 'raw') { 69 return $this->buildRawResponse($path, $data); 70 } 71 72 $this->loadLintMessages(); 73 $this->coverage = $drequest->loadCoverage(); 74 75 $binary_uri = null; 76 if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) { 77 $file = $this->loadFileForData($path, $data); 78 $file_uri = $file->getBestURI(); 79 80 if ($file->isViewableImage()) { 81 $corpus = $this->buildImageCorpus($file_uri); 82 } else { 83 $corpus = $this->buildBinaryCorpus($file_uri, $data); 84 $binary_uri = $file_uri; 85 } 86 } else { 87 // Build the content of the file. 88 $corpus = $this->buildCorpus( 89 $show_blame, 90 $show_color, 91 $file_content, 92 $needs_blame, 93 $drequest, 94 $path, 95 $data); 96 } 97 98 if ($request->isAjax()) { 99 return id(new AphrontAjaxResponse())->setContent($corpus); 100 } 101 102 require_celerity_resource('diffusion-source-css'); 103 104 // Render the page. 105 $view = $this->buildActionView($drequest); 106 $action_list = $this->enrichActionView( 107 $view, 108 $drequest, 109 $show_blame, 110 $show_color); 111 112 $properties = $this->buildPropertyView($drequest, $action_list); 113 $object_box = id(new PHUIObjectBoxView()) 114 ->setHeader($this->buildHeaderView($drequest)) 115 ->addPropertyList($properties); 116 117 $content = array(); 118 $content[] = $object_box; 119 120 $follow = $request->getStr('follow'); 121 if ($follow) { 122 $notice = new AphrontErrorView(); 123 $notice->setSeverity(AphrontErrorView::SEVERITY_WARNING); 124 $notice->setTitle(pht('Unable to Continue')); 125 switch ($follow) { 126 case 'first': 127 $notice->appendChild( 128 pht('Unable to continue tracing the history of this file because '. 129 'this commit is the first commit in the repository.')); 130 break; 131 case 'created': 132 $notice->appendChild( 133 pht('Unable to continue tracing the history of this file because '. 134 'this commit created the file.')); 135 break; 136 } 137 $content[] = $notice; 138 } 139 140 $renamed = $request->getStr('renamed'); 141 if ($renamed) { 142 $notice = new AphrontErrorView(); 143 $notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE); 144 $notice->setTitle(pht('File Renamed')); 145 $notice->appendChild( 146 pht("File history passes through a rename from '%s' to '%s'.", 147 $drequest->getPath(), $renamed)); 148 $content[] = $notice; 149 } 150 151 $content[] = $corpus; 152 $content[] = $this->buildOpenRevisions(); 153 154 $crumbs = $this->buildCrumbs( 155 array( 156 'branch' => true, 157 'path' => true, 158 'view' => 'browse', 159 )); 160 161 $basename = basename($this->getDiffusionRequest()->getPath()); 162 163 return $this->buildApplicationPage( 164 array( 165 $crumbs, 166 $content, 167 ), 168 array( 169 'title' => $basename, 170 'device' => false, 171 )); 172 } 173 174 private function loadLintMessages() { 175 $drequest = $this->getDiffusionRequest(); 176 $branch = $drequest->loadBranch(); 177 178 if (!$branch || !$branch->getLintCommit()) { 179 return; 180 } 181 182 $this->lintCommit = $branch->getLintCommit(); 183 184 $conn = id(new PhabricatorRepository())->establishConnection('r'); 185 186 $where = ''; 187 if ($drequest->getLint()) { 188 $where = qsprintf( 189 $conn, 190 'AND code = %s', 191 $drequest->getLint()); 192 } 193 194 $this->lintMessages = queryfx_all( 195 $conn, 196 'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s', 197 PhabricatorRepository::TABLE_LINTMESSAGE, 198 $branch->getID(), 199 $where, 200 '/'.$drequest->getPath()); 201 } 202 203 private function buildCorpus( 204 $show_blame, 205 $show_color, 206 DiffusionFileContent $file_content, 207 $needs_blame, 208 DiffusionRequest $drequest, 209 $path, 210 $data) { 211 212 if (!$show_color) { 213 $style = 214 'border: none; width: 100%; height: 80em; font-family: monospace'; 215 if (!$show_blame) { 216 $corpus = phutil_tag( 217 'textarea', 218 array( 219 'style' => $style, 220 ), 221 $file_content->getCorpus()); 222 } else { 223 $text_list = $file_content->getTextList(); 224 $rev_list = $file_content->getRevList(); 225 $blame_dict = $file_content->getBlameDict(); 226 227 $rows = array(); 228 foreach ($text_list as $k => $line) { 229 $rev = $rev_list[$k]; 230 $author = $blame_dict[$rev]['author']; 231 $rows[] = 232 sprintf('%-10s %-20s %s', substr($rev, 0, 7), $author, $line); 233 } 234 235 $corpus = phutil_tag( 236 'textarea', 237 array( 238 'style' => $style, 239 ), 240 implode("\n", $rows)); 241 } 242 } else { 243 require_celerity_resource('syntax-highlighting-css'); 244 $text_list = $file_content->getTextList(); 245 $rev_list = $file_content->getRevList(); 246 $blame_dict = $file_content->getBlameDict(); 247 248 $text_list = implode("\n", $text_list); 249 $text_list = PhabricatorSyntaxHighlighter::highlightWithFilename( 250 $path, 251 $text_list); 252 $text_list = explode("\n", $text_list); 253 254 $rows = $this->buildDisplayRows($text_list, $rev_list, $blame_dict, 255 $needs_blame, $drequest, $show_blame, $show_color); 256 257 $corpus_table = javelin_tag( 258 'table', 259 array( 260 'class' => 'diffusion-source remarkup-code PhabricatorMonospaced', 261 'sigil' => 'phabricator-source', 262 ), 263 $rows); 264 265 if ($this->getRequest()->isAjax()) { 266 return $corpus_table; 267 } 268 269 $id = celerity_generate_unique_node_id(); 270 271 $projects = $drequest->loadArcanistProjects(); 272 $langs = array(); 273 foreach ($projects as $project) { 274 $ls = $project->getSymbolIndexLanguages(); 275 if (!$ls) { 276 continue; 277 } 278 $dep_projects = $project->getSymbolIndexProjects(); 279 $dep_projects[] = $project->getPHID(); 280 foreach ($ls as $lang) { 281 if (!isset($langs[$lang])) { 282 $langs[$lang] = array(); 283 } 284 $langs[$lang] += $dep_projects + array($project); 285 } 286 } 287 288 $lang = last(explode('.', $drequest->getPath())); 289 290 if (isset($langs[$lang])) { 291 Javelin::initBehavior( 292 'repository-crossreference', 293 array( 294 'container' => $id, 295 'lang' => $lang, 296 'projects' => $langs[$lang], 297 )); 298 } 299 300 $corpus = phutil_tag( 301 'div', 302 array( 303 'id' => $id, 304 ), 305 $corpus_table); 306 307 Javelin::initBehavior('load-blame', array('id' => $id)); 308 } 309 310 $edit = $this->renderEditButton(); 311 $file = $this->renderFileButton(); 312 $header = id(new PHUIHeaderView()) 313 ->setHeader(pht('File Contents')) 314 ->addActionLink($edit) 315 ->addActionLink($file); 316 317 $corpus = id(new PHUIObjectBoxView()) 318 ->setHeader($header) 319 ->appendChild($corpus); 320 321 return $corpus; 322 } 323 324 private function enrichActionView( 325 PhabricatorActionListView $view, 326 DiffusionRequest $drequest, 327 $show_blame, 328 $show_color) { 329 330 $viewer = $this->getRequest()->getUser(); 331 $base_uri = $this->getRequest()->getRequestURI(); 332 333 $view->addAction( 334 id(new PhabricatorActionView()) 335 ->setName(pht('Show Last Change')) 336 ->setHref( 337 $drequest->generateURI( 338 array( 339 'action' => 'change', 340 ))) 341 ->setIcon('fa-backward')); 342 343 if ($show_blame) { 344 $blame_text = pht('Disable Blame'); 345 $blame_icon = 'fa-exclamation-circle lightgreytext'; 346 $blame_value = 0; 347 } else { 348 $blame_text = pht('Enable Blame'); 349 $blame_icon = 'fa-exclamation-circle'; 350 $blame_value = 1; 351 } 352 353 $view->addAction( 354 id(new PhabricatorActionView()) 355 ->setName($blame_text) 356 ->setHref($base_uri->alter('blame', $blame_value)) 357 ->setIcon($blame_icon) 358 ->setUser($viewer) 359 ->setRenderAsForm($viewer->isLoggedIn())); 360 361 if ($show_color) { 362 $highlight_text = pht('Disable Highlighting'); 363 $highlight_icon = 'fa-star-o grey'; 364 $highlight_value = 0; 365 } else { 366 $highlight_text = pht('Enable Highlighting'); 367 $highlight_icon = 'fa-star'; 368 $highlight_value = 1; 369 } 370 371 $view->addAction( 372 id(new PhabricatorActionView()) 373 ->setName($highlight_text) 374 ->setHref($base_uri->alter('color', $highlight_value)) 375 ->setIcon($highlight_icon) 376 ->setUser($viewer) 377 ->setRenderAsForm($viewer->isLoggedIn())); 378 379 $href = null; 380 if ($this->getRequest()->getStr('lint') !== null) { 381 $lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages)); 382 $href = $base_uri->alter('lint', null); 383 384 } else if ($this->lintCommit === null) { 385 $lint_text = pht('Lint not Available'); 386 } else { 387 $lint_text = pht( 388 'Show %d Lint Message(s)', 389 count($this->lintMessages)); 390 $href = $this->getDiffusionRequest()->generateURI(array( 391 'action' => 'browse', 392 'commit' => $this->lintCommit, 393 ))->alter('lint', ''); 394 } 395 396 $view->addAction( 397 id(new PhabricatorActionView()) 398 ->setName($lint_text) 399 ->setHref($href) 400 ->setIcon('fa-exclamation-triangle') 401 ->setDisabled(!$href)); 402 403 return $view; 404 } 405 406 private function renderEditButton() { 407 $request = $this->getRequest(); 408 $user = $request->getUser(); 409 410 $drequest = $this->getDiffusionRequest(); 411 412 $repository = $drequest->getRepository(); 413 $path = $drequest->getPath(); 414 $line = nonempty((int)$drequest->getLine(), 1); 415 416 $callsign = $repository->getCallsign(); 417 $editor_link = $user->loadEditorLink($path, $line, $callsign); 418 $template = $user->loadEditorLink($path, '%l', $callsign); 419 420 $icon_edit = id(new PHUIIconView()) 421 ->setIconFont('fa-pencil'); 422 $button = id(new PHUIButtonView()) 423 ->setTag('a') 424 ->setText(pht('Open in Editor')) 425 ->setHref($editor_link) 426 ->setIcon($icon_edit) 427 ->setID('editor_link') 428 ->setMetadata(array('link_template' => $template)) 429 ->setDisabled(!$editor_link); 430 431 return $button; 432 } 433 434 private function renderFileButton($file_uri = null) { 435 436 $base_uri = $this->getRequest()->getRequestURI(); 437 438 if ($file_uri) { 439 $text = pht('Download Raw File'); 440 $href = $file_uri; 441 $icon = 'fa-download'; 442 } else { 443 $text = pht('View Raw File'); 444 $href = $base_uri->alter('view', 'raw'); 445 $icon = 'fa-file-text'; 446 } 447 448 $iconview = id(new PHUIIconView()) 449 ->setIconFont($icon); 450 $button = id(new PHUIButtonView()) 451 ->setTag('a') 452 ->setText($text) 453 ->setHref($href) 454 ->setIcon($iconview); 455 456 return $button; 457 } 458 459 460 private function buildDisplayRows( 461 array $text_list, 462 array $rev_list, 463 array $blame_dict, 464 $needs_blame, 465 DiffusionRequest $drequest, 466 $show_blame, 467 $show_color) { 468 469 $handles = array(); 470 if ($blame_dict) { 471 $epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch'); 472 $epoch_min = min($epoch_list); 473 $epoch_max = max($epoch_list); 474 $epoch_range = ($epoch_max - $epoch_min) + 1; 475 476 $author_phids = ipull(ifilter($blame_dict, 'authorPHID'), 'authorPHID'); 477 $handles = $this->loadViewerHandles($author_phids); 478 } 479 480 $line_arr = array(); 481 $line_str = $drequest->getLine(); 482 $ranges = explode(',', $line_str); 483 foreach ($ranges as $range) { 484 if (strpos($range, '-') !== false) { 485 list($min, $max) = explode('-', $range, 2); 486 $line_arr[] = array( 487 'min' => min($min, $max), 488 'max' => max($min, $max), 489 ); 490 } else if (strlen($range)) { 491 $line_arr[] = array( 492 'min' => $range, 493 'max' => $range, 494 ); 495 } 496 } 497 498 $display = array(); 499 500 $line_number = 1; 501 $last_rev = null; 502 $color = null; 503 foreach ($text_list as $k => $line) { 504 $display_line = array( 505 'epoch' => null, 506 'commit' => null, 507 'author' => null, 508 'target' => null, 509 'highlighted' => null, 510 'line' => $line_number, 511 'data' => $line, 512 ); 513 514 if ($show_blame) { 515 // If the line's rev is same as the line above, show empty content 516 // with same color; otherwise generate blame info. The newer a change 517 // is, the more saturated the color. 518 519 $rev = idx($rev_list, $k, $last_rev); 520 521 if ($last_rev == $rev) { 522 $display_line['color'] = $color; 523 } else { 524 $blame = $blame_dict[$rev]; 525 526 if (!isset($blame['epoch'])) { 527 $color = '#ffd'; // Render as warning. 528 } else { 529 $color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range; 530 $color_value = 0xE6 * (1.0 - $color_ratio); 531 $color = sprintf( 532 '#%02x%02x%02x', 533 $color_value, 534 0xF6, 535 $color_value); 536 } 537 538 $display_line['epoch'] = idx($blame, 'epoch'); 539 $display_line['color'] = $color; 540 $display_line['commit'] = $rev; 541 542 $author_phid = idx($blame, 'authorPHID'); 543 if ($author_phid && $handles[$author_phid]) { 544 $author_link = $handles[$author_phid]->renderLink(); 545 } else { 546 $author_link = $blame['author']; 547 } 548 $display_line['author'] = $author_link; 549 550 $last_rev = $rev; 551 } 552 } 553 554 if ($line_arr) { 555 if ($line_number == $line_arr[0]['min']) { 556 $display_line['target'] = true; 557 } 558 foreach ($line_arr as $range) { 559 if ($line_number >= $range['min'] && 560 $line_number <= $range['max']) { 561 $display_line['highlighted'] = true; 562 } 563 } 564 } 565 566 $display[] = $display_line; 567 ++$line_number; 568 } 569 570 $request = $this->getRequest(); 571 $viewer = $request->getUser(); 572 573 $commits = array_filter(ipull($display, 'commit')); 574 if ($commits) { 575 $commits = id(new DiffusionCommitQuery()) 576 ->setViewer($viewer) 577 ->withRepository($drequest->getRepository()) 578 ->withIdentifiers($commits) 579 ->execute(); 580 $commits = mpull($commits, null, 'getCommitIdentifier'); 581 } 582 583 $revision_ids = id(new DifferentialRevision()) 584 ->loadIDsByCommitPHIDs(mpull($commits, 'getPHID')); 585 $revisions = array(); 586 if ($revision_ids) { 587 $revisions = id(new DifferentialRevisionQuery()) 588 ->setViewer($viewer) 589 ->withIDs($revision_ids) 590 ->execute(); 591 } 592 593 $phids = array(); 594 foreach ($commits as $commit) { 595 if ($commit->getAuthorPHID()) { 596 $phids[] = $commit->getAuthorPHID(); 597 } 598 } 599 foreach ($revisions as $revision) { 600 if ($revision->getAuthorPHID()) { 601 $phids[] = $revision->getAuthorPHID(); 602 } 603 } 604 $handles = $this->loadViewerHandles($phids); 605 606 Javelin::initBehavior('phabricator-oncopy', array()); 607 608 $engine = null; 609 $inlines = array(); 610 if ($this->getRequest()->getStr('lint') !== null && $this->lintMessages) { 611 $engine = new PhabricatorMarkupEngine(); 612 $engine->setViewer($viewer); 613 614 foreach ($this->lintMessages as $message) { 615 $inline = id(new PhabricatorAuditInlineComment()) 616 ->setSyntheticAuthor( 617 ArcanistLintSeverity::getStringForSeverity($message['severity']). 618 ' '.$message['code'].' ('.$message['name'].')') 619 ->setLineNumber($message['line']) 620 ->setContent($message['description']); 621 $inlines[$message['line']][] = $inline; 622 623 $engine->addObject( 624 $inline, 625 PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY); 626 } 627 628 $engine->process(); 629 require_celerity_resource('differential-changeset-view-css'); 630 } 631 632 $rows = $this->renderInlines( 633 idx($inlines, 0, array()), 634 $show_blame, 635 (bool)$this->coverage, 636 $engine); 637 638 foreach ($display as $line) { 639 640 $line_href = $drequest->generateURI( 641 array( 642 'action' => 'browse', 643 'line' => $line['line'], 644 'stable' => true, 645 )); 646 647 $blame = array(); 648 $style = null; 649 if (array_key_exists('color', $line)) { 650 if ($line['color']) { 651 $style = 'background: '.$line['color'].';'; 652 } 653 654 $before_link = null; 655 $commit_link = null; 656 $revision_link = null; 657 if (idx($line, 'commit')) { 658 $commit = $line['commit']; 659 660 if (idx($commits, $commit)) { 661 $tooltip = $this->renderCommitTooltip( 662 $commits[$commit], 663 $handles, 664 $line['author']); 665 } else { 666 $tooltip = null; 667 } 668 669 Javelin::initBehavior('phabricator-tooltips', array()); 670 require_celerity_resource('aphront-tooltip-css'); 671 672 $commit_link = javelin_tag( 673 'a', 674 array( 675 'href' => $drequest->generateURI( 676 array( 677 'action' => 'commit', 678 'commit' => $line['commit'], 679 )), 680 'sigil' => 'has-tooltip', 681 'meta' => array( 682 'tip' => $tooltip, 683 'align' => 'E', 684 'size' => 600, 685 ), 686 ), 687 id(new PhutilUTF8StringTruncator()) 688 ->setMaximumGlyphs(9) 689 ->setTerminator('') 690 ->truncateString($line['commit'])); 691 692 $revision_id = null; 693 if (idx($commits, $commit)) { 694 $revision_id = idx($revision_ids, $commits[$commit]->getPHID()); 695 } 696 697 if ($revision_id) { 698 $revision = idx($revisions, $revision_id); 699 if ($revision) { 700 $tooltip = $this->renderRevisionTooltip($revision, $handles); 701 $revision_link = javelin_tag( 702 'a', 703 array( 704 'href' => '/D'.$revision->getID(), 705 'sigil' => 'has-tooltip', 706 'meta' => array( 707 'tip' => $tooltip, 708 'align' => 'E', 709 'size' => 600, 710 ), 711 ), 712 'D'.$revision->getID()); 713 } 714 } 715 716 $uri = $line_href->alter('before', $commit); 717 $before_link = javelin_tag( 718 'a', 719 array( 720 'href' => $uri->setQueryParam('view', 'blame'), 721 'sigil' => 'has-tooltip', 722 'meta' => array( 723 'tip' => pht('Skip Past This Commit'), 724 'align' => 'E', 725 'size' => 300, 726 ), 727 ), 728 "\xC2\xAB"); 729 } 730 731 $blame[] = phutil_tag( 732 'th', 733 array( 734 'class' => 'diffusion-blame-link', 735 ), 736 $before_link); 737 738 $object_links = array(); 739 $object_links[] = $commit_link; 740 if ($revision_link) { 741 $object_links[] = phutil_tag('span', array(), '/'); 742 $object_links[] = $revision_link; 743 } 744 745 $blame[] = phutil_tag( 746 'th', 747 array( 748 'class' => 'diffusion-rev-link', 749 ), 750 $object_links); 751 } 752 753 $line_link = phutil_tag( 754 'a', 755 array( 756 'href' => $line_href, 757 'style' => $style, 758 ), 759 $line['line']); 760 761 $blame[] = javelin_tag( 762 'th', 763 array( 764 'class' => 'diffusion-line-link', 765 'sigil' => 'phabricator-source-line', 766 'style' => $style, 767 ), 768 $line_link); 769 770 Javelin::initBehavior('phabricator-line-linker'); 771 772 if ($line['target']) { 773 Javelin::initBehavior( 774 'diffusion-jump-to', 775 array( 776 'target' => 'scroll_target', 777 )); 778 $anchor_text = phutil_tag( 779 'a', 780 array( 781 'id' => 'scroll_target', 782 ), 783 ''); 784 } else { 785 $anchor_text = null; 786 } 787 788 $blame[] = phutil_tag( 789 'td', 790 array( 791 ), 792 array( 793 $anchor_text, 794 795 // NOTE: See phabricator-oncopy behavior. 796 "\xE2\x80\x8B", 797 798 // TODO: [HTML] Not ideal. 799 phutil_safe_html(str_replace("\t", ' ', $line['data'])), 800 )); 801 802 if ($this->coverage) { 803 require_celerity_resource('differential-changeset-view-css'); 804 $cov_index = $line['line'] - 1; 805 806 if (isset($this->coverage[$cov_index])) { 807 $cov_class = $this->coverage[$cov_index]; 808 } else { 809 $cov_class = 'N'; 810 } 811 812 $blame[] = phutil_tag( 813 'td', 814 array( 815 'class' => 'cov cov-'.$cov_class, 816 ), 817 ''); 818 } 819 820 $rows[] = phutil_tag( 821 'tr', 822 array( 823 'class' => ($line['highlighted'] ? 824 'phabricator-source-highlight' : 825 null), 826 ), 827 $blame); 828 829 $cur_inlines = $this->renderInlines( 830 idx($inlines, $line['line'], array()), 831 $show_blame, 832 $this->coverage, 833 $engine); 834 foreach ($cur_inlines as $cur_inline) { 835 $rows[] = $cur_inline; 836 } 837 } 838 839 return $rows; 840 } 841 842 private function renderInlines( 843 array $inlines, 844 $needs_blame, 845 $has_coverage, 846 $engine) { 847 848 $rows = array(); 849 foreach ($inlines as $inline) { 850 $inline_view = id(new DifferentialInlineCommentView()) 851 ->setMarkupEngine($engine) 852 ->setInlineComment($inline) 853 ->render(); 854 855 $row = array_fill(0, ($needs_blame ? 3 : 1), phutil_tag('th')); 856 857 $row[] = phutil_tag('td', array(), $inline_view); 858 859 if ($has_coverage) { 860 $row[] = phutil_tag( 861 'td', 862 array( 863 'class' => 'cov cov-I', 864 )); 865 } 866 867 $rows[] = phutil_tag('tr', array('class' => 'inline'), $row); 868 } 869 870 return $rows; 871 } 872 873 private function loadFileForData($path, $data) { 874 $file = PhabricatorFile::buildFromFileDataOrHash( 875 $data, 876 array( 877 'name' => basename($path), 878 'ttl' => time() + 60 * 60 * 24, 879 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 880 )); 881 882 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 883 $file->attachToObject( 884 $this->getDiffusionRequest()->getRepository()->getPHID()); 885 unset($unguarded); 886 887 return $file; 888 } 889 890 private function buildRawResponse($path, $data) { 891 $file = $this->loadFileForData($path, $data); 892 return $file->getRedirectResponse(); 893 } 894 895 private function buildImageCorpus($file_uri) { 896 $properties = new PHUIPropertyListView(); 897 898 $properties->addImageContent( 899 phutil_tag( 900 'img', 901 array( 902 'src' => $file_uri, 903 ))); 904 905 $file = $this->renderFileButton($file_uri); 906 $header = id(new PHUIHeaderView()) 907 ->setHeader(pht('Image')) 908 ->addActionLink($file); 909 910 return id(new PHUIObjectBoxView()) 911 ->setHeader($header) 912 ->addPropertyList($properties); 913 } 914 915 private function buildBinaryCorpus($file_uri, $data) { 916 917 $size = new PhutilNumber(strlen($data)); 918 $text = pht('This is a binary file. It is %s byte(s) in length.', $size); 919 $text = id(new PHUIBoxView()) 920 ->addPadding(PHUI::PADDING_LARGE) 921 ->appendChild($text); 922 923 $file = $this->renderFileButton($file_uri); 924 $header = id(new PHUIHeaderView()) 925 ->setHeader(pht('Details')) 926 ->addActionLink($file); 927 928 $box = id(new PHUIObjectBoxView()) 929 ->setHeader($header) 930 ->appendChild($text); 931 932 return $box; 933 } 934 935 private function buildBeforeResponse($before) { 936 $request = $this->getRequest(); 937 $drequest = $this->getDiffusionRequest(); 938 939 // NOTE: We need to get the grandparent so we can capture filename changes 940 // in the parent. 941 942 $parent = $this->loadParentCommitOf($before); 943 $old_filename = null; 944 $was_created = false; 945 if ($parent) { 946 $grandparent = $this->loadParentCommitOf($parent); 947 948 if ($grandparent) { 949 $rename_query = new DiffusionRenameHistoryQuery(); 950 $rename_query->setRequest($drequest); 951 $rename_query->setOldCommit($grandparent); 952 $rename_query->setViewer($request->getUser()); 953 $old_filename = $rename_query->loadOldFilename(); 954 $was_created = $rename_query->getWasCreated(); 955 } 956 } 957 958 $follow = null; 959 if ($was_created) { 960 // If the file was created in history, that means older commits won't 961 // have it. Since we know it existed at 'before', it must have been 962 // created then; jump there. 963 $target_commit = $before; 964 $follow = 'created'; 965 } else if ($parent) { 966 // If we found a parent, jump to it. This is the normal case. 967 $target_commit = $parent; 968 } else { 969 // If there's no parent, this was probably created in the initial commit? 970 // And the "was_created" check will fail because we can't identify the 971 // grandparent. Keep the user at 'before'. 972 $target_commit = $before; 973 $follow = 'first'; 974 } 975 976 $path = $drequest->getPath(); 977 $renamed = null; 978 if ($old_filename !== null && 979 $old_filename !== '/'.$path) { 980 $renamed = $path; 981 $path = $old_filename; 982 } 983 984 $line = null; 985 // If there's a follow error, drop the line so the user sees the message. 986 if (!$follow) { 987 $line = $this->getBeforeLineNumber($target_commit); 988 } 989 990 $before_uri = $drequest->generateURI( 991 array( 992 'action' => 'browse', 993 'commit' => $target_commit, 994 'line' => $line, 995 'path' => $path, 996 )); 997 998 $before_uri->setQueryParams($request->getRequestURI()->getQueryParams()); 999 $before_uri = $before_uri->alter('before', null); 1000 $before_uri = $before_uri->alter('renamed', $renamed); 1001 $before_uri = $before_uri->alter('follow', $follow); 1002 1003 return id(new AphrontRedirectResponse())->setURI($before_uri); 1004 } 1005 1006 private function getBeforeLineNumber($target_commit) { 1007 $drequest = $this->getDiffusionRequest(); 1008 1009 $line = $drequest->getLine(); 1010 if (!$line) { 1011 return null; 1012 } 1013 1014 $raw_diff = $this->callConduitWithDiffusionRequest( 1015 'diffusion.rawdiffquery', 1016 array( 1017 'commit' => $drequest->getCommit(), 1018 'path' => $drequest->getPath(), 1019 'againstCommit' => $target_commit, 1020 )); 1021 $old_line = 0; 1022 $new_line = 0; 1023 1024 foreach (explode("\n", $raw_diff) as $text) { 1025 if ($text[0] == '-' || $text[0] == ' ') { 1026 $old_line++; 1027 } 1028 if ($text[0] == '+' || $text[0] == ' ') { 1029 $new_line++; 1030 } 1031 if ($new_line == $line) { 1032 return $old_line; 1033 } 1034 } 1035 1036 // We didn't find the target line. 1037 return $line; 1038 } 1039 1040 private function loadParentCommitOf($commit) { 1041 $drequest = $this->getDiffusionRequest(); 1042 $user = $this->getRequest()->getUser(); 1043 1044 $before_req = DiffusionRequest::newFromDictionary( 1045 array( 1046 'user' => $user, 1047 'repository' => $drequest->getRepository(), 1048 'commit' => $commit, 1049 )); 1050 1051 $parents = DiffusionQuery::callConduitWithDiffusionRequest( 1052 $user, 1053 $before_req, 1054 'diffusion.commitparentsquery', 1055 array( 1056 'commit' => $commit, 1057 )); 1058 1059 return head($parents); 1060 } 1061 1062 private function renderRevisionTooltip( 1063 DifferentialRevision $revision, 1064 array $handles) { 1065 $viewer = $this->getRequest()->getUser(); 1066 1067 $date = phabricator_date($revision->getDateModified(), $viewer); 1068 $id = $revision->getID(); 1069 $title = $revision->getTitle(); 1070 $header = "D{$id} {$title}"; 1071 1072 $author = $handles[$revision->getAuthorPHID()]->getName(); 1073 1074 return "{$header}\n{$date} \xC2\xB7 {$author}"; 1075 } 1076 1077 private function renderCommitTooltip( 1078 PhabricatorRepositoryCommit $commit, 1079 array $handles, 1080 $author) { 1081 1082 $viewer = $this->getRequest()->getUser(); 1083 1084 $date = phabricator_date($commit->getEpoch(), $viewer); 1085 $summary = trim($commit->getSummary()); 1086 1087 if ($commit->getAuthorPHID()) { 1088 $author = $handles[$commit->getAuthorPHID()]->getName(); 1089 } 1090 1091 return "{$summary}\n{$date} \xC2\xB7 {$author}"; 1092 } 1093 1094 }
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 |