diff --git a/app/views/errors/missing_action.thtml b/app/views/errors/missing_action.thtml new file mode 100644 index 000000000..0dbd36f19 --- /dev/null +++ b/app/views/errors/missing_action.thtml @@ -0,0 +1,22 @@ +

Missing action

+ +

You are seeing this error because the action missing_action;?> + is not defined in controller name);?> +

+ + +

+Notice: this error is being rendered by the app/views/errors/missing_action.thtml +view file, a user-customizable error page for handling invalid action dispatches. +

+ + +

+Error: Unable to execute action missing_action;?> on +name);?> +

+ +

Controller dump:

+
+
+
diff --git a/app/views/errors/missing_controller.thtml b/app/views/errors/missing_controller.thtml new file mode 100644 index 000000000..7423fbbd5 --- /dev/null +++ b/app/views/errors/missing_controller.thtml @@ -0,0 +1,20 @@ +

Missing controller

+ +

You are seeing this error because controller missing_controller;?> + could not be found. +

+ +

+Notice: this error is being rendered by the app/views/errors/missing_controller.thtml +view file, a user-customizable error page for handling invalid controller dispatches. +

+ +

+Fatal: Unable to load controller missing_controller;?> +

+ + +

Controller dump:

+
+
+
diff --git a/app/views/errors/missing_view.thtml b/app/views/errors/missing_view.thtml new file mode 100644 index 000000000..4324fb03e --- /dev/null +++ b/app/views/errors/missing_view.thtml @@ -0,0 +1,22 @@ +

Missing view

+ +

You are seeing this error because the view missing_view;?> + for action params['action'];?> + in controller name);?> could not be found. +

+ +

+Notice: this error is being rendered by the app/views/errors/missing_view.thtml +view file, a user-customizable error page for handling missing/invalid views during rendering. +

+ +

+Fatal: Unable to load view file missing_view;?> for +action params['controller'];?>::params['action'];?> +

+ + +

Controller dump:

+
+
+
diff --git a/app/views/layouts/default.thtml b/app/views/layouts/default.thtml index a4323b652..447c447c1 100644 --- a/app/views/layouts/default.thtml +++ b/app/views/layouts/default.thtml @@ -7,10 +7,10 @@ - -

- - - +
+
+ +
+
diff --git a/app/views/pages/home.thtml b/app/views/pages/home.thtml index 0fc08c4bb..ab9ccfc86 100644 --- a/app/views/pages/home.thtml +++ b/app/views/pages/home.thtml @@ -1,4 +1,4 @@ -

Cake works!

+

Cake works!

Your installation of Cake is functional. Edit /app/views/pages/home.thtml to change the contents of this page.

@@ -8,13 +8,13 @@

Features

-

Cake is in its early infancy, but it works and I'm using it on a few projects. Currently the Dispatcher is working, the Model has CRUD functionality but with no joins between tables yet. The Controller is working and has most basic functions (including render() for templating).

@@ -22,6 +22,6 @@

NEW! linkOut('My Amazon Wishlish','http://www.amazon.com/gp/registry/registry.html?id=NODP8QT6LFTO')?> for when you'll want to show your appreciation.

-

linkOut('Cake PHP Google Group','http://groups-beta.google.com/group/cake-php')?> · linkOut('Cake Wiki (temporary)','http://cake.bplusf.net/')?> · linkOut('Cake TRAC (SVN repository, etc.)','https://developers.nextco.com/cake')?>

+

linkOut('Cake PHP Google Group','http://groups-beta.google.com/group/cake-php')?> · linkOut('Cake Wiki (temporary)','http://cake.bplusf.net/')?> · linkOut('Cake TRAC (SVN repository, etc.)','https://developers.nextco.com/cake')?>

See linkOut('Cake website','http://sputnik.pl/cake')?> for more information.

