diff --git a/src/applications/diffusion/conduit/ConduitAPI_diffusion_tagsquery_Method.php b/src/applications/diffusion/conduit/ConduitAPI_diffusion_tagsquery_Method.php index 8d2448ff7..037c1a6de 100644 --- a/src/applications/diffusion/conduit/ConduitAPI_diffusion_tagsquery_Method.php +++ b/src/applications/diffusion/conduit/ConduitAPI_diffusion_tagsquery_Method.php @@ -1,132 +1,133 @@ <?php /** * @group conduit */ final class ConduitAPI_diffusion_tagsquery_Method extends ConduitAPI_diffusion_abstractquery_Method { public function getMethodDescription() { return 'Find tags for a given commit or list tags in the repository.'; } public function defineReturnType() { return 'array'; } protected function defineCustomParamTypes() { return array( 'commit' => 'optional string', 'offset' => 'optional int', 'limit' => 'optional int', ); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $commit = $drequest->getCommit(); + $commit = $drequest->getRawCommit(); + $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); if (!$commit) { return $this->loadGitTagList($offset, $limit); } list($err, $stdout) = $repository->execLocalCommand( 'tag -l --contains %s', $commit); if ($err) { // Git exits with an error code if the commit is bogus. return array(); } $stdout = trim($stdout); if (!strlen($stdout)) { return array(); } $tag_names = explode("\n", $stdout); $tag_names = array_fill_keys($tag_names, true); $tags = $this->loadGitTagList($offset = 0, $limit = 0, $serialize = false); $result = array(); foreach ($tags as $tag) { if (isset($tag_names[$tag->getName()])) { $result[] = $tag->toDictionary(); } } if ($offset) { $result = array_slice($result, $offset); } if ($limit) { $result = array_slice($result, 0, $limit); } return $result; } private function loadGitTagList($offset, $limit, $serialize=true) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $count = $offset + $limit; list($stdout) = $repository->execxLocalCommand( 'for-each-ref %C --sort=-creatordate --format=%s refs/tags', $count ? '--count='.(int)$count : null, '%(objectname) %(objecttype) %(refname) %(*objectname) %(*objecttype) '. '%(subject)%01%(creator)'); $stdout = trim($stdout); if (!strlen($stdout)) { return array(); } $tags = array(); foreach (explode("\n", $stdout) as $line) { list($info, $creator) = explode("\1", $line); list( $objectname, $objecttype, $refname, $refobjectname, $refobjecttype, $description) = explode(' ', $info, 6); $matches = null; if (!preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) { // It's possible a tag doesn't have a creator (tagger) $author = null; $epoch = null; } else { $author = $matches[1]; $epoch = $matches[2]; } $tag = new DiffusionRepositoryTag(); $tag->setAuthor($author); $tag->setEpoch($epoch); $tag->setCommitIdentifier(nonempty($refobjectname, $objectname)); $tag->setName(preg_replace('@^refs/tags/@', '', $refname)); $tag->setDescription($description); $tag->setType('git/'.$objecttype); $tags[] = $tag; } if ($offset) { $tags = array_slice($tags, $offset); } if ($serialize) { $tags = mpull($tags, 'toDictionary'); } return $tags; } } diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index a2a73f3c4..6d5f320a2 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -1,273 +1,407 @@ <?php final class DiffusionBrowseController extends DiffusionController { public function processRequest() { $drequest = $this->diffusionRequest; $is_file = false; if ($this->getRequest()->getStr('before')) { $is_file = true; } else if ($this->getRequest()->getStr('grep') == '') { $results = DiffusionBrowseResultSet::newFromConduit( $this->callConduitWithDiffusionRequest( 'diffusion.browsequery', array( 'path' => $drequest->getPath(), 'commit' => $drequest->getCommit(), ))); $reason = $results->getReasonForEmptyResultSet(); $is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE); } if ($is_file) { $controller = new DiffusionBrowseFileController($this->getRequest()); $controller->setDiffusionRequest($drequest); $controller->setCurrentApplication($this->getCurrentApplication()); return $this->delegateToController($controller); } $content = array(); - if ($drequest->getTagContent()) { - $title = pht('Tag: %s', $drequest->getSymbolicCommit()); - - $tag_view = new AphrontPanelView(); - $tag_view->setHeader($title); - $tag_view->appendChild( - $this->markupText($drequest->getTagContent())); - - $content[] = $tag_view; - } + $content[] = $this->buildHeaderView($drequest); + $content[] = $this->buildActionView($drequest); + $content[] = $this->buildPropertyView($drequest); $content[] = $this->renderSearchForm(); if ($this->getRequest()->getStr('grep') != '') { $content[] = $this->renderSearchResults(); - } else { if (!$results->isValidResults()) { $empty_result = new DiffusionEmptyResultView(); $empty_result->setDiffusionRequest($drequest); $empty_result->setDiffusionBrowseResultSet($results); $empty_result->setView($this->getRequest()->getStr('view')); $content[] = $empty_result; - } else { - $phids = array(); foreach ($results->getPaths() as $result) { $data = $result->getLastCommitData(); if ($data) { if ($data->getCommitDetail('authorPHID')) { $phids[$data->getCommitDetail('authorPHID')] = true; } } } $phids = array_keys($phids); $handles = $this->loadViewerHandles($phids); $browse_table = new DiffusionBrowseTableView(); $browse_table->setDiffusionRequest($drequest); $browse_table->setHandles($handles); $browse_table->setPaths($results->getPaths()); $browse_table->setUser($this->getRequest()->getUser()); $browse_panel = new AphrontPanelView(); $browse_panel->appendChild($browse_table); $browse_panel->setNoBackground(); $content[] = $browse_panel; } $content[] = $this->buildOpenRevisions(); $readme = $this->callConduitWithDiffusionRequest( 'diffusion.readmequery', array( 'paths' => $results->getPathDicts(), )); if ($readme) { $box = new PHUIBoxView(); $box->setShadow(true); $box->appendChild($readme); $box->addPadding(PHUI::PADDING_LARGE); $box->addMargin(PHUI::MARGIN_LARGE); $header = id(new PHUIHeaderView()) ->setHeader(pht('README')); $content[] = array( $header, $box, ); } - } - $nav = $this->buildSideNav('browse', false); - $nav->appendChild($content); - $crumbs = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'browse', )); - $nav->setCrumbs($crumbs); + return $this->buildApplicationPage( - $nav, + array( + $crumbs, + $content, + ), array( 'device' => true, 'title' => array( nonempty(basename($drequest->getPath()), '/'), $drequest->getRepository()->getCallsign().' Repository', ), )); } private function renderSearchForm() { $drequest = $this->getDiffusionRequest(); $form = id(new AphrontFormView()) ->setUser($this->getRequest()->getUser()) ->setMethod('GET'); switch ($drequest->getRepository()->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $form->appendChild(pht('Search is not available in Subversion.')); break; default: $form ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Search Here')) ->setName('grep') ->setValue($this->getRequest()->getStr('grep')) - ->setCaption(pht('Regular expression'))) + ->setCaption(pht('Enter a regular expression.'))) ->appendChild( id(new AphrontFormSubmitControl()) - ->setValue(pht('Grep'))); + ->setValue(pht('Search File Content'))); break; } $filter = new AphrontListFilterView(); $filter->appendChild($form); + if (!strlen($this->getRequest()->getStr('grep'))) { + $filter->setCollapsed( + pht('Show Search'), + pht('Hide Search'), + pht('Search for file content in this directory.'), + '#'); + } + return $filter; } private function renderSearchResults() { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $results = array(); $no_data = pht('No results found.'); $limit = 100; $page = $this->getRequest()->getInt('page', 0); $pager = new AphrontPagerView(); $pager->setPageSize($limit); $pager->setOffset($page); $pager->setURI($this->getRequest()->getRequestURI(), 'page'); try { $results = $this->callConduitWithDiffusionRequest( 'diffusion.searchquery', array( 'grep' => $this->getRequest()->getStr('grep'), 'stableCommitName' => $drequest->getStableCommitName(), 'path' => $drequest->getPath(), 'limit' => $limit + 1, 'offset' => $page)); } catch (ConduitException $ex) { $err = $ex->getErrorDescription(); if ($err != '') { return id(new AphrontErrorView()) ->setTitle(pht('Search Error')) ->appendChild($err); } } $results = $pager->sliceResults($results); require_celerity_resource('syntax-highlighting-css'); // NOTE: This can be wrong because we may find the string inside the // comment. But it's correct in most cases and highlighting the whole file // would be too expensive. $futures = array(); $engine = PhabricatorSyntaxHighlighter::newEngine(); foreach ($results as $result) { list($path, $line, $string) = $result; $futures["{$path}:{$line}"] = $engine->getHighlightFuture( $engine->getLanguageFromFilename($path), ltrim($string)); } try { Futures($futures)->limit(8)->resolveAll(); } catch (PhutilSyntaxHighlighterException $ex) { } $rows = array(); foreach ($results as $result) { list($path, $line, $string) = $result; $href = $drequest->generateURI(array( 'action' => 'browse', 'path' => $path, 'line' => $line, )); try { $string = $futures["{$path}:{$line}"]->resolve(); } catch (PhutilSyntaxHighlighterException $ex) { } $string = phutil_tag( 'pre', array('class' => 'PhabricatorMonospaced'), $string); $path = Filesystem::readablePath($path, $drequest->getPath()); $rows[] = array( phutil_tag('a', array('href' => $href), $path), $line, $string, ); } $table = id(new AphrontTableView($rows)) ->setClassName('remarkup-code') ->setHeaders(array(pht('Path'), pht('Line'), pht('String'))) ->setColumnClasses(array('', 'n', 'wide')) ->setNoDataString($no_data); return id(new AphrontPanelView()) - ->setHeader(pht('Search Results')) + ->setNoBackground(true) ->appendChild($table) ->appendChild($pager); } private function markupText($text) { $engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine(); $engine->setConfig('viewer', $this->getRequest()->getUser()); $text = $engine->markupText($text); $text = phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), $text); return $text; } + private function buildHeaderView(DiffusionRequest $drequest) { + $viewer = $this->getRequest()->getUser(); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setHeader($this->renderPathLinks($drequest)) + ->setPolicyObject($drequest->getRepository()); + + return $header; + } + + private function buildActionView(DiffusionRequest $drequest) { + $viewer = $this->getRequest()->getUser(); + + $view = id(new PhabricatorActionListView()) + ->setUser($viewer); + + $history_uri = $drequest->generateURI( + array( + 'action' => 'history', + )); + + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('View History')) + ->setHref($history_uri) + ->setIcon('perflab')); + + $behind_head = $drequest->getRawCommit(); + $head_uri = $drequest->generateURI( + array( + 'commit' => '', + 'action' => 'browse', + )); + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Jump to HEAD')) + ->setHref($head_uri) + ->setIcon('home') + ->setDisabled(!$behind_head)); + + // TODO: Ideally, this should live in Owners and be event-triggered, but + // there's no reasonable object for it to react to right now. + + $owners_uri = id(new PhutilURI('/owners/view/search/')) + ->setQueryParams( + array( + 'repository' => $drequest->getCallsign(), + 'path' => '/'.$drequest->getPath(), + )); + + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Find Owners')) + ->setHref((string)$owners_uri) + ->setIcon('preview')); + + return $view; + } + + private function buildPropertyView(DiffusionRequest $drequest) { + $viewer = $this->getRequest()->getUser(); + + $view = id(new PhabricatorPropertyListView()) + ->setUser($viewer); + + $stable_commit = $drequest->getStableCommitName(); + $callsign = $drequest->getRepository()->getCallsign(); + + $view->addProperty( + pht('Commit'), + phutil_tag( + 'a', + array( + 'href' => $drequest->generateURI( + array( + 'action' => 'commit', + 'commit' => $stable_commit, + )), + ), + $drequest->getRepository()->formatCommitName($stable_commit))); + + if ($drequest->getTagContent()) { + $view->addProperty( + pht('Tag'), + $drequest->getSymbolicCommit()); + + $view->addSectionHeader(pht('Tag Content')); + $view->addTextContent($this->markupText($drequest->getTagContent())); + } + + return $view; + } + + private function renderPathLinks(DiffusionRequest $drequest) { + $path = $drequest->getPath(); + $path_parts = array_filter(explode('/', trim($path, '/'))); + + $links = array(); + if ($path_parts) { + $links[] = phutil_tag( + 'a', + array( + 'href' => $drequest->generateURI( + array( + 'action' => 'browse', + 'path' => '', + )), + ), + 'r'.$drequest->getRepository()->getCallsign().'/'); + $accum = ''; + $last_key = last_key($path_parts); + foreach ($path_parts as $key => $part) { + $links[] = ' '; + $accum .= '/'.$part; + if ($key === $last_key) { + $links[] = $part; + } else { + $links[] = phutil_tag( + 'a', + array( + 'href' => $drequest->generateURI( + array( + 'action' => 'browse', + 'path' => $accum, + )), + ), + $part.'/'); + } + } + } else { + $links[] = 'r'.$drequest->getRepository()->getCallsign().'/'; + } + + return $links; + } + } diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index f2ad87c85..fbb786896 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -1,346 +1,347 @@ <?php abstract class DiffusionController extends PhabricatorController { protected $diffusionRequest; public function willProcessRequest(array $data) { if (isset($data['callsign'])) { $drequest = DiffusionRequest::newFromAphrontRequestDictionary( $data, $this->getRequest()); $this->diffusionRequest = $drequest; } } public function setDiffusionRequest(DiffusionRequest $request) { $this->diffusionRequest = $request; return $this; } protected function getDiffusionRequest() { if (!$this->diffusionRequest) { throw new Exception("No Diffusion request object!"); } return $this->diffusionRequest; } public function buildStandardPageResponse($view, array $data) { $page = $this->buildStandardPageView(); $page->setApplicationName(pht('Diffusion')); $page->setBaseURI('/diffusion/'); $page->setTitle(idx($data, 'title')); $page->setGlyph("\xE2\x89\x88"); $page->setSearchDefaultScope(PhabricatorSearchScope::SCOPE_COMMITS); $page->appendChild($view); $response = new AphrontWebpageResponse(); return $response->setContent($page->render()); } final protected function buildSideNav($selected, $has_change_view) { $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI('')); $navs = array( 'history' => pht('History View'), 'browse' => pht('Browse View'), 'change' => pht('Change View'), ); if (!$has_change_view) { unset($navs['change']); } $drequest = $this->getDiffusionRequest(); $branch = $drequest->loadBranch(); if ($branch && $branch->getLintCommit()) { $navs['lint'] = pht('Lint View'); } $selected_href = null; foreach ($navs as $action => $name) { $href = $drequest->generateURI( array( 'action' => $action, )); if ($action == $selected) { $selected_href = $href; } $nav->addFilter($href, $name, $href); } $nav->selectFilter($selected_href, null); // TODO: URI encoding might need to be sorted out for this link. $nav->addFilter( '', pht("Search Owners \xE2\x86\x97"), '/owners/view/search/'. '?repository='.phutil_escape_uri($drequest->getCallsign()). '&path='.phutil_escape_uri('/'.$drequest->getPath())); return $nav; } public function buildCrumbs(array $spec = array()) { $crumbs = $this->buildApplicationCrumbs(); $crumb_list = $this->buildCrumbList($spec); foreach ($crumb_list as $crumb) { $crumbs->addCrumb($crumb); } return $crumbs; } protected function buildOpenRevisions() { $user = $this->getRequest()->getUser(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $path = $drequest->getPath(); $path_map = id(new DiffusionPathIDQuery(array($path)))->loadPathIDs(); $path_id = idx($path_map, $path); if (!$path_id) { return null; } $revisions = id(new DifferentialRevisionQuery()) ->setViewer($user) ->withPath($repository->getID(), $path_id) ->withStatus(DifferentialRevisionQuery::STATUS_OPEN) ->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED) ->setLimit(10) ->needRelationships(true) ->execute(); if (!$revisions) { return null; } $view = id(new DifferentialRevisionListView()) ->setRevisions($revisions) ->setFields(DifferentialRevisionListView::getDefaultFields($user)) ->setUser($user) ->loadAssets(); $phids = $view->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $view->setHandles($handles); $header = id(new PHUIHeaderView()) ->setHeader(pht('Pending Differential Revisions')); return array( $header, $view, ); } private function buildCrumbList(array $spec = array()) { $spec = $spec + array( 'commit' => null, 'tags' => null, 'branches' => null, 'view' => null, ); $crumb_list = array(); // On the home page, we don't have a DiffusionRequest. if ($this->diffusionRequest) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); } else { $drequest = null; $repository = null; } if (!$repository) { return $crumb_list; } $callsign = $repository->getCallsign(); $repository_name = 'r'.$callsign; if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) { $branch_name = $drequest->getBranch(); if ($branch_name) { $repository_name .= ' ('.$branch_name.')'; } } $crumb = id(new PhabricatorCrumbView()) ->setName($repository_name); if (!$spec['view'] && !$spec['commit'] && !$spec['tags'] && !$spec['branches']) { $crumb_list[] = $crumb; return $crumb_list; } $crumb->setHref("/diffusion/{$callsign}/"); $crumb_list[] = $crumb; $raw_commit = $drequest->getRawCommit(); if ($spec['tags']) { $crumb = new PhabricatorCrumbView(); if ($spec['commit']) { $crumb->setName( pht("Tags for %s", 'r'.$callsign.$raw_commit)); $crumb->setHref($drequest->generateURI( array( 'action' => 'commit', 'commit' => $raw_commit, ))); } else { $crumb->setName(pht('Tags')); } $crumb_list[] = $crumb; return $crumb_list; } if ($spec['branches']) { $crumb = id(new PhabricatorCrumbView()) ->setName(pht('Branches')); $crumb_list[] = $crumb; return $crumb_list; } if ($spec['commit']) { $crumb = id(new PhabricatorCrumbView()) ->setName("r{$callsign}{$raw_commit}") ->setHref("r{$callsign}{$raw_commit}"); $crumb_list[] = $crumb; return $crumb_list; } $crumb = new PhabricatorCrumbView(); $view = $spec['view']; $path = null; if (isset($spec['path'])) { $path = $drequest->getPath(); } if ($raw_commit) { $commit_link = DiffusionView::linkCommit( $repository, $raw_commit); } else { $commit_link = ''; } switch ($view) { case 'history': $view_name = pht('History'); break; case 'browse': $view_name = pht('Browse'); break; case 'lint': $view_name = pht('Lint'); break; case 'change': $view_name = pht('Change'); $crumb_list[] = $crumb->setName( hsprintf('%s (%s)', $path, $commit_link)); return $crumb_list; } $uri_params = array( 'action' => $view, ); $crumb = id(new PhabricatorCrumbView()) ->setName($view_name); - if (!strlen($path)) { - $crumb_list[] = $crumb; - } else { - $crumb->setHref($drequest->generateURI( - array( - 'path' => '', - ) + $uri_params)); + if ($view == 'browse') { $crumb_list[] = $crumb; + return $crumb_list; + } - $path_parts = explode('/', $path); - do { - $last = array_pop($path_parts); - } while ($last == ''); - - $path_sections = array(); - $thus_far = ''; - foreach ($path_parts as $path_part) { - $thus_far .= $path_part.'/'; - $path_sections[] = '/'; - $path_sections[] = phutil_tag( - 'a', - array( - 'href' => $drequest->generateURI( - array( - 'path' => $thus_far, - ) + $uri_params), - ), - $path_part); - } - - $path_sections[] = '/'.$last; + $crumb->setHref($drequest->generateURI( + array( + 'path' => '', + ) + $uri_params)); + $crumb_list[] = $crumb; - $crumb_list[] = id(new PhabricatorCrumbView()) - ->setName($path_sections); + $path_parts = explode('/', $path); + do { + $last = array_pop($path_parts); + } while (count($path_parts) && $last == ''); + + $path_sections = array(); + $thus_far = ''; + foreach ($path_parts as $path_part) { + $thus_far .= $path_part.'/'; + $path_sections[] = '/'; + $path_sections[] = phutil_tag( + 'a', + array( + 'href' => $drequest->generateURI( + array( + 'path' => $thus_far, + ) + $uri_params), + ), + $path_part); } + $path_sections[] = '/'.$last; + + $crumb_list[] = id(new PhabricatorCrumbView()) + ->setName($path_sections); + $last_crumb = array_pop($crumb_list); if ($raw_commit) { $jump_link = phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'commit' => '', ) + $uri_params), ), pht('Jump to HEAD')); $name = $last_crumb->getName(); $name = hsprintf('%s @ %s (%s)', $name, $commit_link, $jump_link); $last_crumb->setName($name); } else if ($spec['view'] != 'lint') { $name = $last_crumb->getName(); $name = hsprintf('%s @ HEAD', $name); $last_crumb->setName($name); } $crumb_list[] = $last_crumb; return $crumb_list; } protected function callConduitWithDiffusionRequest( $method, array $params = array()) { $user = $this->getRequest()->getUser(); $drequest = $this->getDiffusionRequest(); return DiffusionQuery::callConduitWithDiffusionRequest( $user, $drequest, $method, $params); } protected function getRepositoryControllerURI( PhabricatorRepository $repository, $path) { return $this->getApplicationURI($repository->getCallsign().'/'.$path); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 5e0b6a606..8d0c3faf7 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -1,322 +1,327 @@ <?php final class DiffusionRepositoryController extends DiffusionController { public function processRequest() { $drequest = $this->diffusionRequest; $content = array(); $crumbs = $this->buildCrumbs(); $content[] = $crumbs; $content[] = $this->buildPropertiesTable($drequest->getRepository()); $history_results = $this->callConduitWithDiffusionRequest( 'diffusion.historyquery', array( 'commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => 0, 'limit' => 15)); $history = DiffusionPathChange::newFromConduit( $history_results['pathChanges']); $browse_results = DiffusionBrowseResultSet::newFromConduit( $this->callConduitWithDiffusionRequest( 'diffusion.browsequery', array( 'path' => $drequest->getPath(), 'commit' => $drequest->getCommit(), ))); $browse_paths = $browse_results->getPaths(); $phids = array(); foreach ($history as $item) { $data = $item->getCommitData(); if ($data) { if ($data->getCommitDetail('authorPHID')) { $phids[$data->getCommitDetail('authorPHID')] = true; } if ($data->getCommitDetail('committerPHID')) { $phids[$data->getCommitDetail('committerPHID')] = true; } } } foreach ($browse_paths as $item) { $data = $item->getLastCommitData(); if ($data) { if ($data->getCommitDetail('authorPHID')) { $phids[$data->getCommitDetail('authorPHID')] = true; } if ($data->getCommitDetail('committerPHID')) { $phids[$data->getCommitDetail('committerPHID')] = true; } } } $phids = array_keys($phids); $handles = $this->loadViewerHandles($phids); $readme = $this->callConduitWithDiffusionRequest( 'diffusion.readmequery', array( 'paths' => $browse_results->getPathDicts() )); $history_table = new DiffusionHistoryTableView(); $history_table->setUser($this->getRequest()->getUser()); $history_table->setDiffusionRequest($drequest); $history_table->setHandles($handles); $history_table->setHistory($history); $history_table->loadRevisions(); $history_table->setParents($history_results['parents']); $history_table->setIsHead(true); $callsign = $drequest->getRepository()->getCallsign(); $all = phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => 'history', )), ), pht('View Full Commit History')); $panel = new AphrontPanelView(); $panel->setHeader(pht("Recent Commits · %s", $all)); $panel->appendChild($history_table); $panel->setNoBackground(); $content[] = $panel; $browse_table = new DiffusionBrowseTableView(); $browse_table->setDiffusionRequest($drequest); $browse_table->setHandles($handles); $browse_table->setPaths($browse_paths); $browse_table->setUser($this->getRequest()->getUser()); $browse_panel = new AphrontPanelView(); $browse_panel->setHeader(phutil_tag( 'a', array('href' => $drequest->generateURI(array('action' => 'browse'))), pht('Browse Repository'))); $browse_panel->appendChild($browse_table); $browse_panel->setNoBackground(); $content[] = $browse_panel; $content[] = $this->buildTagListTable($drequest); $content[] = $this->buildBranchListTable($drequest); if ($readme) { $box = new PHUIBoxView(); $box->setShadow(true); $box->appendChild($readme); $box->addPadding(PHUI::PADDING_LARGE); $panel = new AphrontPanelView(); $panel->setHeader(pht('README')); $panel->setNoBackground(); $panel->appendChild($box); $content[] = $panel; } return $this->buildApplicationPage( $content, array( 'title' => $drequest->getRepository()->getName(), 'device' => true, )); } private function buildPropertiesTable(PhabricatorRepository $repository) { $user = $this->getRequest()->getUser(); $header = id(new PHUIHeaderView()) ->setHeader($repository->getName()) ->setUser($user) ->setPolicyObject($repository); $actions = $this->buildActionList($repository); $view = id(new PhabricatorPropertyListView()) ->setUser($user); $view->addProperty(pht('Callsign'), $repository->getCallsign()); switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $view->addProperty( pht('Clone URI'), $repository->getPublicRemoteURI()); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $view->addProperty( pht('Repository Root'), $repository->getPublicRemoteURI()); break; } $description = $repository->getDetail('description'); if (strlen($description)) { $description = PhabricatorMarkupEngine::renderOneObject( $repository, 'description', $user); $view->addTextContent($description); } return array($header, $actions, $view); } private function buildBranchListTable(DiffusionRequest $drequest) { if ($drequest->getBranch() !== null) { $limit = 15; $branches = DiffusionBranchInformation::newFromConduit( $this->callConduitWithDiffusionRequest( 'diffusion.branchquery', array( 'limit' => $limit ))); if (!$branches) { return null; } $more_branches = (count($branches) > $limit); $branches = array_slice($branches, 0, $limit); $commits = id(new PhabricatorAuditCommitQuery()) ->withIdentifiers( $drequest->getRepository()->getID(), mpull($branches, 'getHeadCommitIdentifier')) ->needCommitData(true) ->execute(); $table = new DiffusionBranchTableView(); $table->setDiffusionRequest($drequest); $table->setBranches($branches); $table->setCommits($commits); $table->setUser($this->getRequest()->getUser()); $panel = new AphrontPanelView(); $panel->setHeader(pht('Branches')); $panel->setNoBackground(); if ($more_branches) { $panel->setCaption(pht('Showing %d branches.', $limit)); } $panel->addButton( phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => 'branches', )), 'class' => 'grey button', ), pht("Show All Branches \xC2\xBB"))); $panel->appendChild($table); return $panel; } return null; } private function buildTagListTable(DiffusionRequest $drequest) { $tag_limit = 15; $tags = array(); try { $tags = DiffusionRepositoryTag::newFromConduit( $this->callConduitWithDiffusionRequest( 'diffusion.tagsquery', - array('limit' => $tag_limit + 1))); + array( + // On the home page, we want to find tags on any branch. + 'commit' => null, + 'limit' => $tag_limit + 1, + ))); } catch (ConduitException $e) { if ($e->getMessage() != 'ERR-UNSUPPORTED-VCS') { throw $e; } } if (!$tags) { return null; } $more_tags = (count($tags) > $tag_limit); $tags = array_slice($tags, 0, $tag_limit); $commits = id(new PhabricatorAuditCommitQuery()) ->withIdentifiers( $drequest->getRepository()->getID(), mpull($tags, 'getCommitIdentifier')) ->needCommitData(true) ->execute(); $view = new DiffusionTagListView(); $view->setDiffusionRequest($drequest); $view->setTags($tags); $view->setUser($this->getRequest()->getUser()); $view->setCommits($commits); $phids = $view->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $view->setHandles($handles); $panel = new AphrontPanelView(); $panel->setHeader(pht('Tags')); + $panel->setNoBackground(true); if ($more_tags) { $panel->setCaption(pht('Showing the %d most recent tags.', $tag_limit)); } $panel->addButton( phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => 'tags', )), 'class' => 'grey button', ), pht("Show All Tags \xC2\xBB"))); $panel->appendChild($view); return $panel; } private function buildActionList(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view_uri = $this->getApplicationURI($repository->getCallsign().'/'); $edit_uri = $this->getApplicationURI($repository->getCallsign().'/edit/'); $view = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($repository) ->setObjectURI($view_uri); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $repository, PhabricatorPolicyCapability::CAN_EDIT); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Repository')) ->setIcon('edit') ->setHref($edit_uri) ->setWorkflow(!$can_edit) ->setDisabled(!$can_edit)); return $view; } }