Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / diffusion / controller / DiffusionController.php
blobb3595c1b7284d3ee06c7e88a4d25852dab6ca571
1 <?php
3 abstract class DiffusionController extends PhabricatorController {
5 private $diffusionRequest;
7 protected function getDiffusionRequest() {
8 if (!$this->diffusionRequest) {
9 throw new PhutilInvalidStateException('loadDiffusionContext');
11 return $this->diffusionRequest;
14 protected function hasDiffusionRequest() {
15 return (bool)$this->diffusionRequest;
18 public function willBeginExecution() {
19 $request = $this->getRequest();
21 // Check if this is a VCS request, e.g. from "git clone", "hg clone", or
22 // "svn checkout". If it is, we jump off into repository serving code to
23 // process the request.
25 $serve_controller = new DiffusionServeController();
26 if ($serve_controller->isVCSRequest($request)) {
27 return $this->delegateToController($serve_controller);
30 return parent::willBeginExecution();
33 protected function loadDiffusionContextForEdit() {
34 return $this->loadContext(
35 array(
36 'edit' => true,
37 ));
40 protected function loadDiffusionContext() {
41 return $this->loadContext(array());
44 private function loadContext(array $options) {
45 $request = $this->getRequest();
46 $viewer = $this->getViewer();
47 require_celerity_resource('diffusion-repository-css');
49 $identifier = $this->getRepositoryIdentifierFromRequest($request);
51 $params = $options + array(
52 'repository' => $identifier,
53 'user' => $viewer,
54 'blob' => $this->getDiffusionBlobFromRequest($request),
55 'commit' => $request->getURIData('commit'),
56 'path' => $request->getURIData('path'),
57 'line' => $request->getURIData('line'),
58 'branch' => $request->getURIData('branch'),
59 'lint' => $request->getStr('lint'),
62 $drequest = DiffusionRequest::newFromDictionary($params);
64 if (!$drequest) {
65 return new Aphront404Response();
68 // If the client is making a request like "/diffusion/1/...", but the
69 // repository has a different canonical path like "/diffusion/XYZ/...",
70 // redirect them to the canonical path.
72 // Skip this redirect if the request is an AJAX request, like the requests
73 // that Owners makes to complete and validate paths.
75 if (!$request->isAjax()) {
76 $request_path = $request->getPath();
77 $repository = $drequest->getRepository();
79 $canonical_path = $repository->getCanonicalPath($request_path);
80 if ($canonical_path !== null) {
81 if ($canonical_path != $request_path) {
82 return id(new AphrontRedirectResponse())->setURI($canonical_path);
87 $this->diffusionRequest = $drequest;
89 return null;
92 protected function getDiffusionBlobFromRequest(AphrontRequest $request) {
93 return $request->getURIData('dblob');
96 protected function getRepositoryIdentifierFromRequest(
97 AphrontRequest $request) {
99 $short_name = $request->getURIData('repositoryShortName');
100 if (strlen($short_name)) {
101 // If the short name ends in ".git", ignore it.
102 $short_name = preg_replace('/\\.git\z/', '', $short_name);
103 return $short_name;
106 $identifier = $request->getURIData('repositoryCallsign');
107 if (strlen($identifier)) {
108 return $identifier;
111 $id = $request->getURIData('repositoryID');
112 if (strlen($id)) {
113 return (int)$id;
116 return null;
119 public function buildCrumbs(array $spec = array()) {
120 $crumbs = $this->buildApplicationCrumbs();
121 $crumb_list = $this->buildCrumbList($spec);
122 foreach ($crumb_list as $crumb) {
123 $crumbs->addCrumb($crumb);
125 return $crumbs;
128 private function buildCrumbList(array $spec = array()) {
130 $spec = $spec + array(
131 'commit' => null,
132 'tags' => null,
133 'branches' => null,
134 'view' => null,
137 $crumb_list = array();
139 // On the home page, we don't have a DiffusionRequest.
140 if ($this->hasDiffusionRequest()) {
141 $drequest = $this->getDiffusionRequest();
142 $repository = $drequest->getRepository();
143 } else {
144 $drequest = null;
145 $repository = null;
148 if (!$repository) {
149 return $crumb_list;
152 $repository_name = $repository->getName();
154 if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) {
155 $branch_name = $drequest->getBranch();
156 if (strlen($branch_name)) {
157 $repository_name .= ' ('.$branch_name.')';
161 $crumb = id(new PHUICrumbView())
162 ->setName($repository_name);
163 if (!$spec['view'] && !$spec['commit'] &&
164 !$spec['tags'] && !$spec['branches']) {
165 $crumb_list[] = $crumb;
166 return $crumb_list;
168 $crumb->setHref(
169 $drequest->generateURI(
170 array(
171 'action' => 'branch',
172 'path' => '/',
173 )));
174 $crumb_list[] = $crumb;
176 $stable_commit = $drequest->getStableCommit();
177 $commit_name = $repository->formatCommitName($stable_commit, $local = true);
178 $commit_uri = $repository->getCommitURI($stable_commit);
180 if ($spec['tags']) {
181 $crumb = new PHUICrumbView();
182 if ($spec['commit']) {
183 $crumb->setName(pht('Tags for %s', $commit_name));
184 $crumb->setHref($commit_uri);
185 } else {
186 $crumb->setName(pht('Tags'));
188 $crumb_list[] = $crumb;
189 return $crumb_list;
192 if ($spec['branches']) {
193 $crumb = id(new PHUICrumbView())
194 ->setName(pht('Branches'));
195 $crumb_list[] = $crumb;
196 return $crumb_list;
199 if ($spec['commit']) {
200 $crumb = id(new PHUICrumbView())
201 ->setName($commit_name);
202 $crumb_list[] = $crumb;
203 return $crumb_list;
206 $crumb = new PHUICrumbView();
207 $view = $spec['view'];
209 switch ($view) {
210 case 'history':
211 $view_name = pht('History');
212 break;
213 case 'browse':
214 $view_name = pht('Browse');
215 break;
216 case 'lint':
217 $view_name = pht('Lint');
218 break;
219 case 'change':
220 $view_name = pht('Change');
221 break;
222 case 'compare':
223 $view_name = pht('Compare');
224 break;
227 $crumb = id(new PHUICrumbView())
228 ->setName($view_name);
230 $crumb_list[] = $crumb;
231 return $crumb_list;
234 protected function callConduitWithDiffusionRequest(
235 $method,
236 array $params = array()) {
238 $user = $this->getRequest()->getUser();
239 $drequest = $this->getDiffusionRequest();
241 return DiffusionQuery::callConduitWithDiffusionRequest(
242 $user,
243 $drequest,
244 $method,
245 $params);
248 protected function callConduitMethod($method, array $params = array()) {
249 $user = $this->getViewer();
250 $drequest = $this->getDiffusionRequest();
252 return DiffusionQuery::callConduitWithDiffusionRequest(
253 $user,
254 $drequest,
255 $method,
256 $params,
257 true);
260 protected function getRepositoryControllerURI(
261 PhabricatorRepository $repository,
262 $path) {
263 return $repository->getPathURI($path);
266 protected function renderPathLinks(DiffusionRequest $drequest, $action) {
267 $path = $drequest->getPath();
268 $path_parts = array_filter(explode('/', trim($path, '/')));
270 $divider = phutil_tag(
271 'span',
272 array(
273 'class' => 'phui-header-divider',
275 '/');
277 $links = array();
278 if ($path_parts) {
279 $links[] = phutil_tag(
280 'a',
281 array(
282 'href' => $drequest->generateURI(
283 array(
284 'action' => $action,
285 'path' => '',
288 $drequest->getRepository()->getDisplayName());
289 $links[] = $divider;
290 $accum = '';
291 $last_key = last_key($path_parts);
292 foreach ($path_parts as $key => $part) {
293 $accum .= '/'.$part;
294 if ($key === $last_key) {
295 $links[] = $part;
296 } else {
297 $links[] = phutil_tag(
298 'a',
299 array(
300 'href' => $drequest->generateURI(
301 array(
302 'action' => $action,
303 'path' => $accum.'/',
306 $part);
307 $links[] = $divider;
310 } else {
311 $links[] = $drequest->getRepository()->getDisplayName();
312 $links[] = $divider;
315 return $links;
318 protected function renderStatusMessage($title, $body) {
319 return id(new PHUIInfoView())
320 ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
321 ->setTitle($title)
322 ->setFlush(true)
323 ->appendChild($body);
326 protected function renderCommitHashTag(DiffusionRequest $drequest) {
327 $stable_commit = $drequest->getStableCommit();
328 $commit = phutil_tag(
329 'a',
330 array(
331 'href' => $drequest->generateURI(
332 array(
333 'action' => 'commit',
334 'commit' => $stable_commit,
337 $drequest->getRepository()->formatCommitName($stable_commit, true));
339 $tag = id(new PHUITagView())
340 ->setName($commit)
341 ->setColor(PHUITagView::COLOR_INDIGO)
342 ->setBorder(PHUITagView::BORDER_NONE)
343 ->setType(PHUITagView::TYPE_SHADE);
345 return $tag;
348 protected function renderBranchTag(DiffusionRequest $drequest) {
349 $branch = $drequest->getBranch();
350 $branch = id(new PhutilUTF8StringTruncator())
351 ->setMaximumGlyphs(24)
352 ->truncateString($branch);
354 $tag = id(new PHUITagView())
355 ->setName($branch)
356 ->setColor(PHUITagView::COLOR_INDIGO)
357 ->setBorder(PHUITagView::BORDER_NONE)
358 ->setType(PHUITagView::TYPE_OUTLINE)
359 ->addClass('diffusion-header-branch-tag');
361 return $tag;
364 protected function renderSymbolicCommit(DiffusionRequest $drequest) {
365 $symbolic_tag = $drequest->getSymbolicCommit();
366 $symbolic_tag = id(new PhutilUTF8StringTruncator())
367 ->setMaximumGlyphs(24)
368 ->truncateString($symbolic_tag);
370 $tag = id(new PHUITagView())
371 ->setName($symbolic_tag)
372 ->setIcon('fa-tag')
373 ->setColor(PHUITagView::COLOR_INDIGO)
374 ->setBorder(PHUITagView::BORDER_NONE)
375 ->setType(PHUITagView::TYPE_SHADE);
377 return $tag;
380 protected function renderDirectoryReadme(DiffusionBrowseResultSet $browse) {
381 $readme_path = $browse->getReadmePath();
382 if ($readme_path === null) {
383 return null;
386 $drequest = $this->getDiffusionRequest();
387 $viewer = $this->getViewer();
388 $repository = $drequest->getRepository();
389 $repository_phid = $repository->getPHID();
390 $stable_commit = $drequest->getStableCommit();
392 $stable_commit_hash = PhabricatorHash::digestForIndex($stable_commit);
393 $readme_path_hash = PhabricatorHash::digestForIndex($readme_path);
395 $cache = PhabricatorCaches::getMutableStructureCache();
396 $cache_key = "diffusion".
397 ".repository({$repository_phid})".
398 ".commit({$stable_commit_hash})".
399 ".readme({$readme_path_hash})";
401 $readme_cache = $cache->getKey($cache_key);
402 if (!$readme_cache) {
403 try {
404 $result = $this->callConduitWithDiffusionRequest(
405 'diffusion.filecontentquery',
406 array(
407 'path' => $readme_path,
408 'commit' => $drequest->getStableCommit(),
410 } catch (Exception $ex) {
411 return null;
414 $file_phid = $result['filePHID'];
415 if (!$file_phid) {
416 return null;
419 $file = id(new PhabricatorFileQuery())
420 ->setViewer($viewer)
421 ->withPHIDs(array($file_phid))
422 ->executeOne();
423 if (!$file) {
424 return null;
427 $corpus = $file->loadFileData();
429 $readme_cache = array(
430 'corpus' => $corpus,
433 $cache->setKey($cache_key, $readme_cache);
436 $readme_corpus = $readme_cache['corpus'];
437 if (!strlen($readme_corpus)) {
438 return null;
441 return id(new DiffusionReadmeView())
442 ->setUser($this->getViewer())
443 ->setPath($readme_path)
444 ->setContent($readme_corpus);
447 protected function renderSearchForm($path = '/') {
448 $drequest = $this->getDiffusionRequest();
449 $viewer = $this->getViewer();
450 switch ($drequest->getRepository()->getVersionControlSystem()) {
451 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
452 return null;
455 $search_term = $this->getRequest()->getStr('grep');
456 require_celerity_resource('diffusion-icons-css');
457 require_celerity_resource('diffusion-css');
459 $href = $drequest->generateURI(array(
460 'action' => 'browse',
461 'path' => $path,
464 $bar = javelin_tag(
465 'input',
466 array(
467 'type' => 'text',
468 'id' => 'diffusion-search-input',
469 'name' => 'grep',
470 'class' => 'diffusion-search-input',
471 'sigil' => 'diffusion-search-input',
472 'placeholder' => pht('Pattern Search'),
473 'value' => $search_term,
476 $form = phabricator_form(
477 $viewer,
478 array(
479 'method' => 'GET',
480 'action' => $href,
481 'sigil' => 'diffusion-search-form',
482 'class' => 'diffusion-search-form',
483 'id' => 'diffusion-search-form',
485 array(
486 $bar,
489 $form_view = phutil_tag(
490 'div',
491 array(
492 'class' => 'diffusion-search-form-view',
494 $form);
496 return $form_view;
499 protected function buildTabsView($key) {
500 $drequest = $this->getDiffusionRequest();
501 $repository = $drequest->getRepository();
503 $view = new PHUIListView();
505 $view->addMenuItem(
506 id(new PHUIListItemView())
507 ->setKey('code')
508 ->setName(pht('Code'))
509 ->setIcon('fa-code')
510 ->setHref($drequest->generateURI(
511 array(
512 'action' => 'browse',
514 ->setSelected($key == 'code'));
516 if (!$repository->isSVN()) {
517 $view->addMenuItem(
518 id(new PHUIListItemView())
519 ->setKey('branch')
520 ->setName(pht('Branches'))
521 ->setIcon('fa-code-fork')
522 ->setHref($drequest->generateURI(
523 array(
524 'action' => 'branches',
526 ->setSelected($key == 'branch'));
529 if (!$repository->isSVN()) {
530 $view->addMenuItem(
531 id(new PHUIListItemView())
532 ->setKey('tags')
533 ->setName(pht('Tags'))
534 ->setIcon('fa-tags')
535 ->setHref($drequest->generateURI(
536 array(
537 'action' => 'tags',
539 ->setSelected($key == 'tags'));
542 $view->addMenuItem(
543 id(new PHUIListItemView())
544 ->setKey('history')
545 ->setName(pht('History'))
546 ->setIcon('fa-history')
547 ->setHref($drequest->generateURI(
548 array(
549 'action' => 'history',
551 ->setSelected($key == 'history'));
553 return $view;