@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 615 lines 16 kB view raw
1<?php 2 3abstract class DiffusionController extends PhabricatorController { 4 5 private $diffusionRequest; 6 7 /** 8 * @return DiffusionRequest 9 */ 10 protected function getDiffusionRequest() { 11 if (!$this->diffusionRequest) { 12 throw new PhutilInvalidStateException('loadDiffusionContext'); 13 } 14 return $this->diffusionRequest; 15 } 16 17 protected function hasDiffusionRequest() { 18 return (bool)$this->diffusionRequest; 19 } 20 21 public function willBeginExecution() { 22 $request = $this->getRequest(); 23 24 // Check if this is a VCS request, e.g. from "git clone", "hg clone", or 25 // "svn checkout". If it is, we jump off into repository serving code to 26 // process the request. 27 28 $serve_controller = new DiffusionServeController(); 29 if ($serve_controller->isVCSRequest($request)) { 30 return $this->delegateToController($serve_controller); 31 } 32 33 return parent::willBeginExecution(); 34 } 35 36 protected function loadDiffusionContextForEdit() { 37 return $this->loadContext( 38 array( 39 'edit' => true, 40 )); 41 } 42 43 protected function loadDiffusionContext() { 44 return $this->loadContext(array()); 45 } 46 47 private function loadContext(array $options) { 48 $request = $this->getRequest(); 49 $viewer = $this->getViewer(); 50 require_celerity_resource('diffusion-repository-css'); 51 52 $identifier = $this->getRepositoryIdentifierFromRequest($request); 53 54 $params = $options + array( 55 'repository' => $identifier, 56 'user' => $viewer, 57 'blob' => $this->getDiffusionBlobFromRequest($request), 58 'commit' => $request->getURIData('commit'), 59 'path' => $request->getURIData('path'), 60 'line' => $request->getURIData('line'), 61 'branch' => $request->getURIData('branch'), 62 'lint' => $request->getStr('lint'), 63 ); 64 65 $drequest = DiffusionRequest::newFromDictionary($params); 66 67 if (!$drequest) { 68 return new Aphront404Response(); 69 } 70 71 // If the client is making a request like "/diffusion/1/...", but the 72 // repository has a different canonical path like "/diffusion/XYZ/...", 73 // redirect them to the canonical path. 74 75 // Skip this redirect if the request is an AJAX request, like the requests 76 // that Owners makes to complete and validate paths. 77 78 if (!$request->isAjax()) { 79 $request_path = $request->getPath(); 80 $repository = $drequest->getRepository(); 81 82 $canonical_path = $repository->getCanonicalPath($request_path); 83 if ($canonical_path !== null) { 84 if ($canonical_path != $request_path) { 85 return id(new AphrontRedirectResponse())->setURI($canonical_path); 86 } 87 } 88 } 89 90 $this->diffusionRequest = $drequest; 91 92 return null; 93 } 94 95 protected function getDiffusionBlobFromRequest(AphrontRequest $request) { 96 return $request->getURIData('dblob'); 97 } 98 99 protected function getRepositoryIdentifierFromRequest( 100 AphrontRequest $request) { 101 102 $short_name = $request->getURIData('repositoryShortName'); 103 if (phutil_nonempty_string($short_name)) { 104 // If the short name ends in ".git", ignore it. 105 $short_name = preg_replace('/\\.git\z/', '', $short_name); 106 return $short_name; 107 } 108 109 $identifier = $request->getURIData('repositoryCallsign'); 110 if (phutil_nonempty_string($identifier)) { 111 return $identifier; 112 } 113 114 $id = $request->getURIData('repositoryID'); 115 if (phutil_nonempty_string($id)) { 116 return (int)$id; 117 } 118 119 return null; 120 } 121 122 public function buildCrumbs(array $spec = array()) { 123 $crumbs = $this->buildApplicationCrumbs(); 124 $crumb_list = $this->buildCrumbList($spec); 125 foreach ($crumb_list as $crumb) { 126 $crumbs->addCrumb($crumb); 127 } 128 return $crumbs; 129 } 130 131 private function buildCrumbList(array $spec = array()) { 132 133 $spec = $spec + array( 134 'commit' => null, 135 'tags' => null, 136 'branches' => null, 137 'view' => null, 138 ); 139 140 $crumb_list = array(); 141 142 // On the home page, we don't have a DiffusionRequest. 143 if ($this->hasDiffusionRequest()) { 144 $drequest = $this->getDiffusionRequest(); 145 $repository = $drequest->getRepository(); 146 } else { 147 $drequest = null; 148 $repository = null; 149 } 150 151 if (!$repository) { 152 return $crumb_list; 153 } 154 155 $repository_name = $repository->getName(); 156 157 if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) { 158 $branch_name = $drequest->getBranch(); 159 if (phutil_nonempty_string($branch_name)) { 160 $repository_name .= ' ('.$branch_name.')'; 161 } 162 } 163 164 $crumb = id(new PHUICrumbView()) 165 ->setName($repository_name); 166 if (!$spec['view'] && !$spec['commit'] && 167 !$spec['tags'] && !$spec['branches']) { 168 $crumb_list[] = $crumb; 169 return $crumb_list; 170 } 171 $crumb->setHref( 172 $drequest->generateURI( 173 array( 174 'action' => 'branch', 175 'path' => '/', 176 ))); 177 $crumb_list[] = $crumb; 178 179 $stable_commit = $drequest->getStableCommit(); 180 $commit_name = $repository->formatCommitName($stable_commit, $local = true); 181 $commit_uri = $repository->getCommitURI($stable_commit); 182 183 if ($spec['tags']) { 184 $crumb = new PHUICrumbView(); 185 if ($spec['commit']) { 186 $crumb->setName(pht('Tags for %s', $commit_name)); 187 $crumb->setHref($commit_uri); 188 } else { 189 $crumb->setName(pht('Tags')); 190 } 191 $crumb_list[] = $crumb; 192 return $crumb_list; 193 } 194 195 if ($spec['branches']) { 196 $crumb = id(new PHUICrumbView()) 197 ->setName(pht('Branches')); 198 $crumb_list[] = $crumb; 199 return $crumb_list; 200 } 201 202 if ($spec['commit']) { 203 $crumb = id(new PHUICrumbView()) 204 ->setName($commit_name); 205 $crumb_list[] = $crumb; 206 return $crumb_list; 207 } 208 209 $crumb = new PHUICrumbView(); 210 $view = $spec['view']; 211 212 switch ($view) { 213 case 'history': 214 $view_name = pht('History'); 215 break; 216 case 'browse': 217 $view_name = pht('Code'); 218 break; 219 case 'lint': 220 $view_name = pht('Lint'); 221 break; 222 case 'change': 223 $view_name = pht('Change'); 224 break; 225 case 'compare': 226 $view_name = pht('Compare'); 227 break; 228 } 229 230 $crumb = id(new PHUICrumbView()) 231 ->setName($view_name); 232 233 $crumb_list[] = $crumb; 234 return $crumb_list; 235 } 236 237 protected function callConduitWithDiffusionRequest( 238 $method, 239 array $params = array()) { 240 241 $user = $this->getRequest()->getUser(); 242 $drequest = $this->getDiffusionRequest(); 243 244 return DiffusionQuery::callConduitWithDiffusionRequest( 245 $user, 246 $drequest, 247 $method, 248 $params); 249 } 250 251 protected function callConduitMethod($method, array $params = array()) { 252 $user = $this->getViewer(); 253 $drequest = $this->getDiffusionRequest(); 254 255 return DiffusionQuery::callConduitWithDiffusionRequest( 256 $user, 257 $drequest, 258 $method, 259 $params, 260 true); 261 } 262 263 protected function getRepositoryControllerURI( 264 PhabricatorRepository $repository, 265 $path) { 266 return $repository->getPathURI($path); 267 } 268 269 protected function renderPathLinks(DiffusionRequest $drequest, $action) { 270 $path = $drequest->getPath(); 271 $path_parts = array_filter(explode('/', trim($path, '/'))); 272 273 $divider = phutil_tag( 274 'span', 275 array( 276 'class' => 'phui-header-divider', 277 ), 278 '/'); 279 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' => '', 289 )), 290 ), 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.'/', 307 )), 308 ), 309 $part); 310 $links[] = $divider; 311 } 312 } 313 } else { 314 $links[] = $drequest->getRepository()->getDisplayName(); 315 $links[] = $divider; 316 } 317 318 return $links; 319 } 320 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); 327 } 328 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, 338 )), 339 ), 340 $drequest->getRepository()->formatCommitName($stable_commit, true)); 341 342 $tag = id(new PHUITagView()) 343 ->setName($commit) 344 ->setColor(PHUITagView::COLOR_INDIGO) 345 ->setBorder(PHUITagView::BORDER_NONE) 346 ->setType(PHUITagView::TYPE_SHADE); 347 348 return $tag; 349 } 350 351 protected function renderBranchTag(DiffusionRequest $drequest) { 352 $branch = $drequest->getBranch(); 353 $branch = id(new PhutilUTF8StringTruncator()) 354 ->setMaximumGlyphs(24) 355 ->truncateString($branch); 356 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'); 363 364 return $tag; 365 } 366 367 protected function renderSymbolicCommit(DiffusionRequest $drequest) { 368 $symbolic_tag = $drequest->getSymbolicCommit(); 369 $symbolic_tag = id(new PhutilUTF8StringTruncator()) 370 ->setMaximumGlyphs(24) 371 ->truncateString($symbolic_tag); 372 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); 379 380 return $tag; 381 } 382 383 protected function renderDirectoryReadme(DiffusionBrowseResultSet $browse) { 384 $readme_path = $browse->getReadmePath(); 385 if ($readme_path === null) { 386 return null; 387 } 388 389 $drequest = $this->getDiffusionRequest(); 390 $viewer = $this->getViewer(); 391 $repository = $drequest->getRepository(); 392 $repository_phid = $repository->getPHID(); 393 $stable_commit = $drequest->getStableCommit(); 394 395 $stable_commit_hash = PhabricatorHash::digestForIndex($stable_commit); 396 $readme_path_hash = PhabricatorHash::digestForIndex($readme_path); 397 398 $cache = PhabricatorCaches::getMutableStructureCache(); 399 $cache_key = "diffusion". 400 ".repository({$repository_phid})". 401 ".commit({$stable_commit_hash})". 402 ".readme({$readme_path_hash})"; 403 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(), 412 )); 413 } catch (Exception $ex) { 414 return null; 415 } 416 417 $file_phid = $result['filePHID']; 418 if (!$file_phid) { 419 return null; 420 } 421 422 $file = id(new PhabricatorFileQuery()) 423 ->setViewer($viewer) 424 ->withPHIDs(array($file_phid)) 425 ->executeOne(); 426 if (!$file) { 427 return null; 428 } 429 430 $corpus = $file->loadFileData(); 431 432 $readme_cache = array( 433 'corpus' => $corpus, 434 ); 435 436 $cache->setKey($cache_key, $readme_cache); 437 } 438 439 $readme_corpus = $readme_cache['corpus']; 440 if (!strlen($readme_corpus)) { 441 return null; 442 } 443 444 return id(new DiffusionReadmeView()) 445 ->setUser($this->getViewer()) 446 ->setPath($readme_path) 447 ->setContent($readme_corpus); 448 } 449 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; 456 } 457 458 $search_term = $this->getRequest()->getStr('grep'); 459 require_celerity_resource('diffusion-icons-css'); 460 require_celerity_resource('diffusion-css'); 461 462 $href = $drequest->generateURI(array( 463 'action' => 'browse', 464 'path' => $path, 465 )); 466 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, 477 )); 478 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', 487 ), 488 array( 489 $bar, 490 )); 491 492 $form_view = phutil_tag( 493 'div', 494 array( 495 'class' => 'diffusion-search-form-view', 496 ), 497 $form); 498 499 return $form_view; 500 } 501 502 protected function buildTabsView($key) { 503 $drequest = $this->getDiffusionRequest(); 504 $repository = $drequest->getRepository(); 505 506 $view = new PHUIListView(); 507 508 $view->addMenuItem( 509 id(new PHUIListItemView()) 510 ->setKey('home') 511 ->setName(pht('Home')) 512 ->setIcon('fa-home') 513 ->setHref($drequest->generateURI( 514 array( 515 'action' => 'branch', 516 'path' => '', 517 ))) 518 ->setSelected($key == 'home')); 519 520 $view->addMenuItem( 521 id(new PHUIListItemView()) 522 ->setKey('code') 523 ->setName(pht('Code')) 524 ->setIcon('fa-code') 525 ->setHref($drequest->generateURI( 526 array( 527 'action' => 'browse', 528 ))) 529 ->setSelected($key == 'code')); 530 531 if (!$repository->isSVN()) { 532 $view->addMenuItem( 533 id(new PHUIListItemView()) 534 ->setKey('branch') 535 ->setName(pht('Branches')) 536 ->setIcon('fa-code-fork') 537 ->setHref($drequest->generateURI( 538 array( 539 'action' => 'branches', 540 ))) 541 ->setSelected($key == 'branch')); 542 } 543 544 if (!$repository->isSVN()) { 545 $view->addMenuItem( 546 id(new PHUIListItemView()) 547 ->setKey('tags') 548 ->setName(pht('Tags')) 549 ->setIcon('fa-tags') 550 ->setHref($drequest->generateURI( 551 array( 552 'action' => 'tags', 553 ))) 554 ->setSelected($key == 'tags')); 555 } 556 557 $view->addMenuItem( 558 id(new PHUIListItemView()) 559 ->setKey('history') 560 ->setName(pht('History')) 561 ->setIcon('fa-history') 562 ->setHref($drequest->generateURI( 563 array( 564 'action' => 'history', 565 ))) 566 ->setSelected($key == 'history')); 567 568 return $view; 569 570 } 571 572 /** 573 * @return PHUIBoxView|null 574 */ 575 protected function buildLocateFile() { 576 $request = $this->getRequest(); 577 $viewer = $request->getUser(); 578 $drequest = $this->getDiffusionRequest(); 579 $repository = $drequest->getRepository(); 580 581 $form_box = null; 582 if ($repository->canUsePathTree()) { 583 Javelin::initBehavior( 584 'diffusion-locate-file', 585 array( 586 'controlID' => 'locate-control', 587 'inputID' => 'locate-input', 588 'symbolicCommit' => $drequest->getSymbolicCommit(), 589 'browseBaseURI' => (string)$drequest->generateURI( 590 array( 591 'action' => 'browse', 592 'commit' => '', 593 'path' => '', 594 )), 595 'uri' => (string)$drequest->generateURI( 596 array( 597 'action' => 'pathtree', 598 )), 599 )); 600 601 $form = id(new AphrontFormView()) 602 ->setUser($viewer) 603 ->appendChild( 604 id(new AphrontFormTypeaheadControl()) 605 ->setHardpointID('locate-control') 606 ->setID('locate-input') 607 ->setPlaceholder(pht('Locate File'))); 608 $form_box = id(new PHUIBoxView()) 609 ->appendChild($form->buildLayoutView()) 610 ->addClass('diffusion-profile-locate'); 611 } 612 return $form_box; 613 } 614 615}