Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2895611
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
87 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php
index aa36662093..ca9fd9e6f9 100644
--- a/src/applications/diffusion/controller/DiffusionBranchTableController.php
+++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php
@@ -1,76 +1,75 @@
<?php
final class DiffusionBranchTableController extends DiffusionController {
public function shouldAllowPublic() {
return true;
}
public function processRequest() {
$drequest = $this->getDiffusionRequest();
$request = $this->getRequest();
$viewer = $request->getUser();
$repository = $drequest->getRepository();
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'offset');
$pager->setOffset($request->getInt('offset'));
// TODO: Add support for branches that contain commit
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
array(
'offset' => $pager->getOffset(),
'limit' => $pager->getPageSize() + 1,
));
$branches = $pager->sliceResults($branches);
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);
$content = null;
if (!$branches) {
$content = $this->renderStatusMessage(
pht('No Branches'),
pht('This repository has no branches.'));
} else {
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIdentifiers(mpull($branches, 'getCommitIdentifier'))
->withRepository($repository)
->execute();
$view = id(new DiffusionBranchTableView())
->setUser($viewer)
->setBranches($branches)
->setCommits($commits)
->setDiffusionRequest($drequest);
$panel = id(new AphrontPanelView())
->setNoBackground(true)
->appendChild($view)
->appendChild($pager);
$content = $panel;
}
$crumbs = $this->buildCrumbs(
array(
'branches' => true,
));
return $this->buildApplicationPage(
array(
$crumbs,
$content,
),
array(
'title' => array(
pht('Branches'),
'r'.$repository->getCallsign(),
),
- 'device' => false,
));
}
}
diff --git a/src/applications/diffusion/controller/DiffusionBrowseFileController.php b/src/applications/diffusion/controller/DiffusionBrowseFileController.php
index 6e04da9b18..4e01c7ce5b 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseFileController.php
+++ b/src/applications/diffusion/controller/DiffusionBrowseFileController.php
@@ -1,1094 +1,1093 @@
<?php
final class DiffusionBrowseFileController extends DiffusionBrowseController {
private $lintCommit;
private $lintMessages;
private $coverage;
public function processRequest() {
$request = $this->getRequest();
$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());
$file_content = DiffusionFileContent::newFromConduit(
$this->callConduitWithDiffusionRequest(
'diffusion.filecontentquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
'needsBlame' => $needs_blame,
)));
$data = $file_content->getCorpus();
if ($view === 'raw') {
return $this->buildRawResponse($path, $data);
}
$this->loadLintMessages();
$this->coverage = $drequest->loadCoverage();
$binary_uri = null;
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);
$binary_uri = $file_uri;
}
} 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 AphrontErrorView();
$notice->setSeverity(AphrontErrorView::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 AphrontErrorView();
$notice->setSeverity(AphrontErrorView::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,
- 'device' => false,
));
}
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();
$projects = $drequest->loadArcanistProjects();
$langs = array();
foreach ($projects as $project) {
$ls = $project->getSymbolIndexLanguages();
if (!$ls) {
continue;
}
$dep_projects = $project->getSymbolIndexProjects();
$dep_projects[] = $project->getPHID();
foreach ($ls as $lang) {
if (!isset($langs[$lang])) {
$langs[$lang] = array();
}
$langs[$lang] += $dep_projects + array($project);
}
}
$lang = last(explode('.', $drequest->getPath()));
if (isset($langs[$lang])) {
Javelin::initBehavior(
'repository-crossreference',
array(
'container' => $id,
'lang' => $lang,
'projects' => $langs[$lang],
));
}
$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);
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);
$callsign = $repository->getCallsign();
$editor_link = $user->loadEditorLink($path, $line, $callsign);
$template = $user->loadEditorLink($path, '%l', $callsign);
$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) {
$inline_view = id(new DifferentialInlineCommentView())
->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 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/DiffusionChangeController.php b/src/applications/diffusion/controller/DiffusionChangeController.php
index c510b403cd..ddfa283883 100644
--- a/src/applications/diffusion/controller/DiffusionChangeController.php
+++ b/src/applications/diffusion/controller/DiffusionChangeController.php
@@ -1,165 +1,164 @@
<?php
final class DiffusionChangeController extends DiffusionController {
public function shouldAllowPublic() {
return true;
}
public function processRequest() {
$drequest = $this->diffusionRequest;
$viewer = $this->getRequest()->getUser();
$content = array();
$data = $this->callConduitWithDiffusionRequest(
'diffusion.diffquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
));
$drequest->updateSymbolicCommit($data['effectiveCommit']);
$raw_changes = ArcanistDiffChange::newFromConduit($data['changes']);
$diff = DifferentialDiff::newFromRawChanges(
$viewer,
$raw_changes);
$changesets = $diff->getChangesets();
$changeset = reset($changesets);
if (!$changeset) {
// TODO: Refine this.
return new Aphront404Response();
}
$repository = $drequest->getRepository();
$callsign = $repository->getCallsign();
$changesets = array(
0 => $changeset,
);
$changeset_view = new DifferentialChangesetListView();
$changeset_view->setTitle(pht('Change'));
$changeset_view->setChangesets($changesets);
$changeset_view->setVisibleChangesets($changesets);
$changeset_view->setRenderingReferences(
array(
0 => $drequest->generateURI(array('action' => 'rendering-ref')),
));
$raw_params = array(
'action' => 'browse',
'params' => array(
'view' => 'raw',
),
);
$right_uri = $drequest->generateURI($raw_params);
$raw_params['params']['before'] = $drequest->getStableCommit();
$left_uri = $drequest->generateURI($raw_params);
$changeset_view->setRawFileURIs($left_uri, $right_uri);
$changeset_view->setRenderURI('/diffusion/'.$callsign.'/diff/');
$changeset_view->setWhitespace(
DifferentialChangesetParser::WHITESPACE_SHOW_ALL);
$changeset_view->setUser($this->getRequest()->getUser());
// TODO: This is pretty awkward, unify the CSS between Diffusion and
// Differential better.
require_celerity_resource('differential-core-view-css');
$content[] = $changeset_view->render();
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'change',
));
$links = $this->renderPathLinks($drequest, $mode = 'browse');
$header = id(new PHUIHeaderView())
->setHeader($links)
->setUser($viewer)
->setPolicyObject($drequest->getRepository());
$actions = $this->buildActionView($drequest);
$properties = $this->buildPropertyView($drequest, $actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$content,
),
array(
'title' => pht('Change'),
- 'device' => false,
));
}
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('fa-clock-o'));
$browse_uri = $drequest->generateURI(
array(
'action' => 'browse',
));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Browse Content'))
->setHref($browse_uri)
->setIcon('fa-files-o'));
return $view;
}
protected function buildPropertyView(
DiffusionRequest $drequest,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$stable_commit = $drequest->getStableCommit();
$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)));
return $view;
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index 7b92ae062e..54abc7e4d5 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -1,1097 +1,1095 @@
<?php
final class DiffusionCommitController extends DiffusionController {
const CHANGES_LIMIT = 100;
private $auditAuthorityPHIDs;
private $highlightedAudits;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
// This controller doesn't use blob/path stuff, just pass the dictionary
// in directly instead of using the AphrontRequest parsing mechanism.
$data['user'] = $this->getRequest()->getUser();
$drequest = DiffusionRequest::newFromDictionary($data);
$this->diffusionRequest = $drequest;
}
public function processRequest() {
$drequest = $this->getDiffusionRequest();
$request = $this->getRequest();
$user = $request->getUser();
if ($request->getStr('diff')) {
return $this->buildRawDiffResponse($drequest);
}
$repository = $drequest->getRepository();
$callsign = $repository->getCallsign();
$content = array();
$commit = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->withRepository($repository)
->withIdentifiers(array($drequest->getCommit()))
->needCommitData(true)
->needAuditRequests(true)
->executeOne();
$crumbs = $this->buildCrumbs(array(
'commit' => true,
));
if (!$commit) {
$exists = $this->callConduitWithDiffusionRequest(
'diffusion.existsquery',
array('commit' => $drequest->getCommit()));
if (!$exists) {
return new Aphront404Response();
}
$error = id(new AphrontErrorView())
->setTitle(pht('Commit Still Parsing'))
->appendChild(
pht(
'Failed to load the commit because the commit has not been '.
'parsed yet.'));
return $this->buildApplicationPage(
array(
$crumbs,
$error,
),
array(
'title' => pht('Commit Still Parsing'),
- 'device' => false,
));
}
$top_anchor = id(new PhabricatorAnchorView())
->setAnchorName('top')
->setNavigationMarker(true);
$audit_requests = $commit->getAudits();
$this->auditAuthorityPHIDs =
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
$commit_data = $commit->getCommitData();
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
$changesets = null;
if ($is_foreign) {
$subpath = $commit_data->getCommitDetail('svn-subpath');
$error_panel = new AphrontErrorView();
$error_panel->setTitle(pht('Commit Not Tracked'));
$error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$error_panel->appendChild(
pht("This Diffusion repository is configured to track only one ".
"subdirectory of the entire Subversion repository, and this commit ".
"didn't affect the tracked subdirectory ('%s'), so no ".
"information is available.", $subpath));
$content[] = $error_panel;
$content[] = $top_anchor;
} else {
$engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
$engine->setConfig('viewer', $user);
require_celerity_resource('phabricator-remarkup-css');
$parents = $this->callConduitWithDiffusionRequest(
'diffusion.commitparentsquery',
array('commit' => $drequest->getCommit()));
if ($parents) {
$parents = id(new DiffusionCommitQuery())
->setViewer($user)
->withRepository($repository)
->withIdentifiers($parents)
->execute();
}
$headsup_view = id(new PHUIHeaderView())
->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
$headsup_actions = $this->renderHeadsupActionList($commit, $repository);
$commit_properties = $this->loadCommitProperties(
$commit,
$commit_data,
$parents,
$audit_requests);
$property_list = id(new PHUIPropertyListView())
->setHasKeyboardShortcuts(true)
->setUser($user)
->setObject($commit);
foreach ($commit_properties as $key => $value) {
$property_list->addProperty($key, $value);
}
$message = $commit_data->getCommitMessage();
$revision = $commit->getCommitIdentifier();
$message = $this->linkBugtraq($message);
$message = $engine->markupText($message);
$property_list->invokeWillRenderEvent();
$property_list->setActionList($headsup_actions);
$detail_list = new PHUIPropertyListView();
$detail_list->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$detail_list->addTextContent(
phutil_tag(
'div',
array(
'class' => 'diffusion-commit-message phabricator-remarkup',
),
$message));
$content[] = $top_anchor;
$object_box = id(new PHUIObjectBoxView())
->setHeader($headsup_view)
->addPropertyList($property_list)
->addPropertyList($detail_list);
$content[] = $object_box;
}
$content[] = $this->buildComments($commit);
$hard_limit = 1000;
if ($commit->isImported()) {
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$change_query->setLimit($hard_limit + 1);
$changes = $change_query->loadChanges();
} else {
$changes = array();
}
$was_limited = (count($changes) > $hard_limit);
if ($was_limited) {
$changes = array_slice($changes, 0, $hard_limit);
}
$content[] = $this->buildMergesTable($commit);
$highlighted_audits = $commit->getAuthorityAudits(
$user,
$this->auditAuthorityPHIDs);
$owners_paths = array();
if ($highlighted_audits) {
$packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
'phid IN (%Ls)',
mpull($highlighted_audits, 'getAuditorPHID'));
if ($packages) {
$owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'repositoryPHID = %s AND packageID IN (%Ld)',
$repository->getPHID(),
mpull($packages, 'getID'));
}
}
$change_table = new DiffusionCommitChangeTableView();
$change_table->setDiffusionRequest($drequest);
$change_table->setPathChanges($changes);
$change_table->setOwnersPaths($owners_paths);
$count = count($changes);
$bad_commit = null;
if ($count == 0) {
$bad_commit = queryfx_one(
id(new PhabricatorRepository())->establishConnection('r'),
'SELECT * FROM %T WHERE fullCommitName = %s',
PhabricatorRepository::TABLE_BADCOMMIT,
'r'.$callsign.$commit->getCommitIdentifier());
}
if ($bad_commit) {
$content[] = $this->renderStatusMessage(
pht('Bad Commit'),
$bad_commit['description']);
} else if ($is_foreign) {
// Don't render anything else.
} else if (!$commit->isImported()) {
$content[] = $this->renderStatusMessage(
pht('Still Importing...'),
pht(
'This commit is still importing. Changes will be visible once '.
'the import finishes.'));
} else if (!count($changes)) {
$content[] = $this->renderStatusMessage(
pht('Empty Commit'),
pht(
'This commit is empty and does not affect any paths.'));
} else if ($was_limited) {
$content[] = $this->renderStatusMessage(
pht('Enormous Commit'),
pht(
'This commit is enormous, and affects more than %d files. '.
'Changes are not shown.',
$hard_limit));
} else {
// The user has clicked "Show All Changes", and we should show all the
// changes inline even if there are more than the soft limit.
$show_all_details = $request->getBool('show_all');
$change_panel = new PHUIObjectBoxView();
$header = new PHUIHeaderView();
$header->setHeader('Changes ('.number_format($count).')');
$change_panel->setID('toc');
if ($count > self::CHANGES_LIMIT && !$show_all_details) {
$icon = id(new PHUIIconView())
->setIconFont('fa-files-o');
$button = id(new PHUIButtonView())
->setText(pht('Show All Changes'))
->setHref('?show_all=true')
->setTag('a')
->setIcon($icon);
$warning_view = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_WARNING)
->setTitle('Very Large Commit')
->appendChild(
pht('This commit is very large. Load each file individually.'));
$change_panel->setErrorView($warning_view);
$header->addActionLink($button);
}
$change_panel->appendChild($change_table);
$change_panel->setHeader($header);
$content[] = $change_panel;
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
$user,
$changes);
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$vcs_supports_directory_changes = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$vcs_supports_directory_changes = false;
break;
default:
throw new Exception('Unknown VCS.');
}
$references = array();
foreach ($changesets as $key => $changeset) {
$file_type = $changeset->getFileType();
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
if (!$vcs_supports_directory_changes) {
unset($changesets[$key]);
continue;
}
}
$references[$key] = $drequest->generateURI(
array(
'action' => 'rendering-ref',
'path' => $changeset->getFilename(),
));
}
// TODO: Some parts of the views still rely on properties of the
// DifferentialChangeset. Make the objects ephemeral to make sure we don't
// accidentally save them, and then set their ID to the appropriate ID for
// this application (the path IDs).
$path_ids = array_flip(mpull($changes, 'getPath'));
foreach ($changesets as $changeset) {
$changeset->makeEphemeral();
$changeset->setID($path_ids[$changeset->getFilename()]);
}
if ($count <= self::CHANGES_LIMIT || $show_all_details) {
$visible_changesets = $changesets;
} else {
$visible_changesets = array();
$inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments(
$user,
$commit->getPHID());
$path_ids = mpull($inlines, null, 'getPathID');
foreach ($changesets as $key => $changeset) {
if (array_key_exists($changeset->getID(), $path_ids)) {
$visible_changesets[$key] = $changeset;
}
}
}
$change_list_title = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier());
$change_list = new DifferentialChangesetListView();
$change_list->setTitle($change_list_title);
$change_list->setChangesets($changesets);
$change_list->setVisibleChangesets($visible_changesets);
$change_list->setRenderingReferences($references);
$change_list->setRenderURI('/diffusion/'.$callsign.'/diff/');
$change_list->setRepository($repository);
$change_list->setUser($user);
// TODO: Try to setBranch() to something reasonable here?
$change_list->setStandaloneURI(
'/diffusion/'.$callsign.'/diff/');
$change_list->setRawFileURIs(
// TODO: Implement this, somewhat tricky if there's an octopus merge
// or whatever?
null,
'/diffusion/'.$callsign.'/diff/?view=r');
$change_list->setInlineCommentControllerURI(
'/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
$change_references = array();
foreach ($changesets as $key => $changeset) {
$change_references[$changeset->getID()] = $references[$key];
}
$change_table->setRenderingReferences($change_references);
$content[] = $change_list->render();
}
$content[] = $this->renderAddCommentPanel($commit, $audit_requests);
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
$short_name = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier());
$prefs = $user->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
$pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
$show_filetree = $prefs->getPreference($pref_filetree);
$collapsed = $prefs->getPreference($pref_collapse);
if ($changesets && $show_filetree) {
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setAnchorName('top')
->setTitle($short_name)
->setBaseURI(new PhutilURI('/'.$commit_id))
->build($changesets)
->setCrumbs($crumbs)
->setCollapsed((bool)$collapsed)
->appendChild($content);
$content = $nav;
} else {
$content = array($crumbs, $content);
}
return $this->buildApplicationPage(
$content,
array(
'title' => $commit_id,
'pageObjects' => array($commit->getPHID()),
- 'device' => false,
));
}
private function loadCommitProperties(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data,
array $parents,
array $audit_requests) {
assert_instances_of($parents, 'PhabricatorRepositoryCommit');
$viewer = $this->getRequest()->getUser();
$commit_phid = $commit->getPHID();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid))
->withEdgeTypes(array(
DiffusionCommitHasTaskEdgeType::EDGECONST,
PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV,
));
$edges = $edge_query->execute();
$task_phids = array_keys(
$edges[$commit_phid][DiffusionCommitHasTaskEdgeType::EDGECONST]);
$revision_phid = key(
$edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV]);
$phids = $edge_query->getDestinationPHIDs(array($commit_phid));
if ($data->getCommitDetail('authorPHID')) {
$phids[] = $data->getCommitDetail('authorPHID');
}
if ($data->getCommitDetail('reviewerPHID')) {
$phids[] = $data->getCommitDetail('reviewerPHID');
}
if ($data->getCommitDetail('committerPHID')) {
$phids[] = $data->getCommitDetail('committerPHID');
}
if ($parents) {
foreach ($parents as $parent) {
$phids[] = $parent->getPHID();
}
}
// NOTE: We should never normally have more than a single push log, but
// it can occur naturally if a commit is pushed, then the branch it was
// on is deleted, then the commit is pushed again (or through other similar
// chains of events). This should be rare, but does not indicate a bug
// or data issue.
// NOTE: We never query push logs in SVN because the commiter is always
// the pusher and the commit time is always the push time; the push log
// is redundant and we save a query by skipping it.
$push_logs = array();
if ($repository->isHosted() && !$repository->isSVN()) {
$push_logs = id(new PhabricatorRepositoryPushLogQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withNewRefs(array($commit->getCommitIdentifier()))
->withRefTypes(array(PhabricatorRepositoryPushLog::REFTYPE_COMMIT))
->execute();
foreach ($push_logs as $log) {
$phids[] = $log->getPusherPHID();
}
}
$handles = array();
if ($phids) {
$handles = $this->loadViewerHandles($phids);
}
$props = array();
if ($commit->getAuditStatus()) {
$status = PhabricatorAuditCommitStatusConstants::getStatusName(
$commit->getAuditStatus());
$tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_STATE)
->setName($status);
switch ($commit->getAuditStatus()) {
case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT:
$tag->setBackgroundColor(PHUITagView::COLOR_ORANGE);
break;
case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED:
$tag->setBackgroundColor(PHUITagView::COLOR_RED);
break;
case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED:
$tag->setBackgroundColor(PHUITagView::COLOR_BLUE);
break;
case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED:
$tag->setBackgroundColor(PHUITagView::COLOR_GREEN);
break;
}
$props['Status'] = $tag;
}
if ($audit_requests) {
$user_requests = array();
$other_requests = array();
foreach ($audit_requests as $audit_request) {
if ($audit_request->isUser()) {
$user_requests[] = $audit_request;
} else {
$other_requests[] = $audit_request;
}
}
if ($user_requests) {
$props['Auditors'] = $this->renderAuditStatusView(
$user_requests);
}
if ($other_requests) {
$props['Project/Package Auditors'] = $this->renderAuditStatusView(
$other_requests);
}
}
$author_phid = $data->getCommitDetail('authorPHID');
$author_name = $data->getAuthorName();
if (!$repository->isSVN()) {
$authored_info = id(new PHUIStatusItemView());
// TODO: In Git, a distinct authorship date is available. When present,
// we should show it here.
if ($author_phid) {
$authored_info->setTarget($handles[$author_phid]->renderLink());
} else if (strlen($author_name)) {
$authored_info->setTarget($author_name);
}
$props['Authored'] = id(new PHUIStatusListView())
->addItem($authored_info);
}
$committed_info = id(new PHUIStatusItemView())
->setNote(phabricator_datetime($commit->getEpoch(), $viewer));
$committer_phid = $data->getCommitDetail('committerPHID');
$committer_name = $data->getCommitDetail('committer');
if ($committer_phid) {
$committed_info->setTarget($handles[$committer_phid]->renderLink());
} else if (strlen($committer_name)) {
$committed_info->setTarget($committer_name);
} else if ($author_phid) {
$committed_info->setTarget($handles[$author_phid]->renderLink());
} else if (strlen($author_name)) {
$committed_info->setTarget($author_name);
}
$props['Committed'] = id(new PHUIStatusListView())
->addItem($committed_info);
if ($push_logs) {
$pushed_list = new PHUIStatusListView();
foreach ($push_logs as $push_log) {
$pushed_item = id(new PHUIStatusItemView())
->setTarget($handles[$push_log->getPusherPHID()]->renderLink())
->setNote(phabricator_datetime($push_log->getEpoch(), $viewer));
$pushed_list->addItem($pushed_item);
}
$props['Pushed'] = $pushed_list;
}
$reviewer_phid = $data->getCommitDetail('reviewerPHID');
if ($reviewer_phid) {
$props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
}
if ($revision_phid) {
$props['Differential Revision'] = $handles[$revision_phid]->renderLink();
}
if ($parents) {
$parent_links = array();
foreach ($parents as $parent) {
$parent_links[] = $handles[$parent->getPHID()]->renderLink();
}
$props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links);
}
$props['Branches'] = phutil_tag(
'span',
array(
'id' => 'commit-branches',
),
pht('Unknown'));
$props['Tags'] = phutil_tag(
'span',
array(
'id' => 'commit-tags',
),
pht('Unknown'));
$callsign = $repository->getCallsign();
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
Javelin::initBehavior(
'diffusion-commit-branches',
array(
$root.'/branches/' => 'commit-branches',
$root.'/tags/' => 'commit-tags',
));
$refs = $this->buildRefs($drequest);
if ($refs) {
$props['References'] = $refs;
}
if ($task_phids) {
$task_list = array();
foreach ($task_phids as $phid) {
$task_list[] = $handles[$phid]->renderLink();
}
$task_list = phutil_implode_html(phutil_tag('br'), $task_list);
$props['Tasks'] = $task_list;
}
return $props;
}
private function buildComments(PhabricatorRepositoryCommit $commit) {
$viewer = $this->getRequest()->getUser();
$xactions = id(new PhabricatorAuditTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($commit->getPHID()))
->needComments(true)
->execute();
$path_ids = array();
foreach ($xactions as $xaction) {
if ($xaction->hasComment()) {
$path_id = $xaction->getComment()->getPathID();
if ($path_id) {
$path_ids[] = $path_id;
}
}
}
$path_map = array();
if ($path_ids) {
$path_map = id(new DiffusionPathQuery())
->withPathIDs($path_ids)
->execute();
$path_map = ipull($path_map, 'path', 'id');
}
return id(new PhabricatorAuditTransactionView())
->setUser($viewer)
->setObjectPHID($commit->getPHID())
->setPathMap($path_map)
->setTransactions($xactions);
}
private function renderAddCommentPanel(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$request = $this->getRequest();
$user = $request->getUser();
if (!$user->isLoggedIn()) {
return id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setRequestURI($request->getRequestURI());
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
'diffusion-audit-'.$commit->getID());
if ($draft) {
$draft = $draft->getDraft();
} else {
$draft = null;
}
$actions = $this->getAuditActions($commit, $audit_requests);
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/audit/addcomment/')
->addHiddenInput('commit', $commit->getPHID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Action'))
->setName('action')
->setID('audit-action')
->setOptions($actions))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add Auditors'))
->setName('auditors')
->setControlID('add-auditors')
->setControlStyle('display: none')
->setID('add-auditors-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add CCs'))
->setName('ccs')
->setControlID('add-ccs')
->setControlStyle('display: none')
->setID('add-ccs-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new PhabricatorRemarkupControl())
->setLabel(pht('Comments'))
->setName('content')
->setValue($draft)
->setID('audit-content')
->setUser($user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Submit')));
$header = new PHUIHeaderView();
$header->setHeader(
$is_serious ? pht('Audit Commit') : pht('Creative Accounting'));
require_celerity_resource('phabricator-transaction-view-css');
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
$auditor_source = new DiffusionAuditorDatasource();
Javelin::initBehavior(
'differential-add-reviewers-and-ccs',
array(
'dynamic' => array(
'add-auditors-tokenizer' => array(
'actions' => array('add_auditors' => 1),
'src' => $auditor_source->getDatasourceURI(),
'row' => 'add-auditors',
'placeholder' => $auditor_source->getPlaceholderText(),
),
'add-ccs-tokenizer' => array(
'actions' => array('add_ccs' => 1),
'src' => $mailable_source->getDatasourceURI(),
'row' => 'add-ccs',
'placeholder' => $mailable_source->getPlaceholderText(),
),
),
'select' => 'audit-action',
));
Javelin::initBehavior('differential-feedback-preview', array(
'uri' => '/audit/preview/'.$commit->getID().'/',
'preview' => 'audit-preview',
'content' => 'audit-content',
'action' => 'audit-action',
'previewTokenizers' => array(
'auditors' => 'add-auditors-tokenizer',
'ccs' => 'add-ccs-tokenizer',
),
'inline' => 'inline-comment-preview',
'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/',
));
$loading = phutil_tag_div(
'aphront-panel-preview-loading-text',
pht('Loading preview...'));
$preview_panel = phutil_tag_div(
'aphront-panel-preview aphront-panel-flush',
array(
phutil_tag('div', array('id' => 'audit-preview'), $loading),
phutil_tag('div', array('id' => 'inline-comment-preview')),
));
// TODO: This is pretty awkward, unify the CSS between Diffusion and
// Differential better.
require_celerity_resource('differential-core-view-css');
$anchor = id(new PhabricatorAnchorView())
->setAnchorName('comment')
->setNavigationMarker(true)
->render();
$comment_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($form);
return phutil_tag(
'div',
array(
'id' => $pane_id,
),
phutil_tag_div(
'differential-add-comment-panel',
array($anchor, $comment_box, $preview_panel)));
}
/**
* Return a map of available audit actions for rendering into a <select />.
* This shows the user valid actions, and does not show nonsense/invalid
* actions (like closing an already-closed commit, or resigning from a commit
* you have no association with).
*/
private function getAuditActions(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$user = $this->getRequest()->getUser();
$user_is_author = ($commit->getAuthorPHID() == $user->getPHID());
$user_request = null;
foreach ($audit_requests as $audit_request) {
if ($audit_request->getAuditorPHID() == $user->getPHID()) {
$user_request = $audit_request;
break;
}
}
$actions = array();
$actions[PhabricatorAuditActionConstants::COMMENT] = true;
$actions[PhabricatorAuditActionConstants::ADD_CCS] = true;
$actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true;
// We allow you to accept your own commits. A use case here is that you
// notice an issue with your own commit and "Raise Concern" as an indicator
// to other auditors that you're on top of the issue, then later resolve it
// and "Accept". You can not accept on behalf of projects or packages,
// however.
$actions[PhabricatorAuditActionConstants::ACCEPT] = true;
$actions[PhabricatorAuditActionConstants::CONCERN] = true;
// To resign, a user must have authority on some request and not be the
// commit's author.
if (!$user_is_author) {
$may_resign = false;
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
foreach ($audit_requests as $request) {
if (empty($authority_map[$request->getAuditorPHID()])) {
continue;
}
$may_resign = true;
break;
}
// If the user has already resigned, don't show "Resign...".
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
if ($user_request) {
if ($user_request->getAuditStatus() == $status_resigned) {
$may_resign = false;
}
}
if ($may_resign) {
$actions[PhabricatorAuditActionConstants::RESIGN] = true;
}
}
$status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
$concern_raised = ($commit->getAuditStatus() == $status_concern);
$can_close_option = PhabricatorEnv::getEnvConfig(
'audit.can-author-close-audit');
if ($can_close_option && $user_is_author && $concern_raised) {
$actions[PhabricatorAuditActionConstants::CLOSE] = true;
}
foreach ($actions as $constant => $ignored) {
$actions[$constant] =
PhabricatorAuditActionConstants::getActionName($constant);
}
return $actions;
}
private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
$drequest = $this->getDiffusionRequest();
$limit = 50;
$merges = array();
try {
$merges = $this->callConduitWithDiffusionRequest(
'diffusion.mergedcommitsquery',
array(
'commit' => $drequest->getCommit(),
'limit' => $limit + 1,
));
} catch (ConduitException $ex) {
if ($ex->getMessage() != 'ERR-UNSUPPORTED-VCS') {
throw $ex;
}
}
if (!$merges) {
return null;
}
$caption = null;
if (count($merges) > $limit) {
$merges = array_slice($merges, 0, $limit);
$caption =
"This commit merges more than {$limit} changes. Only the first ".
"{$limit} are shown.";
}
$history_table = new DiffusionHistoryTableView();
$history_table->setUser($this->getRequest()->getUser());
$history_table->setDiffusionRequest($drequest);
$history_table->setHistory($merges);
$history_table->loadRevisions();
$phids = $history_table->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$history_table->setHandles($handles);
$panel = new AphrontPanelView();
$panel->setHeader(pht('Merged Changes'));
$panel->setCaption($caption);
$panel->appendChild($history_table);
$panel->setNoBackground();
return $panel;
}
private function renderHeadsupActionList(
PhabricatorRepositoryCommit $commit,
PhabricatorRepository $repository) {
$request = $this->getRequest();
$user = $request->getUser();
$actions = id(new PhabricatorActionListView())
->setUser($user)
->setObject($commit)
->setObjectURI($request->getRequestURI());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$commit,
PhabricatorPolicyCapability::CAN_EDIT);
$uri = '/diffusion/'.$repository->getCallsign().'/commit/'.
$commit->getCommitIdentifier().'/edit/';
$action = id(new PhabricatorActionView())
->setName(pht('Edit Commit'))
->setHref($uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$actions->addAction($action);
require_celerity_resource('phabricator-object-selector-css');
require_celerity_resource('javelin-behavior-phabricator-object-selector');
$maniphest = 'PhabricatorManiphestApplication';
if (PhabricatorApplication::isClassInstalled($maniphest)) {
$action = id(new PhabricatorActionView())
->setName(pht('Edit Maniphest Tasks'))
->setIcon('fa-anchor')
->setHref('/search/attach/'.$commit->getPHID().'/TASK/edge/')
->setWorkflow(true)
->setDisabled(!$can_edit);
$actions->addAction($action);
}
$action = id(new PhabricatorActionView())
->setName(pht('Download Raw Diff'))
->setHref($request->getRequestURI()->alter('diff', true))
->setIcon('fa-download');
$actions->addAction($action);
return $actions;
}
private function buildRefs(DiffusionRequest $request) {
// this is git-only, so save a conduit round trip and just get out of
// here if the repository isn't git
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$repository = $request->getRepository();
if ($repository->getVersionControlSystem() != $type_git) {
return null;
}
$results = $this->callConduitWithDiffusionRequest(
'diffusion.refsquery',
array('commit' => $request->getCommit()));
$ref_links = array();
foreach ($results as $ref_data) {
$ref_links[] = phutil_tag('a',
array('href' => $ref_data['href']),
$ref_data['ref']);
}
return phutil_implode_html(', ', $ref_links);
}
private function buildRawDiffResponse(DiffusionRequest $drequest) {
$raw_diff = $this->callConduitWithDiffusionRequest(
'diffusion.rawdiffquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
));
$file = PhabricatorFile::buildFromFileDataOrHash(
$raw_diff,
array(
'name' => $drequest->getCommit().'.diff',
'ttl' => (60 * 60 * 24),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file->attachToObject($drequest->getRepository()->getPHID());
unset($unguarded);
return $file->getRedirectResponse();
}
private function renderAuditStatusView(array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$phids = mpull($audit_requests, 'getAuditorPHID');
$this->loadHandles($phids);
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
$view = new PHUIStatusListView();
foreach ($audit_requests as $request) {
$code = $request->getAuditStatus();
$item = new PHUIStatusItemView();
$item->setIcon(
PhabricatorAuditStatusConstants::getStatusIcon($code),
PhabricatorAuditStatusConstants::getStatusColor($code),
PhabricatorAuditStatusConstants::getStatusName($code));
$note = array();
foreach ($request->getAuditReasons() as $reason) {
$note[] = phutil_tag('div', array(), $reason);
}
$item->setNote($note);
$auditor_phid = $request->getAuditorPHID();
$target = $this->getHandle($auditor_phid)->renderLink();
$item->setTarget($target);
if (isset($authority_map[$auditor_phid])) {
$item->setHighlighted(true);
}
$view->addItem($item);
}
return $view;
}
private function linkBugtraq($corpus) {
$url = PhabricatorEnv::getEnvConfig('bugtraq.url');
if (!strlen($url)) {
return $corpus;
}
$regexes = PhabricatorEnv::getEnvConfig('bugtraq.logregex');
if (!$regexes) {
return $corpus;
}
$parser = id(new PhutilBugtraqParser())
->setBugtraqPattern("[[ {$url} | %BUGID% ]]")
->setBugtraqCaptureExpression(array_shift($regexes));
$select = array_shift($regexes);
if ($select) {
$parser->setBugtraqSelectExpression($select);
}
return $parser->processCorpus($corpus);
}
}
diff --git a/src/applications/diffusion/controller/DiffusionLintController.php b/src/applications/diffusion/controller/DiffusionLintController.php
index 91e4fbf709..b3b2ae935f 100644
--- a/src/applications/diffusion/controller/DiffusionLintController.php
+++ b/src/applications/diffusion/controller/DiffusionLintController.php
@@ -1,346 +1,345 @@
<?php
final class DiffusionLintController extends DiffusionController {
public function shouldAllowPublic() {
return true;
}
public function processRequest() {
$request = $this->getRequest();
$user = $this->getRequest()->getUser();
$drequest = $this->diffusionRequest;
if ($request->getStr('lint') !== null) {
$controller = new DiffusionLintDetailsController();
$controller->setDiffusionRequest($drequest);
$controller->setCurrentApplication($this->getCurrentApplication());
return $this->delegateToController($controller);
}
$owners = array();
if (!$drequest) {
if (!$request->getArr('owner')) {
$owners = array($user->getPHID());
} else {
$owners = array(head($request->getArr('owner')));
}
$owner_handles = $this->loadViewerHandles($owners);
}
$codes = $this->loadLintCodes($owners);
if ($codes && !$drequest) {
// TODO: Build some real Query classes for this stuff.
$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(
'id IN (%Ld)',
array_unique(ipull($codes, 'branchID')));
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->withIDs(mpull($branches, 'getRepositoryID'))
->execute();
$drequests = array();
foreach ($branches as $id => $branch) {
if (empty($repositories[$branch->getRepositoryID()])) {
continue;
}
$drequests[$id] = DiffusionRequest::newFromDictionary(array(
'user' => $user,
'repository' => $repositories[$branch->getRepositoryID()],
'branch' => $branch->getName(),
));
}
}
$rows = array();
$total = 0;
foreach ($codes as $code) {
if (!$this->diffusionRequest) {
$drequest = idx($drequests, $code['branchID']);
}
if (!$drequest) {
continue;
}
$total += $code['n'];
$href_lint = $drequest->generateURI(array(
'action' => 'lint',
'lint' => $code['code'],
));
$href_browse = $drequest->generateURI(array(
'action' => 'browse',
'lint' => $code['code'],
));
$href_repo = $drequest->generateURI(array('action' => 'lint'));
$rows[] = array(
phutil_tag('a', array('href' => $href_lint), $code['n']),
phutil_tag('a', array('href' => $href_browse), $code['files']),
phutil_tag('a', array('href' => $href_repo), $drequest->getCallsign()),
ArcanistLintSeverity::getStringForSeverity($code['maxSeverity']),
$code['code'],
$code['maxName'],
$code['maxDescription'],
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(array(
pht('Problems'),
pht('Files'),
pht('Repository'),
pht('Severity'),
pht('Code'),
pht('Name'),
pht('Example'),
))
->setColumnVisibility(array(true, true, !$this->diffusionRequest))
->setColumnClasses(array('n', 'n', '', '', 'pri', '', ''));
$content = array();
$link = null;
if (!$this->diffusionRequest) {
$form = id(new AphrontFormView())
->setUser($user)
->setMethod('GET')
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLimit(1)
->setName('owner')
->setLabel(pht('Owner'))
->setValue($owner_handles))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Filter'));
$content[] = id(new AphrontListFilterView())->appendChild($form);
}
$content[] = id(new AphrontPanelView())
->setNoBackground(true)
->setCaption($link)
->appendChild($table);
$title = array('Lint');
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'lint',
));
if ($this->diffusionRequest) {
$title[] = $drequest->getCallsign();
} else {
$crumbs->addTextCrumb(pht('All Lint'));
}
if ($this->diffusionRequest) {
$branch = $drequest->loadBranch();
$header = id(new PHUIHeaderView())
->setHeader($this->renderPathLinks($drequest, 'lint'))
->setUser($user)
->setPolicyObject($drequest->getRepository());
$actions = $this->buildActionView($drequest);
$properties = $this->buildPropertyView(
$drequest,
$branch,
$total,
$actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
} else {
$object_box = null;
}
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$content,
),
array(
'title' => $title,
- 'device' => false,
));
}
private function loadLintCodes(array $owner_phids) {
$drequest = $this->diffusionRequest;
$conn = id(new PhabricatorRepository())->establishConnection('r');
$where = array('1 = 1');
if ($drequest) {
$branch = $drequest->loadBranch();
if (!$branch) {
return array();
}
$where[] = qsprintf($conn, 'branchID = %d', $branch->getID());
if ($drequest->getPath() != '') {
$path = '/'.$drequest->getPath();
$is_dir = (substr($path, -1) == '/');
$where[] = ($is_dir
? qsprintf($conn, 'path LIKE %>', $path)
: qsprintf($conn, 'path = %s', $path));
}
}
if ($owner_phids) {
$or = array();
$or[] = qsprintf($conn, 'authorPHID IN (%Ls)', $owner_phids);
$paths = array();
$packages = id(new PhabricatorOwnersOwner())
->loadAllWhere('userPHID IN (%Ls)', $owner_phids);
if ($packages) {
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID IN (%Ld)',
mpull($packages, 'getPackageID'));
}
if ($paths) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
->withPHIDs(mpull($paths, 'getRepositoryPHID'))
->execute();
$repositories = mpull($repositories, 'getID', 'getPHID');
$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(
'repositoryID IN (%Ld)',
$repositories);
$branches = mgroup($branches, 'getRepositoryID');
}
foreach ($paths as $path) {
$branch = idx(
$branches,
idx(
$repositories,
$path->getRepositoryPHID()));
if ($branch) {
$condition = qsprintf(
$conn,
'(branchID IN (%Ld) AND path LIKE %>)',
array_keys($branch),
$path->getPath());
if ($path->getExcluded()) {
$where[] = 'NOT '.$condition;
} else {
$or[] = $condition;
}
}
}
$where[] = '('.implode(' OR ', $or).')';
}
return queryfx_all(
$conn,
'SELECT
branchID,
code,
MAX(severity) AS maxSeverity,
MAX(name) AS maxName,
MAX(description) AS maxDescription,
COUNT(DISTINCT path) AS files,
COUNT(*) AS n
FROM %T
WHERE %Q
GROUP BY branchID, code
ORDER BY n DESC',
PhabricatorRepository::TABLE_LINTMESSAGE,
implode(' AND ', $where));
}
protected function buildActionView(DiffusionRequest $drequest) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setUser($viewer);
$list_uri = $drequest->generateURI(
array(
'action' => 'lint',
'lint' => '',
));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View As List'))
->setHref($list_uri)
->setIcon('fa-list'));
$history_uri = $drequest->generateURI(
array(
'action' => 'history',
));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setHref($history_uri)
->setIcon('fa-clock-o'));
$browse_uri = $drequest->generateURI(
array(
'action' => 'browse',
));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Browse Content'))
->setHref($browse_uri)
->setIcon('fa-files-o'));
return $view;
}
protected function buildPropertyView(
DiffusionRequest $drequest,
PhabricatorRepositoryBranch $branch,
$total,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$callsign = $drequest->getRepository()->getCallsign();
$lint_commit = $branch->getLintCommit();
$view->addProperty(
pht('Lint Commit'),
phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'commit',
'commit' => $lint_commit,
)),
),
$drequest->getRepository()->formatCommitName($lint_commit)));
$view->addProperty(
pht('Total Messages'),
pht('%s', new PhutilNumber($total)));
return $view;
}
}
diff --git a/src/applications/diffusion/controller/DiffusionTagListController.php b/src/applications/diffusion/controller/DiffusionTagListController.php
index d4c2d5dc40..f52ef44c60 100644
--- a/src/applications/diffusion/controller/DiffusionTagListController.php
+++ b/src/applications/diffusion/controller/DiffusionTagListController.php
@@ -1,98 +1,97 @@
<?php
final class DiffusionTagListController extends DiffusionController {
public function shouldAllowPublic() {
return true;
}
public function processRequest() {
$drequest = $this->getDiffusionRequest();
$request = $this->getRequest();
$viewer = $request->getUser();
$repository = $drequest->getRepository();
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'offset');
$pager->setOffset($request->getInt('offset'));
$params = array(
'limit' => $pager->getPageSize() + 1,
'offset' => $pager->getOffset(),
);
if ($drequest->getSymbolicCommit()) {
$is_commit = true;
$params['commit'] = $drequest->getSymbolicCommit();
} else {
$is_commit = false;
}
$tags = array();
try {
$conduit_result = $this->callConduitWithDiffusionRequest(
'diffusion.tagsquery',
$params);
$tags = DiffusionRepositoryTag::newFromConduit($conduit_result);
} catch (ConduitException $ex) {
if ($ex->getMessage() != 'ERR-UNSUPPORTED-VCS') {
throw $ex;
}
}
$tags = $pager->sliceResults($tags);
$content = null;
if (!$tags) {
$content = $this->renderStatusMessage(
pht('No Tags'),
$is_commit
? pht('This commit has no tags.')
: pht('This repository has no tags.'));
} else {
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers(mpull($tags, 'getCommitIdentifier'))
->needCommitData(true)
->execute();
$view = id(new DiffusionTagListView())
->setTags($tags)
->setUser($viewer)
->setCommits($commits)
->setDiffusionRequest($drequest);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
$panel = id(new AphrontPanelView())
->setNoBackground(true)
->appendChild($view)
->appendChild($pager);
$content = $panel;
}
$crumbs = $this->buildCrumbs(
array(
'tags' => true,
'commit' => $drequest->getSymbolicCommit(),
));
return $this->buildApplicationPage(
array(
$crumbs,
$content,
),
array(
'title' => array(
pht('Tags'),
- $repository->getCallsign().' Repository',
+ pht('%s Repository', $repository->getCallsign()),
),
- 'device' => false,
));
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jan 19 2025, 21:45 (6 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1129072
Default Alt Text
(87 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment