1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +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:
epriestley 2015-08-31 04:01:01 -07:00
parent 2665970762
commit bcc5e55af2
18 changed files with 481 additions and 287 deletions

View file

@ -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";
}

View file

@ -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',

View file

@ -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 )--------------------------------------------- */

View file

@ -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);
}
}

View file

@ -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;
}

View 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());
}
}

View 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;
}
}

View file

@ -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() {

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -243,6 +243,10 @@ abstract class PhabricatorApplication
return array();
}
public function getResourceRoutes() {
return array();
}
/* -( Email Integration )-------------------------------------------------- */

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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',
);
}

View file

@ -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',
),
);
}

View file

@ -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())) {

View file

@ -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();
}
}

View file

@ -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;
}
}