diff --git a/libs/controller.php b/libs/controller.php index 8a3158c9e..0cf0285c3 100644 --- a/libs/controller.php +++ b/libs/controller.php @@ -142,6 +142,24 @@ class Controller extends Template parent::__construct(); } + + function missing_controller() + { + $this->autoRender = false; + $this->render('../errors/missing_controller'); + } + + function missing_action() + { + $this->autoRender = false; + $this->render('../errors/missing_action'); + } + + function missing_view() + { + $this->autoRender = false; + $this->render('../errors/missing_view'); + } /** * Redirects to given $url, after turning off $this->autoRender. diff --git a/libs/dispatcher.php b/libs/dispatcher.php index de824dce6..3bfa39926 100644 --- a/libs/dispatcher.php +++ b/libs/dispatcher.php @@ -34,6 +34,12 @@ * Description: * Dispatches the request, creating appropriate models and controllers. */ + +define('DISPATCH_NO_CONTROLLER', 'missing_controller'); +define('DISPATCH_UNKNOWN_CONTROLLER', 'missing_controller'); +define('DISPATCH_NO_ACTION', 'missing_action'); +define('DISPATCH_UNKNOWN_ACTION', 'missing_action'); +define('DISPATCHER_UNKNOWN_VIEW', 'missing_view'); uses('error_messages', 'object', 'router', 'controller'); @@ -44,7 +50,8 @@ uses('error_messages', 'object', 'router', 'controller'); * @subpackage cake.libs * @since Cake v 0.2.9 */ -class Dispatcher extends Object { +class Dispatcher extends Object +{ /** * Base URL @@ -61,78 +68,129 @@ class Dispatcher extends Object { /** * Constructor. - * */ - function __construct () { + function __construct () + { $this->base = $this->baseUrl(); parent::__construct(); } + +/** + * Enter description here... + * + * @param unknown_type $url + * @return unknown + */ + function dispatch($url) + { + $params = $this->parseParams($url); + $result = $this->invoke($url); + + return $result === true? $params: array(); + } + /** * Enter description here... * * @param string $url * @return unknown */ - function dispatch ($url) + function invoke ($url) { global $_POST, $_GET, $_FILES, $_SESSION; + + $params = $this->parseParams($url); + $missing_controller = false; + $missing_action = false; + $missing_view = false; - $params = $this->parseParams($url); + if (empty($params['controller'])) + { + $missing_controller = true; + } + else + { + $ctrl_name = Inflector::camelize($params['controller']); + $ctrl_class = $ctrl_name.'Controller'; + + if (!loadController($ctrl_name) || !class_exists($ctrl_class)) + { + $missing_controller = true; + } + } - // die if no controller set - if (empty($params['controller'])) - $this->errorNoController($url); - - $ctrl_name = Inflector::camelize($params['controller']); - $ctrl_class = $ctrl_name.'Controller'; - - /** - * Find out if the specified controller exists, and die if not. - */ - if (!loadController($ctrl_name) || !class_exists($ctrl_class)) - $this->errorUnknownController($url, $ctrl_name); - - $ctrl_methods = get_class_methods($ctrl_class); - $ctrl_vars = get_class_vars($ctrl_class); - - /** - * If _no_action_is set, check if the default action, index() exists. If it doesn't, die. - */ - if (empty($params['action'])) + if ($missing_controller) { - if (in_array('index', $ctrl_methods)) + $ctrl_class = 'AppController'; + $controller = new $ctrl_class($this); + $params['action'] = 'missing_controller'; + $controller->missing_controller = $params['controller']; + } + else + { + // create controller + $controller = new $ctrl_class($this); + } + + // if action is not set, and the default Controller::index() method doesn't exist + if (empty($params['action'])) + { + if (method_exists($controller, 'index')) { $params['action'] = 'index'; - } + } else { - $this->errorNoAction($url); + $missing_action = true; } } - - /** - * Check if the specified action really exists. - */ - if (!in_array($params['action'], $ctrl_methods)) + + // if the requested action doesn't exist + if (!method_exists($controller, $params['action'])) { - $this->errorUnknownAction($url, $ctrl_class, $params['action']); + $missing_action = true; + } + + if ($missing_action) + { + $controller->missing_action = $params['action']; + $params['action'] = 'missing_action'; + } + + // initialize the controller + $controller->base = $this->base; + $controller->here = $this->base.'/'.$url; + $controller->params = $params; + $controller->action = $params['action']; + $controller->data = empty($params['data'])? null: $params['data']; + $controller->passed_args = empty($params['pass'])? null: $params['pass']; + + // EXECUTE THE REQUESTED ACTION + call_user_func_array(array(&$controller, $params['action']), empty($params['pass'])? null: $params['pass']); + + $isFatal = isset($controller->isFatal) ? $controller->isFatal : false; + + if ($isFatal) + { + switch($params['action']) + { + case 'missing_controller': + $this->errorUnknownController($url, $ctrl_name); + break; + + case 'missing_action': + $this->errorUnknownAction($url, $ctrl_class, $controller->missing_action); + break; + } + } + + if ($controller->autoRender) + { + $controller->render(); } - $controller = new $ctrl_class ($params); - $controller->base = $this->base; - $controller->here = $this->base.'/'.$url; - $controller->action = $params['action']; - $controller->data = empty($params['data'])? null: $params['data']; - $controller->passed_args = empty($params['pass'])? null: $params['pass']; - - // EXECUTE THE REQUESTED ACTION - call_user_func_array(array(&$controller, $params['action']), empty($params['pass'])? null: $params['pass']); - - if ($controller->autoRender) - $controller->render(); - - return $params; + return true; } /** @@ -166,7 +224,8 @@ class Dispatcher extends Object { * * @return string */ - function baseUrl () { + function baseUrl () + { global $_SERVER; //non mod_rewrite use: @@ -191,7 +250,8 @@ class Dispatcher extends Object { * @param string $name Name of the error message (e.g. Not found) * @param string $message */ - function error ($code, $name, $message) { + function error ($code, $name, $message) + { $controller = new Controller ($this); $controller->base = $this->base; $controller->error($code, $name, $message); @@ -203,7 +263,8 @@ class Dispatcher extends Object { * @param unknown_type $url * @param unknown_type $message */ - function error404 ($url, $message) { + function error404 ($url, $message) + { $this->error('404', 'Not found', sprintf(ERROR_404, $url, $message)); } @@ -212,7 +273,8 @@ class Dispatcher extends Object { * * @param string $url */ - function errorNoController ($url) { + function errorNoController ($url) + { DEBUG? trigger_error (ERROR_NO_CONTROLLER_SET, E_USER_ERROR): $this->error404($url, "no controller set"); @@ -225,7 +287,8 @@ class Dispatcher extends Object { * @param string $url * @param string $controller_class */ - function errorUnknownController ($url, $controller_class) { + function errorUnknownController ($url, $controller_class) + { DEBUG? trigger_error (sprintf(ERROR_UNKNOWN_CONTROLLER, $controller_class), E_USER_ERROR): $this->error404($url, "missing controller \"{$controller_class}\""); @@ -237,7 +300,8 @@ class Dispatcher extends Object { * * @param string $url */ - function errorNoAction ($url) { + function errorNoAction ($url) + { DEBUG? trigger_error (ERROR_NO_ACTION_SET, E_USER_ERROR): $this->error404(sprintf(ERROR_404, $url, "no action set")); @@ -251,7 +315,8 @@ class Dispatcher extends Object { * @param string $controller_class * @param string $action */ - function errorUnknownAction ($url,$controller_class, $action) { + function errorUnknownAction ($url,$controller_class, $action) + { DEBUG? trigger_error (sprintf(ERROR_NO_ACTION, $action, $controller_class), E_USER_ERROR): $this->error404($url, "missing controller \"{$controller_class}\""); @@ -259,4 +324,4 @@ class Dispatcher extends Object { } } -?> \ No newline at end of file +?> diff --git a/libs/template.php b/libs/template.php index bd58dba0a..ea287fae4 100644 --- a/libs/template.php +++ b/libs/template.php @@ -163,30 +163,108 @@ class Template extends Object * @param string $layout * @param string $file Custom filename for view */ - function render ($action=null, $layout=null, $file=null) { + function render ($action=null, $layout=null, $file=null) + { + if (isset($this->hasRendered) && $this->hasRendered) + { + return true; + } + else + { + $this->hasRendered = false; + } + $this->autoRender = false; - + if (!$action) $action = $this->action; if ($layout) $this->setLayout($layout); - + + //$isFatal = isset($this->isFatal) ? $this->isFatal : false; + $view_fn = $file? $file: $this->_getViewFn($action); - - if (!is_file($view_fn)) { - DEBUG? trigger_error (sprintf(ERROR_NO_VIEW, $action, $view_fn), E_USER_ERROR) - : $this->error('404', 'Not found', sprintf(ERROR_404, '', "missing view \"{$action}\"")); - die(); + + if (!is_file($view_fn)) + { + if (strtolower(get_class($this)) == 'template') + { + return array('action' => $action, 'layout' => $layout, 'view_fn' => $view_fn); + } + + // check to see if the missing view is due to a custom missing_action + if (strpos($action, 'missing_action') !== false) + { + $error_action = 'missing_action'; + } + else + { + $error_action = 'missing_view'; + } + + + // check for controller-level view handler + foreach(array($this->name, 'errors') as $view_dir) + { + $missing_view_fn = VIEWS.$view_dir.DS.$error_action.'.thtml'; + $missing_view_exists = is_file($missing_view_fn); + if ($missing_view_exists) + { + break; + } + } + + if (strpos($action, 'missing_view') === false) + { + $controller =& $this; + $controller->missing_view = $view_fn; + $controller->action = $action; + call_user_func_array(array(&$controller, 'missing_view'), empty($params['pass'])? null: $params['pass']); + $isFatal = isset($this->isFatal) ? $this->isFatal : false; + if (!$isFatal) + { + $view_fn = $missing_view_fn; + } + } + else + { + $missing_view_exists = false; + } + + if (!$missing_view_exists || $isFatal) + { + // app/errors/missing_view.thtml view is missing! + if (DEBUG) + { + trigger_error (sprintf(ERROR_NO_VIEW, $action, $view_fn), E_USER_ERROR); + } + else + { + $this->error('404', 'Not found', sprintf(ERROR_404, '', "missing view \"{$action}\"")); + } + + die(); + } } + + if ($view_fn && !$this->hasRendered) + { + $out = $this->_render($view_fn, $this->_view_vars, 0); + if ($out !== false) + { + if ($this->layout && $this->autoLayout) + { + $out = $this->renderLayout($out); + } - $out = $this->_render($view_fn, $this->_view_vars, 0); - - if ($out !== false) { - if ($this->layout && $this->autoLayout) - $out = $this->renderLayout($out); - print $out; - } - else { - $out = $this->_render($view_fn, $this->_view_vars, false); - trigger_error (sprintf(ERROR_IN_VIEW, $view_fn, $out), E_USER_ERROR); + print $out; + $this->hasRendered = true; + } + else + { + $out = $this->_render($view_fn, $this->_view_vars, false); + trigger_error (sprintf(ERROR_IN_VIEW, $view_fn, $out), E_USER_ERROR); + } + + return true; } } diff --git a/public/css/default.css b/public/css/default.css index 05fdc3305..96223a9b3 100644 --- a/public/css/default.css +++ b/public/css/default.css @@ -1,5 +1,5 @@ BODY { -font-size:.9em; +font-size: 76%; } BODY, INPUT, TEXTAREA { @@ -8,15 +8,28 @@ font-family:sans-serif; H1 { font-size:2.1em; -text-align:center; +color: #fff; +text-align: left; +position: absolute; +padding-left: 15%; +top: 0.35em; +left: 0; +height: 4em; } H2 { font-size:1.7em; +color: #383; } H3 { font-size:1.4em; +color: #553; +} + +H4 { +font-size:1.15em; +color: #338; } P { @@ -29,6 +42,10 @@ white-space:nowrap; text-decoration:underline; } +A:HOVER { +background-color:#EEE; +} + CODE, PRE { font-family:monospace; font-size:1.1em !important; @@ -55,3 +72,39 @@ HR { height:0; border-top:1px solid #AAA; } + + +#container { +margin: 0 auto; +border-top: solid 5em #69c; +color: #333; +font: normal 1.1em Verdana; +padding: 0 15%; +} + +#content { +padding-top: 1em; +width: 100%; +text-align: left; +line-height: 170%; +} + +.notice { +padding: 1em; +background: #ffd; +border: solid 2px #eeb; +display: block; +font-family: Verdana; +} + +.tip { +background: #efe; +padding: 1em; +border: solid 2px #cdc; +} + +.error { +background: #fee; +padding: 1em; +border: solid 2px #dcc; +}