mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
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
This commit is contained in:
parent
2665970762
commit
bcc5e55af2
18 changed files with 481 additions and 287 deletions
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
if ($argc !== 2 || $argv[1] === '--help') {
|
||||
echo pht('Usage: %s', 'aphrontpath.php <url>')."\n";
|
||||
echo pht(
|
||||
"Purpose: Print controller which will process passed %s.\n",
|
||||
'<url>');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$url = parse_url($argv[1]);
|
||||
$path = '/'.(isset($url['path']) ? ltrim($url['path'], '/') : '');
|
||||
|
||||
$config_key = 'aphront.default-application-configuration-class';
|
||||
$application = PhabricatorEnv::newObjectFromConfig($config_key);
|
||||
$application->setRequest(new AphrontRequest('', $path));
|
||||
|
||||
list($controller) = $application->buildControllerForPath($path);
|
||||
if (!$controller && substr($path, -1) !== '/') {
|
||||
list($controller) = $application->buildControllerForPath($path.'/');
|
||||
}
|
||||
if ($controller) {
|
||||
echo get_class($controller)."\n";
|
||||
}
|
|
@ -158,6 +158,8 @@ phutil_register_library_map(array(
|
|||
'AphrontRequest' => 'aphront/AphrontRequest.php',
|
||||
'AphrontRequestTestCase' => 'aphront/__tests__/AphrontRequestTestCase.php',
|
||||
'AphrontResponse' => 'aphront/response/AphrontResponse.php',
|
||||
'AphrontRoutingMap' => 'aphront/site/AphrontRoutingMap.php',
|
||||
'AphrontRoutingResult' => 'aphront/site/AphrontRoutingResult.php',
|
||||
'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php',
|
||||
'AphrontSite' => 'aphront/site/AphrontSite.php',
|
||||
'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php',
|
||||
|
@ -166,7 +168,6 @@ phutil_register_library_map(array(
|
|||
'AphrontTagView' => 'view/AphrontTagView.php',
|
||||
'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php',
|
||||
'AphrontTypeaheadTemplateView' => 'view/control/AphrontTypeaheadTemplateView.php',
|
||||
'AphrontURIMapper' => 'aphront/AphrontURIMapper.php',
|
||||
'AphrontUnhandledExceptionResponse' => 'aphront/response/AphrontUnhandledExceptionResponse.php',
|
||||
'AphrontUsageException' => 'aphront/exception/AphrontUsageException.php',
|
||||
'AphrontView' => 'view/AphrontView.php',
|
||||
|
@ -3126,7 +3127,6 @@ phutil_register_library_map(array(
|
|||
'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php',
|
||||
'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php',
|
||||
'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php',
|
||||
'PhameBlogResourceSite' => 'applications/phame/site/PhameBlogResourceSite.php',
|
||||
'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php',
|
||||
'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php',
|
||||
'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php',
|
||||
|
@ -3777,6 +3777,8 @@ phutil_register_library_map(array(
|
|||
'AphrontRequest' => 'Phobject',
|
||||
'AphrontRequestTestCase' => 'PhabricatorTestCase',
|
||||
'AphrontResponse' => 'Phobject',
|
||||
'AphrontRoutingMap' => 'Phobject',
|
||||
'AphrontRoutingResult' => 'Phobject',
|
||||
'AphrontSideNavFilterView' => 'AphrontView',
|
||||
'AphrontSite' => 'Phobject',
|
||||
'AphrontStackTraceView' => 'AphrontView',
|
||||
|
@ -3785,7 +3787,6 @@ phutil_register_library_map(array(
|
|||
'AphrontTagView' => 'AphrontView',
|
||||
'AphrontTokenizerTemplateView' => 'AphrontView',
|
||||
'AphrontTypeaheadTemplateView' => 'AphrontView',
|
||||
'AphrontURIMapper' => 'Phobject',
|
||||
'AphrontUnhandledExceptionResponse' => 'AphrontStandaloneHTMLResponse',
|
||||
'AphrontUsageException' => 'AphrontException',
|
||||
'AphrontView' => array(
|
||||
|
@ -7238,7 +7239,6 @@ phutil_register_library_map(array(
|
|||
'PhameBlogListController' => 'PhameController',
|
||||
'PhameBlogLiveController' => 'PhameController',
|
||||
'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhameBlogResourceSite' => 'PhameSite',
|
||||
'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhameBlogSite' => 'PhameSite',
|
||||
'PhameBlogSkin' => 'PhabricatorController',
|
||||
|
|
|
@ -26,6 +26,8 @@ final class AphrontRequest extends Phobject {
|
|||
private $requestData;
|
||||
private $user;
|
||||
private $applicationConfiguration;
|
||||
private $site;
|
||||
private $controller;
|
||||
private $uriData;
|
||||
private $cookiePrefix;
|
||||
|
||||
|
@ -77,6 +79,24 @@ final class AphrontRequest extends Phobject {
|
|||
return $uri->getDomain();
|
||||
}
|
||||
|
||||
public function setSite(AphrontSite $site) {
|
||||
$this->site = $site;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSite() {
|
||||
return $this->site;
|
||||
}
|
||||
|
||||
public function setController(AphrontController $controller) {
|
||||
$this->controller = $controller;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getController() {
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
|
||||
/* -( Accessing Request Data )--------------------------------------------- */
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class AphrontURIMapper extends Phobject {
|
||||
|
||||
private $map;
|
||||
|
||||
public function __construct(array $map) {
|
||||
$this->map = $map;
|
||||
}
|
||||
|
||||
public function mapPath($path) {
|
||||
$map = $this->map;
|
||||
foreach ($map as $rule => $value) {
|
||||
list($controller, $data) = $this->tryRule($rule, $value, $path);
|
||||
if ($controller) {
|
||||
foreach ($data as $k => $v) {
|
||||
if (is_numeric($k)) {
|
||||
unset($data[$k]);
|
||||
}
|
||||
}
|
||||
return array($controller, $data);
|
||||
}
|
||||
}
|
||||
|
||||
return array(null, null);
|
||||
}
|
||||
|
||||
private function tryRule($rule, $value, $path) {
|
||||
$match = null;
|
||||
$pattern = '#^'.$rule.(is_array($value) ? '' : '$').'#';
|
||||
if (!preg_match($pattern, $path, $match)) {
|
||||
return array(null, null);
|
||||
}
|
||||
|
||||
if (!is_array($value)) {
|
||||
return array($value, $match);
|
||||
}
|
||||
|
||||
$path = substr($path, strlen($match[0]));
|
||||
foreach ($value as $srule => $sval) {
|
||||
list($controller, $data) = $this->tryRule($srule, $sval, $path);
|
||||
if ($controller) {
|
||||
return array($controller, $data + $match);
|
||||
}
|
||||
}
|
||||
|
||||
return array(null, null);
|
||||
}
|
||||
|
||||
}
|
|
@ -210,7 +210,9 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
));
|
||||
$multimeter->setEventContext('web.'.$controller_class);
|
||||
|
||||
$request->setController($controller);
|
||||
$request->setURIMap($uri_data);
|
||||
|
||||
$controller->setRequest($request);
|
||||
|
||||
// If execution throws an exception and then trying to render that
|
||||
|
@ -283,35 +285,13 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
|
||||
|
||||
/**
|
||||
* Using builtin and application routes, build the appropriate
|
||||
* @{class:AphrontController} class for the request. To route a request, we
|
||||
* first test if the HTTP_HOST is configured as a valid Phabricator URI. If
|
||||
* it isn't, we do a special check to see if it's a custom domain for a blog
|
||||
* in the Phame application and if that fails we error. Otherwise, we test
|
||||
* against all application routes from installed
|
||||
* @{class:PhabricatorApplication}s.
|
||||
*
|
||||
* If we match a route, we construct the controller it points at, build it,
|
||||
* and return it.
|
||||
*
|
||||
* If we fail to match a route, but the current path is missing a trailing
|
||||
* "/", we try routing the same path with a trailing "/" and do a redirect
|
||||
* if that has a valid route. The idea is to canoncalize URIs for consistency,
|
||||
* but avoid breaking noncanonical URIs that we can easily salvage.
|
||||
*
|
||||
* NOTE: We only redirect on GET. On POST, we'd drop parameters and most
|
||||
* likely mutate the request implicitly, and a bad POST usually indicates a
|
||||
* programming error rather than a sloppy typist.
|
||||
*
|
||||
* If the failing path already has a trailing "/", or we can't route the
|
||||
* version with a "/", we call @{method:build404Controller}, which build a
|
||||
* fallback @{class:AphrontController}.
|
||||
* Build a controller to respond to the request.
|
||||
*
|
||||
* @return pair<AphrontController,dict> Controller and dictionary of request
|
||||
* parameters.
|
||||
* @task routing
|
||||
*/
|
||||
final public function buildController() {
|
||||
final private function buildController() {
|
||||
$request = $this->getRequest();
|
||||
|
||||
// If we're configured to operate in cluster mode, reject requests which
|
||||
|
@ -373,78 +353,48 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Really, the Site should get more control here and be able to
|
||||
// do its own routing logic if it wants, but we don't need that for now.
|
||||
$path = $site->getPathForRouting($request);
|
||||
$maps = $site->getRoutingMaps();
|
||||
$path = $request->getPath();
|
||||
|
||||
list($controller, $uri_data) = $this->buildControllerForPath($path);
|
||||
if (!$controller) {
|
||||
if (!preg_match('@/$@', $path)) {
|
||||
// If we failed to match anything but don't have a trailing slash, try
|
||||
// to add a trailing slash and issue a redirect if that resolves.
|
||||
list($controller, $uri_data) = $this->buildControllerForPath($path.'/');
|
||||
|
||||
// NOTE: For POST, just 404 instead of redirecting, since the redirect
|
||||
// will be a GET without parameters.
|
||||
|
||||
if ($controller && !$request->isHTTPPost()) {
|
||||
$slash_uri = $request->getRequestURI()->setPath($path.'/');
|
||||
|
||||
$external = strlen($request->getRequestURI()->getDomain());
|
||||
return $this->buildRedirectController($slash_uri, $external);
|
||||
}
|
||||
}
|
||||
return $this->build404Controller();
|
||||
$result = $this->routePath($maps, $path);
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return array($controller, $uri_data);
|
||||
}
|
||||
// If we failed to match anything but don't have a trailing slash, try
|
||||
// to add a trailing slash and issue a redirect if that resolves.
|
||||
|
||||
// NOTE: We only do this for GET, since redirects switch to GET and drop
|
||||
// data like POST parameters.
|
||||
if (!preg_match('@/$@', $path) && $request->isHTTPGet()) {
|
||||
$result = $this->routePath($maps, $path.'/');
|
||||
if ($result) {
|
||||
$slash_uri = $request->getRequestURI()->setPath($path.'/');
|
||||
$external = strlen($request->getRequestURI()->getDomain());
|
||||
return $this->buildRedirectController($slash_uri, $external);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->build404Controller();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a specific path to the corresponding controller. For a description
|
||||
* of routing, see @{method:buildController}.
|
||||
*
|
||||
* @param list<AphrontRoutingMap> List of routing maps.
|
||||
* @param string Path to route.
|
||||
* @return pair<AphrontController,dict> Controller and dictionary of request
|
||||
* parameters.
|
||||
* @task routing
|
||||
*/
|
||||
final public function buildControllerForPath($path) {
|
||||
$maps = array();
|
||||
|
||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||
foreach ($applications as $application) {
|
||||
$maps[] = array($application, $application->getRoutes());
|
||||
}
|
||||
|
||||
$current_application = null;
|
||||
$controller_class = null;
|
||||
foreach ($maps as $map_info) {
|
||||
list($application, $map) = $map_info;
|
||||
|
||||
$mapper = new AphrontURIMapper($map);
|
||||
list($controller_class, $uri_data) = $mapper->mapPath($path);
|
||||
|
||||
if ($controller_class) {
|
||||
if ($application) {
|
||||
$current_application = $application;
|
||||
}
|
||||
break;
|
||||
private function routePath(array $maps, $path) {
|
||||
foreach ($maps as $map) {
|
||||
$result = $map->routePath($path);
|
||||
if ($result) {
|
||||
return array($result->getController(), $result->getURIData());
|
||||
}
|
||||
}
|
||||
|
||||
if (!$controller_class) {
|
||||
return array(null, null);
|
||||
}
|
||||
|
||||
$request = $this->getRequest();
|
||||
|
||||
$controller = newv($controller_class, array());
|
||||
if ($current_application) {
|
||||
$controller->setCurrentApplication($current_application);
|
||||
}
|
||||
|
||||
return array($controller, $uri_data);
|
||||
}
|
||||
|
||||
private function buildSiteForRequest(AphrontRequest $request) {
|
||||
|
@ -469,6 +419,8 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
$host));
|
||||
}
|
||||
|
||||
$request->setSite($site);
|
||||
|
||||
return $site;
|
||||
}
|
||||
|
||||
|
|
159
src/aphront/site/AphrontRoutingMap.php
Normal file
159
src/aphront/site/AphrontRoutingMap.php
Normal file
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Collection of routes on a site for an application.
|
||||
*
|
||||
* @task info Map Information
|
||||
* @task routing Routing
|
||||
*/
|
||||
final class AphrontRoutingMap extends Phobject {
|
||||
|
||||
private $site;
|
||||
private $application;
|
||||
private $routes = array();
|
||||
|
||||
|
||||
/* -( Map Info )----------------------------------------------------------- */
|
||||
|
||||
|
||||
public function setSite(AphrontSite $site) {
|
||||
$this->site = $site;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSite() {
|
||||
return $this->site;
|
||||
}
|
||||
|
||||
public function setApplication(PhabricatorApplication $application) {
|
||||
$this->application = $application;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplication() {
|
||||
return $this->application;
|
||||
}
|
||||
|
||||
public function setRoutes(array $routes) {
|
||||
$this->routes = $routes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
|
||||
/* -( Routing )------------------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* Find the route matching a path, if one exists.
|
||||
*
|
||||
* @param string Path to route.
|
||||
* @return AphrontRoutingResult|null Routing result, if path matches map.
|
||||
* @task routing
|
||||
*/
|
||||
public function routePath($path) {
|
||||
$map = $this->getRoutes();
|
||||
|
||||
foreach ($map as $route => $value) {
|
||||
$match = $this->tryRoute($route, $value, $path);
|
||||
if (!$match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->newRoutingResult();
|
||||
$application = $result->getApplication();
|
||||
|
||||
$controller_class = $match['class'];
|
||||
$controller = newv($controller_class, array());
|
||||
$controller->setCurrentApplication($application);
|
||||
|
||||
$result
|
||||
->setController($controller)
|
||||
->setURIData($match['data']);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test a sub-map to see if any routes match a path.
|
||||
*
|
||||
* @param string Path to route.
|
||||
* @param string Pattern from the map.
|
||||
* @param string Value from the map.
|
||||
* @return dict<string, wild>|null Match details, if path matches sub-map.
|
||||
* @task routing
|
||||
*/
|
||||
private function tryRoute($route, $value, $path) {
|
||||
$has_submap = is_array($value);
|
||||
|
||||
if (!$has_submap) {
|
||||
// If the value is a controller rather than a sub-map, any matching
|
||||
// route must completely consume the path.
|
||||
$pattern = '(^'.$route.'\z)';
|
||||
} else {
|
||||
$pattern = '(^'.$route.')';
|
||||
}
|
||||
|
||||
$data = null;
|
||||
$ok = preg_match($pattern, $path, $data);
|
||||
if ($ok === false) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Routing fragment "%s" is not a valid regular expression.',
|
||||
$route));
|
||||
}
|
||||
|
||||
if (!$ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path_match = $data[0];
|
||||
|
||||
// Clean up the data. We only want to retain named capturing groups, not
|
||||
// the duplicated numeric captures.
|
||||
foreach ($data as $k => $v) {
|
||||
if (is_numeric($k)) {
|
||||
unset($data[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$has_submap) {
|
||||
return array(
|
||||
'class' => $value,
|
||||
'data' => $data,
|
||||
);
|
||||
}
|
||||
|
||||
$sub_path = substr($path, strlen($path_match));
|
||||
foreach ($value as $sub_route => $sub_value) {
|
||||
$result = $this->tryRoute($sub_route, $sub_value, $sub_path);
|
||||
if ($result) {
|
||||
$result['data'] += $data;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a new routing result for this map.
|
||||
*
|
||||
* @return AphrontRoutingResult New, empty routing result.
|
||||
* @task routing
|
||||
*/
|
||||
private function newRoutingResult() {
|
||||
return id(new AphrontRoutingResult())
|
||||
->setSite($this->getSite())
|
||||
->setApplication($this->getApplication());
|
||||
}
|
||||
|
||||
}
|
55
src/aphront/site/AphrontRoutingResult.php
Normal file
55
src/aphront/site/AphrontRoutingResult.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Details about a routing map match for a path.
|
||||
*
|
||||
* @param info Result Information
|
||||
*/
|
||||
final class AphrontRoutingResult extends Phobject {
|
||||
|
||||
private $site;
|
||||
private $application;
|
||||
private $controller;
|
||||
private $uriData;
|
||||
|
||||
|
||||
/* -( Result Information )------------------------------------------------- */
|
||||
|
||||
|
||||
public function setSite(AphrontSite $site) {
|
||||
$this->site = $site;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSite() {
|
||||
return $this->site;
|
||||
}
|
||||
|
||||
public function setApplication(PhabricatorApplication $application) {
|
||||
$this->application = $application;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplication() {
|
||||
return $this->application;
|
||||
}
|
||||
|
||||
public function setController(AphrontController $controller) {
|
||||
$this->controller = $controller;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getController() {
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
public function setURIData(array $uri_data) {
|
||||
$this->uriData = $uri_data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getURIData() {
|
||||
return $this->uriData;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,14 +7,7 @@ abstract class AphrontSite extends Phobject {
|
|||
|
||||
abstract public function shouldRequireHTTPS();
|
||||
abstract public function newSiteForRequest(AphrontRequest $request);
|
||||
|
||||
/**
|
||||
* NOTE: This is temporary glue; eventually, sites will return an entire
|
||||
* route map.
|
||||
*/
|
||||
public function getPathForRouting(AphrontRequest $request) {
|
||||
return $request->getPath();
|
||||
}
|
||||
abstract public function getRoutingMaps();
|
||||
|
||||
protected function isHostMatch($host, array $uris) {
|
||||
foreach ($uris as $uri) {
|
||||
|
@ -32,14 +25,9 @@ abstract class AphrontSite extends Phobject {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected function isPathPrefixMatch($path, array $paths) {
|
||||
foreach ($paths as $candidate) {
|
||||
if (strncmp($path, $candidate, strlen($candidate)) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
protected function newRoutingMap() {
|
||||
return id(new AphrontRoutingMap())
|
||||
->setSite($this);
|
||||
}
|
||||
|
||||
final public static function getAllSites() {
|
||||
|
|
|
@ -37,4 +37,17 @@ final class PhabricatorPlatformSite extends PhabricatorSite {
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getRoutingMaps() {
|
||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||
|
||||
$maps = array();
|
||||
foreach ($applications as $application) {
|
||||
$maps[] = $this->newRoutingMap()
|
||||
->setApplication($application)
|
||||
->setRoutes($application->getRoutes());
|
||||
}
|
||||
|
||||
return $maps;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,20 +22,20 @@ final class PhabricatorResourceSite extends PhabricatorSite {
|
|||
return new PhabricatorResourceSite();
|
||||
}
|
||||
|
||||
// These are CDN routes, so we let them through even if the "Host" header
|
||||
// doesn't match anything we recognize. The
|
||||
$whitelist = array(
|
||||
'/res/',
|
||||
'/file/data/',
|
||||
'/file/xform/',
|
||||
);
|
||||
|
||||
$path = $request->getPath();
|
||||
if ($this->isPathPrefixMatch($path, $whitelist)) {
|
||||
return new PhabricatorResourceSite();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getRoutingMaps() {
|
||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||
|
||||
$maps = array();
|
||||
foreach ($applications as $application) {
|
||||
$maps[] = $this->newRoutingMap()
|
||||
->setApplication($application)
|
||||
->setRoutes($application->getResourceRoutes());
|
||||
}
|
||||
|
||||
return $maps;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -243,6 +243,10 @@ abstract class PhabricatorApplication
|
|||
return array();
|
||||
}
|
||||
|
||||
public function getResourceRoutes() {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/* -( Email Integration )-------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -15,6 +15,17 @@ final class PhabricatorCelerityApplication extends PhabricatorApplication {
|
|||
}
|
||||
|
||||
public function getRoutes() {
|
||||
// We serve resources from both the platform site and the resource site.
|
||||
// This is safe because the user doesn't have any direct control over
|
||||
// resources.
|
||||
|
||||
// The advantage of serving resources from the resource site (if possible)
|
||||
// is that we can use a CDN there if one is configured, but there is no
|
||||
// particular security concern.
|
||||
return $this->getResourceRoutes();
|
||||
}
|
||||
|
||||
public function getResourceRoutes() {
|
||||
$extensions = CelerityResourceController::getSupportedResourceTypes();
|
||||
$extensions = array_keys($extensions);
|
||||
$extensions = implode('|', $extensions);
|
||||
|
|
|
@ -14,66 +14,121 @@ final class DarkConsoleRequestPlugin extends DarkConsolePlugin {
|
|||
}
|
||||
|
||||
public function generateData() {
|
||||
$addr = idx($_SERVER, 'SERVER_ADDR');
|
||||
if ($addr) {
|
||||
$hostname = @gethostbyaddr($addr);
|
||||
} else {
|
||||
$hostname = null;
|
||||
}
|
||||
|
||||
$controller = $this->getRequest()->getController();
|
||||
if ($controller) {
|
||||
$controller_class = get_class($controller);
|
||||
} else {
|
||||
$controller_class = null;
|
||||
}
|
||||
|
||||
$site = $this->getRequest()->getSite();
|
||||
if ($site) {
|
||||
$site_class = get_class($site);
|
||||
} else {
|
||||
$site_class = null;
|
||||
}
|
||||
|
||||
return array(
|
||||
'Request' => $_REQUEST,
|
||||
'Server' => $_SERVER,
|
||||
'request' => $_REQUEST,
|
||||
'server' => $_SERVER,
|
||||
'special' => array(
|
||||
'site' => $site_class,
|
||||
'controller' => $controller_class,
|
||||
'machine' => php_uname('n'),
|
||||
'host' => $addr,
|
||||
'hostname' => $hostname,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderPanel() {
|
||||
$data = $this->getData();
|
||||
|
||||
$sections = array(
|
||||
'Basics' => array(
|
||||
'Machine' => php_uname('n'),
|
||||
),
|
||||
$special_map = array(
|
||||
'site' => pht('Site'),
|
||||
'controller' => pht('Controller'),
|
||||
'machine' => pht('Machine'),
|
||||
'host' => pht('Host'),
|
||||
'hostname' => pht('Hostname'),
|
||||
);
|
||||
|
||||
// NOTE: This may not be present for some SAPIs, like php-fpm.
|
||||
if (!empty($data['Server']['SERVER_ADDR'])) {
|
||||
$addr = $data['Server']['SERVER_ADDR'];
|
||||
$sections['Basics']['Host'] = $addr;
|
||||
$sections['Basics']['Hostname'] = @gethostbyaddr($addr);
|
||||
$special = idx($data, 'special', array());
|
||||
|
||||
$rows = array();
|
||||
foreach ($special_map as $key => $label) {
|
||||
$rows[] = array(
|
||||
$label,
|
||||
idx($special, $key),
|
||||
);
|
||||
}
|
||||
|
||||
$sections = array_merge($sections, $data);
|
||||
$sections = array();
|
||||
$sections[] = array(
|
||||
'name' => pht('Basics'),
|
||||
'rows' => $rows,
|
||||
);
|
||||
|
||||
$mask = array(
|
||||
'HTTP_COOKIE' => true,
|
||||
'HTTP_X_PHABRICATOR_CSRF' => true,
|
||||
);
|
||||
|
||||
$out = array();
|
||||
foreach ($sections as $header => $map) {
|
||||
$maps = array(
|
||||
array(
|
||||
'name' => pht('Request'),
|
||||
'data' => idx($data, 'request', array()),
|
||||
),
|
||||
array(
|
||||
'name' => pht('Server'),
|
||||
'data' => idx($data, 'server', array()),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($maps as $map) {
|
||||
$data = $map['data'];
|
||||
$rows = array();
|
||||
foreach ($map as $key => $value) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (isset($mask[$key])) {
|
||||
$rows[] = array(
|
||||
$key,
|
||||
phutil_tag('em', array(), pht('(Masked)')),
|
||||
);
|
||||
$value = phutil_tag('em', array(), pht('(Masked)'));
|
||||
} else if (is_array($value)) {
|
||||
$value = @json_encode($value);
|
||||
} else {
|
||||
$rows[] = array(
|
||||
$key,
|
||||
(is_array($value) ? json_encode($value) : $value),
|
||||
);
|
||||
$value = $value;
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
$key,
|
||||
$value,
|
||||
);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setHeaders(
|
||||
array(
|
||||
$header,
|
||||
null,
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'header',
|
||||
'wide wrap',
|
||||
));
|
||||
$out[] = $table->render();
|
||||
$sections[] = array(
|
||||
'name' => $map['name'],
|
||||
'rows' => $rows,
|
||||
);
|
||||
}
|
||||
|
||||
return phutil_implode_html("\n", $out);
|
||||
$out = array();
|
||||
foreach ($sections as $section) {
|
||||
$out[] = id(new AphrontTableView($section['rows']))
|
||||
->setHeaders(
|
||||
array(
|
||||
$section['name'],
|
||||
null,
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'header',
|
||||
'wide wrap',
|
||||
));
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,25 +78,36 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
|||
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController',
|
||||
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorFileEditController',
|
||||
'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController',
|
||||
'data/'.
|
||||
'(?:@(?P<instance>[^/]+)/)?'.
|
||||
'(?P<key>[^/]+)/'.
|
||||
'(?P<phid>[^/]+)/'.
|
||||
'(?:(?P<token>[^/]+)/)?'.
|
||||
'.*'
|
||||
=> 'PhabricatorFileDataController',
|
||||
'proxy/' => 'PhabricatorFileProxyController',
|
||||
'xform/'.
|
||||
'(?:@(?P<instance>[^/]+)/)?'.
|
||||
'(?P<transform>[^/]+)/'.
|
||||
'(?P<phid>[^/]+)/'.
|
||||
'(?P<key>[^/]+)/'
|
||||
=> 'PhabricatorFileTransformController',
|
||||
'transforms/(?P<id>[1-9]\d*)/' =>
|
||||
'PhabricatorFileTransformListController',
|
||||
'uploaddialog/' => 'PhabricatorFileUploadDialogController',
|
||||
'download/(?P<phid>[^/]+)/' => 'PhabricatorFileDialogController',
|
||||
),
|
||||
) + $this->getResourceSubroutes(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getResourceRoutes() {
|
||||
return array(
|
||||
'/file/' => $this->getResourceSubroutes(),
|
||||
);
|
||||
}
|
||||
|
||||
private function getResourceSubroutes() {
|
||||
return array(
|
||||
'data/'.
|
||||
'(?:@(?P<instance>[^/]+)/)?'.
|
||||
'(?P<key>[^/]+)/'.
|
||||
'(?P<phid>[^/]+)/'.
|
||||
'(?:(?P<token>[^/]+)/)?'.
|
||||
'.*'
|
||||
=> 'PhabricatorFileDataController',
|
||||
'xform/'.
|
||||
'(?:@(?P<instance>[^/]+)/)?'.
|
||||
'(?P<transform>[^/]+)/'.
|
||||
'(?P<phid>[^/]+)/'.
|
||||
'(?P<key>[^/]+)/'
|
||||
=> 'PhabricatorFileTransformController',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,9 +39,6 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
|||
return array(
|
||||
'/phame/' => array(
|
||||
'' => 'PhamePostListController',
|
||||
'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)'
|
||||
=> 'PhameResourceController',
|
||||
|
||||
'live/(?P<id>[^/]+)/(?P<more>.*)' => 'PhameBlogLiveController',
|
||||
'post/' => array(
|
||||
'(?:(?P<filter>draft|all)/)?' => 'PhamePostListController',
|
||||
|
@ -65,6 +62,34 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
|||
'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController',
|
||||
'new/' => 'PhameBlogEditController',
|
||||
),
|
||||
) + $this->getResourceSubroutes(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getResourceRoutes() {
|
||||
return array(
|
||||
'/phame/' => $this->getResourceSubroutes(),
|
||||
);
|
||||
}
|
||||
|
||||
private function getResourceSubroutes() {
|
||||
return array(
|
||||
'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' =>
|
||||
'PhameResourceController',
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlogRoutes() {
|
||||
return array(
|
||||
'/(?P<more>.*)' => 'PhameBlogLiveController',
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlogCDNRoutes() {
|
||||
return array(
|
||||
'/phame/' => array(
|
||||
'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' =>
|
||||
'PhameResourceController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,14 +8,20 @@ final class PhameBlogLiveController extends PhameController {
|
|||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$user = $request->getUser();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
$blog = id(new PhameBlogQuery())
|
||||
->setViewer($user)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$blog) {
|
||||
return new Aphront404Response();
|
||||
$site = $request->getSite();
|
||||
if ($site instanceof PhameBlogSite) {
|
||||
$blog = $site->getBlog();
|
||||
} else {
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
$blog = id(new PhameBlogQuery())
|
||||
->setViewer($user)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$blog) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
}
|
||||
|
||||
if ($blog->getDomain() && ($request->getHost() != $blog->getDomain())) {
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhameBlogResourceSite extends PhameSite {
|
||||
|
||||
public function getDescription() {
|
||||
return pht('Serves static resources for blogs.');
|
||||
}
|
||||
|
||||
public function getPriority() {
|
||||
return 3000;
|
||||
}
|
||||
|
||||
public function newSiteForRequest(AphrontRequest $request) {
|
||||
if (!$this->isPhameActive()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$whitelist = array(
|
||||
'/phame/r/',
|
||||
);
|
||||
|
||||
$path = $request->getPath();
|
||||
if (!$this->isPathPrefixMatch($path, $whitelist)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PhameBlogResourceSite();
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ final class PhameBlogSite extends PhameSite {
|
|||
}
|
||||
|
||||
public function getPriority() {
|
||||
return 4000;
|
||||
return 3000;
|
||||
}
|
||||
|
||||
public function newSiteForRequest(AphrontRequest $request) {
|
||||
|
@ -53,11 +53,14 @@ final class PhameBlogSite extends PhameSite {
|
|||
return id(new PhameBlogSite())->setBlog($blog);
|
||||
}
|
||||
|
||||
public function getPathForRouting(AphrontRequest $request) {
|
||||
$path = $request->getPath();
|
||||
$id = $this->getBlog()->getID();
|
||||
public function getRoutingMaps() {
|
||||
$app = PhabricatorApplication::getByClass('PhabricatorPhameApplication');
|
||||
|
||||
return "/phame/live/{$id}/{$path}";
|
||||
$maps = array();
|
||||
$maps[] = $this->newRoutingMap()
|
||||
->setApplication($app)
|
||||
->setRoutes($app->getBlogRoutes());
|
||||
return $maps;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue