[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/diffusion/controller/ -> DiffusionRepositoryController.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1