[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DiffusionRepositoryController extends DiffusionController { 4 5 public function shouldAllowPublic() { 6 return true; 7 } 8 9 public function processRequest() { 10 $request = $this->getRequest(); 11 $viewer = $request->getUser(); 12 13 $drequest = $this->getDiffusionRequest(); 14 $repository = $drequest->getRepository(); 15 16 $content = array(); 17 18 $crumbs = $this->buildCrumbs(); 19 $content[] = $crumbs; 20 21 $content[] = $this->buildPropertiesTable($drequest->getRepository()); 22 23 // Before we do any work, make sure we're looking at a some content: we're 24 // on a valid branch, and the repository is not empty. 25 $page_has_content = false; 26 $empty_title = null; 27 $empty_message = null; 28 29 // If this VCS supports branches, check that the selected branch actually 30 // exists. 31 if ($drequest->supportsBranches()) { 32 // NOTE: Mercurial may have multiple branch heads with the same name. 33 $ref_cursors = id(new PhabricatorRepositoryRefCursorQuery()) 34 ->setViewer($viewer) 35 ->withRepositoryPHIDs(array($repository->getPHID())) 36 ->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH)) 37 ->withRefNames(array($drequest->getBranch())) 38 ->execute(); 39 if ($ref_cursors) { 40 // This is a valid branch, so we necessarily have some content. 41 $page_has_content = true; 42 } else { 43 $empty_title = pht('No Such Branch'); 44 $empty_message = pht( 45 'There is no branch named "%s" in this repository.', 46 $drequest->getBranch()); 47 } 48 } 49 50 // If we didn't find any branches, check if there are any commits at all. 51 // This can tailor the message for empty repositories. 52 if (!$page_has_content) { 53 $any_commit = id(new DiffusionCommitQuery()) 54 ->setViewer($viewer) 55 ->withRepository($repository) 56 ->setLimit(1) 57 ->execute(); 58 if ($any_commit) { 59 if (!$drequest->supportsBranches()) { 60 $page_has_content = true; 61 } 62 } else { 63 $empty_title = pht('Empty Repository'); 64 $empty_message = pht( 65 'This repository does not have any commits yet.'); 66 } 67 } 68 69 if ($page_has_content) { 70 $content[] = $this->buildNormalContent($drequest); 71 } else { 72 $content[] = id(new AphrontErrorView()) 73 ->setTitle($empty_title) 74 ->setSeverity(AphrontErrorView::SEVERITY_WARNING) 75 ->setErrors(array($empty_message)); 76 } 77 78 return $this->buildApplicationPage( 79 $content, 80 array( 81 'title' => $drequest->getRepository()->getName(), 82 )); 83 } 84 85 86 private function buildNormalContent(DiffusionRequest $drequest) { 87 $repository = $drequest->getRepository(); 88 89 $phids = array(); 90 $content = array(); 91 92 try { 93 $history_results = $this->callConduitWithDiffusionRequest( 94 'diffusion.historyquery', 95 array( 96 'commit' => $drequest->getCommit(), 97 'path' => $drequest->getPath(), 98 'offset' => 0, 99 'limit' => 15, 100 )); 101 $history = DiffusionPathChange::newFromConduit( 102 $history_results['pathChanges']); 103 104 foreach ($history as $item) { 105 $data = $item->getCommitData(); 106 if ($data) { 107 if ($data->getCommitDetail('authorPHID')) { 108 $phids[$data->getCommitDetail('authorPHID')] = true; 109 } 110 if ($data->getCommitDetail('committerPHID')) { 111 $phids[$data->getCommitDetail('committerPHID')] = true; 112 } 113 } 114 } 115 $history_exception = null; 116 } catch (Exception $ex) { 117 $history_results = null; 118 $history = null; 119 $history_exception = $ex; 120 } 121 122 try { 123 $browse_results = DiffusionBrowseResultSet::newFromConduit( 124 $this->callConduitWithDiffusionRequest( 125 'diffusion.browsequery', 126 array( 127 'path' => $drequest->getPath(), 128 'commit' => $drequest->getCommit(), 129 ))); 130 $browse_paths = $browse_results->getPaths(); 131 132 foreach ($browse_paths as $item) { 133 $data = $item->getLastCommitData(); 134 if ($data) { 135 if ($data->getCommitDetail('authorPHID')) { 136 $phids[$data->getCommitDetail('authorPHID')] = true; 137 } 138 if ($data->getCommitDetail('committerPHID')) { 139 $phids[$data->getCommitDetail('committerPHID')] = true; 140 } 141 } 142 } 143 144 $browse_exception = null; 145 } catch (Exception $ex) { 146 $browse_results = null; 147 $browse_paths = null; 148 $browse_exception = $ex; 149 } 150 151 $phids = array_keys($phids); 152 $handles = $this->loadViewerHandles($phids); 153 154 if ($browse_results) { 155 $readme = $this->callConduitWithDiffusionRequest( 156 'diffusion.readmequery', 157 array( 158 'paths' => $browse_results->getPathDicts(), 159 'commit' => $drequest->getStableCommit(), 160 )); 161 } else { 162 $readme = null; 163 } 164 165 $content[] = $this->buildBrowseTable( 166 $browse_results, 167 $browse_paths, 168 $browse_exception, 169 $handles); 170 171 $content[] = $this->buildHistoryTable( 172 $history_results, 173 $history, 174 $history_exception, 175 $handles); 176 177 try { 178 $content[] = $this->buildTagListTable($drequest); 179 } catch (Exception $ex) { 180 if (!$repository->isImporting()) { 181 $content[] = $this->renderStatusMessage( 182 pht('Unable to Load Tags'), 183 $ex->getMessage()); 184 } 185 } 186 187 try { 188 $content[] = $this->buildBranchListTable($drequest); 189 } catch (Exception $ex) { 190 if (!$repository->isImporting()) { 191 $content[] = $this->renderStatusMessage( 192 pht('Unable to Load Branches'), 193 $ex->getMessage()); 194 } 195 } 196 197 if ($readme) { 198 $box = new PHUIBoxView(); 199 $box->appendChild($readme); 200 $box->addPadding(PHUI::PADDING_LARGE); 201 202 $panel = new PHUIObjectBoxView(); 203 $panel->setHeaderText(pht('README')); 204 $panel->appendChild($box); 205 $content[] = $panel; 206 } 207 208 return $content; 209 } 210 211 private function buildPropertiesTable(PhabricatorRepository $repository) { 212 $user = $this->getRequest()->getUser(); 213 214 $header = id(new PHUIHeaderView()) 215 ->setHeader($repository->getName()) 216 ->setUser($user) 217 ->setPolicyObject($repository); 218 219 if (!$repository->isTracked()) { 220 $header->setStatus('fa-ban', 'dark', pht('Inactive')); 221 } else if ($repository->isImporting()) { 222 $header->setStatus('fa-clock-o', 'indigo', pht('Importing...')); 223 } else { 224 $header->setStatus('fa-check', 'bluegrey', pht('Active')); 225 } 226 227 228 $actions = $this->buildActionList($repository); 229 230 $view = id(new PHUIPropertyListView()) 231 ->setUser($user); 232 233 $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 234 $repository->getPHID(), 235 PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 236 if ($project_phids) { 237 $this->loadHandles($project_phids); 238 $view->addProperty( 239 pht('Projects'), 240 $this->renderHandlesForPHIDs($project_phids)); 241 } 242 243 if ($repository->isHosted()) { 244 $ssh_uri = $repository->getSSHCloneURIObject(); 245 if ($ssh_uri) { 246 $clone_uri = $this->renderCloneCommand( 247 $repository, 248 $ssh_uri, 249 $repository->getServeOverSSH(), 250 '/settings/panel/ssh/'); 251 252 $view->addProperty( 253 $repository->isSVN() 254 ? pht('Checkout (SSH)') 255 : pht('Clone (SSH)'), 256 $clone_uri); 257 } 258 259 $http_uri = $repository->getHTTPCloneURIObject(); 260 if ($http_uri) { 261 $clone_uri = $this->renderCloneCommand( 262 $repository, 263 $http_uri, 264 $repository->getServeOverHTTP(), 265 PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth') 266 ? '/settings/panel/vcspassword/' 267 : null); 268 269 $view->addProperty( 270 $repository->isSVN() 271 ? pht('Checkout (HTTP)') 272 : pht('Clone (HTTP)'), 273 $clone_uri); 274 } 275 } else { 276 switch ($repository->getVersionControlSystem()) { 277 case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 278 case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 279 $view->addProperty( 280 pht('Clone'), 281 $this->renderCloneCommand( 282 $repository, 283 $repository->getPublicCloneURI())); 284 break; 285 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 286 $view->addProperty( 287 pht('Checkout'), 288 $this->renderCloneCommand( 289 $repository, 290 $repository->getPublicCloneURI())); 291 break; 292 } 293 } 294 295 $description = $repository->getDetail('description'); 296 if (strlen($description)) { 297 $description = PhabricatorMarkupEngine::renderOneObject( 298 $repository, 299 'description', 300 $user); 301 $view->addSectionHeader(pht('Description')); 302 $view->addTextContent($description); 303 } 304 305 $view->setActionList($actions); 306 307 return id(new PHUIObjectBoxView()) 308 ->setHeader($header) 309 ->addPropertyList($view); 310 311 } 312 313 private function buildBranchListTable(DiffusionRequest $drequest) { 314 $viewer = $this->getRequest()->getUser(); 315 316 if ($drequest->getBranch() === null) { 317 return null; 318 } 319 320 $limit = 15; 321 322 $branches = $this->callConduitWithDiffusionRequest( 323 'diffusion.branchquery', 324 array( 325 'limit' => $limit + 1, 326 )); 327 if (!$branches) { 328 return null; 329 } 330 331 $more_branches = (count($branches) > $limit); 332 $branches = array_slice($branches, 0, $limit); 333 334 $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); 335 336 $commits = id(new DiffusionCommitQuery()) 337 ->setViewer($viewer) 338 ->withIdentifiers(mpull($branches, 'getCommitIdentifier')) 339 ->withRepository($drequest->getRepository()) 340 ->execute(); 341 342 $table = id(new DiffusionBranchTableView()) 343 ->setUser($viewer) 344 ->setDiffusionRequest($drequest) 345 ->setBranches($branches) 346 ->setCommits($commits); 347 348 $panel = new PHUIObjectBoxView(); 349 $header = new PHUIHeaderView(); 350 $header->setHeader(pht('Branches')); 351 352 if ($more_branches) { 353 $header->setSubHeader(pht('Showing %d branches.', $limit)); 354 } 355 356 $icon = id(new PHUIIconView()) 357 ->setIconFont('fa-code-fork'); 358 359 $button = new PHUIButtonView(); 360 $button->setText(pht('Show All Branches')); 361 $button->setTag('a'); 362 $button->setIcon($icon); 363 $button->setHref($drequest->generateURI( 364 array( 365 'action' => 'branches', 366 ))); 367 368 $header->addActionLink($button); 369 $panel->setHeader($header); 370 $panel->appendChild($table); 371 372 return $panel; 373 } 374 375 private function buildTagListTable(DiffusionRequest $drequest) { 376 $viewer = $this->getRequest()->getUser(); 377 378 $tag_limit = 15; 379 $tags = array(); 380 try { 381 $tags = DiffusionRepositoryTag::newFromConduit( 382 $this->callConduitWithDiffusionRequest( 383 'diffusion.tagsquery', 384 array( 385 // On the home page, we want to find tags on any branch. 386 'commit' => null, 387 'limit' => $tag_limit + 1, 388 ))); 389 } catch (ConduitException $e) { 390 if ($e->getMessage() != 'ERR-UNSUPPORTED-VCS') { 391 throw $e; 392 } 393 } 394 395 if (!$tags) { 396 return null; 397 } 398 399 $more_tags = (count($tags) > $tag_limit); 400 $tags = array_slice($tags, 0, $tag_limit); 401 402 $commits = id(new DiffusionCommitQuery()) 403 ->setViewer($viewer) 404 ->withIdentifiers(mpull($tags, 'getCommitIdentifier')) 405 ->withRepository($drequest->getRepository()) 406 ->needCommitData(true) 407 ->execute(); 408 409 $view = id(new DiffusionTagListView()) 410 ->setUser($viewer) 411 ->setDiffusionRequest($drequest) 412 ->setTags($tags) 413 ->setCommits($commits); 414 415 $phids = $view->getRequiredHandlePHIDs(); 416 $handles = $this->loadViewerHandles($phids); 417 $view->setHandles($handles); 418 419 $panel = new PHUIObjectBoxView(); 420 $header = new PHUIHeaderView(); 421 $header->setHeader(pht('Tags')); 422 423 if ($more_tags) { 424 $header->setSubHeader( 425 pht('Showing the %d most recent tags.', $tag_limit)); 426 } 427 428 $icon = id(new PHUIIconView()) 429 ->setIconFont('fa-tag'); 430 431 $button = new PHUIButtonView(); 432 $button->setText(pht('Show All Tags')); 433 $button->setTag('a'); 434 $button->setIcon($icon); 435 $button->setHref($drequest->generateURI( 436 array( 437 'action' => 'tags', 438 ))); 439 440 $header->addActionLink($button); 441 442 $panel->setHeader($header); 443 $panel->appendChild($view); 444 445 return $panel; 446 } 447 448 private function buildActionList(PhabricatorRepository $repository) { 449 $viewer = $this->getRequest()->getUser(); 450 451 $view_uri = $this->getApplicationURI($repository->getCallsign().'/'); 452 $edit_uri = $this->getApplicationURI($repository->getCallsign().'/edit/'); 453 454 $view = id(new PhabricatorActionListView()) 455 ->setUser($viewer) 456 ->setObject($repository) 457 ->setObjectURI($view_uri); 458 459 $can_edit = PhabricatorPolicyFilter::hasCapability( 460 $viewer, 461 $repository, 462 PhabricatorPolicyCapability::CAN_EDIT); 463 464 $view->addAction( 465 id(new PhabricatorActionView()) 466 ->setName(pht('Edit Repository')) 467 ->setIcon('fa-pencil') 468 ->setHref($edit_uri) 469 ->setWorkflow(!$can_edit) 470 ->setDisabled(!$can_edit)); 471 472 if ($repository->isHosted()) { 473 $callsign = $repository->getCallsign(); 474 $push_uri = $this->getApplicationURI( 475 'pushlog/?repositories=r'.$callsign); 476 477 $view->addAction( 478 id(new PhabricatorActionView()) 479 ->setName(pht('View Push Logs')) 480 ->setIcon('fa-list-alt') 481 ->setHref($push_uri)); 482 } 483 484 return $view; 485 } 486 487 private function buildHistoryTable( 488 $history_results, 489 $history, 490 $history_exception, 491 array $handles) { 492 493 $request = $this->getRequest(); 494 $viewer = $request->getUser(); 495 $drequest = $this->getDiffusionRequest(); 496 $repository = $drequest->getRepository(); 497 498 if ($history_exception) { 499 if ($repository->isImporting()) { 500 return $this->renderStatusMessage( 501 pht('Still Importing...'), 502 pht( 503 'This repository is still importing. History is not yet '. 504 'available.')); 505 } else { 506 return $this->renderStatusMessage( 507 pht('Unable to Retrieve History'), 508 $history_exception->getMessage()); 509 } 510 } 511 512 $history_table = id(new DiffusionHistoryTableView()) 513 ->setUser($viewer) 514 ->setDiffusionRequest($drequest) 515 ->setHandles($handles) 516 ->setHistory($history); 517 518 // TODO: Super sketchy. 519 $history_table->loadRevisions(); 520 521 if ($history_results) { 522 $history_table->setParents($history_results['parents']); 523 } 524 525 $history_table->setIsHead(true); 526 $callsign = $drequest->getRepository()->getCallsign(); 527 528 $icon = id(new PHUIIconView()) 529 ->setIconFont('fa-list-alt'); 530 531 $button = id(new PHUIButtonView()) 532 ->setText(pht('View Full History')) 533 ->setHref($drequest->generateURI( 534 array( 535 'action' => 'history', 536 ))) 537 ->setTag('a') 538 ->setIcon($icon); 539 540 $panel = new PHUIObjectBoxView(); 541 $header = id(new PHUIHeaderView()) 542 ->setHeader(pht('Recent Commits')) 543 ->addActionLink($button); 544 $panel->setHeader($header); 545 $panel->appendChild($history_table); 546 547 return $panel; 548 } 549 550 private function buildBrowseTable( 551 $browse_results, 552 $browse_paths, 553 $browse_exception, 554 array $handles) { 555 556 require_celerity_resource('diffusion-icons-css'); 557 558 $request = $this->getRequest(); 559 $viewer = $request->getUser(); 560 $drequest = $this->getDiffusionRequest(); 561 $repository = $drequest->getRepository(); 562 563 if ($browse_exception) { 564 if ($repository->isImporting()) { 565 // The history table renders a useful message. 566 return null; 567 } else { 568 return $this->renderStatusMessage( 569 pht('Unable to Retrieve Paths'), 570 $browse_exception->getMessage()); 571 } 572 } 573 574 $browse_table = id(new DiffusionBrowseTableView()) 575 ->setUser($viewer) 576 ->setDiffusionRequest($drequest) 577 ->setHandles($handles); 578 if ($browse_paths) { 579 $browse_table->setPaths($browse_paths); 580 } else { 581 $browse_table->setPaths(array()); 582 } 583 584 $browse_uri = $drequest->generateURI(array('action' => 'browse')); 585 586 $browse_panel = new PHUIObjectBoxView(); 587 $header = id(new PHUIHeaderView()) 588 ->setHeader(pht('Repository')); 589 590 $icon = id(new PHUIIconView()) 591 ->setIconFont('fa-folder-open'); 592 593 $button = new PHUIButtonView(); 594 $button->setText(pht('Browse Repository')); 595 $button->setTag('a'); 596 $button->setIcon($icon); 597 $button->setHref($browse_uri); 598 599 $header->addActionLink($button); 600 $browse_panel->setHeader($header); 601 602 if ($repository->canUsePathTree()) { 603 Javelin::initBehavior( 604 'diffusion-locate-file', 605 array( 606 'controlID' => 'locate-control', 607 'inputID' => 'locate-input', 608 'browseBaseURI' => (string)$drequest->generateURI( 609 array( 610 'action' => 'browse', 611 )), 612 'uri' => (string)$drequest->generateURI( 613 array( 614 'action' => 'pathtree', 615 )), 616 )); 617 618 $form = id(new AphrontFormView()) 619 ->setUser($viewer) 620 ->appendChild( 621 id(new AphrontFormTypeaheadControl()) 622 ->setHardpointID('locate-control') 623 ->setID('locate-input') 624 ->setLabel(pht('Locate File'))); 625 $form_box = id(new PHUIBoxView()) 626 ->addClass('diffusion-locate-file-view') 627 ->appendChild($form->buildLayoutView()); 628 $browse_panel->appendChild($form_box); 629 } 630 631 $browse_panel->appendChild($browse_table); 632 633 return $browse_panel; 634 } 635 636 private function renderCloneCommand( 637 PhabricatorRepository $repository, 638 $uri, 639 $serve_mode = null, 640 $manage_uri = null) { 641 642 require_celerity_resource('diffusion-icons-css'); 643 644 Javelin::initBehavior('select-on-click'); 645 646 switch ($repository->getVersionControlSystem()) { 647 case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 648 $command = csprintf( 649 'git clone %R', 650 $uri); 651 break; 652 case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 653 $command = csprintf( 654 'hg clone %R', 655 $uri); 656 break; 657 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 658 if ($repository->isHosted()) { 659 $command = csprintf( 660 'svn checkout %R %R', 661 $uri, 662 $repository->getCloneName()); 663 } else { 664 $command = csprintf( 665 'svn checkout %R', 666 $uri); 667 } 668 break; 669 } 670 671 $input = javelin_tag( 672 'input', 673 array( 674 'type' => 'text', 675 'value' => (string)$command, 676 'class' => 'diffusion-clone-uri', 677 'sigil' => 'select-on-click', 678 'readonly' => 'true', 679 )); 680 681 $extras = array(); 682 if ($serve_mode) { 683 if ($serve_mode === PhabricatorRepository::SERVE_READONLY) { 684 $extras[] = pht('(Read Only)'); 685 } 686 } 687 688 if ($manage_uri) { 689 if ($this->getRequest()->getUser()->isLoggedIn()) { 690 $extras[] = phutil_tag( 691 'a', 692 array( 693 'href' => $manage_uri, 694 ), 695 pht('Manage Credentials')); 696 } 697 } 698 699 if ($extras) { 700 $extras = phutil_implode_html(' ', $extras); 701 $extras = phutil_tag( 702 'div', 703 array( 704 'class' => 'diffusion-clone-extras', 705 ), 706 $extras); 707 } 708 709 return array($input, $extras); 710 } 711 712 }
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 |