Path: blob/master/src/applications/diffusion/controller/DiffusionRepositoryController.php
12242 views
<?php12final class DiffusionRepositoryController extends DiffusionController {34private $browseFuture;5private $branchButton = null;6private $branchFuture;78public function shouldAllowPublic() {9return true;10}1112public function handleRequest(AphrontRequest $request) {13$response = $this->loadDiffusionContext();14if ($response) {15return $response;16}1718require_celerity_resource('diffusion-css');1920$viewer = $this->getViewer();21$drequest = $this->getDiffusionRequest();22$repository = $drequest->getRepository();2324$crumbs = $this->buildCrumbs();25$crumbs->setBorder(true);2627$header = $this->buildHeaderView($repository);28$actions = $this->buildActionList($repository);29$description = $this->buildDescriptionView($repository);30$locate_file = $this->buildLocateFile();3132// Before we do any work, make sure we're looking at a some content: we're33// on a valid branch, and the repository is not empty.34$page_has_content = false;35$empty_title = null;36$empty_message = null;3738// If this VCS supports branches, check that the selected branch actually39// exists.40if ($drequest->supportsBranches()) {41// NOTE: Mercurial may have multiple branch heads with the same name.42$ref_cursors = id(new PhabricatorRepositoryRefCursorQuery())43->setViewer($viewer)44->withRepositoryPHIDs(array($repository->getPHID()))45->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))46->withRefNames(array($drequest->getBranch()))47->needPositions(true)48->execute();4950// It's possible that this branch previously existed, but has been51// deleted. Make sure we have valid cursor positions, not just cursors.52$any_positions = false;53foreach ($ref_cursors as $ref_cursor) {54if ($ref_cursor->getPositions()) {55$any_positions = true;56break;57}58}5960if ($any_positions) {61// This is a valid branch, so we necessarily have some content.62$page_has_content = true;63} else {64$default = $repository->getDefaultBranch();65if ($default != $drequest->getBranch()) {66$empty_title = pht('No Such Branch');67$empty_message = pht(68'There is no branch named "%s" in this repository.',69$drequest->getBranch());70} else {71$empty_title = pht('No Default Branch');72$empty_message = pht(73'This repository is configured with default branch "%s" but '.74'there is no branch with that name in this repository.',75$default);76}77}78}7980// If we didn't find any branches, check if there are any commits at all.81// This can tailor the message for empty repositories.82$any_commit = null;83if (!$page_has_content) {84$any_commit = id(new DiffusionCommitQuery())85->setViewer($viewer)86->withRepository($repository)87->setLimit(1)88->execute();89if ($any_commit) {90if (!$drequest->supportsBranches()) {91$page_has_content = true;92}93} else {94$empty_title = pht('Empty Repository');95$empty_message = pht('This repository does not have any commits yet.');96}97}9899if ($page_has_content) {100$content = $this->buildNormalContent($drequest);101} else {102// If we have a commit somewhere, find branches.103// TODO: Evan will replace104// $this->buildNormalContent($drequest);105$content = id(new PHUIInfoView())106->setTitle($empty_title)107->setSeverity(PHUIInfoView::SEVERITY_WARNING)108->setErrors(array($empty_message));109}110111$tabs = $this->buildTabsView('code');112113$clone_uri = $drequest->generateURI(114array(115'action' => 'clone',116));117118if ($repository->isSVN()) {119$clone_text = pht('Checkout');120} else {121$clone_text = pht('Clone');122}123124$actions_button = id(new PHUIButtonView())125->setTag('a')126->setText(pht('Actions'))127->setIcon('fa-bars')128->addClass('mmr')129->setColor(PHUIButtonView::GREY)130->setDropdown(true)131->setDropdownMenu($actions);132133$clone_button = id(new PHUIButtonView())134->setTag('a')135->setText($clone_text)136->setColor(PHUIButtonView::GREEN)137->setIcon('fa-download')138->setWorkflow(true)139->setHref($clone_uri);140141$bar = id(new PHUILeftRightView())142->setLeft($locate_file)143->setRight(array($this->branchButton, $actions_button, $clone_button))144->addClass('diffusion-action-bar');145146$status_view = null;147if ($repository->isReadOnly()) {148$status_view = id(new PHUIInfoView())149->setSeverity(PHUIInfoView::SEVERITY_WARNING)150->setErrors(151array(152phutil_escape_html_newlines(153$repository->getReadOnlyMessageForDisplay()),154));155}156157$view = id(new PHUITwoColumnView())158->setHeader($header)159->setFooter(160array(161$status_view,162$bar,163$description,164$content,165));166167if ($page_has_content) {168$view->setTabs($tabs);169}170171return $this->newPage()172->setTitle(173array(174$repository->getName(),175$repository->getDisplayName(),176))177->setCrumbs($crumbs)178->appendChild(array(179$view,180));181}182183184private function buildNormalContent(DiffusionRequest $drequest) {185$request = $this->getRequest();186$repository = $drequest->getRepository();187188$commit = $drequest->getCommit();189$path = $drequest->getPath();190191$futures = array();192193$browse_pager = id(new PHUIPagerView())194->readFromRequest($request);195196$this->browseFuture = $this->callConduitMethod(197'diffusion.browsequery',198array(199'commit' => $commit,200'path' => $path,201'limit' => $browse_pager->getPageSize() + 1,202));203$futures[] = $this->browseFuture;204205if ($this->needBranchFuture()) {206$branch_limit = $this->getBranchLimit();207$this->branchFuture = $this->callConduitMethod(208'diffusion.branchquery',209array(210'closed' => false,211'limit' => $branch_limit + 1,212));213$futures[] = $this->branchFuture;214}215216$futures = array_filter($futures);217$futures = new FutureIterator($futures);218foreach ($futures as $future) {219// Just resolve all the futures before continuing.220}221222$content = array();223224try {225$browse_results = $this->browseFuture->resolve();226$browse_results = DiffusionBrowseResultSet::newFromConduit(227$browse_results);228229$browse_paths = $browse_results->getPaths();230$browse_paths = $browse_pager->sliceResults($browse_paths);231232$browse_exception = null;233} catch (Exception $ex) {234$browse_results = null;235$browse_paths = null;236$browse_exception = $ex;237}238239if ($browse_results) {240$readme = $this->renderDirectoryReadme($browse_results);241} else {242$readme = null;243}244245$content[] = $this->buildBrowseTable(246$browse_results,247$browse_paths,248$browse_exception,249$browse_pager);250251if ($readme) {252$content[] = $readme;253}254255try {256$branch_button = $this->buildBranchList($drequest);257$this->branchButton = $branch_button;258} catch (Exception $ex) {259if (!$repository->isImporting()) {260$content[] = $this->renderStatusMessage(261pht('Unable to Load Branches'),262$ex->getMessage());263}264}265266return $content;267}268269private function buildHeaderView(PhabricatorRepository $repository) {270$viewer = $this->getViewer();271$drequest = $this->getDiffusionRequest();272$search = $this->renderSearchForm();273274$header = id(new PHUIHeaderView())275->setHeader($repository->getName())276->setUser($viewer)277->setPolicyObject($repository)278->setProfileHeader(true)279->setImage($repository->getProfileImageURI())280->setImageEditURL('/diffusion/picture/'.$repository->getID().'/')281->addActionItem($search)282->addClass('diffusion-profile-header');283284if (!$repository->isTracked()) {285$header->setStatus('fa-ban', 'dark', pht('Inactive'));286} else if ($repository->isReadOnly()) {287$header->setStatus('fa-wrench', 'indigo', pht('Under Maintenance'));288} else if ($repository->isImporting()) {289$ratio = $repository->loadImportProgress();290$percentage = sprintf('%.2f%%', 100 * $ratio);291$header->setStatus(292'fa-clock-o',293'indigo',294pht('Importing (%s)...', $percentage));295} else if ($repository->isPublishingDisabled()) {296$header->setStatus('fa-minus', 'bluegrey', pht('Publishing Disabled'));297} else {298$header->setStatus('fa-check', 'bluegrey', pht('Active'));299}300301if (!$repository->isSVN()) {302$default = $repository->getDefaultBranch();303if ($default != $drequest->getBranch()) {304$branch_tag = $this->renderBranchTag($drequest);305$header->addTag($branch_tag);306}307}308309return $header;310}311312private function buildActionList(PhabricatorRepository $repository) {313$viewer = $this->getViewer();314315$edit_uri = $repository->getPathURI('manage/');316$action_view = id(new PhabricatorActionListView())317->setUser($viewer)318->setObject($repository);319320$action_view->addAction(321id(new PhabricatorActionView())322->setName(pht('Manage Repository'))323->setIcon('fa-cogs')324->setHref($edit_uri));325326if ($repository->isHosted()) {327$push_uri = $this->getApplicationURI(328'pushlog/?repositories='.$repository->getPHID());329330$action_view->addAction(331id(new PhabricatorActionView())332->setName(pht('View Push Logs'))333->setIcon('fa-upload')334->setHref($push_uri));335336$pull_uri = $this->getApplicationURI(337'synclog/?repositories='.$repository->getPHID());338339$action_view->addAction(340id(new PhabricatorActionView())341->setName(pht('View Sync Logs'))342->setIcon('fa-exchange')343->setHref($pull_uri));344}345346$pull_uri = $this->getApplicationURI(347'pulllog/?repositories='.$repository->getPHID());348349$action_view->addAction(350id(new PhabricatorActionView())351->setName(pht('View Pull Logs'))352->setIcon('fa-download')353->setHref($pull_uri));354355return $action_view;356}357358private function buildDescriptionView(PhabricatorRepository $repository) {359$viewer = $this->getViewer();360$view = id(new PHUIPropertyListView())361->setUser($viewer);362363$description = $repository->getDetail('description');364if (strlen($description)) {365$description = new PHUIRemarkupView($viewer, $description);366$view->addTextContent($description);367return id(new PHUIObjectBoxView())368->appendChild($view)369->addClass('diffusion-profile-description');370}371return null;372}373374private function buildBranchList(DiffusionRequest $drequest) {375$viewer = $this->getViewer();376377if (!$this->needBranchFuture()) {378return null;379}380381$branches = $this->branchFuture->resolve();382if (!$branches) {383return null;384}385386$limit = $this->getBranchLimit();387$more_branches = (count($branches) > $limit);388$branches = array_slice($branches, 0, $limit);389390$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);391392$actions = id(new PhabricatorActionListView())393->setViewer($viewer);394395foreach ($branches as $branch) {396$branch_uri = $drequest->generateURI(397array(398'action' => 'browse',399'branch' => $branch->getShortname(),400));401$actions->addAction(402id(new PhabricatorActionView())403->setName($branch->getShortname())404->setIcon('fa-code-fork')405->setHref($branch_uri));406}407408if ($more_branches) {409$more_uri = $drequest->generateURI(410array(411'action' => 'branches',412));413$actions->addAction(414id(new PhabricatorActionView())415->setType(PhabricatorActionView::TYPE_DIVIDER));416$actions->addAction(417id(new PhabricatorActionView())418->setName(pht('See More Branches'))419->setIcon('fa-external-link')420->setHref($more_uri));421}422423$button = id(new PHUIButtonView())424->setText(pht('Branch: %s', $drequest->getBranch()))425->setTag('a')426->addClass('mmr')427->setIcon('fa-code-fork')428->setColor(PHUIButtonView::GREY)429->setDropdown(true)430->setDropdownMenu($actions);431432return $button;433}434435private function buildLocateFile() {436$request = $this->getRequest();437$viewer = $request->getUser();438$drequest = $this->getDiffusionRequest();439$repository = $drequest->getRepository();440441$form_box = null;442if ($repository->canUsePathTree()) {443Javelin::initBehavior(444'diffusion-locate-file',445array(446'controlID' => 'locate-control',447'inputID' => 'locate-input',448'browseBaseURI' => (string)$drequest->generateURI(449array(450'action' => 'browse',451)),452'uri' => (string)$drequest->generateURI(453array(454'action' => 'pathtree',455)),456));457458$form = id(new AphrontFormView())459->setUser($viewer)460->appendChild(461id(new AphrontFormTypeaheadControl())462->setHardpointID('locate-control')463->setID('locate-input')464->setPlaceholder(pht('Locate File')));465$form_box = id(new PHUIBoxView())466->appendChild($form->buildLayoutView())467->addClass('diffusion-profile-locate');468}469return $form_box;470}471472private function buildBrowseTable(473$browse_results,474$browse_paths,475$browse_exception,476PHUIPagerView $pager) {477478require_celerity_resource('diffusion-icons-css');479480$request = $this->getRequest();481$viewer = $request->getUser();482$drequest = $this->getDiffusionRequest();483$repository = $drequest->getRepository();484485if ($browse_exception) {486if ($repository->isImporting()) {487// The history table renders a useful message.488return null;489} else {490return $this->renderStatusMessage(491pht('Unable to Retrieve Paths'),492$browse_exception->getMessage());493}494}495496$browse_table = id(new DiffusionBrowseTableView())497->setUser($viewer)498->setDiffusionRequest($drequest);499if ($browse_paths) {500$browse_table->setPaths($browse_paths);501} else {502$browse_table->setPaths(array());503}504505$browse_uri = $drequest->generateURI(array('action' => 'browse'));506$pager->setURI($browse_uri, 'offset');507508$repository_name = $repository->getName();509$branch_name = $drequest->getBranch();510if (strlen($branch_name)) {511$repository_name .= ' ('.$branch_name.')';512}513514$header = phutil_tag(515'a',516array(517'href' => $browse_uri,518'class' => 'diffusion-view-browse-header',519),520$repository_name);521522return id(new PHUIObjectBoxView())523->setHeaderText($header)524->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)525->setTable($browse_table)526->addClass('diffusion-mobile-view')527->setPager($pager);528}529530private function needBranchFuture() {531$drequest = $this->getDiffusionRequest();532533if ($drequest->getBranch() === null) {534return false;535}536537return true;538}539540private function getBranchLimit() {541return 15;542}543544}545546547