diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 1500a5ed97..7c73c57ed0 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -34,6 +34,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/aphront/panel-view.css', ), + 'aphront-request-failure-view-css' => + array( + 'uri' => '/res/d7df3b42/rsrc/css/aphront/request-failure-view.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/aphront/request-failure-view.css', + ), 'aphront-side-nav-view-css' => array( 'uri' => '/res/0fc0545c/rsrc/css/aphront/side-nav-view.css', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 94cf87f8f7..d7dc179c5a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -45,6 +45,7 @@ phutil_register_library_map(array( 'AphrontRedirectException' => 'aphront/exception/redirect', 'AphrontRedirectResponse' => 'aphront/response/redirect', 'AphrontRequest' => 'aphront/request', + 'AphrontRequestFailureView' => 'view/page/failure', 'AphrontResponse' => 'aphront/response/base', 'AphrontSideNavView' => 'view/layout/sidenav', 'AphrontTableView' => 'view/control/table', @@ -97,6 +98,7 @@ phutil_register_library_map(array( 'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus', 'Javelin' => 'infratructure/javelin/api', 'LiskDAO' => 'storage/lisk/dao', + 'Phabricator404Controller' => 'applications/base/controller/404', 'PhabricatorAuthController' => 'applications/auth/controlller/base', 'PhabricatorConduitAPIController' => 'applications/conduit/controller/api', 'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog', @@ -209,6 +211,7 @@ phutil_register_library_map(array( 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontRedirectException' => 'AphrontException', 'AphrontRedirectResponse' => 'AphrontResponse', + 'AphrontRequestFailureView' => 'AphrontView', 'AphrontSideNavView' => 'AphrontView', 'AphrontTableView' => 'AphrontView', 'AphrontWebpageResponse' => 'AphrontResponse', @@ -240,6 +243,7 @@ phutil_register_library_map(array( 'DifferentialRevisionListController' => 'DifferentialController', 'DifferentialRevisionUpdateHistoryView' => 'AphrontView', 'DifferentialRevisionViewController' => 'DifferentialController', + 'Phabricator404Controller' => 'PhabricatorController', 'PhabricatorAuthController' => 'PhabricatorController', 'PhabricatorConduitAPIController' => 'PhabricatorConduitController', 'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO', diff --git a/src/aphront/applicationconfiguration/AphrontApplicationConfiguration.php b/src/aphront/applicationconfiguration/AphrontApplicationConfiguration.php index 32f9bbef9b..cba9578bb4 100644 --- a/src/aphront/applicationconfiguration/AphrontApplicationConfiguration.php +++ b/src/aphront/applicationconfiguration/AphrontApplicationConfiguration.php @@ -28,6 +28,7 @@ abstract class AphrontApplicationConfiguration { abstract public function getApplicationName(); abstract public function getURIMap(); abstract public function buildRequest(); + abstract public function build404Controller(); final public function setRequest(AphrontRequest $request) { $this->request = $request; @@ -45,6 +46,10 @@ abstract class AphrontApplicationConfiguration { $path = $request->getPath(); list($controller_class, $uri_data) = $mapper->mapPath($path); + if (!$controller_class) { + return $this->build404Controller(); + } + PhutilSymbolLoader::loadClass($controller_class); $controller = newv($controller_class, array($request)); diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index a5553423ee..ee7c71caa6 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -150,10 +150,29 @@ class AphrontDefaultApplicationConfiguration $response->setContent($view->render()); return $response; } + } else if ($response instanceof Aphront404Response) { + + $failure = new AphrontRequestFailureView(); + $failure->setHeader('404 Not Found'); + $failure->appendChild( + '

The page you requested was not found.

'); + + $view = new PhabricatorStandardPageView(); + $view->setTitle('404 Not Found'); + $view->appendChild($failure); + + $response = new AphrontWebpageResponse(); + $response->setContent($view->render()); + $response->setHTTPResponseCode(404); + return $response; } return $response; } + public function build404Controller() { + return array(new Phabricator404Controller($this->getRequest()), array()); + } + } diff --git a/src/aphront/default/configuration/__init__.php b/src/aphront/default/configuration/__init__.php index f841860f69..ea6f86450a 100644 --- a/src/aphront/default/configuration/__init__.php +++ b/src/aphront/default/configuration/__init__.php @@ -9,6 +9,8 @@ phutil_require_module('phabricator', 'aphront/applicationconfiguration'); phutil_require_module('phabricator', 'aphront/request'); phutil_require_module('phabricator', 'aphront/response/webpage'); +phutil_require_module('phabricator', 'applications/base/controller/404'); +phutil_require_module('phabricator', 'view/page/failure'); phutil_require_module('phabricator', 'view/page/standard'); phutil_require_module('phutil', 'markup'); diff --git a/src/aphront/response/base/AphrontResponse.php b/src/aphront/response/base/AphrontResponse.php index 29a371c08d..786f56b77e 100644 --- a/src/aphront/response/base/AphrontResponse.php +++ b/src/aphront/response/base/AphrontResponse.php @@ -23,6 +23,7 @@ abstract class AphrontResponse { private $request; private $cacheable = false; + private $responseCode = 200; public function setRequest($request) { $this->request = $request; @@ -36,12 +37,21 @@ abstract class AphrontResponse { public function getHeaders() { return array(); } - + public function setCacheDurationInSeconds($duration) { $this->cacheable = $duration; return $this; } + public function setHTTPResponseCode($code) { + $this->responseCode = $code; + return $this; + } + + public function getHTTPResponseCode() { + return $this->responseCode; + } + public function getCacheHeaders() { if ($this->cacheable) { $epoch = time() + $this->cacheable; diff --git a/src/applications/base/controller/404/Phabricator404Controller.php b/src/applications/base/controller/404/Phabricator404Controller.php new file mode 100644 index 0000000000..591e7a3c3e --- /dev/null +++ b/src/applications/base/controller/404/Phabricator404Controller.php @@ -0,0 +1,25 @@ +header = $header; + return $this; + } + + + final public function render() { + require_celerity_resource('aphront-request-failure-view-css'); + + return + '
'. + '
'. + '

'.phutil_escape_html($this->header).'

'. + '
'. + '
'. + $this->renderChildren(). + '
'. + '
'; + } + +} diff --git a/src/view/page/failure/__init__.php b/src/view/page/failure/__init__.php new file mode 100644 index 0000000000..e753355fdb --- /dev/null +++ b/src/view/page/failure/__init__.php @@ -0,0 +1,14 @@ +willSendResponse($response); $response->setRequest($request); $response_string = $response->buildResponseString(); + +$code = $response->getHTTPResponseCode(); +if ($code != 200) { + header("HTTP/1.0 {$code}"); +} + $headers = $response->getCacheHeaders(); $headers = array_merge($headers, $response->getHeaders()); foreach ($headers as $header) { diff --git a/webroot/rsrc/css/aphront/request-failure-view.css b/webroot/rsrc/css/aphront/request-failure-view.css new file mode 100644 index 0000000000..de13f0a6a0 --- /dev/null +++ b/webroot/rsrc/css/aphront/request-failure-view.css @@ -0,0 +1,27 @@ +/** + * @provides aphront-request-failure-view-css + */ + +.aphront-request-failure-view { + margin: 2em auto; + background: #eff2f7; + width: 600px; +} + +.aphront-request-failure-view .aphront-request-failure-head { + padding: 1em 2em; + border-bottom: 1px solid #afb2b7; + background: #dfe2e7; +} + +.aphront-request-failure-view .aphront-request-failure-head h1 { + font-size: 24px; +} + +.aphront-request-failure-view .aphront-request-failure-body { + padding: 1em 2em; +} + +.aphront-request-failure-view .aphront-request-failure-body p { + margin: .5em 0 1.25em; +}