diff --git a/scripts/aphront/aphrontpath.php b/scripts/aphront/aphrontpath.php deleted file mode 100755 index 09cc04eeea..0000000000 --- a/scripts/aphront/aphrontpath.php +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env php -')."\n"; - echo pht( - "Purpose: Print controller which will process passed %s.\n", - ''); - 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"; -} diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 697a5ad11b..a80ca41d9a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -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', diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index d29cd926d1..026129404e 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -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 )--------------------------------------------- */ diff --git a/src/aphront/AphrontURIMapper.php b/src/aphront/AphrontURIMapper.php deleted file mode 100644 index 13108729b7..0000000000 --- a/src/aphront/AphrontURIMapper.php +++ /dev/null @@ -1,50 +0,0 @@ -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); - } - -} diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index 7943b8046d..e37268380c 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -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 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 List of routing maps. + * @param string Path to route. * @return pair 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; } diff --git a/src/aphront/site/AphrontRoutingMap.php b/src/aphront/site/AphrontRoutingMap.php new file mode 100644 index 0000000000..bda98429f9 --- /dev/null +++ b/src/aphront/site/AphrontRoutingMap.php @@ -0,0 +1,159 @@ +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|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()); + } + +} diff --git a/src/aphront/site/AphrontRoutingResult.php b/src/aphront/site/AphrontRoutingResult.php new file mode 100644 index 0000000000..a6a5da767e --- /dev/null +++ b/src/aphront/site/AphrontRoutingResult.php @@ -0,0 +1,55 @@ +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; + } + +} diff --git a/src/aphront/site/AphrontSite.php b/src/aphront/site/AphrontSite.php index 8c2373565b..b4d4fed0d1 100644 --- a/src/aphront/site/AphrontSite.php +++ b/src/aphront/site/AphrontSite.php @@ -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() { diff --git a/src/aphront/site/PhabricatorPlatformSite.php b/src/aphront/site/PhabricatorPlatformSite.php index 63c2b9cde3..0973758bcd 100644 --- a/src/aphront/site/PhabricatorPlatformSite.php +++ b/src/aphront/site/PhabricatorPlatformSite.php @@ -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; + } + } diff --git a/src/aphront/site/PhabricatorResourceSite.php b/src/aphront/site/PhabricatorResourceSite.php index 006fcf99d7..88f7777607 100644 --- a/src/aphront/site/PhabricatorResourceSite.php +++ b/src/aphront/site/PhabricatorResourceSite.php @@ -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; + } + } diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 1292282d25..f499bea5c9 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -243,6 +243,10 @@ abstract class PhabricatorApplication return array(); } + public function getResourceRoutes() { + return array(); + } + /* -( Email Integration )-------------------------------------------------- */ diff --git a/src/applications/celerity/application/PhabricatorCelerityApplication.php b/src/applications/celerity/application/PhabricatorCelerityApplication.php index e6c2015c9d..77ae9448ef 100644 --- a/src/applications/celerity/application/PhabricatorCelerityApplication.php +++ b/src/applications/celerity/application/PhabricatorCelerityApplication.php @@ -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); diff --git a/src/applications/console/plugin/DarkConsoleRequestPlugin.php b/src/applications/console/plugin/DarkConsoleRequestPlugin.php index 8333903131..adba9e73e2 100644 --- a/src/applications/console/plugin/DarkConsoleRequestPlugin.php +++ b/src/applications/console/plugin/DarkConsoleRequestPlugin.php @@ -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; } } diff --git a/src/applications/files/application/PhabricatorFilesApplication.php b/src/applications/files/application/PhabricatorFilesApplication.php index 6fed230fd2..7ad7b8f52c 100644 --- a/src/applications/files/application/PhabricatorFilesApplication.php +++ b/src/applications/files/application/PhabricatorFilesApplication.php @@ -78,25 +78,36 @@ final class PhabricatorFilesApplication extends PhabricatorApplication { 'delete/(?P[1-9]\d*)/' => 'PhabricatorFileDeleteController', 'edit/(?P[1-9]\d*)/' => 'PhabricatorFileEditController', 'info/(?P[^/]+)/' => 'PhabricatorFileInfoController', - 'data/'. - '(?:@(?P[^/]+)/)?'. - '(?P[^/]+)/'. - '(?P[^/]+)/'. - '(?:(?P[^/]+)/)?'. - '.*' - => 'PhabricatorFileDataController', 'proxy/' => 'PhabricatorFileProxyController', - 'xform/'. - '(?:@(?P[^/]+)/)?'. - '(?P[^/]+)/'. - '(?P[^/]+)/'. - '(?P[^/]+)/' - => 'PhabricatorFileTransformController', 'transforms/(?P[1-9]\d*)/' => 'PhabricatorFileTransformListController', 'uploaddialog/' => 'PhabricatorFileUploadDialogController', 'download/(?P[^/]+)/' => 'PhabricatorFileDialogController', - ), + ) + $this->getResourceSubroutes(), + ); + } + + public function getResourceRoutes() { + return array( + '/file/' => $this->getResourceSubroutes(), + ); + } + + private function getResourceSubroutes() { + return array( + 'data/'. + '(?:@(?P[^/]+)/)?'. + '(?P[^/]+)/'. + '(?P[^/]+)/'. + '(?:(?P[^/]+)/)?'. + '.*' + => 'PhabricatorFileDataController', + 'xform/'. + '(?:@(?P[^/]+)/)?'. + '(?P[^/]+)/'. + '(?P[^/]+)/'. + '(?P[^/]+)/' + => 'PhabricatorFileTransformController', ); } diff --git a/src/applications/phame/application/PhabricatorPhameApplication.php b/src/applications/phame/application/PhabricatorPhameApplication.php index 75c68852d0..f7643c6097 100644 --- a/src/applications/phame/application/PhabricatorPhameApplication.php +++ b/src/applications/phame/application/PhabricatorPhameApplication.php @@ -39,9 +39,6 @@ final class PhabricatorPhameApplication extends PhabricatorApplication { return array( '/phame/' => array( '' => 'PhamePostListController', - 'r/(?P\d+)/(?P[^/]+)/(?P.*)' - => 'PhameResourceController', - 'live/(?P[^/]+)/(?P.*)' => 'PhameBlogLiveController', 'post/' => array( '(?:(?Pdraft|all)/)?' => 'PhamePostListController', @@ -65,6 +62,34 @@ final class PhabricatorPhameApplication extends PhabricatorApplication { 'feed/(?P[^/]+)/' => 'PhameBlogFeedController', 'new/' => 'PhameBlogEditController', ), + ) + $this->getResourceSubroutes(), + ); + } + + public function getResourceRoutes() { + return array( + '/phame/' => $this->getResourceSubroutes(), + ); + } + + private function getResourceSubroutes() { + return array( + 'r/(?P\d+)/(?P[^/]+)/(?P.*)' => + 'PhameResourceController', + ); + } + + public function getBlogRoutes() { + return array( + '/(?P.*)' => 'PhameBlogLiveController', + ); + } + + public function getBlogCDNRoutes() { + return array( + '/phame/' => array( + 'r/(?P\d+)/(?P[^/]+)/(?P.*)' => + 'PhameResourceController', ), ); } diff --git a/src/applications/phame/controller/blog/PhameBlogLiveController.php b/src/applications/phame/controller/blog/PhameBlogLiveController.php index 1ac4486183..a79b45b799 100644 --- a/src/applications/phame/controller/blog/PhameBlogLiveController.php +++ b/src/applications/phame/controller/blog/PhameBlogLiveController.php @@ -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())) { diff --git a/src/applications/phame/site/PhameBlogResourceSite.php b/src/applications/phame/site/PhameBlogResourceSite.php deleted file mode 100644 index 8bf6094a77..0000000000 --- a/src/applications/phame/site/PhameBlogResourceSite.php +++ /dev/null @@ -1,30 +0,0 @@ -isPhameActive()) { - return null; - } - - $whitelist = array( - '/phame/r/', - ); - - $path = $request->getPath(); - if (!$this->isPathPrefixMatch($path, $whitelist)) { - return null; - } - - return new PhameBlogResourceSite(); - } - -} diff --git a/src/applications/phame/site/PhameBlogSite.php b/src/applications/phame/site/PhameBlogSite.php index f4e0a62c20..253fca0fbd 100644 --- a/src/applications/phame/site/PhameBlogSite.php +++ b/src/applications/phame/site/PhameBlogSite.php @@ -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; } }