Page MenuHomePhabricator

D14942.id36094.diff
No OneTemporary

D14942.id36094.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -533,12 +533,8 @@
'DiffusionBranchTableController' => 'applications/diffusion/controller/DiffusionBranchTableController.php',
'DiffusionBranchTableView' => 'applications/diffusion/view/DiffusionBranchTableView.php',
'DiffusionBrowseController' => 'applications/diffusion/controller/DiffusionBrowseController.php',
- 'DiffusionBrowseDirectoryController' => 'applications/diffusion/controller/DiffusionBrowseDirectoryController.php',
- 'DiffusionBrowseFileController' => 'applications/diffusion/controller/DiffusionBrowseFileController.php',
- 'DiffusionBrowseMainController' => 'applications/diffusion/controller/DiffusionBrowseMainController.php',
'DiffusionBrowseQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php',
'DiffusionBrowseResultSet' => 'applications/diffusion/data/DiffusionBrowseResultSet.php',
- 'DiffusionBrowseSearchController' => 'applications/diffusion/controller/DiffusionBrowseSearchController.php',
'DiffusionBrowseTableView' => 'applications/diffusion/view/DiffusionBrowseTableView.php',
'DiffusionCachedResolveRefsQuery' => 'applications/diffusion/query/DiffusionCachedResolveRefsQuery.php',
'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php',
@@ -4501,12 +4497,8 @@
'DiffusionBranchTableController' => 'DiffusionController',
'DiffusionBranchTableView' => 'DiffusionView',
'DiffusionBrowseController' => 'DiffusionController',
- 'DiffusionBrowseDirectoryController' => 'DiffusionBrowseController',
- 'DiffusionBrowseFileController' => 'DiffusionBrowseController',
- 'DiffusionBrowseMainController' => 'DiffusionBrowseController',
'DiffusionBrowseQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionBrowseResultSet' => 'Phobject',
- 'DiffusionBrowseSearchController' => 'DiffusionBrowseController',
'DiffusionBrowseTableView' => 'DiffusionView',
'DiffusionCachedResolveRefsQuery' => 'DiffusionLowLevelQuery',
'DiffusionChangeController' => 'DiffusionController',
diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
--- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
+++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
@@ -70,7 +70,7 @@
'repository/(?P<dblob>.*)' => 'DiffusionRepositoryController',
'change/(?P<dblob>.*)' => 'DiffusionChangeController',
'history/(?P<dblob>.*)' => 'DiffusionHistoryController',
- 'browse/(?P<dblob>.*)' => 'DiffusionBrowseMainController',
+ 'browse/(?P<dblob>.*)' => 'DiffusionBrowseController',
'lastmodified/(?P<dblob>.*)' => 'DiffusionLastModifiedController',
'diff/' => 'DiffusionDiffController',
'tags/(?P<dblob>.*)' => 'DiffusionTagListController',
diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php
--- a/src/applications/diffusion/controller/DiffusionBrowseController.php
+++ b/src/applications/diffusion/controller/DiffusionBrowseController.php
@@ -1,11 +1,1485 @@
<?php
-abstract class DiffusionBrowseController extends DiffusionController {
+final class DiffusionBrowseController extends DiffusionController {
+
+ private $lintCommit;
+ private $lintMessages;
+ private $coverage;
public function shouldAllowPublic() {
return true;
}
+ public function handleRequest(AphrontRequest $request) {
+ $response = $this->loadDiffusionContext();
+ if ($response) {
+ return $response;
+ }
+
+ $drequest = $this->getDiffusionRequest();
+
+ // Figure out if we're browsing a directory, a file, or a search result
+ // list.
+
+ $grep = $request->getStr('grep');
+ $find = $request->getStr('find');
+ if (strlen($grep) || strlen($find)) {
+ return $this->browseSearch();
+ }
+
+ $results = DiffusionBrowseResultSet::newFromConduit(
+ $this->callConduitWithDiffusionRequest(
+ 'diffusion.browsequery',
+ array(
+ 'path' => $drequest->getPath(),
+ 'commit' => $drequest->getStableCommit(),
+ )));
+ $reason = $results->getReasonForEmptyResultSet();
+ $is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);
+
+ if ($is_file) {
+ return $this->browseFile($results);
+ } else {
+ return $this->browseDirectory($results);
+ }
+ }
+
+ private function browseSearch() {
+ $drequest = $this->getDiffusionRequest();
+
+ $actions = $this->buildActionView($drequest);
+ $properties = $this->buildPropertyView($drequest, $actions);
+
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeader($this->buildHeaderView($drequest))
+ ->addPropertyList($properties);
+
+ $content = array();
+
+ $content[] = $object_box;
+ $content[] = $this->renderSearchForm($collapsed = false);
+ $content[] = $this->renderSearchResults();
+
+ $crumbs = $this->buildCrumbs(
+ array(
+ 'branch' => true,
+ 'path' => true,
+ 'view' => 'browse',
+ ));
+
+ return $this->newPage()
+ ->setTitle(
+ array(
+ nonempty(basename($drequest->getPath()), '/'),
+ $drequest->getRepository()->getDisplayName(),
+ ))
+ ->setCrumbs($crumbs)
+ ->appendChild($content);
+ }
+
+ private function browseFile() {
+ $viewer = $this->getViewer();
+ $request = $this->getRequest();
+ $drequest = $this->getDiffusionRequest();
+ $repository = $drequest->getRepository();
+
+ $before = $request->getStr('before');
+ if ($before) {
+ return $this->buildBeforeResponse($before);
+ }
+
+ $path = $drequest->getPath();
+
+ $preferences = $viewer->loadPreferences();
+
+ $show_blame = $request->getBool(
+ 'blame',
+ $preferences->getPreference(
+ PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME,
+ false));
+ $show_color = $request->getBool(
+ 'color',
+ $preferences->getPreference(
+ PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR,
+ true));
+
+ $view = $request->getStr('view');
+ if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) {
+ $preferences->setPreference(
+ PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME,
+ $show_blame);
+ $preferences->setPreference(
+ PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR,
+ $show_color);
+ $preferences->save();
+
+ $uri = $request->getRequestURI()
+ ->alter('blame', null)
+ ->alter('color', null);
+
+ return id(new AphrontRedirectResponse())->setURI($uri);
+ }
+
+ // We need the blame information if blame is on and we're building plain
+ // text, or blame is on and this is an Ajax request. If blame is on and
+ // this is a colorized request, we don't show blame at first (we ajax it
+ // in afterward) so we don't need to query for it.
+ $needs_blame = ($show_blame && !$show_color) ||
+ ($show_blame && $request->isAjax());
+
+ $params = array(
+ 'commit' => $drequest->getCommit(),
+ 'path' => $drequest->getPath(),
+ 'needsBlame' => $needs_blame,
+ );
+
+ $byte_limit = null;
+ if ($view !== 'raw') {
+ $byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();
+ $time_limit = 10;
+
+ $params += array(
+ 'timeout' => $time_limit,
+ 'byteLimit' => $byte_limit,
+ );
+ }
+
+ $file_content = DiffusionFileContent::newFromConduit(
+ $this->callConduitWithDiffusionRequest(
+ 'diffusion.filecontentquery',
+ $params));
+ $data = $file_content->getCorpus();
+
+ if ($view === 'raw') {
+ return $this->buildRawResponse($path, $data);
+ }
+
+ $this->loadLintMessages();
+ $this->coverage = $drequest->loadCoverage();
+
+ if ($byte_limit && (strlen($data) == $byte_limit)) {
+ $corpus = $this->buildErrorCorpus(
+ pht(
+ 'This file is larger than %s byte(s), and too large to display '.
+ 'in the web UI.',
+ $byte_limit));
+ } else if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
+ $file = $this->loadFileForData($path, $data);
+ $file_uri = $file->getBestURI();
+
+ if ($file->isViewableImage()) {
+ $corpus = $this->buildImageCorpus($file_uri);
+ } else {
+ $corpus = $this->buildBinaryCorpus($file_uri, $data);
+ }
+ } else {
+ // Build the content of the file.
+ $corpus = $this->buildCorpus(
+ $show_blame,
+ $show_color,
+ $file_content,
+ $needs_blame,
+ $drequest,
+ $path,
+ $data);
+ }
+
+ if ($request->isAjax()) {
+ return id(new AphrontAjaxResponse())->setContent($corpus);
+ }
+
+ require_celerity_resource('diffusion-source-css');
+
+ // Render the page.
+ $view = $this->buildActionView($drequest);
+ $action_list = $this->enrichActionView(
+ $view,
+ $drequest,
+ $show_blame,
+ $show_color);
+
+ $properties = $this->buildPropertyView($drequest, $action_list);
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeader($this->buildHeaderView($drequest))
+ ->addPropertyList($properties);
+
+ $content = array();
+ $content[] = $object_box;
+
+ $follow = $request->getStr('follow');
+ if ($follow) {
+ $notice = new PHUIInfoView();
+ $notice->setSeverity(PHUIInfoView::SEVERITY_WARNING);
+ $notice->setTitle(pht('Unable to Continue'));
+ switch ($follow) {
+ case 'first':
+ $notice->appendChild(
+ pht(
+ 'Unable to continue tracing the history of this file because '.
+ 'this commit is the first commit in the repository.'));
+ break;
+ case 'created':
+ $notice->appendChild(
+ pht(
+ 'Unable to continue tracing the history of this file because '.
+ 'this commit created the file.'));
+ break;
+ }
+ $content[] = $notice;
+ }
+
+ $renamed = $request->getStr('renamed');
+ if ($renamed) {
+ $notice = new PHUIInfoView();
+ $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
+ $notice->setTitle(pht('File Renamed'));
+ $notice->appendChild(
+ pht(
+ 'File history passes through a rename from "%s" to "%s".',
+ $drequest->getPath(),
+ $renamed));
+ $content[] = $notice;
+ }
+
+ $content[] = $corpus;
+ $content[] = $this->buildOpenRevisions();
+
+ $crumbs = $this->buildCrumbs(
+ array(
+ 'branch' => true,
+ 'path' => true,
+ 'view' => 'browse',
+ ));
+
+ $basename = basename($this->getDiffusionRequest()->getPath());
+
+ return $this->newPage()
+ ->setTitle(
+ array(
+ $basename,
+ $repository->getDisplayName(),
+ ))
+ ->setCrumbs($crumbs)
+ ->appendChild($content);
+ }
+
+ public function browseDirectory(DiffusionBrowseResultSet $results) {
+ $request = $this->getRequest();
+ $drequest = $this->getDiffusionRequest();
+ $repository = $drequest->getRepository();
+
+ $reason = $results->getReasonForEmptyResultSet();
+
+ $content = array();
+ $actions = $this->buildActionView($drequest);
+ $properties = $this->buildPropertyView($drequest, $actions);
+
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeader($this->buildHeaderView($drequest))
+ ->addPropertyList($properties);
+
+ $content[] = $object_box;
+ $content[] = $this->renderSearchForm($collapsed = true);
+
+ if (!$results->isValidResults()) {
+ $empty_result = new DiffusionEmptyResultView();
+ $empty_result->setDiffusionRequest($drequest);
+ $empty_result->setDiffusionBrowseResultSet($results);
+ $empty_result->setView($request->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($request->getUser());
+
+ $browse_panel = new PHUIObjectBoxView();
+ $browse_panel->setHeaderText($drequest->getPath(), '/');
+ $browse_panel->setTable($browse_table);
+
+ $content[] = $browse_panel;
+ }
+
+ $content[] = $this->buildOpenRevisions();
+
+
+ $readme_path = $results->getReadmePath();
+ if ($readme_path) {
+ $readme_content = $this->callConduitWithDiffusionRequest(
+ 'diffusion.filecontentquery',
+ array(
+ 'path' => $readme_path,
+ 'commit' => $drequest->getStableCommit(),
+ ));
+ if ($readme_content) {
+ $content[] = id(new DiffusionReadmeView())
+ ->setUser($this->getViewer())
+ ->setPath($readme_path)
+ ->setContent($readme_content['corpus']);
+ }
+ }
+
+ $crumbs = $this->buildCrumbs(
+ array(
+ 'branch' => true,
+ 'path' => true,
+ 'view' => 'browse',
+ ));
+
+ return $this->newPage()
+ ->setTitle(
+ array(
+ nonempty(basename($drequest->getPath()), '/'),
+ $repository->getDisplayName(),
+ ))
+ ->setCrumbs($crumbs)
+ ->appendChild($content);
+ }
+
+ private function renderSearchResults() {
+ $drequest = $this->getDiffusionRequest();
+ $repository = $drequest->getRepository();
+ $results = array();
+
+ $limit = 100;
+ $page = $this->getRequest()->getInt('page', 0);
+ $pager = new PHUIPagerView();
+ $pager->setPageSize($limit);
+ $pager->setOffset($page);
+ $pager->setURI($this->getRequest()->getRequestURI(), 'page');
+
+ $search_mode = null;
+
+ switch ($repository->getVersionControlSystem()) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ $results = array();
+ break;
+ default:
+ if (strlen($this->getRequest()->getStr('grep'))) {
+ $search_mode = 'grep';
+ $query_string = $this->getRequest()->getStr('grep');
+ $results = $this->callConduitWithDiffusionRequest(
+ 'diffusion.searchquery',
+ array(
+ 'grep' => $query_string,
+ 'commit' => $drequest->getStableCommit(),
+ 'path' => $drequest->getPath(),
+ 'limit' => $limit + 1,
+ 'offset' => $page,
+ ));
+ } else { // Filename search.
+ $search_mode = 'find';
+ $query_string = $this->getRequest()->getStr('find');
+ $results = $this->callConduitWithDiffusionRequest(
+ 'diffusion.querypaths',
+ array(
+ 'pattern' => $query_string,
+ 'commit' => $drequest->getStableCommit(),
+ 'path' => $drequest->getPath(),
+ 'limit' => $limit + 1,
+ 'offset' => $page,
+ ));
+ }
+ break;
+ }
+
+ $results = $pager->sliceResults($results);
+
+ if ($search_mode == 'grep') {
+ $table = $this->renderGrepResults($results, $query_string);
+ $header = pht(
+ 'File content matching "%s" under "%s"',
+ $query_string,
+ nonempty($drequest->getPath(), '/'));
+ } else {
+ $table = $this->renderFindResults($results);
+ $header = pht(
+ 'Paths matching "%s" under "%s"',
+ $query_string,
+ nonempty($drequest->getPath(), '/'));
+ }
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeaderText($header)
+ ->setTable($table);
+
+ $pager_box = $this->renderTablePagerBox($pager);
+
+ return array($box, $pager_box);
+ }
+
+ private function renderGrepResults(array $results, $pattern) {
+ $drequest = $this->getDiffusionRequest();
+
+ require_celerity_resource('phabricator-search-results-css');
+
+ $rows = array();
+ foreach ($results as $result) {
+ list($path, $line, $string) = $result;
+
+ $href = $drequest->generateURI(array(
+ 'action' => 'browse',
+ 'path' => $path,
+ 'line' => $line,
+ ));
+
+ $matches = null;
+ $count = @preg_match_all(
+ '('.$pattern.')u',
+ $string,
+ $matches,
+ PREG_OFFSET_CAPTURE);
+
+ if (!$count) {
+ $output = ltrim($string);
+ } else {
+ $output = array();
+ $cursor = 0;
+ $length = strlen($string);
+ foreach ($matches[0] as $match) {
+ $offset = $match[1];
+ if ($cursor != $offset) {
+ $output[] = array(
+ 'text' => substr($string, $cursor, $offset),
+ 'highlight' => false,
+ );
+ }
+ $output[] = array(
+ 'text' => $match[0],
+ 'highlight' => true,
+ );
+ $cursor = $offset + strlen($match[0]);
+ }
+ if ($cursor != $length) {
+ $output[] = array(
+ 'text' => substr($string, $cursor),
+ 'highlight' => false,
+ );
+ }
+
+ if ($output) {
+ $output[0]['text'] = ltrim($output[0]['text']);
+ }
+
+ foreach ($output as $key => $segment) {
+ if ($segment['highlight']) {
+ $output[$key] = phutil_tag('strong', array(), $segment['text']);
+ } else {
+ $output[$key] = $segment['text'];
+ }
+ }
+ }
+
+ $string = phutil_tag(
+ 'pre',
+ array('class' => 'PhabricatorMonospaced phui-source-fragment'),
+ $output);
+
+ $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(
+ pht(
+ 'The pattern you searched for was not found in the content of any '.
+ 'files.'));
+
+ return $table;
+ }
+
+ private function renderFindResults(array $results) {
+ $drequest = $this->getDiffusionRequest();
+
+ $rows = array();
+ foreach ($results as $result) {
+ $href = $drequest->generateURI(array(
+ 'action' => 'browse',
+ 'path' => $result,
+ ));
+
+ $readable = Filesystem::readablePath($result, $drequest->getPath());
+
+ $rows[] = array(
+ phutil_tag('a', array('href' => $href), $readable),
+ );
+ }
+
+ $table = id(new AphrontTableView($rows))
+ ->setHeaders(array(pht('Path')))
+ ->setColumnClasses(array('wide'))
+ ->setNoDataString(
+ pht(
+ 'The pattern you searched for did not match the names of any '.
+ 'files.'));
+
+ return $table;
+ }
+
+ private function loadLintMessages() {
+ $drequest = $this->getDiffusionRequest();
+ $branch = $drequest->loadBranch();
+
+ if (!$branch || !$branch->getLintCommit()) {
+ return;
+ }
+
+ $this->lintCommit = $branch->getLintCommit();
+
+ $conn = id(new PhabricatorRepository())->establishConnection('r');
+
+ $where = '';
+ if ($drequest->getLint()) {
+ $where = qsprintf(
+ $conn,
+ 'AND code = %s',
+ $drequest->getLint());
+ }
+
+ $this->lintMessages = queryfx_all(
+ $conn,
+ 'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
+ PhabricatorRepository::TABLE_LINTMESSAGE,
+ $branch->getID(),
+ $where,
+ '/'.$drequest->getPath());
+ }
+
+ private function buildCorpus(
+ $show_blame,
+ $show_color,
+ DiffusionFileContent $file_content,
+ $needs_blame,
+ DiffusionRequest $drequest,
+ $path,
+ $data) {
+
+ if (!$show_color) {
+ $style =
+ 'border: none; width: 100%; height: 80em; font-family: monospace';
+ if (!$show_blame) {
+ $corpus = phutil_tag(
+ 'textarea',
+ array(
+ 'style' => $style,
+ ),
+ $file_content->getCorpus());
+ } else {
+ $text_list = $file_content->getTextList();
+ $rev_list = $file_content->getRevList();
+ $blame_dict = $file_content->getBlameDict();
+
+ $rows = array();
+ foreach ($text_list as $k => $line) {
+ $rev = $rev_list[$k];
+ $author = $blame_dict[$rev]['author'];
+ $rows[] =
+ sprintf('%-10s %-20s %s', substr($rev, 0, 7), $author, $line);
+ }
+
+ $corpus = phutil_tag(
+ 'textarea',
+ array(
+ 'style' => $style,
+ ),
+ implode("\n", $rows));
+ }
+ } else {
+ require_celerity_resource('syntax-highlighting-css');
+ $text_list = $file_content->getTextList();
+ $rev_list = $file_content->getRevList();
+ $blame_dict = $file_content->getBlameDict();
+
+ $text_list = implode("\n", $text_list);
+ $text_list = PhabricatorSyntaxHighlighter::highlightWithFilename(
+ $path,
+ $text_list);
+ $text_list = explode("\n", $text_list);
+
+ $rows = $this->buildDisplayRows($text_list, $rev_list, $blame_dict,
+ $needs_blame, $drequest, $show_blame, $show_color);
+
+ $corpus_table = javelin_tag(
+ 'table',
+ array(
+ 'class' => 'diffusion-source remarkup-code PhabricatorMonospaced',
+ 'sigil' => 'phabricator-source',
+ ),
+ $rows);
+
+ if ($this->getRequest()->isAjax()) {
+ return $corpus_table;
+ }
+
+ $id = celerity_generate_unique_node_id();
+
+ $repo = $drequest->getRepository();
+ $symbol_repos = nonempty($repo->getSymbolSources(), array());
+ $symbol_repos[] = $repo->getPHID();
+
+ $lang = last(explode('.', $drequest->getPath()));
+ $repo_languages = $repo->getSymbolLanguages();
+ $repo_languages = nonempty($repo_languages, array());
+ $repo_languages = array_fill_keys($repo_languages, true);
+
+ $needs_symbols = true;
+ if ($repo_languages && $symbol_repos) {
+ $have_symbols = id(new DiffusionSymbolQuery())
+ ->existsSymbolsInRepository($repo->getPHID());
+ if (!$have_symbols) {
+ $needs_symbols = false;
+ }
+ }
+
+ if ($needs_symbols && $repo_languages) {
+ $needs_symbols = isset($repo_languages[$lang]);
+ }
+
+ if ($needs_symbols) {
+ Javelin::initBehavior(
+ 'repository-crossreference',
+ array(
+ 'container' => $id,
+ 'lang' => $lang,
+ 'repositories' => $symbol_repos,
+ ));
+ }
+
+ $corpus = phutil_tag(
+ 'div',
+ array(
+ 'id' => $id,
+ ),
+ $corpus_table);
+
+ Javelin::initBehavior('load-blame', array('id' => $id));
+ }
+
+ $edit = $this->renderEditButton();
+ $file = $this->renderFileButton();
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('File Contents'))
+ ->addActionLink($edit)
+ ->addActionLink($file);
+
+ $corpus = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->appendChild($corpus)
+ ->setCollapsed(true);
+
+ return $corpus;
+ }
+
+ private function enrichActionView(
+ PhabricatorActionListView $view,
+ DiffusionRequest $drequest,
+ $show_blame,
+ $show_color) {
+
+ $viewer = $this->getRequest()->getUser();
+ $base_uri = $this->getRequest()->getRequestURI();
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Show Last Change'))
+ ->setHref(
+ $drequest->generateURI(
+ array(
+ 'action' => 'change',
+ )))
+ ->setIcon('fa-backward'));
+
+ if ($show_blame) {
+ $blame_text = pht('Disable Blame');
+ $blame_icon = 'fa-exclamation-circle lightgreytext';
+ $blame_value = 0;
+ } else {
+ $blame_text = pht('Enable Blame');
+ $blame_icon = 'fa-exclamation-circle';
+ $blame_value = 1;
+ }
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName($blame_text)
+ ->setHref($base_uri->alter('blame', $blame_value))
+ ->setIcon($blame_icon)
+ ->setUser($viewer)
+ ->setRenderAsForm($viewer->isLoggedIn()));
+
+ if ($show_color) {
+ $highlight_text = pht('Disable Highlighting');
+ $highlight_icon = 'fa-star-o grey';
+ $highlight_value = 0;
+ } else {
+ $highlight_text = pht('Enable Highlighting');
+ $highlight_icon = 'fa-star';
+ $highlight_value = 1;
+ }
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName($highlight_text)
+ ->setHref($base_uri->alter('color', $highlight_value))
+ ->setIcon($highlight_icon)
+ ->setUser($viewer)
+ ->setRenderAsForm($viewer->isLoggedIn()));
+
+ $href = null;
+ if ($this->getRequest()->getStr('lint') !== null) {
+ $lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages));
+ $href = $base_uri->alter('lint', null);
+
+ } else if ($this->lintCommit === null) {
+ $lint_text = pht('Lint not Available');
+ } else {
+ $lint_text = pht(
+ 'Show %d Lint Message(s)',
+ count($this->lintMessages));
+ $href = $this->getDiffusionRequest()->generateURI(array(
+ 'action' => 'browse',
+ 'commit' => $this->lintCommit,
+ ))->alter('lint', '');
+ }
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName($lint_text)
+ ->setHref($href)
+ ->setIcon('fa-exclamation-triangle')
+ ->setDisabled(!$href));
+
+ return $view;
+ }
+
+ private function renderEditButton() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $drequest = $this->getDiffusionRequest();
+
+ $repository = $drequest->getRepository();
+ $path = $drequest->getPath();
+ $line = nonempty((int)$drequest->getLine(), 1);
+
+ $editor_link = $user->loadEditorLink($path, $line, $repository);
+ $template = $user->loadEditorLink($path, '%l', $repository);
+
+ $icon_edit = id(new PHUIIconView())
+ ->setIconFont('fa-pencil');
+ $button = id(new PHUIButtonView())
+ ->setTag('a')
+ ->setText(pht('Open in Editor'))
+ ->setHref($editor_link)
+ ->setIcon($icon_edit)
+ ->setID('editor_link')
+ ->setMetadata(array('link_template' => $template))
+ ->setDisabled(!$editor_link);
+
+ return $button;
+ }
+
+ private function renderFileButton($file_uri = null) {
+
+ $base_uri = $this->getRequest()->getRequestURI();
+
+ if ($file_uri) {
+ $text = pht('Download Raw File');
+ $href = $file_uri;
+ $icon = 'fa-download';
+ } else {
+ $text = pht('View Raw File');
+ $href = $base_uri->alter('view', 'raw');
+ $icon = 'fa-file-text';
+ }
+
+ $iconview = id(new PHUIIconView())
+ ->setIconFont($icon);
+ $button = id(new PHUIButtonView())
+ ->setTag('a')
+ ->setText($text)
+ ->setHref($href)
+ ->setIcon($iconview);
+
+ return $button;
+ }
+
+
+ private function buildDisplayRows(
+ array $text_list,
+ array $rev_list,
+ array $blame_dict,
+ $needs_blame,
+ DiffusionRequest $drequest,
+ $show_blame,
+ $show_color) {
+
+ $handles = array();
+ if ($blame_dict) {
+ $epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch');
+ $epoch_min = min($epoch_list);
+ $epoch_max = max($epoch_list);
+ $epoch_range = ($epoch_max - $epoch_min) + 1;
+
+ $author_phids = ipull(ifilter($blame_dict, 'authorPHID'), 'authorPHID');
+ $handles = $this->loadViewerHandles($author_phids);
+ }
+
+ $line_arr = array();
+ $line_str = $drequest->getLine();
+ $ranges = explode(',', $line_str);
+ foreach ($ranges as $range) {
+ if (strpos($range, '-') !== false) {
+ list($min, $max) = explode('-', $range, 2);
+ $line_arr[] = array(
+ 'min' => min($min, $max),
+ 'max' => max($min, $max),
+ );
+ } else if (strlen($range)) {
+ $line_arr[] = array(
+ 'min' => $range,
+ 'max' => $range,
+ );
+ }
+ }
+
+ $display = array();
+
+ $line_number = 1;
+ $last_rev = null;
+ $color = null;
+ foreach ($text_list as $k => $line) {
+ $display_line = array(
+ 'epoch' => null,
+ 'commit' => null,
+ 'author' => null,
+ 'target' => null,
+ 'highlighted' => null,
+ 'line' => $line_number,
+ 'data' => $line,
+ );
+
+ if ($show_blame) {
+ // If the line's rev is same as the line above, show empty content
+ // with same color; otherwise generate blame info. The newer a change
+ // is, the more saturated the color.
+
+ $rev = idx($rev_list, $k, $last_rev);
+
+ if ($last_rev == $rev) {
+ $display_line['color'] = $color;
+ } else {
+ $blame = $blame_dict[$rev];
+
+ if (!isset($blame['epoch'])) {
+ $color = '#ffd'; // Render as warning.
+ } else {
+ $color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range;
+ $color_value = 0xE6 * (1.0 - $color_ratio);
+ $color = sprintf(
+ '#%02x%02x%02x',
+ $color_value,
+ 0xF6,
+ $color_value);
+ }
+
+ $display_line['epoch'] = idx($blame, 'epoch');
+ $display_line['color'] = $color;
+ $display_line['commit'] = $rev;
+
+ $author_phid = idx($blame, 'authorPHID');
+ if ($author_phid && $handles[$author_phid]) {
+ $author_link = $handles[$author_phid]->renderLink();
+ } else {
+ $author_link = $blame['author'];
+ }
+ $display_line['author'] = $author_link;
+
+ $last_rev = $rev;
+ }
+ }
+
+ if ($line_arr) {
+ if ($line_number == $line_arr[0]['min']) {
+ $display_line['target'] = true;
+ }
+ foreach ($line_arr as $range) {
+ if ($line_number >= $range['min'] &&
+ $line_number <= $range['max']) {
+ $display_line['highlighted'] = true;
+ }
+ }
+ }
+
+ $display[] = $display_line;
+ ++$line_number;
+ }
+
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $commits = array_filter(ipull($display, 'commit'));
+ if ($commits) {
+ $commits = id(new DiffusionCommitQuery())
+ ->setViewer($viewer)
+ ->withRepository($drequest->getRepository())
+ ->withIdentifiers($commits)
+ ->execute();
+ $commits = mpull($commits, null, 'getCommitIdentifier');
+ }
+
+ $revision_ids = id(new DifferentialRevision())
+ ->loadIDsByCommitPHIDs(mpull($commits, 'getPHID'));
+ $revisions = array();
+ if ($revision_ids) {
+ $revisions = id(new DifferentialRevisionQuery())
+ ->setViewer($viewer)
+ ->withIDs($revision_ids)
+ ->execute();
+ }
+
+ $phids = array();
+ foreach ($commits as $commit) {
+ if ($commit->getAuthorPHID()) {
+ $phids[] = $commit->getAuthorPHID();
+ }
+ }
+ foreach ($revisions as $revision) {
+ if ($revision->getAuthorPHID()) {
+ $phids[] = $revision->getAuthorPHID();
+ }
+ }
+ $handles = $this->loadViewerHandles($phids);
+
+ Javelin::initBehavior('phabricator-oncopy', array());
+
+ $engine = null;
+ $inlines = array();
+ if ($this->getRequest()->getStr('lint') !== null && $this->lintMessages) {
+ $engine = new PhabricatorMarkupEngine();
+ $engine->setViewer($viewer);
+
+ foreach ($this->lintMessages as $message) {
+ $inline = id(new PhabricatorAuditInlineComment())
+ ->setSyntheticAuthor(
+ ArcanistLintSeverity::getStringForSeverity($message['severity']).
+ ' '.$message['code'].' ('.$message['name'].')')
+ ->setLineNumber($message['line'])
+ ->setContent($message['description']);
+ $inlines[$message['line']][] = $inline;
+
+ $engine->addObject(
+ $inline,
+ PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
+ }
+
+ $engine->process();
+ require_celerity_resource('differential-changeset-view-css');
+ }
+
+ $rows = $this->renderInlines(
+ idx($inlines, 0, array()),
+ $show_blame,
+ (bool)$this->coverage,
+ $engine);
+
+ foreach ($display as $line) {
+
+ $line_href = $drequest->generateURI(
+ array(
+ 'action' => 'browse',
+ 'line' => $line['line'],
+ 'stable' => true,
+ ));
+
+ $blame = array();
+ $style = null;
+ if (array_key_exists('color', $line)) {
+ if ($line['color']) {
+ $style = 'background: '.$line['color'].';';
+ }
+
+ $before_link = null;
+ $commit_link = null;
+ $revision_link = null;
+ if (idx($line, 'commit')) {
+ $commit = $line['commit'];
+
+ if (idx($commits, $commit)) {
+ $tooltip = $this->renderCommitTooltip(
+ $commits[$commit],
+ $handles,
+ $line['author']);
+ } else {
+ $tooltip = null;
+ }
+
+ Javelin::initBehavior('phabricator-tooltips', array());
+ require_celerity_resource('aphront-tooltip-css');
+
+ $commit_link = javelin_tag(
+ 'a',
+ array(
+ 'href' => $drequest->generateURI(
+ array(
+ 'action' => 'commit',
+ 'commit' => $line['commit'],
+ )),
+ 'sigil' => 'has-tooltip',
+ 'meta' => array(
+ 'tip' => $tooltip,
+ 'align' => 'E',
+ 'size' => 600,
+ ),
+ ),
+ id(new PhutilUTF8StringTruncator())
+ ->setMaximumGlyphs(9)
+ ->setTerminator('')
+ ->truncateString($line['commit']));
+
+ $revision_id = null;
+ if (idx($commits, $commit)) {
+ $revision_id = idx($revision_ids, $commits[$commit]->getPHID());
+ }
+
+ if ($revision_id) {
+ $revision = idx($revisions, $revision_id);
+ if ($revision) {
+ $tooltip = $this->renderRevisionTooltip($revision, $handles);
+ $revision_link = javelin_tag(
+ 'a',
+ array(
+ 'href' => '/D'.$revision->getID(),
+ 'sigil' => 'has-tooltip',
+ 'meta' => array(
+ 'tip' => $tooltip,
+ 'align' => 'E',
+ 'size' => 600,
+ ),
+ ),
+ 'D'.$revision->getID());
+ }
+ }
+
+ $uri = $line_href->alter('before', $commit);
+ $before_link = javelin_tag(
+ 'a',
+ array(
+ 'href' => $uri->setQueryParam('view', 'blame'),
+ 'sigil' => 'has-tooltip',
+ 'meta' => array(
+ 'tip' => pht('Skip Past This Commit'),
+ 'align' => 'E',
+ 'size' => 300,
+ ),
+ ),
+ "\xC2\xAB");
+ }
+
+ $blame[] = phutil_tag(
+ 'th',
+ array(
+ 'class' => 'diffusion-blame-link',
+ ),
+ $before_link);
+
+ $object_links = array();
+ $object_links[] = $commit_link;
+ if ($revision_link) {
+ $object_links[] = phutil_tag('span', array(), '/');
+ $object_links[] = $revision_link;
+ }
+
+ $blame[] = phutil_tag(
+ 'th',
+ array(
+ 'class' => 'diffusion-rev-link',
+ ),
+ $object_links);
+ }
+
+ $line_link = phutil_tag(
+ 'a',
+ array(
+ 'href' => $line_href,
+ 'style' => $style,
+ ),
+ $line['line']);
+
+ $blame[] = javelin_tag(
+ 'th',
+ array(
+ 'class' => 'diffusion-line-link',
+ 'sigil' => 'phabricator-source-line',
+ 'style' => $style,
+ ),
+ $line_link);
+
+ Javelin::initBehavior('phabricator-line-linker');
+
+ if ($line['target']) {
+ Javelin::initBehavior(
+ 'diffusion-jump-to',
+ array(
+ 'target' => 'scroll_target',
+ ));
+ $anchor_text = phutil_tag(
+ 'a',
+ array(
+ 'id' => 'scroll_target',
+ ),
+ '');
+ } else {
+ $anchor_text = null;
+ }
+
+ $blame[] = phutil_tag(
+ 'td',
+ array(
+ ),
+ array(
+ $anchor_text,
+
+ // NOTE: See phabricator-oncopy behavior.
+ "\xE2\x80\x8B",
+
+ // TODO: [HTML] Not ideal.
+ phutil_safe_html(str_replace("\t", ' ', $line['data'])),
+ ));
+
+ if ($this->coverage) {
+ require_celerity_resource('differential-changeset-view-css');
+ $cov_index = $line['line'] - 1;
+
+ if (isset($this->coverage[$cov_index])) {
+ $cov_class = $this->coverage[$cov_index];
+ } else {
+ $cov_class = 'N';
+ }
+
+ $blame[] = phutil_tag(
+ 'td',
+ array(
+ 'class' => 'cov cov-'.$cov_class,
+ ),
+ '');
+ }
+
+ $rows[] = phutil_tag(
+ 'tr',
+ array(
+ 'class' => ($line['highlighted'] ?
+ 'phabricator-source-highlight' :
+ null),
+ ),
+ $blame);
+
+ $cur_inlines = $this->renderInlines(
+ idx($inlines, $line['line'], array()),
+ $show_blame,
+ $this->coverage,
+ $engine);
+ foreach ($cur_inlines as $cur_inline) {
+ $rows[] = $cur_inline;
+ }
+ }
+
+ return $rows;
+ }
+
+ private function renderInlines(
+ array $inlines,
+ $needs_blame,
+ $has_coverage,
+ $engine) {
+
+ $rows = array();
+ foreach ($inlines as $inline) {
+
+ // TODO: This should use modern scaffolding code.
+
+ $inline_view = id(new PHUIDiffInlineCommentDetailView())
+ ->setUser($this->getViewer())
+ ->setMarkupEngine($engine)
+ ->setInlineComment($inline)
+ ->render();
+
+ $row = array_fill(0, ($needs_blame ? 3 : 1), phutil_tag('th'));
+
+ $row[] = phutil_tag('td', array(), $inline_view);
+
+ if ($has_coverage) {
+ $row[] = phutil_tag(
+ 'td',
+ array(
+ 'class' => 'cov cov-I',
+ ));
+ }
+
+ $rows[] = phutil_tag('tr', array('class' => 'inline'), $row);
+ }
+
+ return $rows;
+ }
+
+ private function loadFileForData($path, $data) {
+ $file = PhabricatorFile::buildFromFileDataOrHash(
+ $data,
+ array(
+ 'name' => basename($path),
+ 'ttl' => time() + 60 * 60 * 24,
+ 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
+ ));
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ $file->attachToObject(
+ $this->getDiffusionRequest()->getRepository()->getPHID());
+ unset($unguarded);
+
+ return $file;
+ }
+
+ private function buildRawResponse($path, $data) {
+ $file = $this->loadFileForData($path, $data);
+ return $file->getRedirectResponse();
+ }
+
+ private function buildImageCorpus($file_uri) {
+ $properties = new PHUIPropertyListView();
+
+ $properties->addImageContent(
+ phutil_tag(
+ 'img',
+ array(
+ 'src' => $file_uri,
+ )));
+
+ $file = $this->renderFileButton($file_uri);
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Image'))
+ ->addActionLink($file);
+
+ return id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($properties);
+ }
+
+ private function buildBinaryCorpus($file_uri, $data) {
+
+ $size = new PhutilNumber(strlen($data));
+ $text = pht('This is a binary file. It is %s byte(s) in length.', $size);
+ $text = id(new PHUIBoxView())
+ ->addPadding(PHUI::PADDING_LARGE)
+ ->appendChild($text);
+
+ $file = $this->renderFileButton($file_uri);
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Details'))
+ ->addActionLink($file);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->appendChild($text);
+
+ return $box;
+ }
+
+ private function buildErrorCorpus($message) {
+ $text = id(new PHUIBoxView())
+ ->addPadding(PHUI::PADDING_LARGE)
+ ->appendChild($message);
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Details'));
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->appendChild($text);
+
+ return $box;
+ }
+
+ private function buildBeforeResponse($before) {
+ $request = $this->getRequest();
+ $drequest = $this->getDiffusionRequest();
+
+ // NOTE: We need to get the grandparent so we can capture filename changes
+ // in the parent.
+
+ $parent = $this->loadParentCommitOf($before);
+ $old_filename = null;
+ $was_created = false;
+ if ($parent) {
+ $grandparent = $this->loadParentCommitOf($parent);
+
+ if ($grandparent) {
+ $rename_query = new DiffusionRenameHistoryQuery();
+ $rename_query->setRequest($drequest);
+ $rename_query->setOldCommit($grandparent);
+ $rename_query->setViewer($request->getUser());
+ $old_filename = $rename_query->loadOldFilename();
+ $was_created = $rename_query->getWasCreated();
+ }
+ }
+
+ $follow = null;
+ if ($was_created) {
+ // If the file was created in history, that means older commits won't
+ // have it. Since we know it existed at 'before', it must have been
+ // created then; jump there.
+ $target_commit = $before;
+ $follow = 'created';
+ } else if ($parent) {
+ // If we found a parent, jump to it. This is the normal case.
+ $target_commit = $parent;
+ } else {
+ // If there's no parent, this was probably created in the initial commit?
+ // And the "was_created" check will fail because we can't identify the
+ // grandparent. Keep the user at 'before'.
+ $target_commit = $before;
+ $follow = 'first';
+ }
+
+ $path = $drequest->getPath();
+ $renamed = null;
+ if ($old_filename !== null &&
+ $old_filename !== '/'.$path) {
+ $renamed = $path;
+ $path = $old_filename;
+ }
+
+ $line = null;
+ // If there's a follow error, drop the line so the user sees the message.
+ if (!$follow) {
+ $line = $this->getBeforeLineNumber($target_commit);
+ }
+
+ $before_uri = $drequest->generateURI(
+ array(
+ 'action' => 'browse',
+ 'commit' => $target_commit,
+ 'line' => $line,
+ 'path' => $path,
+ ));
+
+ $before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
+ $before_uri = $before_uri->alter('before', null);
+ $before_uri = $before_uri->alter('renamed', $renamed);
+ $before_uri = $before_uri->alter('follow', $follow);
+
+ return id(new AphrontRedirectResponse())->setURI($before_uri);
+ }
+
+ private function getBeforeLineNumber($target_commit) {
+ $drequest = $this->getDiffusionRequest();
+
+ $line = $drequest->getLine();
+ if (!$line) {
+ return null;
+ }
+
+ $raw_diff = $this->callConduitWithDiffusionRequest(
+ 'diffusion.rawdiffquery',
+ array(
+ 'commit' => $drequest->getCommit(),
+ 'path' => $drequest->getPath(),
+ 'againstCommit' => $target_commit,
+ ));
+ $old_line = 0;
+ $new_line = 0;
+
+ foreach (explode("\n", $raw_diff) as $text) {
+ if ($text[0] == '-' || $text[0] == ' ') {
+ $old_line++;
+ }
+ if ($text[0] == '+' || $text[0] == ' ') {
+ $new_line++;
+ }
+ if ($new_line == $line) {
+ return $old_line;
+ }
+ }
+
+ // We didn't find the target line.
+ return $line;
+ }
+
+ private function loadParentCommitOf($commit) {
+ $drequest = $this->getDiffusionRequest();
+ $user = $this->getRequest()->getUser();
+
+ $before_req = DiffusionRequest::newFromDictionary(
+ array(
+ 'user' => $user,
+ 'repository' => $drequest->getRepository(),
+ 'commit' => $commit,
+ ));
+
+ $parents = DiffusionQuery::callConduitWithDiffusionRequest(
+ $user,
+ $before_req,
+ 'diffusion.commitparentsquery',
+ array(
+ 'commit' => $commit,
+ ));
+
+ return head($parents);
+ }
+
+ private function renderRevisionTooltip(
+ DifferentialRevision $revision,
+ array $handles) {
+ $viewer = $this->getRequest()->getUser();
+
+ $date = phabricator_date($revision->getDateModified(), $viewer);
+ $id = $revision->getID();
+ $title = $revision->getTitle();
+ $header = "D{$id} {$title}";
+
+ $author = $handles[$revision->getAuthorPHID()]->getName();
+
+ return "{$header}\n{$date} \xC2\xB7 {$author}";
+ }
+
+ private function renderCommitTooltip(
+ PhabricatorRepositoryCommit $commit,
+ array $handles,
+ $author) {
+
+ $viewer = $this->getRequest()->getUser();
+
+ $date = phabricator_date($commit->getEpoch(), $viewer);
+ $summary = trim($commit->getSummary());
+
+ if ($commit->getAuthorPHID()) {
+ $author = $handles[$commit->getAuthorPHID()]->getName();
+ }
+
+ return "{$summary}\n{$date} \xC2\xB7 {$author}";
+ }
+
protected function renderSearchForm($collapsed) {
$drequest = $this->getDiffusionRequest();
@@ -204,8 +1678,8 @@
return $view;
}
- protected function buildOpenRevisions() {
- $user = $this->getRequest()->getUser();
+ private function buildOpenRevisions() {
+ $viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
@@ -220,7 +1694,7 @@
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
$revisions = id(new DifferentialRevisionQuery())
- ->setViewer($user)
+ ->setViewer($viewer)
->withPath($repository->getID(), $path_id)
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->withUpdatedEpochBetween($recent, null)
@@ -243,7 +1717,7 @@
$view = id(new DifferentialRevisionListView())
->setHeader($header)
->setRevisions($revisions)
- ->setUser($user);
+ ->setUser($viewer);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
diff --git a/src/applications/diffusion/controller/DiffusionBrowseDirectoryController.php b/src/applications/diffusion/controller/DiffusionBrowseDirectoryController.php
deleted file mode 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseDirectoryController.php
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-
-final class DiffusionBrowseDirectoryController
- extends DiffusionBrowseController {
-
- private $browseQueryResults;
-
- public function setBrowseQueryResults(DiffusionBrowseResultSet $results) {
- $this->browseQueryResults = $results;
- return $this;
- }
-
- public function getBrowseQueryResults() {
- return $this->browseQueryResults;
- }
-
- protected function processDiffusionRequest(AphrontRequest $request) {
- $drequest = $this->diffusionRequest;
-
- $results = $this->getBrowseQueryResults();
- $reason = $results->getReasonForEmptyResultSet();
-
- $content = array();
- $actions = $this->buildActionView($drequest);
- $properties = $this->buildPropertyView($drequest, $actions);
-
- $object_box = id(new PHUIObjectBoxView())
- ->setHeader($this->buildHeaderView($drequest))
- ->addPropertyList($properties);
-
- $content[] = $object_box;
- $content[] = $this->renderSearchForm($collapsed = true);
-
- if (!$results->isValidResults()) {
- $empty_result = new DiffusionEmptyResultView();
- $empty_result->setDiffusionRequest($drequest);
- $empty_result->setDiffusionBrowseResultSet($results);
- $empty_result->setView($request->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($request->getUser());
-
- $browse_panel = new PHUIObjectBoxView();
- $browse_panel->setHeaderText($drequest->getPath(), '/');
- $browse_panel->setTable($browse_table);
-
- $content[] = $browse_panel;
- }
-
- $content[] = $this->buildOpenRevisions();
-
-
- $readme_path = $results->getReadmePath();
- if ($readme_path) {
- $readme_content = $this->callConduitWithDiffusionRequest(
- 'diffusion.filecontentquery',
- array(
- 'path' => $readme_path,
- 'commit' => $drequest->getStableCommit(),
- ));
- if ($readme_content) {
- $content[] = id(new DiffusionReadmeView())
- ->setUser($this->getViewer())
- ->setPath($readme_path)
- ->setContent($readme_content['corpus']);
- }
- }
-
- $crumbs = $this->buildCrumbs(
- array(
- 'branch' => true,
- 'path' => true,
- 'view' => 'browse',
- ));
-
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $content,
- ),
- array(
- 'title' => array(
- nonempty(basename($drequest->getPath()), '/'),
- $drequest->getRepository()->getDisplayName(),
- ),
- ));
- }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionBrowseFileController.php b/src/applications/diffusion/controller/DiffusionBrowseFileController.php
deleted file mode 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseFileController.php
+++ /dev/null
@@ -1,1134 +0,0 @@
-<?php
-
-final class DiffusionBrowseFileController extends DiffusionBrowseController {
-
- private $lintCommit;
- private $lintMessages;
- private $coverage;
-
- protected function processDiffusionRequest(AphrontRequest $request) {
- $drequest = $this->getDiffusionRequest();
- $viewer = $request->getUser();
-
- $before = $request->getStr('before');
- if ($before) {
- return $this->buildBeforeResponse($before);
- }
-
- $path = $drequest->getPath();
-
- $preferences = $viewer->loadPreferences();
-
- $show_blame = $request->getBool(
- 'blame',
- $preferences->getPreference(
- PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME,
- false));
- $show_color = $request->getBool(
- 'color',
- $preferences->getPreference(
- PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR,
- true));
-
- $view = $request->getStr('view');
- if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) {
- $preferences->setPreference(
- PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME,
- $show_blame);
- $preferences->setPreference(
- PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR,
- $show_color);
- $preferences->save();
-
- $uri = $request->getRequestURI()
- ->alter('blame', null)
- ->alter('color', null);
-
- return id(new AphrontRedirectResponse())->setURI($uri);
- }
-
- // We need the blame information if blame is on and we're building plain
- // text, or blame is on and this is an Ajax request. If blame is on and
- // this is a colorized request, we don't show blame at first (we ajax it
- // in afterward) so we don't need to query for it.
- $needs_blame = ($show_blame && !$show_color) ||
- ($show_blame && $request->isAjax());
-
- $params = array(
- 'commit' => $drequest->getCommit(),
- 'path' => $drequest->getPath(),
- 'needsBlame' => $needs_blame,
- );
-
- $byte_limit = null;
- if ($view !== 'raw') {
- $byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();
- $time_limit = 10;
-
- $params += array(
- 'timeout' => $time_limit,
- 'byteLimit' => $byte_limit,
- );
- }
-
- $file_content = DiffusionFileContent::newFromConduit(
- $this->callConduitWithDiffusionRequest(
- 'diffusion.filecontentquery',
- $params));
- $data = $file_content->getCorpus();
-
- if ($view === 'raw') {
- return $this->buildRawResponse($path, $data);
- }
-
- $this->loadLintMessages();
- $this->coverage = $drequest->loadCoverage();
-
- if ($byte_limit && (strlen($data) == $byte_limit)) {
- $corpus = $this->buildErrorCorpus(
- pht(
- 'This file is larger than %s byte(s), and too large to display '.
- 'in the web UI.',
- $byte_limit));
- } else if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
- $file = $this->loadFileForData($path, $data);
- $file_uri = $file->getBestURI();
-
- if ($file->isViewableImage()) {
- $corpus = $this->buildImageCorpus($file_uri);
- } else {
- $corpus = $this->buildBinaryCorpus($file_uri, $data);
- }
- } else {
- // Build the content of the file.
- $corpus = $this->buildCorpus(
- $show_blame,
- $show_color,
- $file_content,
- $needs_blame,
- $drequest,
- $path,
- $data);
- }
-
- if ($request->isAjax()) {
- return id(new AphrontAjaxResponse())->setContent($corpus);
- }
-
- require_celerity_resource('diffusion-source-css');
-
- // Render the page.
- $view = $this->buildActionView($drequest);
- $action_list = $this->enrichActionView(
- $view,
- $drequest,
- $show_blame,
- $show_color);
-
- $properties = $this->buildPropertyView($drequest, $action_list);
- $object_box = id(new PHUIObjectBoxView())
- ->setHeader($this->buildHeaderView($drequest))
- ->addPropertyList($properties);
-
- $content = array();
- $content[] = $object_box;
-
- $follow = $request->getStr('follow');
- if ($follow) {
- $notice = new PHUIInfoView();
- $notice->setSeverity(PHUIInfoView::SEVERITY_WARNING);
- $notice->setTitle(pht('Unable to Continue'));
- switch ($follow) {
- case 'first':
- $notice->appendChild(
- pht(
- 'Unable to continue tracing the history of this file because '.
- 'this commit is the first commit in the repository.'));
- break;
- case 'created':
- $notice->appendChild(
- pht(
- 'Unable to continue tracing the history of this file because '.
- 'this commit created the file.'));
- break;
- }
- $content[] = $notice;
- }
-
- $renamed = $request->getStr('renamed');
- if ($renamed) {
- $notice = new PHUIInfoView();
- $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
- $notice->setTitle(pht('File Renamed'));
- $notice->appendChild(
- pht(
- "File history passes through a rename from '%s' to '%s'.",
- $drequest->getPath(), $renamed));
- $content[] = $notice;
- }
-
- $content[] = $corpus;
- $content[] = $this->buildOpenRevisions();
-
- $crumbs = $this->buildCrumbs(
- array(
- 'branch' => true,
- 'path' => true,
- 'view' => 'browse',
- ));
-
- $basename = basename($this->getDiffusionRequest()->getPath());
-
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $content,
- ),
- array(
- 'title' => $basename,
- ));
- }
-
- private function loadLintMessages() {
- $drequest = $this->getDiffusionRequest();
- $branch = $drequest->loadBranch();
-
- if (!$branch || !$branch->getLintCommit()) {
- return;
- }
-
- $this->lintCommit = $branch->getLintCommit();
-
- $conn = id(new PhabricatorRepository())->establishConnection('r');
-
- $where = '';
- if ($drequest->getLint()) {
- $where = qsprintf(
- $conn,
- 'AND code = %s',
- $drequest->getLint());
- }
-
- $this->lintMessages = queryfx_all(
- $conn,
- 'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
- PhabricatorRepository::TABLE_LINTMESSAGE,
- $branch->getID(),
- $where,
- '/'.$drequest->getPath());
- }
-
- private function buildCorpus(
- $show_blame,
- $show_color,
- DiffusionFileContent $file_content,
- $needs_blame,
- DiffusionRequest $drequest,
- $path,
- $data) {
-
- if (!$show_color) {
- $style =
- 'border: none; width: 100%; height: 80em; font-family: monospace';
- if (!$show_blame) {
- $corpus = phutil_tag(
- 'textarea',
- array(
- 'style' => $style,
- ),
- $file_content->getCorpus());
- } else {
- $text_list = $file_content->getTextList();
- $rev_list = $file_content->getRevList();
- $blame_dict = $file_content->getBlameDict();
-
- $rows = array();
- foreach ($text_list as $k => $line) {
- $rev = $rev_list[$k];
- $author = $blame_dict[$rev]['author'];
- $rows[] =
- sprintf('%-10s %-20s %s', substr($rev, 0, 7), $author, $line);
- }
-
- $corpus = phutil_tag(
- 'textarea',
- array(
- 'style' => $style,
- ),
- implode("\n", $rows));
- }
- } else {
- require_celerity_resource('syntax-highlighting-css');
- $text_list = $file_content->getTextList();
- $rev_list = $file_content->getRevList();
- $blame_dict = $file_content->getBlameDict();
-
- $text_list = implode("\n", $text_list);
- $text_list = PhabricatorSyntaxHighlighter::highlightWithFilename(
- $path,
- $text_list);
- $text_list = explode("\n", $text_list);
-
- $rows = $this->buildDisplayRows($text_list, $rev_list, $blame_dict,
- $needs_blame, $drequest, $show_blame, $show_color);
-
- $corpus_table = javelin_tag(
- 'table',
- array(
- 'class' => 'diffusion-source remarkup-code PhabricatorMonospaced',
- 'sigil' => 'phabricator-source',
- ),
- $rows);
-
- if ($this->getRequest()->isAjax()) {
- return $corpus_table;
- }
-
- $id = celerity_generate_unique_node_id();
-
- $repo = $drequest->getRepository();
- $symbol_repos = nonempty($repo->getSymbolSources(), array());
- $symbol_repos[] = $repo->getPHID();
-
- $lang = last(explode('.', $drequest->getPath()));
- $repo_languages = $repo->getSymbolLanguages();
- $repo_languages = nonempty($repo_languages, array());
- $repo_languages = array_fill_keys($repo_languages, true);
-
- $needs_symbols = true;
- if ($repo_languages && $symbol_repos) {
- $have_symbols = id(new DiffusionSymbolQuery())
- ->existsSymbolsInRepository($repo->getPHID());
- if (!$have_symbols) {
- $needs_symbols = false;
- }
- }
-
- if ($needs_symbols && $repo_languages) {
- $needs_symbols = isset($repo_languages[$lang]);
- }
-
- if ($needs_symbols) {
- Javelin::initBehavior(
- 'repository-crossreference',
- array(
- 'container' => $id,
- 'lang' => $lang,
- 'repositories' => $symbol_repos,
- ));
- }
-
- $corpus = phutil_tag(
- 'div',
- array(
- 'id' => $id,
- ),
- $corpus_table);
-
- Javelin::initBehavior('load-blame', array('id' => $id));
- }
-
- $edit = $this->renderEditButton();
- $file = $this->renderFileButton();
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('File Contents'))
- ->addActionLink($edit)
- ->addActionLink($file);
-
- $corpus = id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->appendChild($corpus)
- ->setCollapsed(true);
-
- return $corpus;
- }
-
- private function enrichActionView(
- PhabricatorActionListView $view,
- DiffusionRequest $drequest,
- $show_blame,
- $show_color) {
-
- $viewer = $this->getRequest()->getUser();
- $base_uri = $this->getRequest()->getRequestURI();
-
- $view->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Show Last Change'))
- ->setHref(
- $drequest->generateURI(
- array(
- 'action' => 'change',
- )))
- ->setIcon('fa-backward'));
-
- if ($show_blame) {
- $blame_text = pht('Disable Blame');
- $blame_icon = 'fa-exclamation-circle lightgreytext';
- $blame_value = 0;
- } else {
- $blame_text = pht('Enable Blame');
- $blame_icon = 'fa-exclamation-circle';
- $blame_value = 1;
- }
-
- $view->addAction(
- id(new PhabricatorActionView())
- ->setName($blame_text)
- ->setHref($base_uri->alter('blame', $blame_value))
- ->setIcon($blame_icon)
- ->setUser($viewer)
- ->setRenderAsForm($viewer->isLoggedIn()));
-
- if ($show_color) {
- $highlight_text = pht('Disable Highlighting');
- $highlight_icon = 'fa-star-o grey';
- $highlight_value = 0;
- } else {
- $highlight_text = pht('Enable Highlighting');
- $highlight_icon = 'fa-star';
- $highlight_value = 1;
- }
-
- $view->addAction(
- id(new PhabricatorActionView())
- ->setName($highlight_text)
- ->setHref($base_uri->alter('color', $highlight_value))
- ->setIcon($highlight_icon)
- ->setUser($viewer)
- ->setRenderAsForm($viewer->isLoggedIn()));
-
- $href = null;
- if ($this->getRequest()->getStr('lint') !== null) {
- $lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages));
- $href = $base_uri->alter('lint', null);
-
- } else if ($this->lintCommit === null) {
- $lint_text = pht('Lint not Available');
- } else {
- $lint_text = pht(
- 'Show %d Lint Message(s)',
- count($this->lintMessages));
- $href = $this->getDiffusionRequest()->generateURI(array(
- 'action' => 'browse',
- 'commit' => $this->lintCommit,
- ))->alter('lint', '');
- }
-
- $view->addAction(
- id(new PhabricatorActionView())
- ->setName($lint_text)
- ->setHref($href)
- ->setIcon('fa-exclamation-triangle')
- ->setDisabled(!$href));
-
- return $view;
- }
-
- private function renderEditButton() {
- $request = $this->getRequest();
- $user = $request->getUser();
-
- $drequest = $this->getDiffusionRequest();
-
- $repository = $drequest->getRepository();
- $path = $drequest->getPath();
- $line = nonempty((int)$drequest->getLine(), 1);
-
- $editor_link = $user->loadEditorLink($path, $line, $repository);
- $template = $user->loadEditorLink($path, '%l', $repository);
-
- $icon_edit = id(new PHUIIconView())
- ->setIconFont('fa-pencil');
- $button = id(new PHUIButtonView())
- ->setTag('a')
- ->setText(pht('Open in Editor'))
- ->setHref($editor_link)
- ->setIcon($icon_edit)
- ->setID('editor_link')
- ->setMetadata(array('link_template' => $template))
- ->setDisabled(!$editor_link);
-
- return $button;
- }
-
- private function renderFileButton($file_uri = null) {
-
- $base_uri = $this->getRequest()->getRequestURI();
-
- if ($file_uri) {
- $text = pht('Download Raw File');
- $href = $file_uri;
- $icon = 'fa-download';
- } else {
- $text = pht('View Raw File');
- $href = $base_uri->alter('view', 'raw');
- $icon = 'fa-file-text';
- }
-
- $iconview = id(new PHUIIconView())
- ->setIconFont($icon);
- $button = id(new PHUIButtonView())
- ->setTag('a')
- ->setText($text)
- ->setHref($href)
- ->setIcon($iconview);
-
- return $button;
- }
-
-
- private function buildDisplayRows(
- array $text_list,
- array $rev_list,
- array $blame_dict,
- $needs_blame,
- DiffusionRequest $drequest,
- $show_blame,
- $show_color) {
-
- $handles = array();
- if ($blame_dict) {
- $epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch');
- $epoch_min = min($epoch_list);
- $epoch_max = max($epoch_list);
- $epoch_range = ($epoch_max - $epoch_min) + 1;
-
- $author_phids = ipull(ifilter($blame_dict, 'authorPHID'), 'authorPHID');
- $handles = $this->loadViewerHandles($author_phids);
- }
-
- $line_arr = array();
- $line_str = $drequest->getLine();
- $ranges = explode(',', $line_str);
- foreach ($ranges as $range) {
- if (strpos($range, '-') !== false) {
- list($min, $max) = explode('-', $range, 2);
- $line_arr[] = array(
- 'min' => min($min, $max),
- 'max' => max($min, $max),
- );
- } else if (strlen($range)) {
- $line_arr[] = array(
- 'min' => $range,
- 'max' => $range,
- );
- }
- }
-
- $display = array();
-
- $line_number = 1;
- $last_rev = null;
- $color = null;
- foreach ($text_list as $k => $line) {
- $display_line = array(
- 'epoch' => null,
- 'commit' => null,
- 'author' => null,
- 'target' => null,
- 'highlighted' => null,
- 'line' => $line_number,
- 'data' => $line,
- );
-
- if ($show_blame) {
- // If the line's rev is same as the line above, show empty content
- // with same color; otherwise generate blame info. The newer a change
- // is, the more saturated the color.
-
- $rev = idx($rev_list, $k, $last_rev);
-
- if ($last_rev == $rev) {
- $display_line['color'] = $color;
- } else {
- $blame = $blame_dict[$rev];
-
- if (!isset($blame['epoch'])) {
- $color = '#ffd'; // Render as warning.
- } else {
- $color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range;
- $color_value = 0xE6 * (1.0 - $color_ratio);
- $color = sprintf(
- '#%02x%02x%02x',
- $color_value,
- 0xF6,
- $color_value);
- }
-
- $display_line['epoch'] = idx($blame, 'epoch');
- $display_line['color'] = $color;
- $display_line['commit'] = $rev;
-
- $author_phid = idx($blame, 'authorPHID');
- if ($author_phid && $handles[$author_phid]) {
- $author_link = $handles[$author_phid]->renderLink();
- } else {
- $author_link = $blame['author'];
- }
- $display_line['author'] = $author_link;
-
- $last_rev = $rev;
- }
- }
-
- if ($line_arr) {
- if ($line_number == $line_arr[0]['min']) {
- $display_line['target'] = true;
- }
- foreach ($line_arr as $range) {
- if ($line_number >= $range['min'] &&
- $line_number <= $range['max']) {
- $display_line['highlighted'] = true;
- }
- }
- }
-
- $display[] = $display_line;
- ++$line_number;
- }
-
- $request = $this->getRequest();
- $viewer = $request->getUser();
-
- $commits = array_filter(ipull($display, 'commit'));
- if ($commits) {
- $commits = id(new DiffusionCommitQuery())
- ->setViewer($viewer)
- ->withRepository($drequest->getRepository())
- ->withIdentifiers($commits)
- ->execute();
- $commits = mpull($commits, null, 'getCommitIdentifier');
- }
-
- $revision_ids = id(new DifferentialRevision())
- ->loadIDsByCommitPHIDs(mpull($commits, 'getPHID'));
- $revisions = array();
- if ($revision_ids) {
- $revisions = id(new DifferentialRevisionQuery())
- ->setViewer($viewer)
- ->withIDs($revision_ids)
- ->execute();
- }
-
- $phids = array();
- foreach ($commits as $commit) {
- if ($commit->getAuthorPHID()) {
- $phids[] = $commit->getAuthorPHID();
- }
- }
- foreach ($revisions as $revision) {
- if ($revision->getAuthorPHID()) {
- $phids[] = $revision->getAuthorPHID();
- }
- }
- $handles = $this->loadViewerHandles($phids);
-
- Javelin::initBehavior('phabricator-oncopy', array());
-
- $engine = null;
- $inlines = array();
- if ($this->getRequest()->getStr('lint') !== null && $this->lintMessages) {
- $engine = new PhabricatorMarkupEngine();
- $engine->setViewer($viewer);
-
- foreach ($this->lintMessages as $message) {
- $inline = id(new PhabricatorAuditInlineComment())
- ->setSyntheticAuthor(
- ArcanistLintSeverity::getStringForSeverity($message['severity']).
- ' '.$message['code'].' ('.$message['name'].')')
- ->setLineNumber($message['line'])
- ->setContent($message['description']);
- $inlines[$message['line']][] = $inline;
-
- $engine->addObject(
- $inline,
- PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
- }
-
- $engine->process();
- require_celerity_resource('differential-changeset-view-css');
- }
-
- $rows = $this->renderInlines(
- idx($inlines, 0, array()),
- $show_blame,
- (bool)$this->coverage,
- $engine);
-
- foreach ($display as $line) {
-
- $line_href = $drequest->generateURI(
- array(
- 'action' => 'browse',
- 'line' => $line['line'],
- 'stable' => true,
- ));
-
- $blame = array();
- $style = null;
- if (array_key_exists('color', $line)) {
- if ($line['color']) {
- $style = 'background: '.$line['color'].';';
- }
-
- $before_link = null;
- $commit_link = null;
- $revision_link = null;
- if (idx($line, 'commit')) {
- $commit = $line['commit'];
-
- if (idx($commits, $commit)) {
- $tooltip = $this->renderCommitTooltip(
- $commits[$commit],
- $handles,
- $line['author']);
- } else {
- $tooltip = null;
- }
-
- Javelin::initBehavior('phabricator-tooltips', array());
- require_celerity_resource('aphront-tooltip-css');
-
- $commit_link = javelin_tag(
- 'a',
- array(
- 'href' => $drequest->generateURI(
- array(
- 'action' => 'commit',
- 'commit' => $line['commit'],
- )),
- 'sigil' => 'has-tooltip',
- 'meta' => array(
- 'tip' => $tooltip,
- 'align' => 'E',
- 'size' => 600,
- ),
- ),
- id(new PhutilUTF8StringTruncator())
- ->setMaximumGlyphs(9)
- ->setTerminator('')
- ->truncateString($line['commit']));
-
- $revision_id = null;
- if (idx($commits, $commit)) {
- $revision_id = idx($revision_ids, $commits[$commit]->getPHID());
- }
-
- if ($revision_id) {
- $revision = idx($revisions, $revision_id);
- if ($revision) {
- $tooltip = $this->renderRevisionTooltip($revision, $handles);
- $revision_link = javelin_tag(
- 'a',
- array(
- 'href' => '/D'.$revision->getID(),
- 'sigil' => 'has-tooltip',
- 'meta' => array(
- 'tip' => $tooltip,
- 'align' => 'E',
- 'size' => 600,
- ),
- ),
- 'D'.$revision->getID());
- }
- }
-
- $uri = $line_href->alter('before', $commit);
- $before_link = javelin_tag(
- 'a',
- array(
- 'href' => $uri->setQueryParam('view', 'blame'),
- 'sigil' => 'has-tooltip',
- 'meta' => array(
- 'tip' => pht('Skip Past This Commit'),
- 'align' => 'E',
- 'size' => 300,
- ),
- ),
- "\xC2\xAB");
- }
-
- $blame[] = phutil_tag(
- 'th',
- array(
- 'class' => 'diffusion-blame-link',
- ),
- $before_link);
-
- $object_links = array();
- $object_links[] = $commit_link;
- if ($revision_link) {
- $object_links[] = phutil_tag('span', array(), '/');
- $object_links[] = $revision_link;
- }
-
- $blame[] = phutil_tag(
- 'th',
- array(
- 'class' => 'diffusion-rev-link',
- ),
- $object_links);
- }
-
- $line_link = phutil_tag(
- 'a',
- array(
- 'href' => $line_href,
- 'style' => $style,
- ),
- $line['line']);
-
- $blame[] = javelin_tag(
- 'th',
- array(
- 'class' => 'diffusion-line-link',
- 'sigil' => 'phabricator-source-line',
- 'style' => $style,
- ),
- $line_link);
-
- Javelin::initBehavior('phabricator-line-linker');
-
- if ($line['target']) {
- Javelin::initBehavior(
- 'diffusion-jump-to',
- array(
- 'target' => 'scroll_target',
- ));
- $anchor_text = phutil_tag(
- 'a',
- array(
- 'id' => 'scroll_target',
- ),
- '');
- } else {
- $anchor_text = null;
- }
-
- $blame[] = phutil_tag(
- 'td',
- array(
- ),
- array(
- $anchor_text,
-
- // NOTE: See phabricator-oncopy behavior.
- "\xE2\x80\x8B",
-
- // TODO: [HTML] Not ideal.
- phutil_safe_html(str_replace("\t", ' ', $line['data'])),
- ));
-
- if ($this->coverage) {
- require_celerity_resource('differential-changeset-view-css');
- $cov_index = $line['line'] - 1;
-
- if (isset($this->coverage[$cov_index])) {
- $cov_class = $this->coverage[$cov_index];
- } else {
- $cov_class = 'N';
- }
-
- $blame[] = phutil_tag(
- 'td',
- array(
- 'class' => 'cov cov-'.$cov_class,
- ),
- '');
- }
-
- $rows[] = phutil_tag(
- 'tr',
- array(
- 'class' => ($line['highlighted'] ?
- 'phabricator-source-highlight' :
- null),
- ),
- $blame);
-
- $cur_inlines = $this->renderInlines(
- idx($inlines, $line['line'], array()),
- $show_blame,
- $this->coverage,
- $engine);
- foreach ($cur_inlines as $cur_inline) {
- $rows[] = $cur_inline;
- }
- }
-
- return $rows;
- }
-
- private function renderInlines(
- array $inlines,
- $needs_blame,
- $has_coverage,
- $engine) {
-
- $rows = array();
- foreach ($inlines as $inline) {
-
- // TODO: This should use modern scaffolding code.
-
- $inline_view = id(new PHUIDiffInlineCommentDetailView())
- ->setUser($this->getViewer())
- ->setMarkupEngine($engine)
- ->setInlineComment($inline)
- ->render();
-
- $row = array_fill(0, ($needs_blame ? 3 : 1), phutil_tag('th'));
-
- $row[] = phutil_tag('td', array(), $inline_view);
-
- if ($has_coverage) {
- $row[] = phutil_tag(
- 'td',
- array(
- 'class' => 'cov cov-I',
- ));
- }
-
- $rows[] = phutil_tag('tr', array('class' => 'inline'), $row);
- }
-
- return $rows;
- }
-
- private function loadFileForData($path, $data) {
- $file = PhabricatorFile::buildFromFileDataOrHash(
- $data,
- array(
- 'name' => basename($path),
- 'ttl' => time() + 60 * 60 * 24,
- 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
- ));
-
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- $file->attachToObject(
- $this->getDiffusionRequest()->getRepository()->getPHID());
- unset($unguarded);
-
- return $file;
- }
-
- private function buildRawResponse($path, $data) {
- $file = $this->loadFileForData($path, $data);
- return $file->getRedirectResponse();
- }
-
- private function buildImageCorpus($file_uri) {
- $properties = new PHUIPropertyListView();
-
- $properties->addImageContent(
- phutil_tag(
- 'img',
- array(
- 'src' => $file_uri,
- )));
-
- $file = $this->renderFileButton($file_uri);
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('Image'))
- ->addActionLink($file);
-
- return id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->addPropertyList($properties);
- }
-
- private function buildBinaryCorpus($file_uri, $data) {
-
- $size = new PhutilNumber(strlen($data));
- $text = pht('This is a binary file. It is %s byte(s) in length.', $size);
- $text = id(new PHUIBoxView())
- ->addPadding(PHUI::PADDING_LARGE)
- ->appendChild($text);
-
- $file = $this->renderFileButton($file_uri);
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('Details'))
- ->addActionLink($file);
-
- $box = id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->appendChild($text);
-
- return $box;
- }
-
- private function buildErrorCorpus($message) {
- $text = id(new PHUIBoxView())
- ->addPadding(PHUI::PADDING_LARGE)
- ->appendChild($message);
-
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('Details'));
-
- $box = id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->appendChild($text);
-
- return $box;
- }
-
- private function buildBeforeResponse($before) {
- $request = $this->getRequest();
- $drequest = $this->getDiffusionRequest();
-
- // NOTE: We need to get the grandparent so we can capture filename changes
- // in the parent.
-
- $parent = $this->loadParentCommitOf($before);
- $old_filename = null;
- $was_created = false;
- if ($parent) {
- $grandparent = $this->loadParentCommitOf($parent);
-
- if ($grandparent) {
- $rename_query = new DiffusionRenameHistoryQuery();
- $rename_query->setRequest($drequest);
- $rename_query->setOldCommit($grandparent);
- $rename_query->setViewer($request->getUser());
- $old_filename = $rename_query->loadOldFilename();
- $was_created = $rename_query->getWasCreated();
- }
- }
-
- $follow = null;
- if ($was_created) {
- // If the file was created in history, that means older commits won't
- // have it. Since we know it existed at 'before', it must have been
- // created then; jump there.
- $target_commit = $before;
- $follow = 'created';
- } else if ($parent) {
- // If we found a parent, jump to it. This is the normal case.
- $target_commit = $parent;
- } else {
- // If there's no parent, this was probably created in the initial commit?
- // And the "was_created" check will fail because we can't identify the
- // grandparent. Keep the user at 'before'.
- $target_commit = $before;
- $follow = 'first';
- }
-
- $path = $drequest->getPath();
- $renamed = null;
- if ($old_filename !== null &&
- $old_filename !== '/'.$path) {
- $renamed = $path;
- $path = $old_filename;
- }
-
- $line = null;
- // If there's a follow error, drop the line so the user sees the message.
- if (!$follow) {
- $line = $this->getBeforeLineNumber($target_commit);
- }
-
- $before_uri = $drequest->generateURI(
- array(
- 'action' => 'browse',
- 'commit' => $target_commit,
- 'line' => $line,
- 'path' => $path,
- ));
-
- $before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
- $before_uri = $before_uri->alter('before', null);
- $before_uri = $before_uri->alter('renamed', $renamed);
- $before_uri = $before_uri->alter('follow', $follow);
-
- return id(new AphrontRedirectResponse())->setURI($before_uri);
- }
-
- private function getBeforeLineNumber($target_commit) {
- $drequest = $this->getDiffusionRequest();
-
- $line = $drequest->getLine();
- if (!$line) {
- return null;
- }
-
- $raw_diff = $this->callConduitWithDiffusionRequest(
- 'diffusion.rawdiffquery',
- array(
- 'commit' => $drequest->getCommit(),
- 'path' => $drequest->getPath(),
- 'againstCommit' => $target_commit,
- ));
- $old_line = 0;
- $new_line = 0;
-
- foreach (explode("\n", $raw_diff) as $text) {
- if ($text[0] == '-' || $text[0] == ' ') {
- $old_line++;
- }
- if ($text[0] == '+' || $text[0] == ' ') {
- $new_line++;
- }
- if ($new_line == $line) {
- return $old_line;
- }
- }
-
- // We didn't find the target line.
- return $line;
- }
-
- private function loadParentCommitOf($commit) {
- $drequest = $this->getDiffusionRequest();
- $user = $this->getRequest()->getUser();
-
- $before_req = DiffusionRequest::newFromDictionary(
- array(
- 'user' => $user,
- 'repository' => $drequest->getRepository(),
- 'commit' => $commit,
- ));
-
- $parents = DiffusionQuery::callConduitWithDiffusionRequest(
- $user,
- $before_req,
- 'diffusion.commitparentsquery',
- array(
- 'commit' => $commit,
- ));
-
- return head($parents);
- }
-
- private function renderRevisionTooltip(
- DifferentialRevision $revision,
- array $handles) {
- $viewer = $this->getRequest()->getUser();
-
- $date = phabricator_date($revision->getDateModified(), $viewer);
- $id = $revision->getID();
- $title = $revision->getTitle();
- $header = "D{$id} {$title}";
-
- $author = $handles[$revision->getAuthorPHID()]->getName();
-
- return "{$header}\n{$date} \xC2\xB7 {$author}";
- }
-
- private function renderCommitTooltip(
- PhabricatorRepositoryCommit $commit,
- array $handles,
- $author) {
-
- $viewer = $this->getRequest()->getUser();
-
- $date = phabricator_date($commit->getEpoch(), $viewer);
- $summary = trim($commit->getSummary());
-
- if ($commit->getAuthorPHID()) {
- $author = $handles[$commit->getAuthorPHID()]->getName();
- }
-
- return "{$summary}\n{$date} \xC2\xB7 {$author}";
- }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionBrowseMainController.php b/src/applications/diffusion/controller/DiffusionBrowseMainController.php
deleted file mode 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseMainController.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-final class DiffusionBrowseMainController extends DiffusionBrowseController {
-
- protected function processDiffusionRequest(AphrontRequest $request) {
- $drequest = $this->diffusionRequest;
-
- // Figure out if we're browsing a directory, a file, or a search result
- // list. Then delegate to the appropriate controller.
-
- $grep = $request->getStr('grep');
- $find = $request->getStr('find');
- if (strlen($grep) || strlen($find)) {
- $controller = new DiffusionBrowseSearchController();
- } else {
- $results = DiffusionBrowseResultSet::newFromConduit(
- $this->callConduitWithDiffusionRequest(
- 'diffusion.browsequery',
- array(
- 'path' => $drequest->getPath(),
- 'commit' => $drequest->getStableCommit(),
- )));
- $reason = $results->getReasonForEmptyResultSet();
- $is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);
-
- if ($is_file) {
- $controller = new DiffusionBrowseFileController($request);
- } else {
- $controller = new DiffusionBrowseDirectoryController($request);
- $controller->setBrowseQueryResults($results);
- }
- }
-
- $controller->setDiffusionRequest($drequest);
- $controller->setCurrentApplication($this->getCurrentApplication());
- return $this->delegateToController($controller);
- }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionBrowseSearchController.php b/src/applications/diffusion/controller/DiffusionBrowseSearchController.php
deleted file mode 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseSearchController.php
+++ /dev/null
@@ -1,231 +0,0 @@
-<?php
-
-final class DiffusionBrowseSearchController extends DiffusionBrowseController {
-
- protected function processDiffusionRequest(AphrontRequest $request) {
- $drequest = $this->diffusionRequest;
-
- $actions = $this->buildActionView($drequest);
- $properties = $this->buildPropertyView($drequest, $actions);
-
- $object_box = id(new PHUIObjectBoxView())
- ->setHeader($this->buildHeaderView($drequest))
- ->addPropertyList($properties);
-
- $content = array();
-
- $content[] = $object_box;
- $content[] = $this->renderSearchForm($collapsed = false);
- $content[] = $this->renderSearchResults();
-
- $crumbs = $this->buildCrumbs(
- array(
- 'branch' => true,
- 'path' => true,
- 'view' => 'browse',
- ));
-
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $content,
- ),
- array(
- 'title' => array(
- nonempty(basename($drequest->getPath()), '/'),
- $drequest->getRepository()->getDisplayName(),
- ),
- ));
- }
-
- private function renderSearchResults() {
- $drequest = $this->getDiffusionRequest();
- $repository = $drequest->getRepository();
- $results = array();
-
- $limit = 100;
- $page = $this->getRequest()->getInt('page', 0);
- $pager = new PHUIPagerView();
- $pager->setPageSize($limit);
- $pager->setOffset($page);
- $pager->setURI($this->getRequest()->getRequestURI(), 'page');
-
- $search_mode = null;
-
- switch ($repository->getVersionControlSystem()) {
- case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
- $results = array();
- break;
- default:
- if (strlen($this->getRequest()->getStr('grep'))) {
- $search_mode = 'grep';
- $query_string = $this->getRequest()->getStr('grep');
- $results = $this->callConduitWithDiffusionRequest(
- 'diffusion.searchquery',
- array(
- 'grep' => $query_string,
- 'commit' => $drequest->getStableCommit(),
- 'path' => $drequest->getPath(),
- 'limit' => $limit + 1,
- 'offset' => $page,
- ));
- } else { // Filename search.
- $search_mode = 'find';
- $query_string = $this->getRequest()->getStr('find');
- $results = $this->callConduitWithDiffusionRequest(
- 'diffusion.querypaths',
- array(
- 'pattern' => $query_string,
- 'commit' => $drequest->getStableCommit(),
- 'path' => $drequest->getPath(),
- 'limit' => $limit + 1,
- 'offset' => $page,
- ));
- }
- break;
- }
-
- $results = $pager->sliceResults($results);
-
- if ($search_mode == 'grep') {
- $table = $this->renderGrepResults($results, $query_string);
- $header = pht(
- 'File content matching "%s" under "%s"',
- $query_string,
- nonempty($drequest->getPath(), '/'));
- } else {
- $table = $this->renderFindResults($results);
- $header = pht(
- 'Paths matching "%s" under "%s"',
- $query_string,
- nonempty($drequest->getPath(), '/'));
- }
-
- $box = id(new PHUIObjectBoxView())
- ->setHeaderText($header)
- ->setTable($table);
-
- $pager_box = id(new PHUIBoxView())
- ->addMargin(PHUI::MARGIN_LARGE)
- ->appendChild($pager);
-
- return array($box, $pager_box);
- }
-
- private function renderGrepResults(array $results, $pattern) {
- $drequest = $this->getDiffusionRequest();
-
- require_celerity_resource('phabricator-search-results-css');
-
- $rows = array();
- foreach ($results as $result) {
- list($path, $line, $string) = $result;
-
- $href = $drequest->generateURI(array(
- 'action' => 'browse',
- 'path' => $path,
- 'line' => $line,
- ));
-
- $matches = null;
- $count = @preg_match_all(
- '('.$pattern.')u',
- $string,
- $matches,
- PREG_OFFSET_CAPTURE);
-
- if (!$count) {
- $output = ltrim($string);
- } else {
- $output = array();
- $cursor = 0;
- $length = strlen($string);
- foreach ($matches[0] as $match) {
- $offset = $match[1];
- if ($cursor != $offset) {
- $output[] = array(
- 'text' => substr($string, $cursor, $offset),
- 'highlight' => false,
- );
- }
- $output[] = array(
- 'text' => $match[0],
- 'highlight' => true,
- );
- $cursor = $offset + strlen($match[0]);
- }
- if ($cursor != $length) {
- $output[] = array(
- 'text' => substr($string, $cursor),
- 'highlight' => false,
- );
- }
-
- if ($output) {
- $output[0]['text'] = ltrim($output[0]['text']);
- }
-
- foreach ($output as $key => $segment) {
- if ($segment['highlight']) {
- $output[$key] = phutil_tag('strong', array(), $segment['text']);
- } else {
- $output[$key] = $segment['text'];
- }
- }
- }
-
- $string = phutil_tag(
- 'pre',
- array('class' => 'PhabricatorMonospaced phui-source-fragment'),
- $output);
-
- $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(
- pht(
- 'The pattern you searched for was not found in the content of any '.
- 'files.'));
-
- return $table;
- }
-
- private function renderFindResults(array $results) {
- $drequest = $this->getDiffusionRequest();
-
- $rows = array();
- foreach ($results as $result) {
- $href = $drequest->generateURI(array(
- 'action' => 'browse',
- 'path' => $result,
- ));
-
- $readable = Filesystem::readablePath($result, $drequest->getPath());
-
- $rows[] = array(
- phutil_tag('a', array('href' => $href), $readable),
- );
- }
-
- $table = id(new AphrontTableView($rows))
- ->setHeaders(array(pht('Path')))
- ->setColumnClasses(array('wide'))
- ->setNoDataString(
- pht(
- 'The pattern you searched for did not match the names of any '.
- 'files.'));
-
- return $table;
- }
-
-}

File Metadata

Mime Type
text/plain
Expires
Fri, May 10, 9:56 PM (3 w, 6 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/w4/rm/hwyvmuxt2otphkmb
Default Alt Text
D14942.id36094.diff (90 KB)

Event Timeline