@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Push construction of routing maps into Sites

Summary:
This enables CORGI.

Currently, `AphrontSite` subclasses can't really have their own routes. They can do this sort of hacky rewriting of paths, but that's a mess and not desirable in the long run.

Instead, let subclasses build their own routing maps. This will let CORP and ORG have their own routing maps.

I was able to get rid of the `PhameBlogResourcesSite` since it can really just share the standard resources site.

Test Plan:
- With no base URI set, and a base URI set, loaded main page and resources (from main site).
- With file domain set, loaded resources from main site and file site.
- Loaded a skinned blog from a domain.
- Loaded a skinned blog from the main site.
- Viewed "Request" tab of DarkConsole to see site/controller info.

Reviewers: chad

Reviewed By: chad

Differential Revision: https://secure.phabricator.com/D14008

+476 -282
-28
scripts/aphront/aphrontpath.php
··· 1 - #!/usr/bin/env php 2 - <?php 3 - 4 - $root = dirname(dirname(dirname(__FILE__))); 5 - require_once $root.'/scripts/__init_script__.php'; 6 - 7 - if ($argc !== 2 || $argv[1] === '--help') { 8 - echo pht('Usage: %s', 'aphrontpath.php <url>')."\n"; 9 - echo pht( 10 - "Purpose: Print controller which will process passed %s.\n", 11 - '<url>'); 12 - exit(1); 13 - } 14 - 15 - $url = parse_url($argv[1]); 16 - $path = '/'.(isset($url['path']) ? ltrim($url['path'], '/') : ''); 17 - 18 - $config_key = 'aphront.default-application-configuration-class'; 19 - $application = PhabricatorEnv::newObjectFromConfig($config_key); 20 - $application->setRequest(new AphrontRequest('', $path)); 21 - 22 - list($controller) = $application->buildControllerForPath($path); 23 - if (!$controller && substr($path, -1) !== '/') { 24 - list($controller) = $application->buildControllerForPath($path.'/'); 25 - } 26 - if ($controller) { 27 - echo get_class($controller)."\n"; 28 - }
+4 -4
src/__phutil_library_map__.php
··· 158 158 'AphrontRequest' => 'aphront/AphrontRequest.php', 159 159 'AphrontRequestTestCase' => 'aphront/__tests__/AphrontRequestTestCase.php', 160 160 'AphrontResponse' => 'aphront/response/AphrontResponse.php', 161 + 'AphrontRoutingMap' => 'aphront/site/AphrontRoutingMap.php', 162 + 'AphrontRoutingResult' => 'aphront/site/AphrontRoutingResult.php', 161 163 'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php', 162 164 'AphrontSite' => 'aphront/site/AphrontSite.php', 163 165 'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php', ··· 166 168 'AphrontTagView' => 'view/AphrontTagView.php', 167 169 'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php', 168 170 'AphrontTypeaheadTemplateView' => 'view/control/AphrontTypeaheadTemplateView.php', 169 - 'AphrontURIMapper' => 'aphront/AphrontURIMapper.php', 170 171 'AphrontUnhandledExceptionResponse' => 'aphront/response/AphrontUnhandledExceptionResponse.php', 171 172 'AphrontUsageException' => 'aphront/exception/AphrontUsageException.php', 172 173 'AphrontView' => 'view/AphrontView.php', ··· 3126 3127 'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php', 3127 3128 'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php', 3128 3129 'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php', 3129 - 'PhameBlogResourceSite' => 'applications/phame/site/PhameBlogResourceSite.php', 3130 3130 'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php', 3131 3131 'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php', 3132 3132 'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php', ··· 3777 3777 'AphrontRequest' => 'Phobject', 3778 3778 'AphrontRequestTestCase' => 'PhabricatorTestCase', 3779 3779 'AphrontResponse' => 'Phobject', 3780 + 'AphrontRoutingMap' => 'Phobject', 3781 + 'AphrontRoutingResult' => 'Phobject', 3780 3782 'AphrontSideNavFilterView' => 'AphrontView', 3781 3783 'AphrontSite' => 'Phobject', 3782 3784 'AphrontStackTraceView' => 'AphrontView', ··· 3785 3787 'AphrontTagView' => 'AphrontView', 3786 3788 'AphrontTokenizerTemplateView' => 'AphrontView', 3787 3789 'AphrontTypeaheadTemplateView' => 'AphrontView', 3788 - 'AphrontURIMapper' => 'Phobject', 3789 3790 'AphrontUnhandledExceptionResponse' => 'AphrontStandaloneHTMLResponse', 3790 3791 'AphrontUsageException' => 'AphrontException', 3791 3792 'AphrontView' => array( ··· 7238 7239 'PhameBlogListController' => 'PhameController', 7239 7240 'PhameBlogLiveController' => 'PhameController', 7240 7241 'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7241 - 'PhameBlogResourceSite' => 'PhameSite', 7242 7242 'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine', 7243 7243 'PhameBlogSite' => 'PhameSite', 7244 7244 'PhameBlogSkin' => 'PhabricatorController',
+20
src/aphront/AphrontRequest.php
··· 26 26 private $requestData; 27 27 private $user; 28 28 private $applicationConfiguration; 29 + private $site; 30 + private $controller; 29 31 private $uriData; 30 32 private $cookiePrefix; 31 33 ··· 75 77 // https://www.djangoproject.com/weblog/2012/oct/17/security/ 76 78 $uri = new PhutilURI('http://'.$this->host); 77 79 return $uri->getDomain(); 80 + } 81 + 82 + public function setSite(AphrontSite $site) { 83 + $this->site = $site; 84 + return $this; 85 + } 86 + 87 + public function getSite() { 88 + return $this->site; 89 + } 90 + 91 + public function setController(AphrontController $controller) { 92 + $this->controller = $controller; 93 + return $this; 94 + } 95 + 96 + public function getController() { 97 + return $this->controller; 78 98 } 79 99 80 100
-50
src/aphront/AphrontURIMapper.php
··· 1 - <?php 2 - 3 - final class AphrontURIMapper extends Phobject { 4 - 5 - private $map; 6 - 7 - public function __construct(array $map) { 8 - $this->map = $map; 9 - } 10 - 11 - public function mapPath($path) { 12 - $map = $this->map; 13 - foreach ($map as $rule => $value) { 14 - list($controller, $data) = $this->tryRule($rule, $value, $path); 15 - if ($controller) { 16 - foreach ($data as $k => $v) { 17 - if (is_numeric($k)) { 18 - unset($data[$k]); 19 - } 20 - } 21 - return array($controller, $data); 22 - } 23 - } 24 - 25 - return array(null, null); 26 - } 27 - 28 - private function tryRule($rule, $value, $path) { 29 - $match = null; 30 - $pattern = '#^'.$rule.(is_array($value) ? '' : '$').'#'; 31 - if (!preg_match($pattern, $path, $match)) { 32 - return array(null, null); 33 - } 34 - 35 - if (!is_array($value)) { 36 - return array($value, $match); 37 - } 38 - 39 - $path = substr($path, strlen($match[0])); 40 - foreach ($value as $srule => $sval) { 41 - list($controller, $data) = $this->tryRule($srule, $sval, $path); 42 - if ($controller) { 43 - return array($controller, $data + $match); 44 - } 45 - } 46 - 47 - return array(null, null); 48 - } 49 - 50 - }
+30 -78
src/aphront/configuration/AphrontApplicationConfiguration.php
··· 210 210 )); 211 211 $multimeter->setEventContext('web.'.$controller_class); 212 212 213 + $request->setController($controller); 213 214 $request->setURIMap($uri_data); 215 + 214 216 $controller->setRequest($request); 215 217 216 218 // If execution throws an exception and then trying to render that ··· 283 285 284 286 285 287 /** 286 - * Using builtin and application routes, build the appropriate 287 - * @{class:AphrontController} class for the request. To route a request, we 288 - * first test if the HTTP_HOST is configured as a valid Phabricator URI. If 289 - * it isn't, we do a special check to see if it's a custom domain for a blog 290 - * in the Phame application and if that fails we error. Otherwise, we test 291 - * against all application routes from installed 292 - * @{class:PhabricatorApplication}s. 293 - * 294 - * If we match a route, we construct the controller it points at, build it, 295 - * and return it. 296 - * 297 - * If we fail to match a route, but the current path is missing a trailing 298 - * "/", we try routing the same path with a trailing "/" and do a redirect 299 - * if that has a valid route. The idea is to canoncalize URIs for consistency, 300 - * but avoid breaking noncanonical URIs that we can easily salvage. 301 - * 302 - * NOTE: We only redirect on GET. On POST, we'd drop parameters and most 303 - * likely mutate the request implicitly, and a bad POST usually indicates a 304 - * programming error rather than a sloppy typist. 305 - * 306 - * If the failing path already has a trailing "/", or we can't route the 307 - * version with a "/", we call @{method:build404Controller}, which build a 308 - * fallback @{class:AphrontController}. 288 + * Build a controller to respond to the request. 309 289 * 310 290 * @return pair<AphrontController,dict> Controller and dictionary of request 311 291 * parameters. 312 292 * @task routing 313 293 */ 314 - final public function buildController() { 294 + final private function buildController() { 315 295 $request = $this->getRequest(); 316 296 317 297 // If we're configured to operate in cluster mode, reject requests which ··· 373 353 } 374 354 } 375 355 376 - // TODO: Really, the Site should get more control here and be able to 377 - // do its own routing logic if it wants, but we don't need that for now. 378 - $path = $site->getPathForRouting($request); 356 + $maps = $site->getRoutingMaps(); 357 + $path = $request->getPath(); 379 358 380 - list($controller, $uri_data) = $this->buildControllerForPath($path); 381 - if (!$controller) { 382 - if (!preg_match('@/$@', $path)) { 383 - // If we failed to match anything but don't have a trailing slash, try 384 - // to add a trailing slash and issue a redirect if that resolves. 385 - list($controller, $uri_data) = $this->buildControllerForPath($path.'/'); 386 - 387 - // NOTE: For POST, just 404 instead of redirecting, since the redirect 388 - // will be a GET without parameters. 359 + $result = $this->routePath($maps, $path); 360 + if ($result) { 361 + return $result; 362 + } 389 363 390 - if ($controller && !$request->isHTTPPost()) { 391 - $slash_uri = $request->getRequestURI()->setPath($path.'/'); 364 + // If we failed to match anything but don't have a trailing slash, try 365 + // to add a trailing slash and issue a redirect if that resolves. 392 366 393 - $external = strlen($request->getRequestURI()->getDomain()); 394 - return $this->buildRedirectController($slash_uri, $external); 395 - } 367 + // NOTE: We only do this for GET, since redirects switch to GET and drop 368 + // data like POST parameters. 369 + if (!preg_match('@/$@', $path) && $request->isHTTPGet()) { 370 + $result = $this->routePath($maps, $path.'/'); 371 + if ($result) { 372 + $slash_uri = $request->getRequestURI()->setPath($path.'/'); 373 + $external = strlen($request->getRequestURI()->getDomain()); 374 + return $this->buildRedirectController($slash_uri, $external); 396 375 } 397 - return $this->build404Controller(); 398 376 } 399 377 400 - return array($controller, $uri_data); 378 + return $this->build404Controller(); 401 379 } 402 - 403 380 404 381 /** 405 382 * Map a specific path to the corresponding controller. For a description 406 383 * of routing, see @{method:buildController}. 407 384 * 385 + * @param list<AphrontRoutingMap> List of routing maps. 386 + * @param string Path to route. 408 387 * @return pair<AphrontController,dict> Controller and dictionary of request 409 388 * parameters. 410 389 * @task routing 411 390 */ 412 - final public function buildControllerForPath($path) { 413 - $maps = array(); 414 - 415 - $applications = PhabricatorApplication::getAllInstalledApplications(); 416 - foreach ($applications as $application) { 417 - $maps[] = array($application, $application->getRoutes()); 418 - } 419 - 420 - $current_application = null; 421 - $controller_class = null; 422 - foreach ($maps as $map_info) { 423 - list($application, $map) = $map_info; 424 - 425 - $mapper = new AphrontURIMapper($map); 426 - list($controller_class, $uri_data) = $mapper->mapPath($path); 427 - 428 - if ($controller_class) { 429 - if ($application) { 430 - $current_application = $application; 431 - } 432 - break; 391 + private function routePath(array $maps, $path) { 392 + foreach ($maps as $map) { 393 + $result = $map->routePath($path); 394 + if ($result) { 395 + return array($result->getController(), $result->getURIData()); 433 396 } 434 397 } 435 - 436 - if (!$controller_class) { 437 - return array(null, null); 438 - } 439 - 440 - $request = $this->getRequest(); 441 - 442 - $controller = newv($controller_class, array()); 443 - if ($current_application) { 444 - $controller->setCurrentApplication($current_application); 445 - } 446 - 447 - return array($controller, $uri_data); 448 398 } 449 399 450 400 private function buildSiteForRequest(AphrontRequest $request) { ··· 468 418 $path, 469 419 $host)); 470 420 } 421 + 422 + $request->setSite($site); 471 423 472 424 return $site; 473 425 }
+159
src/aphront/site/AphrontRoutingMap.php
··· 1 + <?php 2 + 3 + /** 4 + * Collection of routes on a site for an application. 5 + * 6 + * @task info Map Information 7 + * @task routing Routing 8 + */ 9 + final class AphrontRoutingMap extends Phobject { 10 + 11 + private $site; 12 + private $application; 13 + private $routes = array(); 14 + 15 + 16 + /* -( Map Info )----------------------------------------------------------- */ 17 + 18 + 19 + public function setSite(AphrontSite $site) { 20 + $this->site = $site; 21 + return $this; 22 + } 23 + 24 + public function getSite() { 25 + return $this->site; 26 + } 27 + 28 + public function setApplication(PhabricatorApplication $application) { 29 + $this->application = $application; 30 + return $this; 31 + } 32 + 33 + public function getApplication() { 34 + return $this->application; 35 + } 36 + 37 + public function setRoutes(array $routes) { 38 + $this->routes = $routes; 39 + return $this; 40 + } 41 + 42 + public function getRoutes() { 43 + return $this->routes; 44 + } 45 + 46 + 47 + /* -( Routing )------------------------------------------------------------ */ 48 + 49 + 50 + /** 51 + * Find the route matching a path, if one exists. 52 + * 53 + * @param string Path to route. 54 + * @return AphrontRoutingResult|null Routing result, if path matches map. 55 + * @task routing 56 + */ 57 + public function routePath($path) { 58 + $map = $this->getRoutes(); 59 + 60 + foreach ($map as $route => $value) { 61 + $match = $this->tryRoute($route, $value, $path); 62 + if (!$match) { 63 + continue; 64 + } 65 + 66 + $result = $this->newRoutingResult(); 67 + $application = $result->getApplication(); 68 + 69 + $controller_class = $match['class']; 70 + $controller = newv($controller_class, array()); 71 + $controller->setCurrentApplication($application); 72 + 73 + $result 74 + ->setController($controller) 75 + ->setURIData($match['data']); 76 + 77 + return $result; 78 + } 79 + 80 + return null; 81 + } 82 + 83 + 84 + /** 85 + * Test a sub-map to see if any routes match a path. 86 + * 87 + * @param string Path to route. 88 + * @param string Pattern from the map. 89 + * @param string Value from the map. 90 + * @return dict<string, wild>|null Match details, if path matches sub-map. 91 + * @task routing 92 + */ 93 + private function tryRoute($route, $value, $path) { 94 + $has_submap = is_array($value); 95 + 96 + if (!$has_submap) { 97 + // If the value is a controller rather than a sub-map, any matching 98 + // route must completely consume the path. 99 + $pattern = '(^'.$route.'\z)'; 100 + } else { 101 + $pattern = '(^'.$route.')'; 102 + } 103 + 104 + $data = null; 105 + $ok = preg_match($pattern, $path, $data); 106 + if ($ok === false) { 107 + throw new Exception( 108 + pht( 109 + 'Routing fragment "%s" is not a valid regular expression.', 110 + $route)); 111 + } 112 + 113 + if (!$ok) { 114 + return null; 115 + } 116 + 117 + $path_match = $data[0]; 118 + 119 + // Clean up the data. We only want to retain named capturing groups, not 120 + // the duplicated numeric captures. 121 + foreach ($data as $k => $v) { 122 + if (is_numeric($k)) { 123 + unset($data[$k]); 124 + } 125 + } 126 + 127 + if (!$has_submap) { 128 + return array( 129 + 'class' => $value, 130 + 'data' => $data, 131 + ); 132 + } 133 + 134 + $sub_path = substr($path, strlen($path_match)); 135 + foreach ($value as $sub_route => $sub_value) { 136 + $result = $this->tryRoute($sub_route, $sub_value, $sub_path); 137 + if ($result) { 138 + $result['data'] += $data; 139 + return $result; 140 + } 141 + } 142 + 143 + return null; 144 + } 145 + 146 + 147 + /** 148 + * Build a new routing result for this map. 149 + * 150 + * @return AphrontRoutingResult New, empty routing result. 151 + * @task routing 152 + */ 153 + private function newRoutingResult() { 154 + return id(new AphrontRoutingResult()) 155 + ->setSite($this->getSite()) 156 + ->setApplication($this->getApplication()); 157 + } 158 + 159 + }
+55
src/aphront/site/AphrontRoutingResult.php
··· 1 + <?php 2 + 3 + /** 4 + * Details about a routing map match for a path. 5 + * 6 + * @param info Result Information 7 + */ 8 + final class AphrontRoutingResult extends Phobject { 9 + 10 + private $site; 11 + private $application; 12 + private $controller; 13 + private $uriData; 14 + 15 + 16 + /* -( Result Information )------------------------------------------------- */ 17 + 18 + 19 + public function setSite(AphrontSite $site) { 20 + $this->site = $site; 21 + return $this; 22 + } 23 + 24 + public function getSite() { 25 + return $this->site; 26 + } 27 + 28 + public function setApplication(PhabricatorApplication $application) { 29 + $this->application = $application; 30 + return $this; 31 + } 32 + 33 + public function getApplication() { 34 + return $this->application; 35 + } 36 + 37 + public function setController(AphrontController $controller) { 38 + $this->controller = $controller; 39 + return $this; 40 + } 41 + 42 + public function getController() { 43 + return $this->controller; 44 + } 45 + 46 + public function setURIData(array $uri_data) { 47 + $this->uriData = $uri_data; 48 + return $this; 49 + } 50 + 51 + public function getURIData() { 52 + return $this->uriData; 53 + } 54 + 55 + }
+4 -16
src/aphront/site/AphrontSite.php
··· 7 7 8 8 abstract public function shouldRequireHTTPS(); 9 9 abstract public function newSiteForRequest(AphrontRequest $request); 10 - 11 - /** 12 - * NOTE: This is temporary glue; eventually, sites will return an entire 13 - * route map. 14 - */ 15 - public function getPathForRouting(AphrontRequest $request) { 16 - return $request->getPath(); 17 - } 10 + abstract public function getRoutingMaps(); 18 11 19 12 protected function isHostMatch($host, array $uris) { 20 13 foreach ($uris as $uri) { ··· 32 25 return false; 33 26 } 34 27 35 - protected function isPathPrefixMatch($path, array $paths) { 36 - foreach ($paths as $candidate) { 37 - if (strncmp($path, $candidate, strlen($candidate)) === 0) { 38 - return true; 39 - } 40 - } 41 - 42 - return false; 28 + protected function newRoutingMap() { 29 + return id(new AphrontRoutingMap()) 30 + ->setSite($this); 43 31 } 44 32 45 33 final public static function getAllSites() {
+13
src/aphront/site/PhabricatorPlatformSite.php
··· 37 37 return null; 38 38 } 39 39 40 + public function getRoutingMaps() { 41 + $applications = PhabricatorApplication::getAllInstalledApplications(); 42 + 43 + $maps = array(); 44 + foreach ($applications as $application) { 45 + $maps[] = $this->newRoutingMap() 46 + ->setApplication($application) 47 + ->setRoutes($application->getRoutes()); 48 + } 49 + 50 + return $maps; 51 + } 52 + 40 53 }
+11 -11
src/aphront/site/PhabricatorResourceSite.php
··· 22 22 return new PhabricatorResourceSite(); 23 23 } 24 24 25 - // These are CDN routes, so we let them through even if the "Host" header 26 - // doesn't match anything we recognize. The 27 - $whitelist = array( 28 - '/res/', 29 - '/file/data/', 30 - '/file/xform/', 31 - ); 25 + return null; 26 + } 32 27 33 - $path = $request->getPath(); 34 - if ($this->isPathPrefixMatch($path, $whitelist)) { 35 - return new PhabricatorResourceSite(); 28 + public function getRoutingMaps() { 29 + $applications = PhabricatorApplication::getAllInstalledApplications(); 30 + 31 + $maps = array(); 32 + foreach ($applications as $application) { 33 + $maps[] = $this->newRoutingMap() 34 + ->setApplication($application) 35 + ->setRoutes($application->getResourceRoutes()); 36 36 } 37 37 38 - return null; 38 + return $maps; 39 39 } 40 40 41 41 }
+4
src/applications/base/PhabricatorApplication.php
··· 243 243 return array(); 244 244 } 245 245 246 + public function getResourceRoutes() { 247 + return array(); 248 + } 249 + 246 250 247 251 /* -( Email Integration )-------------------------------------------------- */ 248 252
+11
src/applications/celerity/application/PhabricatorCelerityApplication.php
··· 15 15 } 16 16 17 17 public function getRoutes() { 18 + // We serve resources from both the platform site and the resource site. 19 + // This is safe because the user doesn't have any direct control over 20 + // resources. 21 + 22 + // The advantage of serving resources from the resource site (if possible) 23 + // is that we can use a CDN there if one is configured, but there is no 24 + // particular security concern. 25 + return $this->getResourceRoutes(); 26 + } 27 + 28 + public function getResourceRoutes() { 18 29 $extensions = CelerityResourceController::getSupportedResourceTypes(); 19 30 $extensions = array_keys($extensions); 20 31 $extensions = implode('|', $extensions);
+91 -36
src/applications/console/plugin/DarkConsoleRequestPlugin.php
··· 14 14 } 15 15 16 16 public function generateData() { 17 + $addr = idx($_SERVER, 'SERVER_ADDR'); 18 + if ($addr) { 19 + $hostname = @gethostbyaddr($addr); 20 + } else { 21 + $hostname = null; 22 + } 23 + 24 + $controller = $this->getRequest()->getController(); 25 + if ($controller) { 26 + $controller_class = get_class($controller); 27 + } else { 28 + $controller_class = null; 29 + } 30 + 31 + $site = $this->getRequest()->getSite(); 32 + if ($site) { 33 + $site_class = get_class($site); 34 + } else { 35 + $site_class = null; 36 + } 37 + 17 38 return array( 18 - 'Request' => $_REQUEST, 19 - 'Server' => $_SERVER, 39 + 'request' => $_REQUEST, 40 + 'server' => $_SERVER, 41 + 'special' => array( 42 + 'site' => $site_class, 43 + 'controller' => $controller_class, 44 + 'machine' => php_uname('n'), 45 + 'host' => $addr, 46 + 'hostname' => $hostname, 47 + ), 20 48 ); 21 49 } 22 50 23 51 public function renderPanel() { 24 52 $data = $this->getData(); 25 53 26 - $sections = array( 27 - 'Basics' => array( 28 - 'Machine' => php_uname('n'), 29 - ), 54 + $special_map = array( 55 + 'site' => pht('Site'), 56 + 'controller' => pht('Controller'), 57 + 'machine' => pht('Machine'), 58 + 'host' => pht('Host'), 59 + 'hostname' => pht('Hostname'), 30 60 ); 31 61 32 - // NOTE: This may not be present for some SAPIs, like php-fpm. 33 - if (!empty($data['Server']['SERVER_ADDR'])) { 34 - $addr = $data['Server']['SERVER_ADDR']; 35 - $sections['Basics']['Host'] = $addr; 36 - $sections['Basics']['Hostname'] = @gethostbyaddr($addr); 62 + $special = idx($data, 'special', array()); 63 + 64 + $rows = array(); 65 + foreach ($special_map as $key => $label) { 66 + $rows[] = array( 67 + $label, 68 + idx($special, $key), 69 + ); 37 70 } 38 71 39 - $sections = array_merge($sections, $data); 72 + $sections = array(); 73 + $sections[] = array( 74 + 'name' => pht('Basics'), 75 + 'rows' => $rows, 76 + ); 40 77 41 78 $mask = array( 42 79 'HTTP_COOKIE' => true, 43 80 'HTTP_X_PHABRICATOR_CSRF' => true, 44 81 ); 45 82 46 - $out = array(); 47 - foreach ($sections as $header => $map) { 83 + $maps = array( 84 + array( 85 + 'name' => pht('Request'), 86 + 'data' => idx($data, 'request', array()), 87 + ), 88 + array( 89 + 'name' => pht('Server'), 90 + 'data' => idx($data, 'server', array()), 91 + ), 92 + ); 93 + 94 + foreach ($maps as $map) { 95 + $data = $map['data']; 48 96 $rows = array(); 49 - foreach ($map as $key => $value) { 97 + foreach ($data as $key => $value) { 50 98 if (isset($mask[$key])) { 51 - $rows[] = array( 52 - $key, 53 - phutil_tag('em', array(), pht('(Masked)')), 54 - ); 99 + $value = phutil_tag('em', array(), pht('(Masked)')); 100 + } else if (is_array($value)) { 101 + $value = @json_encode($value); 55 102 } else { 56 - $rows[] = array( 57 - $key, 58 - (is_array($value) ? json_encode($value) : $value), 59 - ); 103 + $value = $value; 60 104 } 105 + 106 + $rows[] = array( 107 + $key, 108 + $value, 109 + ); 61 110 } 62 111 63 - $table = new AphrontTableView($rows); 64 - $table->setHeaders( 65 - array( 66 - $header, 67 - null, 68 - )); 69 - $table->setColumnClasses( 70 - array( 71 - 'header', 72 - 'wide wrap', 73 - )); 74 - $out[] = $table->render(); 112 + $sections[] = array( 113 + 'name' => $map['name'], 114 + 'rows' => $rows, 115 + ); 75 116 } 76 117 77 - return phutil_implode_html("\n", $out); 118 + $out = array(); 119 + foreach ($sections as $section) { 120 + $out[] = id(new AphrontTableView($section['rows'])) 121 + ->setHeaders( 122 + array( 123 + $section['name'], 124 + null, 125 + )) 126 + ->setColumnClasses( 127 + array( 128 + 'header', 129 + 'wide wrap', 130 + )); 131 + } 132 + return $out; 78 133 } 79 134 }
+25 -14
src/applications/files/application/PhabricatorFilesApplication.php
··· 78 78 'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController', 79 79 'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorFileEditController', 80 80 'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController', 81 - 'data/'. 82 - '(?:@(?P<instance>[^/]+)/)?'. 83 - '(?P<key>[^/]+)/'. 84 - '(?P<phid>[^/]+)/'. 85 - '(?:(?P<token>[^/]+)/)?'. 86 - '.*' 87 - => 'PhabricatorFileDataController', 88 81 'proxy/' => 'PhabricatorFileProxyController', 89 - 'xform/'. 90 - '(?:@(?P<instance>[^/]+)/)?'. 91 - '(?P<transform>[^/]+)/'. 92 - '(?P<phid>[^/]+)/'. 93 - '(?P<key>[^/]+)/' 94 - => 'PhabricatorFileTransformController', 95 82 'transforms/(?P<id>[1-9]\d*)/' => 96 83 'PhabricatorFileTransformListController', 97 84 'uploaddialog/' => 'PhabricatorFileUploadDialogController', 98 85 'download/(?P<phid>[^/]+)/' => 'PhabricatorFileDialogController', 99 - ), 86 + ) + $this->getResourceSubroutes(), 87 + ); 88 + } 89 + 90 + public function getResourceRoutes() { 91 + return array( 92 + '/file/' => $this->getResourceSubroutes(), 93 + ); 94 + } 95 + 96 + private function getResourceSubroutes() { 97 + return array( 98 + 'data/'. 99 + '(?:@(?P<instance>[^/]+)/)?'. 100 + '(?P<key>[^/]+)/'. 101 + '(?P<phid>[^/]+)/'. 102 + '(?:(?P<token>[^/]+)/)?'. 103 + '.*' 104 + => 'PhabricatorFileDataController', 105 + 'xform/'. 106 + '(?:@(?P<instance>[^/]+)/)?'. 107 + '(?P<transform>[^/]+)/'. 108 + '(?P<phid>[^/]+)/'. 109 + '(?P<key>[^/]+)/' 110 + => 'PhabricatorFileTransformController', 100 111 ); 101 112 } 102 113
+28 -3
src/applications/phame/application/PhabricatorPhameApplication.php
··· 39 39 return array( 40 40 '/phame/' => array( 41 41 '' => 'PhamePostListController', 42 - 'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' 43 - => 'PhameResourceController', 44 - 45 42 'live/(?P<id>[^/]+)/(?P<more>.*)' => 'PhameBlogLiveController', 46 43 'post/' => array( 47 44 '(?:(?P<filter>draft|all)/)?' => 'PhamePostListController', ··· 65 62 'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController', 66 63 'new/' => 'PhameBlogEditController', 67 64 ), 65 + ) + $this->getResourceSubroutes(), 66 + ); 67 + } 68 + 69 + public function getResourceRoutes() { 70 + return array( 71 + '/phame/' => $this->getResourceSubroutes(), 72 + ); 73 + } 74 + 75 + private function getResourceSubroutes() { 76 + return array( 77 + 'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' => 78 + 'PhameResourceController', 79 + ); 80 + } 81 + 82 + public function getBlogRoutes() { 83 + return array( 84 + '/(?P<more>.*)' => 'PhameBlogLiveController', 85 + ); 86 + } 87 + 88 + public function getBlogCDNRoutes() { 89 + return array( 90 + '/phame/' => array( 91 + 'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' => 92 + 'PhameResourceController', 68 93 ), 69 94 ); 70 95 }
+13 -7
src/applications/phame/controller/blog/PhameBlogLiveController.php
··· 8 8 9 9 public function handleRequest(AphrontRequest $request) { 10 10 $user = $request->getUser(); 11 - $id = $request->getURIData('id'); 12 11 13 - $blog = id(new PhameBlogQuery()) 14 - ->setViewer($user) 15 - ->withIDs(array($id)) 16 - ->executeOne(); 17 - if (!$blog) { 18 - return new Aphront404Response(); 12 + $site = $request->getSite(); 13 + if ($site instanceof PhameBlogSite) { 14 + $blog = $site->getBlog(); 15 + } else { 16 + $id = $request->getURIData('id'); 17 + 18 + $blog = id(new PhameBlogQuery()) 19 + ->setViewer($user) 20 + ->withIDs(array($id)) 21 + ->executeOne(); 22 + if (!$blog) { 23 + return new Aphront404Response(); 24 + } 19 25 } 20 26 21 27 if ($blog->getDomain() && ($request->getHost() != $blog->getDomain())) {
-30
src/applications/phame/site/PhameBlogResourceSite.php
··· 1 - <?php 2 - 3 - final class PhameBlogResourceSite extends PhameSite { 4 - 5 - public function getDescription() { 6 - return pht('Serves static resources for blogs.'); 7 - } 8 - 9 - public function getPriority() { 10 - return 3000; 11 - } 12 - 13 - public function newSiteForRequest(AphrontRequest $request) { 14 - if (!$this->isPhameActive()) { 15 - return null; 16 - } 17 - 18 - $whitelist = array( 19 - '/phame/r/', 20 - ); 21 - 22 - $path = $request->getPath(); 23 - if (!$this->isPathPrefixMatch($path, $whitelist)) { 24 - return null; 25 - } 26 - 27 - return new PhameBlogResourceSite(); 28 - } 29 - 30 - }
+8 -5
src/applications/phame/site/PhameBlogSite.php
··· 24 24 } 25 25 26 26 public function getPriority() { 27 - return 4000; 27 + return 3000; 28 28 } 29 29 30 30 public function newSiteForRequest(AphrontRequest $request) { ··· 53 53 return id(new PhameBlogSite())->setBlog($blog); 54 54 } 55 55 56 - public function getPathForRouting(AphrontRequest $request) { 57 - $path = $request->getPath(); 58 - $id = $this->getBlog()->getID(); 56 + public function getRoutingMaps() { 57 + $app = PhabricatorApplication::getByClass('PhabricatorPhameApplication'); 59 58 60 - return "/phame/live/{$id}/{$path}"; 59 + $maps = array(); 60 + $maps[] = $this->newRoutingMap() 61 + ->setApplication($app) 62 + ->setRoutes($app->getBlogRoutes()); 63 + return $maps; 61 64 } 62 65 63 66 }