2011-03-08 18:54:55 +01:00
|
|
|
<?php
|
|
|
|
|
2012-03-10 00:46:25 +01:00
|
|
|
final class DiffusionBrowseFileController extends DiffusionController {
|
2011-03-08 18:54:55 +01:00
|
|
|
|
2012-03-20 03:52:24 +01:00
|
|
|
private $corpusType = 'text';
|
2011-04-02 00:34:45 +02:00
|
|
|
|
2012-11-07 07:46:23 +01:00
|
|
|
private $lintCommit;
|
|
|
|
private $lintMessages;
|
|
|
|
|
2011-03-08 18:54:55 +01:00
|
|
|
public function processRequest() {
|
2011-03-13 01:17:34 +01:00
|
|
|
|
2011-03-23 03:34:47 +01:00
|
|
|
$request = $this->getRequest();
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$drequest = $this->getDiffusionRequest();
|
2012-04-08 02:24:35 +02:00
|
|
|
|
|
|
|
$before = $request->getStr('before');
|
|
|
|
if ($before) {
|
|
|
|
return $this->buildBeforeResponse($before);
|
|
|
|
}
|
|
|
|
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$path = $drequest->getPath();
|
2012-07-18 02:13:03 +02:00
|
|
|
|
2011-03-23 03:34:47 +01:00
|
|
|
$selected = $request->getStr('view');
|
2012-07-18 02:13:03 +02:00
|
|
|
$preferences = $request->getUser()->loadPreferences();
|
2012-06-07 19:55:10 +02:00
|
|
|
if (!$selected) {
|
2012-07-18 02:13:03 +02:00
|
|
|
$selected = $preferences->getPreference(
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_VIEW,
|
|
|
|
'highlighted');
|
|
|
|
} else if ($request->isFormPost() && $selected != 'raw') {
|
|
|
|
$preferences->setPreference(
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_VIEW,
|
|
|
|
$selected);
|
|
|
|
$preferences->save();
|
2012-07-19 19:15:06 +02:00
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI($request->getRequestURI()->alter('view', $selected));
|
2012-06-07 19:55:10 +02:00
|
|
|
}
|
2012-07-18 02:13:03 +02:00
|
|
|
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$needs_blame = ($selected == 'blame' || $selected == 'plainblame');
|
2012-04-08 02:24:35 +02:00
|
|
|
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$file_query = DiffusionFileContentQuery::newFromDiffusionRequest(
|
|
|
|
$this->diffusionRequest);
|
2012-09-05 04:22:03 +02:00
|
|
|
$file_query->setViewer($request->getUser());
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$file_query->setNeedsBlame($needs_blame);
|
|
|
|
$file_query->loadFileContent();
|
|
|
|
$data = $file_query->getRawData();
|
2011-03-22 07:57:32 +01:00
|
|
|
|
2012-03-20 03:52:24 +01:00
|
|
|
if ($selected === 'raw') {
|
|
|
|
return $this->buildRawResponse($path, $data);
|
2012-03-14 07:50:34 +01:00
|
|
|
}
|
|
|
|
|
2012-11-07 07:46:23 +01:00
|
|
|
$this->loadLintMessages();
|
|
|
|
|
2011-03-26 20:42:12 +01:00
|
|
|
// Build the content of the file.
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$corpus = $this->buildCorpus(
|
|
|
|
$selected,
|
|
|
|
$file_query,
|
|
|
|
$needs_blame,
|
|
|
|
$drequest,
|
|
|
|
$path,
|
2012-03-20 03:52:24 +01:00
|
|
|
$data);
|
|
|
|
|
|
|
|
require_celerity_resource('diffusion-source-css');
|
|
|
|
|
|
|
|
if ($this->corpusType == 'text') {
|
2012-07-18 02:13:03 +02:00
|
|
|
$view_select_panel = $this->renderViewSelectPanel($selected);
|
2012-03-20 03:52:24 +01:00
|
|
|
} else {
|
|
|
|
$view_select_panel = null;
|
|
|
|
}
|
2011-03-26 20:42:12 +01:00
|
|
|
|
|
|
|
// Render the page.
|
|
|
|
$content = array();
|
|
|
|
$content[] = $this->buildCrumbs(
|
|
|
|
array(
|
|
|
|
'branch' => true,
|
|
|
|
'path' => true,
|
|
|
|
'view' => 'browse',
|
|
|
|
));
|
2012-04-24 03:16:38 +02:00
|
|
|
|
|
|
|
$follow = $request->getStr('follow');
|
|
|
|
if ($follow) {
|
|
|
|
$notice = new AphrontErrorView();
|
|
|
|
$notice->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
|
|
|
$notice->setTitle('Unable to Continue');
|
|
|
|
switch ($follow) {
|
|
|
|
case 'first':
|
|
|
|
$notice->appendChild(
|
|
|
|
"Unable to continue tracing the history of this file because ".
|
|
|
|
"this commit is the first commit in the repository.");
|
|
|
|
break;
|
|
|
|
case 'created':
|
|
|
|
$notice->appendChild(
|
|
|
|
"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('File Renamed');
|
|
|
|
$notice->appendChild(
|
|
|
|
"File history passes through a rename from '".
|
|
|
|
phutil_escape_html($drequest->getPath())."' to '".
|
|
|
|
phutil_escape_html($renamed)."'.");
|
|
|
|
$content[] = $notice;
|
|
|
|
}
|
|
|
|
|
2011-03-26 20:42:12 +01:00
|
|
|
$content[] = $view_select_panel;
|
|
|
|
$content[] = $corpus;
|
2011-10-02 21:52:54 +02:00
|
|
|
$content[] = $this->buildOpenRevisions();
|
2011-03-26 20:42:12 +01:00
|
|
|
|
|
|
|
$nav = $this->buildSideNav('browse', true);
|
|
|
|
$nav->appendChild($content);
|
|
|
|
|
2011-04-19 06:21:08 +02:00
|
|
|
$basename = basename($this->getDiffusionRequest()->getPath());
|
|
|
|
|
2011-03-26 20:42:12 +01:00
|
|
|
return $this->buildStandardPageResponse(
|
|
|
|
$nav,
|
|
|
|
array(
|
2011-04-19 06:21:08 +02:00
|
|
|
'title' => $basename,
|
2011-03-26 20:42:12 +01:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2012-11-07 07:46:23 +01:00
|
|
|
private function loadLintMessages() {
|
|
|
|
$drequest = $this->getDiffusionRequest();
|
|
|
|
$branch = $drequest->loadBranch();
|
|
|
|
|
|
|
|
if (!$branch || !$branch->getLintCommit()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_history = DiffusionHistoryQuery::newFromDiffusionRequest(
|
|
|
|
$drequest)->setLimit(1)->loadHistory();
|
|
|
|
|
|
|
|
$lint_request = clone $drequest;
|
|
|
|
$lint_request->setCommit($branch->getLintCommit());
|
2012-11-17 03:17:18 +01:00
|
|
|
try {
|
|
|
|
$lint_history = DiffusionHistoryQuery::newFromDiffusionRequest(
|
|
|
|
$lint_request)->setLimit(1)->loadHistory();
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
// This can happen if lintCommit is invalid.
|
|
|
|
$lint_history = null;
|
|
|
|
}
|
2012-11-07 07:46:23 +01:00
|
|
|
|
|
|
|
$this->lintCommit = '';
|
|
|
|
if (!$file_history || !$lint_history ||
|
|
|
|
reset($file_history)->getCommitIdentifier() !=
|
|
|
|
reset($lint_history)->getCommitIdentifier()) {
|
|
|
|
$this->lintCommit = $branch->getLintCommit();
|
|
|
|
}
|
|
|
|
|
2012-11-09 00:14:44 +01:00
|
|
|
$conn = id(new PhabricatorRepository())->establishConnection('r');
|
|
|
|
|
|
|
|
$where = '';
|
|
|
|
if ($drequest->getLint()) {
|
|
|
|
$where = qsprintf(
|
|
|
|
$conn,
|
|
|
|
'AND code = %s',
|
|
|
|
$drequest->getLint());
|
|
|
|
}
|
|
|
|
|
2012-11-07 07:46:23 +01:00
|
|
|
$this->lintMessages = queryfx_all(
|
2012-11-09 00:14:44 +01:00
|
|
|
$conn,
|
|
|
|
'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
|
2012-11-07 07:46:23 +01:00
|
|
|
PhabricatorRepository::TABLE_LINTMESSAGE,
|
|
|
|
$branch->getID(),
|
2012-11-09 00:14:44 +01:00
|
|
|
$where,
|
2012-11-07 07:46:23 +01:00
|
|
|
'/'.$drequest->getPath());
|
|
|
|
}
|
|
|
|
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
private function buildCorpus($selected,
|
2012-04-07 21:01:25 +02:00
|
|
|
DiffusionFileContentQuery $file_query,
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$needs_blame,
|
2012-04-07 21:01:25 +02:00
|
|
|
DiffusionRequest $drequest,
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
$path,
|
|
|
|
$data) {
|
2011-04-01 22:42:00 +02:00
|
|
|
|
2012-03-20 03:52:24 +01:00
|
|
|
if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
|
|
|
|
$file = $this->loadFileForData($path, $data);
|
|
|
|
$file_uri = $file->getBestURI();
|
|
|
|
|
|
|
|
if ($file->isViewableImage()) {
|
|
|
|
$this->corpusType = 'image';
|
|
|
|
return $this->buildImageCorpus($file_uri);
|
|
|
|
} else {
|
|
|
|
$this->corpusType = 'binary';
|
|
|
|
return $this->buildBinaryCorpus($file_uri, $data);
|
|
|
|
}
|
Allow Diffusion to display PDF files
Summary:
When Diffusion encounters an image file, it displays it as an
image, but when it encounters a PDF file, it currently shows only some
gibberish. This fixes that.
Test Plan:
I tried it. Embedding a large PDF in a data URL is a little
bit slow, but it works.
Reviewers: tuomaspelkonen, epriestley, gc3, waltermundt, jungejason, nh
Reviewed By: epriestley
CC: aran, tuomaspelkonen, epriestley, jaapweel
Differential Revision: 915
2011-09-09 00:23:58 +02:00
|
|
|
}
|
|
|
|
|
2011-03-23 03:34:47 +01:00
|
|
|
switch ($selected) {
|
|
|
|
case 'plain':
|
|
|
|
$style =
|
|
|
|
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
|
|
|
|
$corpus = phutil_render_tag(
|
|
|
|
'textarea',
|
|
|
|
array(
|
|
|
|
'style' => $style,
|
|
|
|
),
|
2011-03-26 20:42:12 +01:00
|
|
|
phutil_escape_html($file_query->getRawData()));
|
2011-04-01 03:28:24 +02:00
|
|
|
|
2011-03-23 03:34:47 +01:00
|
|
|
break;
|
|
|
|
|
2011-04-01 03:28:24 +02:00
|
|
|
case 'plainblame':
|
|
|
|
$style =
|
|
|
|
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
|
|
|
|
list($text_list, $rev_list, $blame_dict) =
|
|
|
|
$file_query->getBlameData();
|
|
|
|
|
|
|
|
$rows = array();
|
|
|
|
foreach ($text_list as $k => $line) {
|
|
|
|
$rev = $rev_list[$k];
|
2011-10-18 21:58:45 +02:00
|
|
|
if (isset($blame_dict[$rev]['handle'])) {
|
|
|
|
$author = $blame_dict[$rev]['handle']->getName();
|
|
|
|
} else {
|
|
|
|
$author = $blame_dict[$rev]['author'];
|
|
|
|
}
|
2011-04-01 03:28:24 +02:00
|
|
|
$rows[] =
|
2011-06-07 01:08:49 +02:00
|
|
|
sprintf("%-10s %-20s %s", substr($rev, 0, 7), $author, $line);
|
2011-04-01 03:28:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$corpus = phutil_render_tag(
|
|
|
|
'textarea',
|
|
|
|
array(
|
|
|
|
'style' => $style,
|
|
|
|
),
|
|
|
|
phutil_escape_html(implode("\n", $rows)));
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2011-03-23 03:34:47 +01:00
|
|
|
case 'highlighted':
|
2011-03-26 20:42:12 +01:00
|
|
|
case 'blame':
|
2011-03-23 03:34:47 +01:00
|
|
|
default:
|
|
|
|
require_celerity_resource('syntax-highlighting-css');
|
2011-03-08 18:54:55 +01:00
|
|
|
|
2011-04-01 03:28:24 +02:00
|
|
|
list($text_list, $rev_list, $blame_dict) = $file_query->getBlameData();
|
2011-03-26 20:42:12 +01:00
|
|
|
|
2011-07-06 21:12:17 +02:00
|
|
|
$text_list = implode("\n", $text_list);
|
|
|
|
$text_list = PhabricatorSyntaxHighlighter::highlightWithFilename(
|
|
|
|
$path,
|
|
|
|
$text_list);
|
|
|
|
$text_list = explode("\n", $text_list);
|
2011-03-23 03:34:47 +01:00
|
|
|
|
2011-04-01 03:28:24 +02:00
|
|
|
$rows = $this->buildDisplayRows($text_list, $rev_list, $blame_dict,
|
2011-04-02 00:34:45 +02:00
|
|
|
$needs_blame, $drequest, $file_query, $selected);
|
2011-03-23 03:34:47 +01:00
|
|
|
|
2012-07-20 07:01:31 +02:00
|
|
|
$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()));
|
|
|
|
|
2012-07-27 05:25:27 +02:00
|
|
|
$prefs = $this->getRequest()->getUser()->loadPreferences();
|
|
|
|
$pref_symbols = $prefs->getPreference(
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_SYMBOLS);
|
|
|
|
if (isset($langs[$lang]) && $pref_symbols != 'disabled') {
|
2012-07-20 07:01:31 +02:00
|
|
|
Javelin::initBehavior(
|
|
|
|
'repository-crossreference',
|
|
|
|
array(
|
|
|
|
'container' => $id,
|
|
|
|
'lang' => $lang,
|
|
|
|
'projects' => $langs[$lang],
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2012-08-04 18:47:51 +02:00
|
|
|
$corpus_table = javelin_render_tag(
|
2011-03-23 03:34:47 +01:00
|
|
|
'table',
|
|
|
|
array(
|
2011-03-31 04:21:09 +02:00
|
|
|
'class' => "diffusion-source remarkup-code PhabricatorMonospaced",
|
2012-08-04 18:47:51 +02:00
|
|
|
'sigil' => 'diffusion-source',
|
2011-03-23 03:34:47 +01:00
|
|
|
),
|
|
|
|
implode("\n", $rows));
|
|
|
|
$corpus = phutil_render_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
2012-04-08 02:24:35 +02:00
|
|
|
'style' => 'padding: 0 2em;',
|
2012-07-20 07:01:31 +02:00
|
|
|
'id' => $id,
|
2011-03-23 03:34:47 +01:00
|
|
|
),
|
|
|
|
$corpus_table);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-03-26 20:42:12 +01:00
|
|
|
return $corpus;
|
|
|
|
}
|
2011-03-13 01:17:34 +01:00
|
|
|
|
2012-07-18 02:13:03 +02:00
|
|
|
private function renderViewSelectPanel($selected) {
|
2012-10-03 19:58:56 +02:00
|
|
|
$toggle_blame = array(
|
|
|
|
'highlighted' => 'blame',
|
|
|
|
'blame' => 'highlighted',
|
|
|
|
'plain' => 'plainblame',
|
|
|
|
'plainblame' => 'plain',
|
|
|
|
'raw' => 'raw', // not a real case.
|
|
|
|
);
|
|
|
|
$toggle_highlight = array(
|
|
|
|
'highlighted' => 'plain',
|
|
|
|
'blame' => 'plainblame',
|
|
|
|
'plain' => 'highlighted',
|
|
|
|
'plainblame' => 'blame',
|
|
|
|
'raw' => 'raw', // not a real case.
|
|
|
|
);
|
|
|
|
|
|
|
|
$user = $this->getRequest()->getUser();
|
2012-11-07 07:46:23 +01:00
|
|
|
$base_uri = $this->getRequest()->getRequestURI();
|
2012-10-03 19:58:56 +02:00
|
|
|
|
|
|
|
$blame_on = ($selected == 'blame' || $selected == 'plainblame');
|
|
|
|
if ($blame_on) {
|
|
|
|
$blame_text = pht('Disable Blame');
|
|
|
|
} else {
|
|
|
|
$blame_text = pht('Enable Blame');
|
|
|
|
}
|
2012-03-20 03:52:24 +01:00
|
|
|
|
2012-10-03 19:58:56 +02:00
|
|
|
$blame_button = $this->createViewAction(
|
|
|
|
$blame_text,
|
2012-11-07 07:46:23 +01:00
|
|
|
$base_uri->alter('view', $toggle_blame[$selected]),
|
2012-10-03 19:58:56 +02:00
|
|
|
$user);
|
2012-03-20 03:52:24 +01:00
|
|
|
|
|
|
|
|
2012-10-03 19:58:56 +02:00
|
|
|
$highlight_on = ($selected == 'blame' || $selected == 'highlighted');
|
|
|
|
if ($highlight_on) {
|
|
|
|
$highlight_text = pht('Disable Highlighting');
|
|
|
|
} else {
|
|
|
|
$highlight_text = pht('Enable Highlighting');
|
|
|
|
}
|
|
|
|
$highlight_button = $this->createViewAction(
|
|
|
|
$highlight_text,
|
2012-11-07 07:46:23 +01:00
|
|
|
$base_uri->alter('view', $toggle_highlight[$selected]),
|
|
|
|
$user);
|
|
|
|
|
|
|
|
|
|
|
|
$href = null;
|
2012-11-09 00:14:44 +01:00
|
|
|
if ($this->getRequest()->getStr('lint') !== null) {
|
2012-11-09 01:13:21 +01:00
|
|
|
$lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages));
|
2012-11-07 07:46:23 +01:00
|
|
|
$href = $base_uri->alter('lint', null);
|
|
|
|
|
|
|
|
} else if ($this->lintCommit === null) {
|
|
|
|
$lint_text = pht('Lint not Available');
|
|
|
|
|
|
|
|
} else if ($this->lintCommit) {
|
|
|
|
$lint_text = pht(
|
|
|
|
'Switch for %d Lint Message(s)',
|
|
|
|
count($this->lintMessages));
|
|
|
|
$href = $this->getDiffusionRequest()->generateURI(array(
|
|
|
|
'action' => 'browse',
|
|
|
|
'commit' => $this->lintCommit,
|
2012-11-09 00:14:44 +01:00
|
|
|
))->alter('lint', '');
|
2012-11-07 07:46:23 +01:00
|
|
|
|
|
|
|
} else if (!$this->lintMessages) {
|
2012-11-09 01:13:21 +01:00
|
|
|
$lint_text = pht('No Lint Messages');
|
2012-11-07 07:46:23 +01:00
|
|
|
|
|
|
|
} else {
|
|
|
|
$lint_text = pht('Show %d Lint Message(s)', count($this->lintMessages));
|
2012-11-09 00:14:44 +01:00
|
|
|
$href = $base_uri->alter('lint', '');
|
2012-11-07 07:46:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$lint_button = $this->createViewAction(
|
|
|
|
$lint_text,
|
|
|
|
$href,
|
2012-10-03 19:58:56 +02:00
|
|
|
$user);
|
|
|
|
|
2012-11-07 07:46:23 +01:00
|
|
|
if (!$href) {
|
|
|
|
$lint_button->setDisabled(true);
|
|
|
|
}
|
|
|
|
|
2012-10-03 19:58:56 +02:00
|
|
|
|
|
|
|
$raw_button = $this->createViewAction(
|
|
|
|
pht('View Raw File'),
|
2012-11-07 07:46:23 +01:00
|
|
|
$base_uri->alter('view', 'raw'),
|
2012-10-03 19:58:56 +02:00
|
|
|
$user,
|
|
|
|
'file');
|
|
|
|
|
|
|
|
$edit_button = $this->createEditAction();
|
|
|
|
|
|
|
|
return id(new PhabricatorActionListView())
|
|
|
|
->setUser($user)
|
|
|
|
->addAction($blame_button)
|
|
|
|
->addAction($highlight_button)
|
2012-11-07 07:46:23 +01:00
|
|
|
->addAction($lint_button)
|
2012-10-03 19:58:56 +02:00
|
|
|
->addAction($raw_button)
|
|
|
|
->addAction($edit_button);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function createViewAction(
|
|
|
|
$localized_text,
|
2012-11-07 07:46:23 +01:00
|
|
|
$href,
|
2012-10-03 19:58:56 +02:00
|
|
|
$user,
|
|
|
|
$icon = null) {
|
|
|
|
|
|
|
|
return id(new PhabricatorActionView())
|
|
|
|
->setName($localized_text)
|
|
|
|
->setIcon($icon)
|
|
|
|
->setUser($user)
|
|
|
|
->setRenderAsForm(true)
|
2012-11-07 07:46:23 +01:00
|
|
|
->setHref($href);
|
2012-03-20 03:52:24 +01:00
|
|
|
}
|
|
|
|
|
2012-10-03 19:58:56 +02:00
|
|
|
private function createEditAction() {
|
2012-03-20 03:52:24 +01:00
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
$drequest = $this->getDiffusionRequest();
|
|
|
|
|
|
|
|
$repository = $drequest->getRepository();
|
|
|
|
$path = $drequest->getPath();
|
2012-09-13 20:01:41 +02:00
|
|
|
$line = nonempty((int)$drequest->getLine(), 1);
|
2012-03-20 03:52:24 +01:00
|
|
|
|
2012-04-05 02:53:16 +02:00
|
|
|
$callsign = $repository->getCallsign();
|
|
|
|
$editor_link = $user->loadEditorLink($path, $line, $callsign);
|
2012-03-20 03:52:24 +01:00
|
|
|
|
2012-10-03 19:58:56 +02:00
|
|
|
$action = id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Open in Editor'))
|
|
|
|
->setIcon('edit');
|
|
|
|
|
|
|
|
$action->setHref($editor_link);
|
|
|
|
$action->setDisabled(!$editor_link);
|
|
|
|
|
|
|
|
return $action;
|
2012-03-20 03:52:24 +01:00
|
|
|
}
|
2011-03-13 01:17:34 +01:00
|
|
|
|
2012-04-07 21:01:25 +02:00
|
|
|
private function buildDisplayRows(
|
|
|
|
array $text_list,
|
|
|
|
array $rev_list,
|
|
|
|
array $blame_dict,
|
|
|
|
$needs_blame,
|
|
|
|
DiffusionRequest $drequest,
|
|
|
|
DiffusionFileContentQuery $file_query,
|
|
|
|
$selected) {
|
|
|
|
|
2011-05-18 16:44:53 +02:00
|
|
|
if ($blame_dict) {
|
2012-04-09 08:08:26 +02:00
|
|
|
$epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch');
|
2012-04-08 02:24:35 +02:00
|
|
|
$epoch_min = min($epoch_list);
|
|
|
|
$epoch_max = max($epoch_list);
|
|
|
|
$epoch_range = ($epoch_max - $epoch_min) + 1;
|
2011-05-18 16:44:53 +02:00
|
|
|
}
|
2011-04-01 03:28:24 +02:00
|
|
|
|
2012-07-04 23:04:45 +02:00
|
|
|
$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,
|
|
|
|
);
|
|
|
|
}
|
2012-02-25 20:45:51 +01:00
|
|
|
}
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$display = array();
|
|
|
|
|
|
|
|
$line_number = 1;
|
|
|
|
$last_rev = null;
|
|
|
|
$color = null;
|
2011-04-01 03:28:24 +02:00
|
|
|
foreach ($text_list as $k => $line) {
|
2012-04-08 02:24:35 +02:00
|
|
|
$display_line = array(
|
|
|
|
'color' => null,
|
|
|
|
'epoch' => null,
|
|
|
|
'commit' => null,
|
|
|
|
'author' => null,
|
|
|
|
'target' => null,
|
|
|
|
'highlighted' => null,
|
|
|
|
'line' => $line_number,
|
|
|
|
'data' => $line,
|
|
|
|
);
|
|
|
|
|
2011-04-01 03:28:24 +02:00
|
|
|
if ($needs_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
|
2012-04-08 02:24:35 +02:00
|
|
|
// is, the more saturated the color.
|
|
|
|
|
|
|
|
// TODO: SVN doesn't always give us blame for the last line, if empty?
|
|
|
|
// Bug with our stuff or with SVN?
|
|
|
|
$rev = idx($rev_list, $k, $last_rev);
|
|
|
|
|
2011-04-01 03:28:24 +02:00
|
|
|
if ($last_rev == $rev) {
|
2012-04-08 02:24:35 +02:00
|
|
|
$display_line['color'] = $color;
|
2011-03-26 20:42:12 +01:00
|
|
|
} else {
|
2012-04-08 02:24:35 +02:00
|
|
|
$blame = $blame_dict[$rev];
|
2011-04-01 03:28:24 +02:00
|
|
|
|
2012-04-09 08:08:26 +02:00
|
|
|
if (!isset($blame['epoch'])) {
|
|
|
|
$color = '#ffd'; // Render as warning.
|
|
|
|
} else {
|
|
|
|
$color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range;
|
|
|
|
$color_value = 0xF6 * (1.0 - $color_ratio);
|
|
|
|
$color = sprintf(
|
|
|
|
'#%02x%02x%02x',
|
|
|
|
$color_value,
|
|
|
|
0xF6,
|
|
|
|
$color_value);
|
|
|
|
}
|
2011-03-26 20:42:12 +01:00
|
|
|
|
2012-04-09 08:08:26 +02:00
|
|
|
$display_line['epoch'] = idx($blame, 'epoch');
|
2012-04-08 02:24:35 +02:00
|
|
|
$display_line['color'] = $color;
|
|
|
|
$display_line['commit'] = $rev;
|
2011-04-02 00:34:45 +02:00
|
|
|
|
2012-04-09 08:08:26 +02:00
|
|
|
if (isset($blame['handle'])) {
|
|
|
|
$author_link = $blame['handle']->renderLink();
|
2011-10-18 21:58:45 +02:00
|
|
|
} else {
|
2012-04-08 02:24:35 +02:00
|
|
|
$author_link = phutil_render_tag(
|
|
|
|
'span',
|
|
|
|
array(
|
|
|
|
),
|
2012-04-09 08:08:26 +02:00
|
|
|
phutil_escape_html($blame['author']));
|
2011-10-18 21:58:45 +02:00
|
|
|
}
|
2012-04-08 02:24:35 +02:00
|
|
|
$display_line['author'] = $author_link;
|
|
|
|
|
2011-04-01 03:28:24 +02:00
|
|
|
$last_rev = $rev;
|
2011-03-26 20:42:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-04 23:04:45 +02:00
|
|
|
if ($line_arr) {
|
|
|
|
if ($line_number == $line_arr[0]['min']) {
|
2012-04-08 02:24:35 +02:00
|
|
|
$display_line['target'] = true;
|
|
|
|
}
|
2012-07-04 23:04:45 +02:00
|
|
|
foreach ($line_arr as $range) {
|
|
|
|
if ($line_number >= $range['min'] &&
|
|
|
|
$line_number <= $range['max']) {
|
|
|
|
$display_line['highlighted'] = true;
|
|
|
|
}
|
2012-02-25 20:45:51 +01:00
|
|
|
}
|
2011-03-26 20:42:12 +01:00
|
|
|
}
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$display[] = $display_line;
|
|
|
|
++$line_number;
|
|
|
|
}
|
|
|
|
|
2012-04-10 10:32:47 +02:00
|
|
|
$commits = array_filter(ipull($display, 'commit'));
|
|
|
|
if ($commits) {
|
|
|
|
$commits = id(new PhabricatorAuditCommitQuery())
|
|
|
|
->withIdentifiers($drequest->getRepository()->getID(), $commits)
|
|
|
|
->needCommitData(true)
|
|
|
|
->execute();
|
|
|
|
$commits = mpull($commits, null, 'getCommitIdentifier');
|
|
|
|
}
|
2012-04-08 02:24:35 +02:00
|
|
|
|
2012-06-23 01:52:08 +02:00
|
|
|
$revision_ids = id(new DifferentialRevision())
|
|
|
|
->loadIDsByCommitPHIDs(mpull($commits, 'getPHID'));
|
2012-06-21 21:48:45 +02:00
|
|
|
$revisions = array();
|
2012-06-23 01:52:08 +02:00
|
|
|
if ($revision_ids) {
|
|
|
|
$revisions = id(new DifferentialRevision())->loadAllWhere(
|
|
|
|
'id IN (%Ld)',
|
|
|
|
$revision_ids);
|
2012-06-21 21:48:45 +02:00
|
|
|
}
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
Use "user-select: none" to provide a visual cue about copy/paste JS magic
Summary:
- For line numbers, use "user-select: none" to make them unselectable. This provides a stronger visual cue that copy/paste is enchanted.
- In Paste, make it look sensible again after the blame-on-blame refactor in Diffusion. See also TODO to share this code formally.
- In Diffusion, use the "phabricator-oncopy" behavior.
NOTE: I left blame/commit columns selectable in Diffusion, since you might reasonably want to copy/paste them?
NOTE: In Differential, the left side of the diff still highlights, even though it will be copied only if you select part of a line on the left and nothing else. But this seemed like a reasonable behavior, so I left it.
Test Plan:
- Looked at Paste. Saw a nice line number column. Selected text, got the expected selection. Copied text, got the expected copy.
- Looked at Diffusion. Saw a nice line number column, still. Selected text, got expected selection. Copied text, got expected copy.
- Looked at Differential. Highlighted stuff, got expected results. Copied stuff, got expected results.
Reviewers: btrahan, vrana, jungejason
Reviewed By: vrana
CC: aran
Maniphest Tasks: T1123
Differential Revision: https://secure.phabricator.com/D2242
2012-04-17 00:55:16 +02:00
|
|
|
Javelin::initBehavior('phabricator-oncopy', array());
|
|
|
|
|
2012-11-07 07:46:23 +01:00
|
|
|
$engine = null;
|
|
|
|
$inlines = array();
|
2012-11-09 00:14:44 +01:00
|
|
|
if ($this->getRequest()->getStr('lint') !== null && $this->lintMessages) {
|
2012-11-07 07:46:23 +01:00
|
|
|
$engine = new PhabricatorMarkupEngine();
|
|
|
|
$engine->setViewer($user);
|
|
|
|
|
|
|
|
foreach ($this->lintMessages as $message) {
|
|
|
|
$inline = id(new PhabricatorAuditInlineComment())
|
2012-11-10 02:45:19 +01:00
|
|
|
->setID($message['id'])
|
2012-11-09 01:13:21 +01:00
|
|
|
->setSyntheticAuthor(
|
|
|
|
ArcanistLintSeverity::getStringForSeverity($message['severity']).
|
|
|
|
' '.$message['code'].' ('.$message['name'].')')
|
2012-11-07 07:46:23 +01:00
|
|
|
->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()),
|
|
|
|
$needs_blame,
|
|
|
|
$engine);
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
foreach ($display as $line) {
|
|
|
|
|
|
|
|
$line_href = $drequest->generateURI(
|
Fix many encoding and architecture problems in Diffusion request and URI handling
Summary:
Diffusion request/uri handling is currently a big, hastily ported mess. In particular, it has:
- Tons and tons of duplicated code.
- Bugs with handling unusual branch and file names.
- An excessively large (and yet insufficiently expressive) API on DiffusionRequest, including a nonsensical concrete base class.
- Other tools were doing hacky things like passing ":" branch names.
This diff attempts to fix these issues.
- Make the base class abstract (it was concrete ONLY for "/diffusion/").
- Move all URI generation to DiffusionRequest. Make the core static. Add unit tests.
- Delete the 300 copies of URI generation code throughout Diffusion.
- Move all URI parsing to DiffusionRequest. Make the core static. Add unit tests.
- Add an appropriate static initializer for other callers.
- Convert all code calling `newFromAphrontRequestDictionary` outside of Diffusion to the new `newFromDictionary` API.
- Refactor static initializers to be sensibly-sized.
- Refactor derived DiffusionRequest classes to remove duplicated code.
- Properly encode branch names (fixes branches with "/", see <https://github.com/facebook/phabricator/issues/100>).
- Properly encode path names (fixes issues in D1742).
- Properly escape delimiter characters ";" and "$" in path names so files like "$100" are not interpreted as "line 100".
- Fix a couple warnings.
- Fix a couple lint issues.
- Fix a bug where we would not parse filenames with spaces in them correctly in the Git browse query.
- Fix a bug where Git change queries would fail unnecessarily.
- Provide or improve some documentation.
This thing is pretty gigantic but also kind of hard to split up. If it's unreasonably difficult to review, let me know and I can take a stab at it though.
This supplants D1742.
Test Plan:
- Used home, repository, branch, browse, change, history, diff (ajax), lastmodified (ajax) views of Diffusion.
- Used Owners typeaheads and search.
- Used diffusion.getrecentcommitsbypath method.
- Pushed a change to an absurdly-named file on an absurdly-named branch, everything worked properly.
{F9185}
Reviewers: nh, vrana, btrahan
Reviewed By: btrahan
CC: aran, epriestley
Differential Revision: https://secure.phabricator.com/D1921
2012-03-20 03:52:14 +01:00
|
|
|
array(
|
2012-04-08 02:24:35 +02:00
|
|
|
'action' => 'browse',
|
|
|
|
'line' => $line['line'],
|
|
|
|
'stable' => true,
|
Fix many encoding and architecture problems in Diffusion request and URI handling
Summary:
Diffusion request/uri handling is currently a big, hastily ported mess. In particular, it has:
- Tons and tons of duplicated code.
- Bugs with handling unusual branch and file names.
- An excessively large (and yet insufficiently expressive) API on DiffusionRequest, including a nonsensical concrete base class.
- Other tools were doing hacky things like passing ":" branch names.
This diff attempts to fix these issues.
- Make the base class abstract (it was concrete ONLY for "/diffusion/").
- Move all URI generation to DiffusionRequest. Make the core static. Add unit tests.
- Delete the 300 copies of URI generation code throughout Diffusion.
- Move all URI parsing to DiffusionRequest. Make the core static. Add unit tests.
- Add an appropriate static initializer for other callers.
- Convert all code calling `newFromAphrontRequestDictionary` outside of Diffusion to the new `newFromDictionary` API.
- Refactor static initializers to be sensibly-sized.
- Refactor derived DiffusionRequest classes to remove duplicated code.
- Properly encode branch names (fixes branches with "/", see <https://github.com/facebook/phabricator/issues/100>).
- Properly encode path names (fixes issues in D1742).
- Properly escape delimiter characters ";" and "$" in path names so files like "$100" are not interpreted as "line 100".
- Fix a couple warnings.
- Fix a couple lint issues.
- Fix a bug where we would not parse filenames with spaces in them correctly in the Git browse query.
- Fix a bug where Git change queries would fail unnecessarily.
- Provide or improve some documentation.
This thing is pretty gigantic but also kind of hard to split up. If it's unreasonably difficult to review, let me know and I can take a stab at it though.
This supplants D1742.
Test Plan:
- Used home, repository, branch, browse, change, history, diff (ajax), lastmodified (ajax) views of Diffusion.
- Used Owners typeaheads and search.
- Used diffusion.getrecentcommitsbypath method.
- Pushed a change to an absurdly-named file on an absurdly-named branch, everything worked properly.
{F9185}
Reviewers: nh, vrana, btrahan
Reviewed By: btrahan
CC: aran, epriestley
Differential Revision: https://secure.phabricator.com/D1921
2012-03-20 03:52:14 +01:00
|
|
|
));
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$blame = array();
|
|
|
|
if ($line['color']) {
|
|
|
|
$color = $line['color'];
|
|
|
|
|
|
|
|
$before_link = null;
|
|
|
|
$commit_link = null;
|
2012-06-21 21:48:45 +02:00
|
|
|
$revision_link = null;
|
2012-04-08 02:24:35 +02:00
|
|
|
if (idx($line, 'commit')) {
|
|
|
|
$commit = $line['commit'];
|
|
|
|
|
|
|
|
$summary = 'Unknown';
|
|
|
|
if (idx($commits, $commit)) {
|
|
|
|
$summary = $commits[$commit]->getCommitData()->getSummary();
|
|
|
|
}
|
|
|
|
|
|
|
|
$tooltip = phabricator_date(
|
|
|
|
$line['epoch'],
|
|
|
|
$user)." \xC2\xB7 ".$summary;
|
|
|
|
|
|
|
|
Javelin::initBehavior('phabricator-tooltips', array());
|
|
|
|
require_celerity_resource('aphront-tooltip-css');
|
|
|
|
|
|
|
|
$commit_link = javelin_render_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $drequest->generateURI(
|
|
|
|
array(
|
|
|
|
'action' => 'commit',
|
|
|
|
'commit' => $line['commit'],
|
|
|
|
)),
|
|
|
|
'sigil' => 'has-tooltip',
|
|
|
|
'meta' => array(
|
|
|
|
'tip' => $tooltip,
|
|
|
|
'align' => 'E',
|
|
|
|
'size' => 600,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
phutil_escape_html(phutil_utf8_shorten($line['commit'], 9, '')));
|
|
|
|
|
2012-07-12 02:02:15 +02:00
|
|
|
$revision_id = null;
|
|
|
|
if (idx($commits, $commit)) {
|
|
|
|
$revision_id = idx($revision_ids, $commits[$commit]->getPHID());
|
|
|
|
}
|
|
|
|
|
2012-06-21 21:48:45 +02:00
|
|
|
if ($revision_id) {
|
|
|
|
$revision = idx($revisions, $revision_id);
|
|
|
|
if (!$revision) {
|
|
|
|
$tooltip = '(Invalid revision)';
|
|
|
|
} else {
|
|
|
|
$tooltip =
|
|
|
|
phabricator_date($revision->getDateModified(), $user).
|
|
|
|
" \xC2\xB7 ".
|
|
|
|
$revision->getTitle();
|
|
|
|
}
|
|
|
|
$revision_link = javelin_render_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/D'.$revision_id,
|
|
|
|
'sigil' => 'has-tooltip',
|
|
|
|
'meta' => array(
|
|
|
|
'tip' => $tooltip,
|
|
|
|
'align' => 'E',
|
|
|
|
'size' => 600,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'D'.$revision_id);
|
|
|
|
}
|
|
|
|
|
2012-09-29 01:41:39 +02:00
|
|
|
$uri = $line_href->alter('before', $commit);
|
2012-04-08 02:24:35 +02:00
|
|
|
$before_link = javelin_render_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
2012-09-29 01:41:39 +02:00
|
|
|
'href' => $uri->setQueryParam('view', 'blame'),
|
2012-04-08 02:24:35 +02:00
|
|
|
'sigil' => 'has-tooltip',
|
|
|
|
'meta' => array(
|
|
|
|
'tip' => 'Skip Past This Commit',
|
|
|
|
'align' => 'E',
|
|
|
|
'size' => 300,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
"\xC2\xAB");
|
|
|
|
}
|
|
|
|
|
|
|
|
$blame[] = phutil_render_tag(
|
|
|
|
'th',
|
|
|
|
array(
|
|
|
|
'class' => 'diffusion-blame-link',
|
|
|
|
'style' => 'background: '.$color,
|
|
|
|
),
|
|
|
|
$before_link);
|
|
|
|
|
|
|
|
$blame[] = phutil_render_tag(
|
|
|
|
'th',
|
|
|
|
array(
|
|
|
|
'class' => 'diffusion-rev-link',
|
|
|
|
'style' => 'background: '.$color,
|
|
|
|
),
|
|
|
|
$commit_link);
|
|
|
|
|
2012-06-21 21:48:45 +02:00
|
|
|
$blame[] = phutil_render_tag(
|
|
|
|
'th',
|
|
|
|
array(
|
|
|
|
'class' => 'diffusion-rev-link',
|
|
|
|
'style' => 'background: '.$color,
|
|
|
|
),
|
|
|
|
$revision_link);
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$blame[] = phutil_render_tag(
|
|
|
|
'th',
|
|
|
|
array(
|
|
|
|
'class' => 'diffusion-author-link',
|
|
|
|
'style' => 'background: '.$color,
|
|
|
|
),
|
|
|
|
idx($line, 'author'));
|
Fix many encoding and architecture problems in Diffusion request and URI handling
Summary:
Diffusion request/uri handling is currently a big, hastily ported mess. In particular, it has:
- Tons and tons of duplicated code.
- Bugs with handling unusual branch and file names.
- An excessively large (and yet insufficiently expressive) API on DiffusionRequest, including a nonsensical concrete base class.
- Other tools were doing hacky things like passing ":" branch names.
This diff attempts to fix these issues.
- Make the base class abstract (it was concrete ONLY for "/diffusion/").
- Move all URI generation to DiffusionRequest. Make the core static. Add unit tests.
- Delete the 300 copies of URI generation code throughout Diffusion.
- Move all URI parsing to DiffusionRequest. Make the core static. Add unit tests.
- Add an appropriate static initializer for other callers.
- Convert all code calling `newFromAphrontRequestDictionary` outside of Diffusion to the new `newFromDictionary` API.
- Refactor static initializers to be sensibly-sized.
- Refactor derived DiffusionRequest classes to remove duplicated code.
- Properly encode branch names (fixes branches with "/", see <https://github.com/facebook/phabricator/issues/100>).
- Properly encode path names (fixes issues in D1742).
- Properly escape delimiter characters ";" and "$" in path names so files like "$100" are not interpreted as "line 100".
- Fix a couple warnings.
- Fix a couple lint issues.
- Fix a bug where we would not parse filenames with spaces in them correctly in the Git browse query.
- Fix a bug where Git change queries would fail unnecessarily.
- Provide or improve some documentation.
This thing is pretty gigantic but also kind of hard to split up. If it's unreasonably difficult to review, let me know and I can take a stab at it though.
This supplants D1742.
Test Plan:
- Used home, repository, branch, browse, change, history, diff (ajax), lastmodified (ajax) views of Diffusion.
- Used Owners typeaheads and search.
- Used diffusion.getrecentcommitsbypath method.
- Pushed a change to an absurdly-named file on an absurdly-named branch, everything worked properly.
{F9185}
Reviewers: nh, vrana, btrahan
Reviewed By: btrahan
CC: aran, epriestley
Differential Revision: https://secure.phabricator.com/D1921
2012-03-20 03:52:14 +01:00
|
|
|
}
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$line_link = phutil_render_tag(
|
2011-03-26 20:42:12 +01:00
|
|
|
'a',
|
|
|
|
array(
|
2012-04-08 02:24:35 +02:00
|
|
|
'href' => $line_href,
|
|
|
|
),
|
|
|
|
phutil_escape_html($line['line']));
|
|
|
|
|
2012-08-04 18:47:51 +02:00
|
|
|
$blame[] = javelin_render_tag(
|
2012-04-08 02:24:35 +02:00
|
|
|
'th',
|
|
|
|
array(
|
|
|
|
'class' => 'diffusion-line-link',
|
2012-08-04 18:47:51 +02:00
|
|
|
'sigil' => 'diffusion-line-link',
|
2012-04-08 02:24:35 +02:00
|
|
|
'style' => isset($color) ? 'background: '.$color : null,
|
|
|
|
),
|
|
|
|
$line_link);
|
|
|
|
|
2012-08-04 18:47:51 +02:00
|
|
|
Javelin::initBehavior('diffusion-line-linker');
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$blame = implode('', $blame);
|
|
|
|
|
|
|
|
if ($line['target']) {
|
|
|
|
Javelin::initBehavior(
|
|
|
|
'diffusion-jump-to',
|
|
|
|
array(
|
|
|
|
'target' => 'scroll_target',
|
|
|
|
));
|
|
|
|
$anchor_text = '<a id="scroll_target"></a>';
|
|
|
|
} else {
|
|
|
|
$anchor_text = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$line_text = phutil_render_tag(
|
|
|
|
'td',
|
|
|
|
array(
|
2011-03-26 20:42:12 +01:00
|
|
|
),
|
Use "user-select: none" to provide a visual cue about copy/paste JS magic
Summary:
- For line numbers, use "user-select: none" to make them unselectable. This provides a stronger visual cue that copy/paste is enchanted.
- In Paste, make it look sensible again after the blame-on-blame refactor in Diffusion. See also TODO to share this code formally.
- In Diffusion, use the "phabricator-oncopy" behavior.
NOTE: I left blame/commit columns selectable in Diffusion, since you might reasonably want to copy/paste them?
NOTE: In Differential, the left side of the diff still highlights, even though it will be copied only if you select part of a line on the left and nothing else. But this seemed like a reasonable behavior, so I left it.
Test Plan:
- Looked at Paste. Saw a nice line number column. Selected text, got the expected selection. Copied text, got the expected copy.
- Looked at Diffusion. Saw a nice line number column, still. Selected text, got expected selection. Copied text, got expected copy.
- Looked at Differential. Highlighted stuff, got expected results. Copied stuff, got expected results.
Reviewers: btrahan, vrana, jungejason
Reviewed By: vrana
CC: aran
Maniphest Tasks: T1123
Differential Revision: https://secure.phabricator.com/D2242
2012-04-17 00:55:16 +02:00
|
|
|
$anchor_text.
|
|
|
|
"\xE2\x80\x8B". // NOTE: See phabricator-oncopy behavior.
|
|
|
|
$line['data']);
|
2011-03-26 20:42:12 +01:00
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$rows[] = phutil_render_tag(
|
|
|
|
'tr',
|
|
|
|
array(
|
2012-08-04 18:47:51 +02:00
|
|
|
'class' => ($line['highlighted'] ? 'highlighted' : null),
|
2012-04-08 02:24:35 +02:00
|
|
|
),
|
|
|
|
$blame.
|
|
|
|
$line_text);
|
2012-11-07 07:46:23 +01:00
|
|
|
|
|
|
|
$rows = array_merge($rows, $this->renderInlines(
|
|
|
|
idx($inlines, $line['line'], array()),
|
|
|
|
$needs_blame,
|
|
|
|
$engine));
|
2011-03-26 20:42:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $rows;
|
2011-03-08 18:54:55 +01:00
|
|
|
}
|
2011-04-01 03:28:24 +02:00
|
|
|
|
2012-11-07 07:46:23 +01:00
|
|
|
private function renderInlines(array $inlines, $needs_blame, $engine) {
|
|
|
|
$rows = array();
|
|
|
|
foreach ($inlines as $inline) {
|
|
|
|
$inline_view = id(new DifferentialInlineCommentView())
|
|
|
|
->setMarkupEngine($engine)
|
|
|
|
->setInlineComment($inline)
|
|
|
|
->render();
|
|
|
|
$rows[] =
|
|
|
|
'<tr class="inline">'.
|
|
|
|
str_repeat('<th></th>', ($needs_blame ? 5 : 1)).
|
|
|
|
'<td>'.$inline_view.'</td>'.
|
|
|
|
'</tr>';
|
|
|
|
}
|
|
|
|
return $rows;
|
|
|
|
}
|
2011-04-01 03:28:24 +02:00
|
|
|
|
|
|
|
private static function renderRevision(DiffusionRequest $drequest,
|
|
|
|
$revision) {
|
|
|
|
|
|
|
|
$callsign = $drequest->getCallsign();
|
|
|
|
|
|
|
|
$name = 'r'.$callsign.$revision;
|
|
|
|
return phutil_render_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/'.$name,
|
|
|
|
),
|
|
|
|
$name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2011-04-01 22:42:00 +02:00
|
|
|
|
2011-04-02 00:34:45 +02:00
|
|
|
private static function renderBrowse(
|
|
|
|
DiffusionRequest $drequest,
|
|
|
|
$path,
|
|
|
|
$name = null,
|
|
|
|
$rev = null,
|
|
|
|
$line = null,
|
2012-01-27 23:00:12 +01:00
|
|
|
$view = null,
|
|
|
|
$title = null) {
|
2011-04-02 00:34:45 +02:00
|
|
|
|
|
|
|
$callsign = $drequest->getCallsign();
|
|
|
|
|
|
|
|
if ($name === null) {
|
|
|
|
$name = $path;
|
|
|
|
}
|
|
|
|
|
|
|
|
$at = null;
|
|
|
|
if ($rev) {
|
|
|
|
$at = ';'.$rev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($view) {
|
|
|
|
$view = '?view='.$view;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($line) {
|
|
|
|
$line = '$'.$line;
|
|
|
|
}
|
|
|
|
|
|
|
|
return phutil_render_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => "/diffusion/{$callsign}/browse/{$path}{$at}{$line}{$view}",
|
2012-01-27 23:00:12 +01:00
|
|
|
'title' => $title,
|
2011-04-02 00:34:45 +02:00
|
|
|
),
|
|
|
|
$name
|
|
|
|
);
|
2011-04-01 22:42:00 +02:00
|
|
|
}
|
|
|
|
|
2012-03-20 03:52:24 +01:00
|
|
|
private function loadFileForData($path, $data) {
|
2012-07-09 19:38:25 +02:00
|
|
|
return PhabricatorFile::buildFromFileDataOrHash(
|
|
|
|
$data,
|
|
|
|
array(
|
|
|
|
'name' => basename($path),
|
|
|
|
));
|
2012-03-20 03:52:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private function buildRawResponse($path, $data) {
|
|
|
|
$file = $this->loadFileForData($path, $data);
|
|
|
|
return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildImageCorpus($file_uri) {
|
2012-10-31 19:43:17 +01:00
|
|
|
$properties = new PhabricatorPropertyListView();
|
|
|
|
|
|
|
|
$properties->addProperty(
|
|
|
|
pht('Image'),
|
2012-03-20 03:52:24 +01:00
|
|
|
phutil_render_tag(
|
|
|
|
'img',
|
|
|
|
array(
|
|
|
|
'src' => $file_uri,
|
|
|
|
)));
|
2012-10-31 19:43:17 +01:00
|
|
|
|
|
|
|
$actions = id(new PhabricatorActionListView())
|
|
|
|
->setUser($this->getRequest()->getUser())
|
|
|
|
->addAction($this->createEditAction());
|
|
|
|
|
|
|
|
return array($actions, $properties);
|
2012-03-20 03:52:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private function buildBinaryCorpus($file_uri, $data) {
|
2012-10-31 19:43:17 +01:00
|
|
|
$properties = new PhabricatorPropertyListView();
|
|
|
|
|
2012-11-13 01:35:26 +01:00
|
|
|
$size = strlen($data);
|
2012-10-31 19:43:17 +01:00
|
|
|
$properties->addTextContent(
|
2012-11-13 01:35:26 +01:00
|
|
|
pht('This is a binary file. It is %2$s byte(s) in length.',
|
|
|
|
$size,
|
|
|
|
PhutilTranslator::getInstance()->formatNumber($size))
|
2012-10-31 19:43:17 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
$actions = id(new PhabricatorActionListView())
|
|
|
|
->setUser($this->getRequest()->getUser())
|
|
|
|
->addAction($this->createEditAction())
|
|
|
|
->addAction(id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Download Binary File...'))
|
|
|
|
->setIcon('download')
|
|
|
|
->setHref($file_uri));
|
|
|
|
|
|
|
|
return array($actions, $properties);
|
|
|
|
|
2012-03-20 03:52:24 +01:00
|
|
|
}
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
private function buildBeforeResponse($before) {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$drequest = $this->getDiffusionRequest();
|
|
|
|
|
2012-04-24 03:16:38 +02:00
|
|
|
// NOTE: We need to get the grandparent so we can capture filename changes
|
|
|
|
// in the parent.
|
|
|
|
|
|
|
|
$parent = $this->loadParentRevisionOf($before);
|
|
|
|
$old_filename = null;
|
|
|
|
$was_created = false;
|
|
|
|
if ($parent) {
|
|
|
|
$grandparent = $this->loadParentRevisionOf(
|
|
|
|
$parent->getCommitIdentifier());
|
|
|
|
|
|
|
|
if ($grandparent) {
|
2012-09-28 23:36:47 +02:00
|
|
|
$rename_query = new DiffusionRenameHistoryQuery();
|
|
|
|
$rename_query->setRequest($drequest);
|
2012-04-24 03:16:38 +02:00
|
|
|
$rename_query->setOldCommit($grandparent->getCommitIdentifier());
|
|
|
|
$old_filename = $rename_query->loadOldFilename();
|
|
|
|
$was_created = $rename_query->getWasCreated();
|
|
|
|
}
|
|
|
|
}
|
2012-04-08 02:24:35 +02:00
|
|
|
|
2012-04-24 03:16:38 +02:00
|
|
|
$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->getCommitIdentifier();
|
|
|
|
} 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';
|
|
|
|
}
|
2012-04-08 02:24:35 +02:00
|
|
|
|
2012-04-24 03:16:38 +02:00
|
|
|
$path = $drequest->getPath();
|
|
|
|
$renamed = null;
|
|
|
|
if ($old_filename !== null &&
|
2012-09-28 23:36:47 +02:00
|
|
|
$old_filename !== '/'.$path) {
|
2012-04-24 03:16:38 +02:00
|
|
|
$renamed = $path;
|
|
|
|
$path = $old_filename;
|
|
|
|
}
|
2012-04-08 02:24:35 +02:00
|
|
|
|
2012-07-27 00:39:13 +02:00
|
|
|
$line = null;
|
|
|
|
// If there's a follow error, drop the line so the user sees the message.
|
|
|
|
if (!$follow) {
|
|
|
|
$line = $this->getBeforeLineNumber($target_commit);
|
|
|
|
}
|
|
|
|
|
2012-04-08 02:24:35 +02:00
|
|
|
$before_uri = $drequest->generateURI(
|
|
|
|
array(
|
|
|
|
'action' => 'browse',
|
2012-04-24 03:16:38 +02:00
|
|
|
'commit' => $target_commit,
|
2012-07-27 00:39:13 +02:00
|
|
|
'line' => $line,
|
2012-04-24 03:16:38 +02:00
|
|
|
'path' => $path,
|
2012-04-08 02:24:35 +02:00
|
|
|
));
|
|
|
|
|
|
|
|
$before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
|
|
|
|
$before_uri = $before_uri->alter('before', null);
|
2012-04-24 03:16:38 +02:00
|
|
|
$before_uri = $before_uri->alter('renamed', $renamed);
|
|
|
|
$before_uri = $before_uri->alter('follow', $follow);
|
2012-04-08 02:24:35 +02:00
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())->setURI($before_uri);
|
|
|
|
}
|
2011-04-02 00:34:45 +02:00
|
|
|
|
2012-07-27 00:39:13 +02:00
|
|
|
private function getBeforeLineNumber($target_commit) {
|
|
|
|
$drequest = $this->getDiffusionRequest();
|
|
|
|
|
|
|
|
$line = $drequest->getLine();
|
|
|
|
if (!$line) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$diff_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest);
|
|
|
|
$diff_query->setAgainstCommit($target_commit);
|
|
|
|
try {
|
|
|
|
$raw_diff = $diff_query->loadRawDiff();
|
|
|
|
$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;
|
|
|
|
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
return $line;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-24 03:16:38 +02:00
|
|
|
private function loadParentRevisionOf($commit) {
|
|
|
|
$drequest = $this->getDiffusionRequest();
|
|
|
|
|
|
|
|
$before_req = DiffusionRequest::newFromDictionary(
|
|
|
|
array(
|
|
|
|
'repository' => $drequest->getRepository(),
|
|
|
|
'commit' => $commit,
|
|
|
|
));
|
|
|
|
|
|
|
|
$query = DiffusionCommitParentsQuery::newFromDiffusionRequest($before_req);
|
|
|
|
$parents = $query->loadParents();
|
|
|
|
|
|
|
|
return head($parents);
|
|
|
|
}
|
|
|
|
|
2011-03-08 18:54:55 +01:00
|
|
|
}
|