Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / diffusion / controller / DiffusionController.php
blob870cb46000c052791892c851bed3758438ae4bad
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 ($short_name !== null && 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 ($identifier !== null && strlen($identifier)) {
108 return $identifier;
111 $id = $request->getURIData('repositoryID');
112 if ($id !== null && 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();
269 if ($path !== null && strlen($path)) {
270 $path_parts = array_filter(explode('/', trim($path, '/')));
273 $divider = phutil_tag(
274 'span',
275 array(
276 'class' => 'phui-header-divider',
278 '/');
280 $links = array();
281 if ($path_parts) {
282 $links[] = phutil_tag(
283 'a',
284 array(
285 'href' => $drequest->generateURI(
286 array(
287 'action' => $action,
288 'path' => '',
291 $drequest->getRepository()->getDisplayName());
292 $links[] = $divider;
293 $accum = '';
294 $last_key = last_key($path_parts);
295 foreach ($path_parts as $key => $part) {
296 $accum .= '/'.$part;
297 if ($key === $last_key) {
298 $links[] = $part;
299 } else {
300 $links[] = phutil_tag(
301 'a',
302 array(
303 'href' => $drequest->generateURI(
304 array(
305 'action' => $action,
306 'path' => $accum.'/',
309 $part);
310 $links[] = $divider;
313 } else {
314 $links[] = $drequest->getRepository()->getDisplayName();
315 $links[] = $divider;
318 return $links;
321 protected function renderStatusMessage($title, $body) {
322 return id(new PHUIInfoView())
323 ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
324 ->setTitle($title)
325 ->setFlush(true)
326 ->appendChild($body);
329 protected function renderCommitHashTag(DiffusionRequest $drequest) {
330 $stable_commit = $drequest->getStableCommit();
331 $commit = phutil_tag(
332 'a',
333 array(
334 'href' => $drequest->generateURI(
335 array(
336 'action' => 'commit',
337 'commit' => $stable_commit,
340 $drequest->getRepository()->formatCommitName($stable_commit, true));
342 $tag = id(new PHUITagView())
343 ->setName($commit)
344 ->setColor(PHUITagView::COLOR_INDIGO)
345 ->setBorder(PHUITagView::BORDER_NONE)
346 ->setType(PHUITagView::TYPE_SHADE);
348 return $tag;
351 protected function renderBranchTag(DiffusionRequest $drequest) {
352 $branch = $drequest->getBranch();
353 $branch = id(new PhutilUTF8StringTruncator())
354 ->setMaximumGlyphs(24)
355 ->truncateString($branch);
357 $tag = id(new PHUITagView())
358 ->setName($branch)
359 ->setColor(PHUITagView::COLOR_INDIGO)
360 ->setBorder(PHUITagView::BORDER_NONE)
361 ->setType(PHUITagView::TYPE_OUTLINE)
362 ->addClass('diffusion-header-branch-tag');
364 return $tag;
367 protected function renderSymbolicCommit(DiffusionRequest $drequest) {
368 $symbolic_tag = $drequest->getSymbolicCommit();
369 $symbolic_tag = id(new PhutilUTF8StringTruncator())
370 ->setMaximumGlyphs(24)
371 ->truncateString($symbolic_tag);
373 $tag = id(new PHUITagView())
374 ->setName($symbolic_tag)
375 ->setIcon('fa-tag')
376 ->setColor(PHUITagView::COLOR_INDIGO)
377 ->setBorder(PHUITagView::BORDER_NONE)
378 ->setType(PHUITagView::TYPE_SHADE);
380 return $tag;
383 protected function renderDirectoryReadme(DiffusionBrowseResultSet $browse) {
384 $readme_path = $browse->getReadmePath();
385 if ($readme_path === null) {
386 return null;
389 $drequest = $this->getDiffusionRequest();
390 $viewer = $this->getViewer();
391 $repository = $drequest->getRepository();
392 $repository_phid = $repository->getPHID();
393 $stable_commit = $drequest->getStableCommit();
395 $stable_commit_hash = PhabricatorHash::digestForIndex($stable_commit);
396 $readme_path_hash = PhabricatorHash::digestForIndex($readme_path);
398 $cache = PhabricatorCaches::getMutableStructureCache();
399 $cache_key = "diffusion".
400 ".repository({$repository_phid})".
401 ".commit({$stable_commit_hash})".
402 ".readme({$readme_path_hash})";
404 $readme_cache = $cache->getKey($cache_key);
405 if (!$readme_cache) {
406 try {
407 $result = $this->callConduitWithDiffusionRequest(
408 'diffusion.filecontentquery',
409 array(
410 'path' => $readme_path,
411 'commit' => $drequest->getStableCommit(),
413 } catch (Exception $ex) {
414 return null;
417 $file_phid = $result['filePHID'];
418 if (!$file_phid) {
419 return null;
422 $file = id(new PhabricatorFileQuery())
423 ->setViewer($viewer)
424 ->withPHIDs(array($file_phid))
425 ->executeOne();
426 if (!$file) {
427 return null;
430 $corpus = $file->loadFileData();
432 $readme_cache = array(
433 'corpus' => $corpus,
436 $cache->setKey($cache_key, $readme_cache);
439 $readme_corpus = $readme_cache['corpus'];
440 if (!strlen($readme_corpus)) {
441 return null;
444 return id(new DiffusionReadmeView())
445 ->setUser($this->getViewer())
446 ->setPath($readme_path)
447 ->setContent($readme_corpus);
450 protected function renderSearchForm($path = '/') {
451 $drequest = $this->getDiffusionRequest();
452 $viewer = $this->getViewer();
453 switch ($drequest->getRepository()->getVersionControlSystem()) {
454 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
455 return null;
458 $search_term = $this->getRequest()->getStr('grep');
459 require_celerity_resource('diffusion-icons-css');
460 require_celerity_resource('diffusion-css');
462 $href = $drequest->generateURI(array(
463 'action' => 'browse',
464 'path' => $path,
467 $bar = javelin_tag(
468 'input',
469 array(
470 'type' => 'text',
471 'id' => 'diffusion-search-input',
472 'name' => 'grep',
473 'class' => 'diffusion-search-input',
474 'sigil' => 'diffusion-search-input',
475 'placeholder' => pht('Pattern Search'),
476 'value' => $search_term,
479 $form = phabricator_form(
480 $viewer,
481 array(
482 'method' => 'GET',
483 'action' => $href,
484 'sigil' => 'diffusion-search-form',
485 'class' => 'diffusion-search-form',
486 'id' => 'diffusion-search-form',
488 array(
489 $bar,
492 $form_view = phutil_tag(
493 'div',
494 array(
495 'class' => 'diffusion-search-form-view',
497 $form);
499 return $form_view;
502 protected function buildTabsView($key) {
503 $drequest = $this->getDiffusionRequest();
504 $repository = $drequest->getRepository();
506 $view = new PHUIListView();
508 $view->addMenuItem(
509 id(new PHUIListItemView())
510 ->setKey('code')
511 ->setName(pht('Code'))
512 ->setIcon('fa-code')
513 ->setHref($drequest->generateURI(
514 array(
515 'action' => 'browse',
517 ->setSelected($key == 'code'));
519 if (!$repository->isSVN()) {
520 $view->addMenuItem(
521 id(new PHUIListItemView())
522 ->setKey('branch')
523 ->setName(pht('Branches'))
524 ->setIcon('fa-code-fork')
525 ->setHref($drequest->generateURI(
526 array(
527 'action' => 'branches',
529 ->setSelected($key == 'branch'));
532 if (!$repository->isSVN()) {
533 $view->addMenuItem(
534 id(new PHUIListItemView())
535 ->setKey('tags')
536 ->setName(pht('Tags'))
537 ->setIcon('fa-tags')
538 ->setHref($drequest->generateURI(
539 array(
540 'action' => 'tags',
542 ->setSelected($key == 'tags'));
545 $view->addMenuItem(
546 id(new PHUIListItemView())
547 ->setKey('history')
548 ->setName(pht('History'))
549 ->setIcon('fa-history')
550 ->setHref($drequest->generateURI(
551 array(
552 'action' => 'history',
554 ->setSelected($key == 'history'));
556 return $view;