Browse files

Initialised nl as a translation with recent EN-files as fall back, no…

…thing translated yet except the menu
  • Loading branch information...
1 parent 656c7ff commit e3e9e89f9a459f3a101aaa9989f5ca2bea652f85 @glamorous glamorous committed Jun 23, 2010
View
6 guide/nl/start.controllers.md → guide/nl/about.controllers.md
@@ -2,9 +2,7 @@
Controllers stand in between the models and the views in an application. They pass information on to the model when data needs to be changed and they request information from the model. For example database inserts, updates and deletes for data change and database selects for information retrieval. Controllers pass on the information of the model to the views, the views contain the final output for the users.
-Controllers are called by a URL, see [URLs and Links](start.urls) for more information.
-
-
+Controllers are called by a URL, see [URLs and Links](about.urls) for more information.
## Controller naming and anatomy
@@ -75,7 +73,7 @@ Article list goes here!
Say we want to display a specific article, for example the article with the title being `your-article-title` and the id of the article is `1`.
-The url would look like yoursite.com/article/view/**your-article-title/1** The last two segments of the url are passed on to the view() method.
+The url would look like yoursite.com/article/view/**your-article-title/1**. The last two segments of the url are passed on to the view() method.
**application/classes/controller/article.php**
~~~
View
27 guide/nl/about.conventions.md
@@ -0,0 +1,27 @@
+# Conventions
+
+It is encouraged to follow Kohana's [coding style](http://dev.kohanaframework.org/wiki/kohana2/CodingStyle). This uses [BSD/Allman style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) bracing, among other things.
+
+## Class Names and File Location {#classes}
+
+Class names in Kohana follow a strict convention to facilitate [autoloading](using.autoloading). Class names should have uppercase first letters with underscores to separate words. Underscores are significant as they directly reflect the file location in the filesystem.
+
+The following conventions apply:
+
+1. CamelCased class names should not be used, except when it is undesirable to create a new directory level.
+2. All class file names and directory names are lowercase.
+3. All classes should be in the `classes` directory. This may be at any level in the [cascading filesystem](about.filesystem).
+
+[!!] Unlike Kohana v2.x, there is no separation between "controllers", "models", "libraries" and "helpers". All classes are placed in the "classes/" directory, regardless if they are static "helpers" or object "libraries". You can use whatever kind of class design you want: static, singleton, adapter, etc.
+
+## Examples
+
+Remember that in a class, an underscore means a new directory. Consider the following examples:
+
+Class Name | File Path
+----------------------|-------------------------------
+Controller_Template | classes/controller/template.php
+Model_User | classes/model/user.php
+Database | classes/database.php
+Database_Query | classes/database/query.php
+Form | classes/form.php
View
86 guide/nl/about.filesystem.md
@@ -0,0 +1,86 @@
+# Cascading Filesystem
+
+The Kohana filesystem is a heirarchy of directory structure. When a file is
+loaded by [Kohana::find_file], it is searched in the following order:
+
+Application Path
+: Defined as `APPPATH` in `index.php`. The default value is `application`.
+
+Module Paths
+: This is set as an associative array using [Kohana::modules] in `APPPATH/bootstrap.php`.
+ Each of the values of the array will be searched in the order that the modules
+ are added.
+
+System Path
+: Defined as `SYSPATH` in `index.php`. The default value is `system`. All of the
+ main or "core" files and classes are defined here.
+
+Files that are in directories higher up the include path order take precedence
+over files of the same name lower down the order, which makes it is possible to
+overload any file by placing a file with the same name in a "higher" directory:
+
+![Cascading Filesystem Infographic](img/cascading_filesystem.png)
+
+If you have a view file called `welcome.php` in the `APPPATH/views` and
+`SYSPATH/views` directories, the one in application will be returned when
+`welcome.php` is loaded because it is at the top of the filesystem.
+
+## Types of Files
+
+The top level directories of the application, module, and system paths has the following
+default directories:
+
+classes/
+: All classes that you want to [autoload](using.autoloading) should be stored here.
+ This includes controllers, models, and all other classes. All classes must
+ follow the [class naming conventions](about.conventions#classes).
+
+config/
+: Configuration files return an associative array of options that can be
+ loaded using [Kohana::config]. See [config usage](using.configuration) for
+ more information.
+
+i18n/
+: Translation files return an associative array of strings. Translation is
+ done using the `__()` method. To translate "Hello, world!" into Spanish,
+ you would call `__('Hello, world!')` with [I18n::$lang] set to "es-es".
+ See [translation usage](using.translation) for more information.
+
+messages/
+: Message files return an associative array of strings that can be loaded
+ using [Kohana::message]. Messages and i18n files differ in that messages
+ are not translated, but always written in the default language and referred
+ to by a single key. See [message usage](using.messages) for more information.
+
+views/
+: Views are plain PHP files which are used to generate HTML or other output. The view file is
+ loaded into a [View] object and assigned variables, which it then converts
+ into an HTML fragment. Multiple views can be used within each other.
+ See [view usage](usings.views) for more information.
+
+## Finding Files
+
+The path to any file within the filesystem can be found by calling [Kohana::find_file]:
+
+ // Find the full path to "classes/cookie.php"
+ $path = Kohana::find_file('classes', 'cookie');
+
+ // Find the full path to "views/user/login.php"
+ $path = Kohana::find_file('views', 'user/login');
+
+
+# Vendor Extensions
+
+We call extensions that are not specific to Kohana "vendor" extensions.
+For example, if you wanted to use [DOMPDF](http://code.google.com/p/dompdf),
+you would copy it to `application/vendor/dompdf` and include the DOMPDF
+autoloading class:
+
+ require Kohana::find_file('vendor', 'dompdf/dompdf/dompdf_config.inc');
+
+Now you can use DOMPDF without loading any more files:
+
+ $pdf = new DOMPDF;
+
+[!!] If you want to convert views into PDFs using DOMPDF, try the
+[PDFView](http://github.com/shadowhand/pdfview) module.
View
73 guide/nl/about.flow.md
@@ -0,0 +1,73 @@
+# Request Flow
+
+Every application follows the same flow:
+
+1. Application starts from `index.php`.
+2. The application, module, and system paths are set.
+3. Error reporting levels are set.
+4. Install file is loaded, if it exists.
+5. The [Kohana] class is loaded.
+6. The bootstrap file, `APPPATH/bootstrap.php`, is included.
+7. [Kohana::init] is called, which sets up error handling, caching, and logging.
+8. [Kohana_Config] readers and [Kohana_Log] writers are attached.
+9. [Kohana::modules] is called to enable additional modules.
+ * Module paths are added to the [cascading filesystem](about.filesystem).
+ * Includes the module `init.php` file, if it exists.
+ * The `init.php` file can perform additional environment setup, including adding routes.
+10. [Route::set] is called multiple times to define the [application routes](using.routing).
+11. [Request::instance] is called to start processing the request.
+ 1. Checks each route that has been set until a match is found.
+ 2. Creates the controller instance and passes the request to it.
+ 3. Calls the [Controller::before] method.
+ 4. Calls the controller action, which generates the request response.
+ 5. Calls the [Controller::after] method.
+ * The above 5 steps can be repeated multiple times when using [HMVC sub-requests](about.mvc).
+12. The main [Request] response is displayed
+
+## index.php
+
+Kohana follows a [front controller] pattern, which means that all requests are sent to `index.php`. This allows a very clean [filesystem](about.filesystem) design. In `index.php`, there are some very basic configuration options available. You can change the `$application`, `$modules`, and `$system` paths and set the error reporting level.
+
+The `$application` variable lets you set the directory that contains your application files. By default, this is `application`. The `$modules` variable lets you set the directory that contains module files. The `$system` variable lets you set the directory that contains the default Kohana files.
+
+You can move these three directories anywhere. For instance, if your directories are set up like this:
+
+ www/
+ index.php
+ application/
+ modules/
+ system/
+
+You could move the directories out of the web root:
+
+ application/
+ modules/
+ system/
+ www/
+ index.php
+
+Then you would change the settings in `index.php` to be:
+
+ $application = '../application';
+ $modules = '../modules';
+ $system = '../system';
+
+Now none of the directories can be accessed by the web server. It is not necessary to make this change, but does make it possible to share the directories with multiple applications, among other things.
+
+[!!] There is a security check at the top of every Kohana file to prevent it from being accessed without using the front controller. However, it is more secure to move the application, modules, and system directories to a location that cannot be accessed via the web.
+
+### Error Reporting
+
+By default, Kohana displays all errors, including strict mode warnings. This is set using [error_reporting](http://php.net/error_reporting):
+
+ error_reporting(E_ALL | E_STRICT);
+
+When you application is live and in production, a more conservative setting is recommended, such as ignoring notices:
+
+ error_reporting(E_ALL & ~E_NOTICE);
+
+If you get a white screen when an error is triggered, your host probably has disabled displaying errors. You can turn it on again by adding this line just after your `error_reporting` call:
+
+ ini_set('display_errors', TRUE);
+
+Errors should **always** be displayed, even in production, because it allows you to use [exception and error handling](debugging.errors) to serve a nice error page rather than a blank white screen when an error happens.
View
9 guide/nl/start.installation.md → guide/nl/about.install.md
@@ -1,19 +1,20 @@
# Installation
-1. Download the latest **stable** release from the [Kohana website](http://kohanaphp.com/)
+1. Download the latest **stable** release from the [Kohana website](http://kohanaframework.org/)
2. Unzip the downloaded package to create a `kohana` directory
3. Upload the contents of this folder to your webserver
4. Open `application/bootstrap.php` and make the following changes:
- Set the default [timezone](http://php.net/timezones) for your application
- Set the `base_url` in the [Kohana::init] call to reflect the location of the kohana folder on your server
-6. Make sure the `application/cache` and `application/logs` directories are world writable with `chmod application/{cache,logs} 0777`
+6. Make sure the `application/cache` and `application/logs` directories are writable by the web server
7. Test your installation by opening the URL you set as the `base_url` in your favorite browser
[!!] Depending on your platform, the installation's subdirs may have lost their permissions thanks to zip extraction. Chmod them all to 755 by running `find . -type d -exec chmod 0755 {} \;` from the root of your Kohana installation.
-You should see the installation page. If it reports any errors, you will need to correct them before contining.
+You should see the installation page. If it reports any errors, you will need to correct them before continuing.
![Install Page](img/install.png "Example of install page")
-Once your install page reports that your environment is set up correctly you need to either rename or delete `install.php` in the root directory. You should then see the Kohana welcome page. (?? Currently just 'Hello World!' in KO3 ??)
+Once your install page reports that your environment is set up correctly you need to either rename or delete `install.php` in the root directory. You should then see the Kohana welcome page:
+![Welcome Page](img/welcome.png "Example of welcome page")
View
15 guide/nl/about.kohana.md
@@ -0,0 +1,15 @@
+# What is Kohana?
+
+Kohana is an open source, [object oriented](http://wikipedia.org/wiki/Object-Oriented_Programming) [MVC](http://wikipedia.org/wiki/Model–View–Controller "Model View Controller") [web framework](http://wikipedia.org/wiki/Web_Framework) built using [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") by a team of volunteers that aims to be swift, secure, and small.
+
+[!!] Kohana is licensed under a [BSD license](http://kohanaframework.org/license), so you can legally use it for any kind of open source, commercial, or personal project.
+
+## What makes Kohana great?
+
+Anything can be extended using the unique [filesystem](about.filesystem) design, little or no [configuration](about.configuration) is necessary, [error handling](debugging.errors) helps locate the source of errors quickly, and [debugging](debugging) and [profiling](debugging.profiling) provide insight into the application.
+
+To help secure your applications, tools for [XSS removal](security.xss), [input validation](security.validation), [signed cookies](security.cookies), [form](security.forms) and [HTML](security.html) generators are all included. The [database](security.database) layer provides protection against [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Of course, all official code is carefully written and reviewed for security.
+
+## This Documentation Sucks!
+
+We are working very hard to provide complete documentation. If you are having trouble finding an answer, check the [unofficial wiki](http://kerkness.ca/wiki/doku.php). If you would like to add or change something in the guide, please [fork the userguide](http://github.com/kohana/userguide), make your changes, and send a pull request. If you are not familar with git, you can also submit a [feature request](http://dev.kohanaframework.org/projects/kohana3/issues) (requires registration).
View
7 guide/nl/about.mvc.md
@@ -0,0 +1,7 @@
+# (Hierarchical) Model View Controller
+
+Model View Controller (Or MVC for short) is a popular design pattern that separates your data sources (Model) from the presentation/templates (View) and the request logic (Controller).
+
+It makes it much easier to develop applications as the system is designed to maximise the code reuse, meaning you don't have to write as much!
+
+[!!] Stub
View
4 guide/nl/about.translation.md
@@ -0,0 +1,4 @@
+# Translation
+
+[!!] This article is a stub!
+
View
290 guide/nl/about.upgrading.md
@@ -0,0 +1,290 @@
+# Upgrading from 2.3.x
+
+Most of Kohana v3 works very differently from Kohana 2.3, here's a list of common gotchas and tips for upgrading.
+
+## Naming conventions
+
+The 2.x series differentiated between different 'types' of class (i.e. controller, model etc.) using suffixes. Folders within model / controller folders didn't have any bearing on the name of the class.
+
+In 3.0 this approach has been scrapped in favour of the Zend framework filesystem conventions, where the name of the class is a path to the class itself, separated by underscores instead of slashes (i.e. `/some/class/file.php` becomes `Some_Class_File`).
+
+See the [conventions documentation](start.conventions) for more information.
+
+## Input Library
+
+The Input Library has been removed from 3.0 in favour of just using `$_GET` and `$_POST`.
+
+### XSS Protection
+
+If you need to XSS clean some user input you can use [Security::xss_clean] to sanitise it, like so:
+
+ $_POST['description'] = security::xss_clean($_POST['description']);
+
+You can also use the [Security::xss_clean] as a filter with the [Validate] library:
+
+ $validation = new Validate($_POST);
+
+ $validate->filter('description', 'Security::xss_clean');
+
+### POST & GET
+
+One of the great features of the Input library was that if you tried to access the value in one of the superglobal arrays and it didn't exist the Input library would return a default value that you could specify i.e.:
+
+ $_GET = array();
+
+ // $id is assigned the value 1
+ $id = Input::instance()->get('id', 1);
+
+ $_GET['id'] = 25;
+
+ // $id is assigned the value 25
+ $id = Input::instance()->get('id', 1);
+
+In 3.0 you can duplicate this functionality using [Arr::get]:
+
+ $_GET = array();
+
+ // $id is assigned the value 1
+ $id = Arr::get($_GET, 'id', 1);
+
+ $_GET['id'] = 42;
+
+ // $id is assigned the value 42
+ $id = Arr::get($_GET, 'id', 1);
+
+## ORM Library
+
+There have been quite a few major changes in ORM since 2.3, here's a list of the more common upgrading problems.
+
+### Member variables
+
+All member variables are now prefixed with an underscore (_) and are no longer accessible via `__get()`. Instead you have to call a function with the name of the property, minus the underscore.
+
+For instance, what was once `loaded` in 2.3 is now `_loaded` and can be accessed from outside the class via `$model->loaded()`.
+
+### Relationships
+
+In 2.3 if you wanted to iterate a model's related objects you could do:
+
+ foreach($model->{relation_name} as $relation)
+
+However, in the new system this won't work. In version 2.3 any queries generated using the Database library were generated in a global scope, meaning that you couldn't try and build two queries simultaneously. Take for example:
+
+# TODO: NEED A DECENT EXAMPLE!!!!
+
+This query would fail as the second, inner query would 'inherit' the conditions of the first one, thus causing pandemonia.
+In v3.0 this has been fixed by creating each query in its own scope, however this also means that some things won't work quite as expected. Take for example:
+
+ foreach(ORM::factory('user', 3)->where('post_date', '>', time() - (3600 * 24))->posts as $post)
+ {
+ echo $post->title;
+ }
+
+[!!] (See [the Database tutorial](tutorials.databases) for the new query syntax)
+
+In 2.3 you would expect this to return an iterator of all posts by user 3 where `post_date` was some time within the last 24 hours, however instead it'll apply the where condition to the user model and return a `Model_Post` with the joining conditions specified.
+
+To achieve the same effect as in 2.3 you need to rearrange the structure slightly:
+
+ foreach(ORM::factory('user', 3)->posts->where('post_date', '>', time() - (36000 * 24))->find_all() as $post)
+ {
+ echo $post->title;
+ }
+
+This also applies to `has_one` relationships:
+
+ // Incorrect
+ $user = ORM::factory('post', 42)->author;
+ // Correct
+ $user = ORM::factory('post', 42)->author->find();
+
+### Has and belongs to many relationships
+
+In 2.3 you could specify `has_and_belongs_to_many` relationships. In 3.0 this functionality has been refactored into `has_many` *through*.
+
+In your models you define a `has_many` relationship to the other model but then you add a `'through' => 'table'` attribute, where `'table'` is the name of your through table. For example (in the context of posts<>categories):
+
+ $_has_many = array
+ (
+ 'categories' => array
+ (
+ 'model' => 'category', // The foreign model
+ 'through' => 'post_categories' // The joining table
+ ),
+ );
+
+If you've set up kohana to use a table prefix then you don't need to worry about explicitly prefixing the table.
+
+### Foreign keys
+
+If you wanted to override a foreign key in 2.x's ORM you had to specify the relationship it belonged to, and your new foreign key in the member variable `$foreign_keys`.
+
+In 3.0 you now define a `foreign_key` key in the relationship's definition, like so:
+
+ Class Model_Post extends ORM
+ {
+ $_belongs_to = array
+ (
+ 'author' => array
+ (
+ 'model' => 'user',
+ 'foreign_key' => 'user_id',
+ ),
+ );
+ }
+
+In this example we should then have a `user_id` field in our posts table.
+
+
+
+In has_many relationships the `far_key` is the field in the through table which links it to the foreign table and the foreign key is the field in the through table which links "this" model's table to the through table.
+
+Consider the following setup, "Posts" have and belong to many "Categories" through `posts_sections`.
+
+| categories | posts_sections | posts |
+|------------|------------------|---------|
+| id | section_id | id |
+| name | post_id | title |
+| | | content |
+
+ Class Model_Post extends ORM
+ {
+ protected $_has_many = array(
+ 'sections' => array(
+ 'model' => 'category',
+ 'through' => 'posts_sections',
+ 'far_key' => 'section_id',
+ ),
+ );
+ }
+
+ Class Model_Category extends ORM
+ {
+ protected $_has_many = array (
+ 'posts' => array(
+ 'model' => 'post',
+ 'through' => 'posts_sections',
+ 'foreign_key' => 'section_id',
+ ),
+ );
+ }
+
+
+Obviously the aliasing setup here is a little crazy, but it's a good example of how the foreign/far key system works.
+
+### ORM Iterator
+
+It's also worth noting that `ORM_Iterator` has now been refactored into `Database_Result`.
+
+If you need to get an array of ORM objects with their keys as the object's pk, you need to call [Database_Result::as_array], e.g.
+
+ $objects = ORM::factory('user')->find_all()->as_array('id');
+
+Where `id` is the user table's primary key.
+
+## Router Library
+
+In version 2 there was a Router library that handled the main request. It let you define basic routes in a `config/routes.php` file and it would allow you to use custom regex for the routes, however it was fairly inflexible if you wanted to do something radical.
+
+## Routes
+
+The routing system (now refered to as the request system) is a lot more flexible in 3.0. Routes are now defined in the bootstrap file (`application/bootstrap.php`) and the module init.php (`modules/module_name/init.php`). It's also worth noting that routes are evaluated in the order that they are defined.
+
+Instead of defining an array of routes you now create a new [Route] object for each route. Unlike in the 2.x series there is no need to map one uri to another. Instead you specify a pattern for a uri, use variables to mark the segments (i.e. controller, method, id).
+
+For example, in 2.x these regexes:
+
+ $config['([a-z]+)/?(\d+)/?([a-z]*)'] = '$1/$3/$1';
+
+Would map the uri `controller/id/method` to `controller/method/id`. In 3.0 you'd use:
+
+ Route::set('reversed','(<controller>(/<id>(/<action>)))')
+ ->defaults(array('controller' => 'posts', 'action' => 'index'));
+
+[!!] Each uri should have be given a unique name (in this case it's `reversed`), the reasoning behind this is explained in [the url tutorial](tutorials.urls).
+
+Angled brackets denote dynamic sections that should be parsed into variables. Rounded brackets mark an optional section which is not required. If you wanted to only match uris beginning with admin you could use:
+
+ Rouse::set('admin', 'admin(/<controller>(/<id>(/<action>)))');
+
+And if you wanted to force the user to specify a controller:
+
+ Route::set('admin', 'admin/<controller>(/<id>(/<action>))');
+
+Also, Kohana does not use any 'default defaults'. If you want Kohana to assume your default action is 'index', then you have to tell it so! You can do this via [Route::defaults]. If you need to use custom regex for uri segments then pass an array of `segment => regex` i.e.:
+
+ Route::set('reversed', '(<controller>(/<id>(/<action>)))', array('id' => '[a-z_]+'))
+ ->defaults(array('controller' => 'posts', 'action' => 'index'))
+
+This would force the `id` value to consist of lowercase alpha characters and underscores.
+
+### Actions
+
+One more thing we need to mention is that methods in a controller that can be accessed via the url are now called "actions", and are prefixed with 'action_'. E.g. in the above example, if the user calls `admin/posts/1/edit` then the action is `edit` but the method called on the controller will be `action_edit`. See [the url tutorial](tutorials.urls) for more info.
+
+## Sessions
+
+There are no longer any Session::set_flash(), Session::keep_flash() or Session::expire_flash() methods, instead you must use [Session::get_once].
+
+## URL Helper
+
+Only a few things have changed with the url helper - `url::redirect()` has been moved into `$this->request->redirect()` within controllers) and `Request::instance()->redirect()` instead.
+
+`url::current` has now been replaced with `$this->request->uri()`
+
+## Valid / Validation
+
+These two classes have been merged into a single class called `Validate`.
+
+The syntax has also changed a little for validating arrays:
+
+ $validate = new Validate($_POST);
+
+ // Apply a filter to all items in the arrays
+ $validate->filter(TRUE, 'trim');
+
+ // To specify rules individually use rule()
+ $validate
+ ->rule('field', 'not_empty')
+ ->rule('field', 'matches', array('another_field'));
+
+ // To set multiple rules for a field use rules(), passing an array of rules => params as the second argument
+ $validate->rules('field', array(
+ 'not_empty' => NULL,
+ 'matches' => array('another_field')
+ ));
+
+The 'required' rule has also been renamed to 'not_empty' for clarity's sake.
+
+## View Library
+
+There have been a few minor changes to the View library which are worth noting.
+
+In 2.3 views were rendered within the scope of the controller, allowing you to use `$this` as a reference to the controller within the view, this has been changed in 3.0. Views now render in an empty scope. If you need to use `$this` in your view you can bind a reference to it using [View::bind]: `$view->bind('this', $this)`.
+
+It's worth noting, though, that this is *very* bad practice as it couples your view to the controller, preventing reuse. The recommended way is to pass the required variables to the view like so:
+
+ $view = View::factory('my/view');
+
+ $view->variable = $this->property;
+
+ // OR if you want to chain this
+
+ $view
+ ->set('variable', $this->property)
+ ->set('another_variable', 42);
+
+ // NOT Recommended
+ $view->bind('this', $this);
+
+Because the view is rendered in an empty scope `Controller::_kohana_load_view` is now redundant. If you need to modify the view before it's rendered (i.e. to add a generate a site-wide menu) you can use [Controller::after].
+
+ Class Controller_Hello extends Controller_Template
+ {
+ function after()
+ {
+ $this->template->menu = '...';
+
+ return parent::after();
+ }
+ }
View
4 guide/nl/debugging.md → guide/nl/debugging.code.md
@@ -2,7 +2,7 @@
Kohana includes several powerful tools to help you debug your application.
-The most basic of these is [Kohana::debug]. This simple method will display any number of variables, similar to [var_export] or [print_r], but using HTML for extra formatting.
+The most basic of these is [Kohana::debug]. This simple method will display any number of variables, similar to [var_export](http://php.net/var_export) or [print_r](http://php.net/print_r), but using HTML for extra formatting.
~~~
// Display a dump of the $foo and $bar variables
@@ -20,5 +20,5 @@ If you want to display information about your application files without exposing
~~~
// Displays "APPPATH/cache" rather than the real path
-echo Kohana::debug_file(APPPATH.'cache');
+echo Kohana::debug_path(APPPATH.'cache');
~~~
View
4 guide/nl/debugging.errors.md
@@ -4,7 +4,7 @@ Kohana provides both an exception handler and an error handler that transforms e
1. Exception class
2. Error level
-3. Errror message
+3. Error message
4. Source of the error, with the error line highlighted
5. A [debug backtrace](http://php.net/debug_backtrace) of the execution flow
6. Included files, loaded extensions, and global variables
@@ -21,4 +21,4 @@ If you do not want to use the internal error handling, you can disable it when c
~~~
Kohana::init(array('errors' => FALSE));
-~~~
+~~~
View
52 guide/nl/menu.md
@@ -1,21 +1,31 @@
-1. [Getting Started](start)
- * [Conventions and Style](start.conventions)
- * [Installation](start.installation)
- * [Configuration](start.configuration)
- * [Model View Controller](start.mvc)
- * [Filesystem](start.filesystem)
- * [Autoloading](start.autoloading)
- * [Request Flow](start.flow)
-2. [Tutorials](tutorials)
- * [Hello, World](tutorials.helloworld)
- * [Routes, URLs, and Links](tutorials.urls)
- * [Databases](tutorials.databases)
-3. [Security](security)
- * [XSS](security.xss)
- * [Validation](security.validation)
- * [Cookies](security.cookies)
- * [Database](security.database)
-4. [Debugging](debugging)
- * [Error Handling](debugging.errors)
- * [Profiling](debugging.profiling)
-5. [API Browser](api)
+1. **Ga van slag**
+ - [Wat is Kohana?](about.kohana)
+ - [Conventions and Style](about.conventions)
+ - [Model View Controller](about.mvc)
+ - [Cascading Filesystem](about.filesystem)
+ - [Request Flow](about.flow)
+ - [Installatie](about.install)
+ - [Upgrading](about.upgrading)
+ - [API Browser](api)
+3. **Basis gebruik**
+ - [Configuratie](using.configuration)
+ - [Laden van classes](using.autoloading)
+ - [Views en HTML](using.views)
+ - [Sessies en Cookies](using.sessions)
+ - [Berichten (Messages)](using.messages)
+4. **Debugging**
+ - [Code](debugging.code)
+ - [Error Handling](debugging.errors)
+ - [Profiling](debugging.profiling)
+5. **Security**
+ - [XSS](security.xss)
+ - [Validatie](security.validation)
+ - [Cookies](security.cookies)
+ - [Database](security.database)
+6. **Tutorials**
+ - [Hello, World](tutorials.helloworld)
+ - [Routes, URLs, en Links](tutorials.urls)
+ - [Clean URLs](tutorials.removeindex)
+ - [Databases](tutorials.databases)
+ - [ORM](tutorials.orm)
+ - [Werken met Git](tutorials.git)
View
3 guide/nl/security.md
@@ -1,3 +0,0 @@
-# Security
-
-[!!] stub
View
242 guide/nl/security.validation.md
@@ -1,3 +1,243 @@
# Validation
-[!!] stub
+Validation can be performed on any array using the [Validate] class. Labels, filters, rules, and callbacks can be attached to a Validate object by the array key, called a "field name".
+
+labels
+: A label is a human-readable version of the field name.
+
+filters
+: A filter modifies the value of an field before rules and callbacks are run.
+
+rules
+: A rule is a check on a field that returns `TRUE` or `FALSE`. If a rule
+ returns `FALSE`, an error will be added to the field.
+
+callbacks
+: A callback is custom method that can access the entire Validate object.
+ The return value of a callback is ignored. Instead, the callback must
+ manually add an error to the object using [Validate::error] on failure.
+
+[!!] Note that [Validate] callbacks and [PHP callbacks](http://php.net/manual/language.pseudo-types.php#language.types.callback) are not the same.
+
+Using `TRUE` as the field name when adding a filter, rule, or callback will by applied to all named fields.
+
+**The [Validate] object will remove all fields from the array that have not been specifically named by a label, filter, rule, or callback. This prevents access to fields that have not been validated as a security precaution.**
+
+Creating a validation object is done using the [Validate::factory] method:
+
+ $post = Validate::factory($_POST);
+
+[!!] The `$post` object will be used for the rest of this tutorial. This tutorial will show you how to validate the registration of a new user.
+
+### Default Rules
+
+Validation also comes with several default rules:
+
+Rule name | Function
+------------------------- |-------------------------------------------------
+[Validate::not_empty] | Value must be a non-empty value
+[Validate::regex] | Match the value against a regular expression
+[Validate::min_length] | Minimum number of characters for value
+[Validate::max_length] | Maximum number of characters for value
+[Validate::exact_length] | Value must be an exact number of characters
+[Validate::email] | An email address is required
+[Validate::email_domain] | Check that the domain of the email exists
+[Validate::url] | Value must be a URL
+[Validate::ip] | Value must be an IP address
+[Validate::phone] | Value must be a phone number
+[Validate::credit_card] | Require a credit card number
+[Validate::date] | Value must be a date (and time)
+[Validate::alpha] | Only alpha characters allowed
+[Validate::alpha_dash] | Only alpha and hyphens allowed
+[Validate::alpha_numeric] | Only alpha and numbers allowed
+[Validate::digit] | Value must be an integer digit
+[Validate::decimal] | Value must be a decimal or float value
+[Validate::numeric] | Only numeric characters allowed
+[Validate::range] | Value must be within a range
+[Validate::color] | Value must be a valid HEX color
+[Validate::matches] | Value matches another field value
+
+[!!] Any method that exists within the [Validate] class may be used as a validation rule without specifying a complete callback. For example, adding `'not_empty'` is the same as `array('Validate', 'not_empty')`.
+
+## Adding Filters
+
+All validation filters are defined as a field name, a method or function (using the [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) syntax), and an array of parameters:
+
+ $object->filter($field, $callback, $parameter);
+
+Filters modify the field value before it is checked using rules or callbacks.
+
+If we wanted to convert the "username" field to lowercase:
+
+ $post->filter('username', 'strtolower');
+
+If we wanted to remove all leading and trailing whitespace from *all* fields:
+
+ $post->filter(TRUE, 'trim');
+
+## Adding Rules
+
+All validation rules are defined as a field name, a method or function (using the [PHP callback](http://php.net/callback) syntax), and an array of parameters:
+
+ $object->rule($field, $callback, $parameter);
+
+To start our example, we will perform validation on a `$_POST` array that contains user registration information:
+
+Next we need to process the POST'ed information using [Validate]. To start, we need to add some rules:
+
+ $post
+ ->rule('username', 'not_empty')
+ ->rule('username', 'regex', array('/^[a-z_.]++$/iD'))
+
+ ->rule('password', 'not_empty')
+ ->rule('password', 'min_length', array('6'))
+ ->rule('confirm', 'matches', array('password'))
+
+ ->rule('use_ssl', 'not_empty');
+
+Any existing PHP function can also be used a rule. For instance, if we want to check if the user entered a proper value for the SSL question:
+
+ $post->rule('use_ssl', 'in_array', array(array('yes', 'no')));
+
+Note that all array parameters must still be wrapped in an array! Without the wrapping array, `in_array` would be called as `in_array($value, 'yes', 'no')`, which would result in a PHP error.
+
+Any custom rules can be added using a [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback]:
+
+ $post->rule('username', array($model, 'unique_username'));
+
+The method `$model->unique_username()` would be defined similar to:
+
+ public function unique_username($username)
+ {
+ // Check if the username already exists in the database
+ return ! DB::select(array(DB::expr('COUNT(username)'), 'total'))
+ ->from('users')
+ ->where('username', '=', $username)
+ ->execute()
+ ->get('total');
+ }
+
+[!!] Custom rules allow many additional checks to be reused for multiple purposes. These methods will almost always exist in a model, but may be defined in any class.
+
+## Adding callbacks
+
+All validation callbacks are defined as a field name and a method or function (using the [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) syntax):
+
+ $object->callback($field, $callback);
+
+[!!] Unlike filters and rules, no parameters can be passed to a callback. All additional callback
+
+The user password must be hashed if it validates, so we will hash it using a callback:
+
+ $post->callback('password', array($model, 'hash_password'));
+
+This would assume that the `$model->hash_password()` method would be defined similar to:
+
+ public function hash_password(Validate $array, $field)
+ {
+ if ($array[$field])
+ {
+ // Hash the password if it exists
+ $array[$field] = sha1($array[$field]);
+ }
+ }
+
+# A Complete Example
+
+First, we need a [View] that contains the HTML form, which will be placed in `application/views/user/register.php`:
+
+ <?php echo Form::open() ?>
+ <?php if ($errors): ?>
+ <p class="message">Some errors were encountered, please check the details you entered.</p>
+ <ul class="errors">
+ <?php foreach ($errors as $message): ?>
+ <li><?php echo $message ?></li>
+ <?php endforeach ?>
+ <?php endif ?>
+
+ <dl>
+ <dt><?php echo Form::label('username', 'Username') ?></dt>
+ <dd><?php echo Form::input('username', $post['username']) ?></dd>
+
+ <dt><?php echo Form::label('password', 'Password') ?></dt>
+ <dd><?php echo From::password('password') ?></dd>
+ <dd class="help">Passwords must be at least 6 characters long.</dd>
+ <dt><?php echo Form::label('confirm', 'Confirm Password') ?></dt>
+ <dd><?php echo Form::password('confirm') ?></dd>
+
+ <dt><?php echo Form::label('use_ssl', 'Use extra security?') ?></dt>
+ <dd><?php echo Form::select('use_ssl', array('yes' => 'Always', 'no' => 'Only when necessary'), $post['use_ssl']) ?></dd>
+ <dd class="help">For security, SSL is always used when making payments.</dd>
+ </dl>
+
+ <?php echo Form::submit(NULL, 'Sign Up') ?>
+ <?php echo Form::close() ?>
+
+[!!] This example uses the [Form] helper extensively. Using [Form] instead of writing HTML ensures that all of the form inputs will properly handle input that includes HTML characters. If you prefer to write the HTML yourself, be sure to use [HTML::chars] to escape user input.
+
+Next, we need a controller and action to process the registration, which will be placed in `application/classes/controller/user.php`:
+
+ class Controller_User extends Controller {
+
+ public function action_register()
+ {
+ $user = Model::factory('user');
+
+ $post = Validate::factory($_POST)
+ ->filter(TRUE, 'trim')
+
+ ->filter('username', 'strtolower')
+
+ ->rule('username', 'not_empty')
+ ->rule('username', 'regex', array('/^[a-z_.]++$/iD'))
+ ->rule('username', array($user, 'unique_username'))
+
+ ->rule('password', 'not_empty')
+ ->rule('password', 'min_length', array('6'))
+ ->rule('confirm', 'matches', array('password'))
+
+ ->rule('use_ssl', 'not_empty')
+ ->rule('use_ssl', 'in_array', array(array('yes', 'no')))
+
+ ->callback('password', array($user, 'hash_password'));
+
+ if ($post->check())
+ {
+ // Data has been validated, register the user
+ $user->register($post);
+
+ // Always redirect after a successful POST to prevent refresh warnings
+ URL::redirect('user/profile');
+ }
+
+ // Validation failed, collect the errors
+ $errors = $post->errors('user');
+
+ // Display the registration form
+ $this->request->response = View::factory('user/register')
+ ->bind('post', $post)
+ ->bind('errors', $errors);
+ }
+
+ }
+
+We will also need a user model, which will be placed in `application/classes/model/user.php`:
+
+ class Model_User extends Model {
+
+ public function register($array)
+ {
+ // Create a new user record in the database
+ $id = DB::insert(array_keys($array))
+ ->values($array)
+ ->execute();
+
+ // Save the new user id to a cookie
+ cookie::set('user', $id);
+
+ return $id;
+ }
+
+ }
+
+That is it, we have a complete user registration example that properly checks user input!
View
4 guide/nl/security.xss.md
@@ -1,10 +1,10 @@
# Cross-Site Scripting (XSS) Security
-The first step to preventing [XSS](http://wikipedia.org/wiki/Cross-Site_Scripting) attacks is knowing when you need to protect youself. XSS can only be triggered when it is displayed within HTML content, sometimes via a form input or being displayed from database results. Any global variable that contains client information can be tainted. This includes $_GET, $_POST, and $_COOKIE data.
+The first step to preventing [XSS](http://wikipedia.org/wiki/Cross-Site_Scripting) attacks is knowing when you need to protect yourself. XSS can only be triggered when it is displayed within HTML content, sometimes via a form input or being displayed from database results. Any global variable that contains client information can be tainted. This includes `$_GET`, `$_POST`, and `$_COOKIE` data.
## Prevention
-There are a few simple rules to follow to guard your application HTML against XSS. The first is to use the [Security::xss] method to clean any input data that comes from a global variable. If you do not want HTML in an variable, use [strip_tags](http://php.net/strip_tags) to remove all unwanted HTML tags from a value.
+There are a few simple rules to follow to guard your application HTML against XSS. The first is to use the [Security::xss] method to clean any input data that comes from a global variable. If you do not want HTML in a variable, use [strip_tags](http://php.net/strip_tags) to remove all unwanted HTML tags from a value.
[!!] If you allow users to submit HTML to your application, it is highly recommended to use an HTML cleaning tool such as [HTML Purifier](http://htmlpurifier.org/) or [HTML Tidy](http://php.net/tidy).
View
17 guide/nl/start.autoloading.md
@@ -1,17 +0,0 @@
-# Autoloading
-
-Kohana takes advantage of PHP [autoloading](http://php.net/manual/language.oop5.autoload.php). This removes the need to call [include](http://php.net/include) or [require](http://php.net/require) before using a class.
-
-Classes are loaded via the [Kohana::auto_load] method, which makes a simple conversion from class name to file name:
-
-1. Classes are placed in the `classes/` directory of the [filesystem](start.filesystem)
-2. Any underscore characters are converted to slashes
-2. The filename is lowercase
-
-When calling a class that has not been loaded (eg: `Session_Cookie`), Kohana will search the filesystem using [Kohana::find_file] for a file named `classes/session/cookie.php`.
-
-## Custom Autoloaders
-
-[!!] The default autoloader is enabled in `application/bootstrap.php`.
-
-Additional class loaders can be added using [spl_autoload_register](http://php.net/spl_autoload_register).
View
96 guide/nl/start.configuration.md
@@ -1,96 +0,0 @@
-# General Configuration
-
-[!!] todo, description of benefits of static properties for configuration
-
-## Core Configuration
-
-The first configuration task of any new Kohana installation is changing the [Kohana::init] settings in `application/bootstrap.php`. These settings are:
-
-`boolean` errors
-: Use internal error and exception handling? (Default `TRUE`) Set to `FALSE` to disable the Kohana
- error and exception handlers.
-
-`boolean` profile
-: Do internal benchmarking? (Default `TRUE`) Set to `FALSE` to disable internal profiling.
- Disable in production for best performance.
-
-`boolean` caching
-: Cache the location of files between requests? (Default `FALSE`) Set to `TRUE` to cache the
- absolute path of files. This dramatically speeds up [Kohana::find_file] and can sometimes
- have a dramatic impact on performance. Only enable in a production environment, or for testing.
-
-`string` charset
-: Character set used for all input and output. (Default `"utf-8"`) Should be a character set that is supported by both [htmlspecialchars](http://php.net/htmlspecialchars) and [iconv](http://php.net/iconv).
-
-`string` base_url
-: Base URL for the application. (Default `"/"`) Can be a complete or partial URL. For example "http://example.com/kohana/" or just "/kohana/" would both work.
-
-`string` index_file
-: The PHP file that starts the application. (Default `"index.php"`) Set to `FALSE` when you remove the index file from with URL rewriting.
-
-`string` cache_dir
-: Cache file directory. (Default `"application/cache"`) Must point to a **writable** directory.
-
-## Cookie Settings
-
-There are several static properties in the [Cookie] class that should be set, particularly on production websites.
-
-`string` salt
-: Unique salt string that is used to used to enable [signed cookies](security.cookies)
-
-`integer` expiration
-: Default expiration lifetime in seconds
-
-`string` path
-: URL path to restrict cookies to be accessed
-
-`string` domain
-: URL domain to restrict cookies to be accessed
-
-`boolean` secure
-: Only allow cookies to be accessed over HTTPS
-
-`boolean` httponly
-: Only allow cookies to be accessed over HTTP (also disables Javascript access)
-
-# Configuration Files
-
-Configuration is done in plain PHP files, which look similar to:
-
-~~~
-<?php defined('SYSPATH') or die('No direct script access.');
-
-return array(
- 'setting' => 'value',
- 'options' => array(
- 'foo' => 'bar',
- ),
-);
-~~~
-
-If the above configuration file was called `myconf.php`, you could acess it using:
-
-~~~
-$config = Kohana::config('myconf');
-$options = $config['options'];
-~~~
-
-[Kohana::config] also provides a shortcut for accessing individual keys from configuration arrays using "dot paths".
-
-Get the "options" array:
-
-~~~
-$options = Kohana::config('myconf.options');
-~~~
-
-Get the "foo" key from the "options" array:
-
-~~~
-$foo = Kohana::config('myconf.options.foo');
-~~~
-
-Configuration arrays can also be accessed as objects, if you prefer that method:
-
-~~~
-$options = Kohana::config('myconf')->options;
-~~~
View
26 guide/nl/start.conventions.md
@@ -1,26 +0,0 @@
-# Conventions
-
-## Class Names and File Location
-
-Class names in Kohana follow a strict convention to facilitate [autoloading](start.autoloading).
-
-Class names should have uppercase first letters with underscores to separate words. Underscores are significant as they directly reflect the file location in the filesystem.
-
- Class File
-
- Controller_Template classes/controller/template.php
- Model_User classes/model/user.php
- Model_Auth_User classes/model/auth/user.php
- Auth classes/auth.php
-
-CamelCased classnames should not be used.
-
-All class file names and directory names are lowercase.
-
-All classes should be in the `classes` directory. This may be at any level in the [cascading filesystem](start.filesystem).
-
-Kohana 3 does not differentioate between *types* of class in the same way that Kohana 2.x and some other frameworks do. There is no distinction between a 'helper' or a 'library' class - in Kohana 3 any class can implement whater interface it like whether it be entirely static (helper-like), or instantiable, or a mixture (e.g. singleton).
-
-## Code Style
-
-It is encouraged to follow Kohana's coding style. This uses [BSD/Allman style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) bracing. (There is a more [thorough discription](http://dev.kohanaphp.com/wiki/kohana2/CodingStyle) of Kohana's prefered style)
View
13 guide/nl/start.filesystem.md
@@ -1,13 +0,0 @@
-# Cascading Filesystem
-
-The Kohana filesystem is made up of a single directory structure that is mirrored in all directories along what we call the include path, which goes as follows:
-
-1. application
-2. modules, in order added
-3. system
-
-Files that are in directories higher up the include path order take precedence over files of the same name lower down the order, which makes it is possible to overload any file by placing a file with the same name in a "higher" directory:
-
-![Cascading Filesystem Infographic](img/cascading_filesystem.png)
-
-If you have a view file called layout.php in the application/views and system/views directories, the one in application will be returned when layout.php is searched for as it is highest in the include path order. If you then delete that file from application/views, the one in system/views will be returned when searched for.
View
17 guide/nl/start.flow.md
@@ -1,17 +0,0 @@
-# Request Flow
-
-Every application follows the same flow:
-
-1. Application starts from `index.php`
-2. Includes `APPPATH/bootstrap.php`
-3. [Request::instance] called to process the request
- 1. Checks each route until a match is found
- 2. Loads controller and passes the request to it
- 3. Calls the [Controller::before] method
- 4. Calls the controller action
- 5. Calls the [Controller::after] method
-4. Displays the [Request] response
-
-The controller action can be changed by [Controller::before] based on the request parameters.
-
-[!!] Stub
View
12 guide/nl/start.md
@@ -1,12 +0,0 @@
-# What is Kohana?
-
-Kohana is an open source, [object oriented](http://wikipedia.org/wiki/Object-Oriented_Programming) [MVC](http://wikipedia.org/wiki/Model–View–Controller "Model View Controller") [web framework](http://wikipedia.org/wiki/Web_Framework) built using [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") by a team of volunteers that aims to be swift, secure, and small.
-
-[!!] Kohana is licensed under a [BSD license](http://kohanaphp.com/license), so you can legally use it for any kind of open source, commercial, or personal project.
-
-## What makes Kohana great?
-
-Anything can be extended using the unique [filesystem](start.filesystem) design, little or no [configuration](start.configuration) is necessary, [error handling](debugging.errors) helps locate the source of errors quickly, and [debugging](debugging.overview) and [profiling](debugging.profiling) provide insight into the application.
-
-To help secure your applications, tools for [XSS removal](security.xss), [input validation](security.validation), [signed cookies](security.cookies), [form](security.forms) and [HTML](security.html) generators are all included. The [database](security.database) layer provides protection against [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Of course, all official code is carefully written and reviewed for security.
-
View
3 guide/nl/start.mvc.md
@@ -1,3 +0,0 @@
-# Model View Controller
-
-[!!] Stub
View
249 guide/nl/tutorials.databases.md
@@ -1,3 +1,248 @@
-# Databases
+# Databases {#top}
-[!!] stub
+Kohana 3.0 comes with a robust module to working with databases. By default the database module supports drivers for [MySQL](http://php.net/mysql) and [PDO](http://php.net/pdo).
+
+The database module is included with the Kohana 3.0 install but needs to be enabled before you can use it. In your `application/bootstrap.php` file modify the call to [Kohana::modules] and include the database module:
+
+ Kohana::modules(array(
+ ...
+ 'database' => MODPATH.'database',
+ ...
+ ));
+
+## Configuration {#configuration}
+
+After the module has been enabled you will need to provide a configuration file so that the module knows how to connect to your database. An example config file can be found at `modules/database/config/database.php`.
+
+The structure of a database configuration group, called an "instance", looks like this:
+
+ string INSTANCE_NAME => array(
+ 'type' => string DATABASE_TYPE,
+ 'connection' => array CONNECTION_ARRAY,
+ 'table_prefix' => string TABLE_PREFIX,
+ 'charset' => string CHARACTER_SET,
+ 'profiling' => boolean QUERY_PROFILING,
+ ),
+
+[!!] Multiple instances of these settings can be defined within the configuration file.
+
+Understanding each of these settings is important.
+
+INSTANCE_NAME
+: Connections can be named anything you want, but you should always have at least one connection called "default".
+
+DATABASE_TYPE
+: One of the installed database drivers. Kohana comes with "mysql" and "pdo" drivers.
+
+CONNECTION_ARRAY
+: Specific driver options for connecting to your database. (Driver options are explained [below](#connection_settings).)
+
+TABLE_PREFIX
+: Prefix that will be added to all table names by the [query builder](#query_building).
+
+QUERY_PROFILING
+: Enables [profiling](debugging.profiling) of database queries.
+
+### Example
+
+The example file below shows 2 MySQL connections, one local and one remote.
+
+ return array
+ (
+ 'default' => array
+ (
+ 'type' => 'mysql',
+ 'connection' => array(
+ 'hostname' => 'localhost',
+ 'username' => 'dbuser',
+ 'password' => 'mypassword',
+ 'persistent' => FALSE,
+ 'database' => 'my_db_name',
+ ),
+ 'table_prefix' => '',
+ 'charset' => 'utf8',
+ 'profiling' => TRUE,
+ ),
+ 'remote' => array(
+ 'type' => 'mysql',
+ 'connection' => array(
+ 'hostname' => '55.55.55.55',
+ 'username' => 'remote_user',
+ 'password' => 'mypassword',
+ 'persistent' => FALSE,
+ 'database' => 'my_remote_db_name',
+ ),
+ 'table_prefix' => '',
+ 'charset' => 'utf8',
+ 'profiling' => TRUE,
+ ),
+ );
+
+### Connection Settings {#connection_settings}
+
+Every database driver has different connection settings.
+
+#### MySQL
+
+A MySQL database can accept the following options in the `connection` array:
+
+Type | Option | Description | Default value
+----------|------------|----------------------------| -------------------------
+`string` | hostname | Hostname of the database | `localhost`
+`integer` | port | Port number | `NULL`
+`string` | socket | UNIX socket | `NULL`
+`string` | username | Database username | `NULL`
+`string` | password | Database password | `NULL`
+`boolean` | persistent | Persistent connections | `FALSE`
+`string` | database | Database name | `kohana`
+
+#### PDO
+
+A PDO database can accept these options in the `connection` array:
+
+Type | Option | Description | Default value
+----------|------------|----------------------------| -------------------------
+`string` | dsn | PDO data source identifier | `localhost`
+`string` | username | Database username | `NULL`
+`string` | password | Database password | `NULL`
+`boolean` | persistent | Persistent connections | `FALSE`
+
+[!!] If you are using PDO and are not sure what to use for the `dsn` option, review [PDO::__construct](http://php.net/pdo.construct).
+
+## Connections and Instances {#connections}
+
+Each configuration group is referred to as a database instance. Each instance can be accessed by calling [Database::instance]:
+
+ $default = Database::instance();
+ $remote = Database::instance('remote');
+
+To disconnect the database, simply destroy the object:
+
+ unset($default, Database::$instances['default']);
+
+If you want to disconnect all of the database instances at once:
+
+ Database::$instances = array();
+
+## Making Queries {#making_queries}
+
+There are two different ways to make queries. The simplest way to make a query is to use the [Database_Query], via [DB::query], to create queries. These queries are called "prepared statements" and allow you to set query parameters which are automatically escaped. The second way to make a query is by building the query using method calls. This is done using the [query builder](#query_building).
+
+[!!] All queries are run using the `execute` method, which accepts a [Database] object or instance name. See [Database_Query::execute] for more information.
+
+### Prepared Statements
+
+Using prepared statements allows you to write SQL queries manually while still escaping the query values automatically to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Creating a query is simple:
+
+ $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user');
+
+The [DB::query] factory method creates a new [Database_Query] class for us, to allow method chaining. The query contains a `:user` parameter, which we can assign to a value:
+
+ $query->param(':user', 'john');
+
+[!!] Parameter names can be any string, as they are replaced using [strtr](http://php.net/strtr). It is highly recommended to **not** use dollars signs as parameter names to prevent confusion.
+
+If you want to display the SQL that will be executed, simply cast the object to a string:
+
+ echo Kohana::debug((string) $query);
+ // Should display:
+ // SELECT * FROM users WHERE username = 'john'
+
+You can also update the `:user` parameter by calling [Database_Query::param] again:
+
+ $query->param(':user', $_GET['search']);
+
+[!!] If you want to set multiple parameters at once, you can use [Database_Query::parameters].
+
+Once you have assigned something to each of the parameters, you can execute the query:
+
+ $query->execute();
+
+It is also possible to bind a parameter to a variable, using a [variable reference]((http://php.net/language.references.whatdo)). This can be extremely useful when running the same query many times:
+
+ $query = DB::query(Database::INSERT, 'INSERT INTO users (username, password) VALUES (:user, :pass)')
+ ->bind(':user', $username)
+ ->bind(':pass', $password);
+
+ foreach ($new_users as $username => $password)
+ {
+ $query->execute();
+ }
+
+In the above example, the variables `$username` and `$password` are changed for every loop of the `foreach` statement. When the parameter changes, it effectively changes the `:user` and `:pass` query parameters. Careful parameter binding can save a lot of code when it is used properly.
+
+### Query Building {#query_building}
+
+Creating queries dynamically using objects and methods allows queries to be written very quickly in an agnostic way. Query building also adds identifier (table and column name) quoting, as well as value quoting.
+
+[!!] At this time, it is not possible to combine query building with prepared statements.
+
+#### SELECT
+
+Each type of database query is represented by a different class, each with their own methods. For instance, to create a SELECT query, we use [DB::select]:
+
+ $query = DB::select()->from('users')->where('username', '=', 'john');
+
+By default, [DB::select] will select all columns (`SELECT * ...`), but you can also specify which columns you want returned:
+
+ $query = DB::select('username', 'password')->from('users')->where('username', '=', 'john');
+
+Now take a minute to look at what this method chain is doing. First, we create a new selection object using the [DB::select] method. Next, we set table(s) using the `from` method. Last, we search for a specific records using the `where` method. We can display the SQL that will be executed by casting the query to a string:
+
+ echo Kohana::debug((string) $query);
+ // Should display:
+ // SELECT `username`, `password` FROM `users` WHERE `username` = 'john'
+
+Notice how the column and table names are automatically escaped, as well as the values? This is one of the key benefits of using the query builder.
+
+It is also possible to create `AS` aliases when selecting:
+
+ $query = DB::select(array('username', 'u'), array('password', 'p'))->from('users');
+
+This query would generate the following SQL:
+
+ SELECT `username` AS `u`, `password` AS `p` FROM `users`
+
+#### INSERT
+
+To create records into the database, use [DB::insert] to create an INSERT query:
+
+ $query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd'));
+
+This query would generate the following SQL:
+
+ INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd')
+
+#### UPDATE
+
+To modify an existing record, use [DB::update] to create an UPDATE query:
+
+ $query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john');
+
+This query would generate the following SQL:
+
+ UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john'
+
+#### DELETE
+
+To remove an existing record, use [DB::delete] to create a DELETE query:
+
+ $query = DB::delete('users')->where('username', 'IN', array('john', 'jane'));
+
+This query would generate the following SQL:
+
+ DELETE FROM `users` WHERE `username` IN ('john', 'jane')
+
+#### Database Functions {#database_functions}
+
+Eventually you will probably run into a situation where you need to call `COUNT` or some other database function within your query. The query builder supports these functions in two ways. The first is using by using quotes within aliases:
+
+ $query = DB::select(array('COUNT("username")', 'total_users'))->from('users');
+
+This looks almost exactly the same as a standard `AS` alias, but note how the column name is wrapped in double quotes. Any time a double-quoted value appears inside of a column name, **only** the part inside the double quotes will be escaped. This query would generate the following SQL:
+
+ SELECT COUNT(`username`) AS `total_users` FROM `users`
+
+#### Complex Expressions
+
+Quoted aliases will solve most problems, but from time to time you may run into a situation where you need a complex expression. In these cases, you will need to use a database expression created with [DB::expr]. A database expression is taken as direct input and no escaping is performed.
View
143 guide/nl/tutorials.git.md
@@ -0,0 +1,143 @@
+# Working With Git
+
+Kohana uses [git](http://git-scm.com/) for version control and [github](http://github.com/kohana) for collaboration. This tutorial will show you how to use git and github to build a simple application.
+
+## Installing and setting up Git on your machine
+
+### Installing Git
+
+- OSX: [Git-OSX](http://code.google.com/p/git-osx-installer/)
+- Windows: [Msygit](http://code.google.com/p/msysgit/)
+- Or download it from [git-site](http://git-scm.com/) and install it manually (see git website)
+
+### Basic global settings
+
+ git config --global user.name "Your Name"
+ git config --global user.email "youremail@website.com"
+
+### Additional but preferable settings
+
+To have a better visualisation of the git commandos and repositories in your command-line, you can set these:
+
+ git config --global color.diff auto
+ git config --global color.status auto
+ git config --global color.branch auto
+
+### Setting auto-completion
+
+[!!] These lines are only to use on an OSX machine
+
+These lines will do all the dirty work for you, so auto-completion can work for your git-environment
+
+ cd /tmp
+ git clone git://git.kernel.org/pub/scm/git/git.git
+ cd git
+ git checkout v`git --version | awk '{print $3}'`
+ cp contrib/completion/git-completion.bash ~/.git-completion.bash
+ cd ~
+ rm -rf /tmp/git
+ echo -e "source ~/.git-completion.bash" >> .profile
+
+### Always use LF line endings
+
+This is the convention that we make for Kohana. Please set this settings for your own good and especially if you want to contribute to the Kohana community.
+
+ git config --global core.autocrlf input
+ git config --global core.savecrlf true
+
+[!!] More information about line endings at [github](http://help.github.com/dealing-with-lineendings/)
+
+### More information to get you on the track
+
+- [Git Screencasts](http://www.gitcasts.com/)
+- [Git Reference](http://gitref.org/)
+- [Pro Git book](http://progit.org/book/)
+
+## Initial Structure
+
+[!!] This tutorial will assume that your web server is already set up, and you are going to create a new application at <http://localhost/gitorial/>.
+
+Using your console, change to the empty directory `gitorial` and run `git init`. This will create the bare structure for a new git repository.
+
+Next, we will create a [submodule](http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html) for the `system` directory. Go to <http://github.com/kohana/core> and copy the "Clone URL":
+
+![Github Clone URL](http://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)
+
+Now use the URL to create the submodule for `system`:
+
+ git submodule add git://github.com/kohana/core.git system
+
+[!!] This will create a link to the current development version of the next stable release. The development version should almost always be safe to use, have the same API as the current stable download with bugfixes applied.
+
+Now add whatever submodules you need. For example if you need the [Database] module:
+
+ git submodule add git://github.com/kohana/database.git modules/database
+
+After submodules are added, they must be initialized:
+
+ git submodule init
+
+Now that the submodules are added, you can commit them:
+
+ git commit -m 'Added initial submodules'
+
+Next, create the application directory structure. This is the bare minimum required:
+
+ mkdir -p application/classes/{controller,model}
+ mkdir -p application/{config,views}
+ mkdir -m 0777 -p application/{cache,logs}
+
+If you run `find application` you should see this:
+
+ application
+ application/cache
+ application/config
+ application/classes
+ application/classes/controller
+ application/classes/model
+ application/logs
+ application/views
+
+We don't want git to track log or cache files, so add a `.gitignore` file to each of the directories. This will ignore all non-hidden files:
+
+ echo '[^.]*' > application/{logs,cache}/.gitignore
+
+[!!] Git ignores empty directories, so adding a `.gitignore` file also makes sure that git will track the directory, but not the files within it.
+
+Now we need the `index.php` and `bootstrap.php` files:
+
+ wget http://github.com/kohana/kohana/raw/master/index.php
+ wget http://github.com/kohana/kohana/raw/master/application/bootstrap.php -O application/bootstrap.php
+
+Commit these changes too:
+
+ git add application
+ git commit -m 'Added initial directory structure'
+
+That's all there is to it. You now have an application that is using Git for versioning.
+
+## Updating Submodules
+
+At some point you will probably also want to upgrade your submodules. To update all of your submodules to the latest `HEAD` version:
+
+ git submodule foreach 'git checkout master && git pull origin master'
+
+To update a single submodule, for example, `system`:
+
+ cd system
+ git checkout master
+ git pull origin master
+ cd ..
+ git add system
+ git commit -m 'Updated system to latest version'
+
+If you want to update a single submodule to a specific revision:
+
+ cd modules/database
+ git pull origin master
+ git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b
+ cd ../..
+ git add database
+ git commit -m 'Updated database module'
+
+All done!
View
105 guide/nl/tutorials.helloworld.md
@@ -1,3 +1,106 @@
# Hello, World
-[!!] stub
+Just about every framework ever written has some kind of hello world example included, so it'd be pretty rude of us to break this tradition!
+
+We'll start out by creating a very very basic hello world, and then we'll expand it to follow MVC principles.
+
+## Bare bones
+
+First off we have to make a controller that Kohana can use to handle a request.
+
+Create the file `application/classes/controller/hello.php` in your application folder and fill it out like so:
+
+ <?php defined('SYSPATH') OR die('No Direct Script Access');
+
+ Class Controller_Hello extends Controller
+ {
+ function action_index()
+ {
+ echo 'hello, world!';
+ }
+ }
+
+Lets see what's going on here:
+
+`<?php defined('SYSPATH') OR die('No Direct Script Access');`
+: You should recognise the first tag as an opening php tag (if you don't you should probably [learn php](http://php.net)). What follows is a small check that makes sure that this file is being included by Kohana. It stops people from accessing files directly from the url.
+
+`Class Controller_Hello extends Controller`
+: This line declares our controller, each controller class has to be prefixed with `Controller_` and an underscore delimited path to the folder the controller is in (see [Conventions and styles](about.conventions) for more info). Each controller should also extend the base `Controller` class which provides a standard structure for controllers.
+
+
+`function action_index()`
+: This defines the "index" action of our controller. Kohana will attempt to call this action if the user hasn't specified an action. (See [Routes, URLs and Links](tutorials.urls))
+
+`echo 'hello, world!';`
+: And this is the line which outputs the customary phrase!
+
+Now if you open your browser and go to http://localhost/index.php/hello you should see something like:
+
+![Hello, World!](img/hello_world_1.png "Hello, World!")
+
+## That was good, but we can do better
+
+What we did in the previous section was a good example of how easy it to create an *extremely* basic Kohana app. (In fact it's so basic, that you should never make it again!)
+
+If you've ever heard anything about MVC you'll probably have realised that echoing content out in a controller is strictly against the principles of MVC.
+
+The proper way to code with an MVC framework is to use _views_ to handle the presentation of your application, and allow the controller to do what it does best – control the flow of the request!
+
+Lets change our original controller slightly:
+
+ <?php defined('SYSPATH') OR die('No Direct Script Access');
+
+ Class Controller_Hello extends Controller_Template
+ {
+ public $template = 'site';
+
+ function action_index()
+ {
+ $this->template->message = 'hello, world!';
+ }
+ }
+
+`extends Controller_Template`
+: We're now extending the template controller, it makes it more convenient to use views within our controller.
+
+`public $template = 'site';`
+: The template controller needs to know what template you want to use. It'll automatically load the view defined in this variable and assign the view object to it.
+
+`$this->template->message = 'hello, world!';`
+: `$this->template` is a reference to the view object for our site template. What we're doing here is assigning a variable called "message", with a value of "hello, world!" to the view.
+
+Now lets try running our code...
+
+<div>{{userguide/examples/hello_world_error}}</div>
+
+For some reason Kohana's thrown a wobbly and isn't showing our amazing message.
+
+If we look at the error message we can see that the View library wasn't able to find our site template, probably because we haven't made it yet – *doh*!
+
+Let's go and make the view file `application/views/site.php` for our message:
+
+ <html>
+ <head>
+ <title>We've got a message for you!</title>
+ <style type="text/css">
+ body {font-family: Georgia;}
+ h1 {font-style: italic;}
+
+ </style>
+ </head>
+ <body>
+ <h1><?php echo $message; ?></h1>
+ <p>We just wanted to say it! :)</p>
+ </body>
+ </html>
+
+If we refresh the page then we can see the fruits of our labour:
+
+![hello, world! We just wanted to say it!](img/hello_world_2.png "hello, world! We just wanted to say it!")
+
+## Stage 3 – Profit!
+
+In this tutorial you've learnt how to create a controller and use a view to separate your logic from your display.
+
+This is obviously a very basic introduction to working with Kohana and doesn't even scrape the potential you have when developing applications with it.
View
7 guide/nl/tutorials.md
@@ -1,7 +0,0 @@
-# Tutorials
-
-[!!] stub
-
-- [Hello, World](tutorials.helloworld)
-- [Routes, URLs, and Links](tutorials.urls)
-- [Databases](tutorials.databases)
View
298 guide/nl/tutorials.orm.md
@@ -0,0 +1,298 @@
+# ORM {#top}
+
+Kohana 3.0 includes a powerful ORM module that uses the active record pattern and database introspection to determine a model's column information.
+
+The ORM module is included with the Kohana 3.0 install but needs to be enabled before you can use it. In your `application/bootstrap.php` file modify the call to [Kohana::modules] and include the ORM module:
+
+ Kohana::modules(array(
+ ...
+ 'orm' => MODPATH.'orm',
+ ...
+ ));
+
+## Configuration {#configuration}
+
+ORM requires little configuration to get started. Extend your model classes with ORM to begin using the module:
+
+ class Model_User extends ORM
+ {
+ ...
+ }
+
+In the example above, the model will look for a `users` table in the default database.
+
+### Model Configuration Properties
+
+The following properties are used to configure each model:
+
+Type | Option | Description | Default value
+----------|---------------------|----------------------------------| -------------------------
+`string` | _table_name | Table name to use | `singular model name`
+`string` | _db | Name of the database to use | `default`
+`string` | _primary_key | Column to use as primary key | `id`
+`string` | _primary_val | Column to use as primary value | `name`
+`bool` | _table_names_plural | Whether tables names are plural | `TRUE`
+`array` | _sorting | Array of column => direction | `primary key => ASC`
+`string` | _foreign_key_suffix | Suffix to use for foreign keys | `_id`
+
+## Using ORM
+
+### Loading a Record
+
+To create an instance of a model, you can use the [ORM::factory] method or the [ORM::__construct]:
+
+ $user = ORM::factory('user');
+ // or
+ $user = new Model_User();
+
+The constructor and factory methods also accept a primary key value to load the given model data:
+
+ // Load user ID 5
+ $user = ORM::factory('user', 5);
+
+ // See if the user was loaded successfully
+ if ($user->loaded()) { ... }
+
+You can optionally pass an array of key => value pairs to load a data object matching the given criteria:
+
+ // Load user with email joe@example.com
+ $user = ORM::factory('user', array('email' => 'joe@example.com'));
+
+### Searching for a Record or Records
+
+ORM supports most of the [Database] methods for powerful searching of your model's data. See the `_db_methods` property for a full list of supported method calls. Records are retrieved using the [ORM::find] and [ORM::find_all] method calls.
+
+ // This will grab the first active user with the name Bob
+ $user = ORM::factory('user')
+ ->where('active', '=', TRUE)
+ ->where('name', '=', 'Bob')
+ ->find();
+
+ // This will grab all users with the name Bob
+ $users = ORM::factory('user')
+ ...
+ ->find_all();
+
+When you are retrieving a list of models using [ORM::find_all], you can iterate through them as you do with database results:
+
+ foreach ($users as $user)
+ {
+ ...
+ }
+
+A powerful feature of ORM is the [ORM::as_array] method which will return the given record as an array. If used with [ORM::find_all], an array of all records will be returned. A good example of when this is useful is for a select list:
+
+ // Display a select field of usernames (using the id as values)
+ form::select('user', ORM::factory('user')->find_all()->as_array('id', 'username') ...
+
+### Counting Records
+
+Use [ORM::count_all] to return the number of records for a given query.
+
+ // Number of users
+ $count = ORM::factory('user')->where('active', '=', TRUE)->count_all();
+
+If you wish to count the total number of users for a given query, while only returning a certain subset of these users, call the [ORM::reset] method with `FALSE` before using `count_all`:
+
+ $user = ORM::factory('user');
+
+ // Total number of users (reset FALSE prevents the query object from being cleared)
+ $count = $user->where('active', '=', TRUE)->reset(FALSE)->count_all();
+
+ // Return only the first 10 of these results
+ $users = $user->limit(10)->find_all();
+
+### Accessing Model Properties
+
+All model properties are accessible using the `__get` and `__set` magic methods.
+
+ $user = ORM::factory('user', 5);
+
+ // Output user name
+ echo $user->name;
+
+ // Change user name
+ $user->name = 'Bob';
+
+To store information/properties that don't exist in the model's table, you can use the `_ignored_columns` data member. Data will be stored in the internal `_object` member, but ignored at the database level.
+
+ class Model_User extends ORM
+ {
+ ...
+ protected $_ignored_columns = array('field1', 'field2', ...)
+ ...
+ }
+
+Multiple key => value pairs can be set by using the [ORM::values] method
+
+ $user->values(array('username' => 'Joe', 'password' => 'bob'));
+
+### Creating and Saving Records
+
+The [ORM::save] method is used to both create new records and update existing records.
+
+ // Creating a record
+ $user = ORM::factory('user');
+ $user->name = 'New user';
+ $user->save();
+
+ // Updating a record
+ $user = ORM::factory('user', 5);
+ $user->name = 'User 2';
+ $user->save();
+
+ // Check to see if the record has been saved
+ if ($user->saved()) { ... }
+
+You can update multiple records by using the [ORM::save_all] method:
+
+ $user = ORM::factory('user');
+ $user->name = 'Bob';
+
+ // Change all active records to name 'Bob'
+ $user->where('active', '=', TRUE)->save_all();
+
+#### Using `Updated` and `Created` Columns
+
+The `_updated_column` and `_created_column` members are provided to automatically be updated when a model is updated and created. These are not used by default. To use them:
+
+ // date_created is the column used for storing the creation date. Use TRUE to store a timestamp
+ protected $_created_column = array('date_created' => TRUE);
+
+ // date_modified is the column used for storing the modified date. In this case, a string specifying a date() format is used
+ protected $_updated_column = array('date_modified' => 'm/d/Y');
+
+### Deleting Records
+
+Records are deleted with [ORM::delete] and [ORM::delete_all]. These methods operate in the same fashion as saving described above with the exception that [ORM::delete] takes one optional parameter, the `id` of the record to delete. Otherwise, the currently loaded record is deleted.
+
+### Relationships
+
+ORM provides for powerful relationship support. Ruby has a great tutorial on relationships at [http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html)
+
+#### Belongs-To and Has-Many
+
+We'll assume we're working with a school that has many students. Each student can belong to only one school. You would define the relationships in this manner:
+
+ // Inside the school model
+ protected $_has_many = array('students' => array());
+
+ // Inside the student model
+ protected $_belongs_to = array('school' => array());
+
+To access a student's school you use:
+
+ $school = $student->school;
+
+To access a school's students, you would use:
+
+ // Note that find_all is required after students
+ $students = $school->students->find_all();
+
+ // To narrow results:
+ $students = $school->students->where('active', '=', TRUE)->find_all();
+
+By default, ORM will look for a `school_id` model in the student table. This can be overriden by using the `foreign_key` attribute:
+
+ protected $_belongs_to = array('school' => array('foreign_key' => 'schoolID'));
+
+The foreign key should be overridden in both the student and school models.
+
+#### Has-One
+
+Has-One is a special case of Has-Many, the only difference being that there is one and only one record. In the above example, each school would have one and only one student (although this is a poor example).
+
+ // Inside the school model
+ protected $_has_one = array('student' => array());
+
+Like Belongs-To, you do not need to use the `find` method when referencing the Has-One related object - it is done automatically.
+
+#### Has-Many "Through"
+
+The Has-Many "through" relationship (also known as Has-And-Belongs-To-Many) is used in the case of one object being related to multiple objects of another type, and visa-versa. For instance, a student may have multiple classes and a class may have multiple students. In this case, a third table and model known as a `pivot` is used. In this case, we will call the pivot object/model `enrollment`.
+
+ // Inside the student model
+ protected $_has_many = array('classes' => array('through' => 'enrollment'));
+
+ // Inside the class model
+ protected $_has_many = array('students' => array('through' => 'enrollment'));
+
+The enrollment table should contain two foreign keys, one for `class_id` and the other for `student_id`. These can be overriden using `foreign_key` and `far_key` when defining the relationship. For example:
+
+ // Inside the student model (the foreign key refers to this model [student], while the far key refers to the other model [class])
+ protected $_has_many = array('classes' => array('through' => 'enrollment', 'foreign_key' => 'studentID', 'far_key' => 'classID'));
+
+ // Inside the class model
+ protected $_has_many = array('students' => array('through' => 'enrollment', 'foreign_key' => 'classID', 'far_key' => 'studentID'));
+
+The enrollment model should be defined as such:
+
+ // Enrollment model belongs to both a student and a class
+ protected $_belongs_to = array('student' => array(), 'class' => array());
+
+To access the related objects, use:
+
+ // To access classes from a student
+ $student->classes->find_all();
+
+ // To access students from a class
+ $class->students->find_all();
+
+### Validation
+
+ORM is integrated tightly with the [Validate] library. The ORM provides the following members for validation
+
+* _rules
+* _callbacks
+* _filters
+* _labels
+
+#### `_rules`
+
+ protected $_rules = array
+ (
+ 'username' => array('not_empty' => array()),
+ 'email' => array('not_empty' => array(), 'email' => array()),
+ );
+
+`username` will be checked to make sure it's not empty. `email` will be checked to also ensure it is a valid email address. The empty arrays passed as values can be used to provide optional additional parameters to these validate method calls.
+
+#### `_callbacks`
+
+ protected $_callbacks = array
+ (
+ 'username' => array('username_unique'),
+ );
+
+`username` will be passed to a callback method `username_unique`. If the method exists in the current model, it will be used, otherwise a global function will be called. Here is an example of the definition of this method:
+
+ public function username_unique(Validate $data, $field)
+ {
+ // Logic to make sure a username is unique
+ ...
+ }
+
+#### `_filters`
+
+ protected $_filters = array
+ (
+ TRUE => array('trim' => array()),
+ 'username' => array('stripslashes' => array()),
+ );
+
+`TRUE` indicates that the `trim` filter is to be used on all fields. `username` will be filtered through `stripslashes` before it is validated. The empty arrays passed as values can be used to provide additional parameters to these filter method calls.
+
+#### Checking if the Object is Valid
+
+Use [ORM::check] to see if the object is currently valid.
+
+ // Setting an object's values, then checking to see if it's valid
+ if ($user->values($_POST)->check())
+ {
+ $user->save();
+ }
+
+You can use the `validate()` method to access the model's validation object
+
+ // Add an additional filter manually
+ $user->validate()->filter('username', 'trim');
View
88 guide/nl/tutorials.removeindex.md
@@ -0,0 +1,88 @@
+# Removing `index.php` From the URL
+
+To keep your URLs clean, you will probably want to be able to access your app without having `/index.php/` in the URL. There are two steps to remove `index.php` from the URL.
+
+1. Edit the bootstrap file
+2. Set up rewriting
+
+# Configure Bootstrap
+
+The first thing you will need to change is the `index_file` setting of [Kohana::init]:
+
+ Kohana::init(array(
+ 'base_url' => '/myapp/',
+ 'index_file' => FALSE,
+ ));
+
+Now all of the links generated using [URL::site], [URL::base], and [HTML::anchor] will no longer include "index.php" in the URL. All generated links will start with `/myapp/` instead of `/myapp/index.php/`.
+
+# URL Rewriting
+
+Enabling rewriting is done differently, depending on your web server.
+
+## Apache
+
+Rename `example.htaccess` to only `.htaccess` and alter the following line of code:
+
+ RewriteBase /kohana/
+
+This needs to match the `base_url` setting of [Kohana::init]:
+
+ RewriteBase /myapp/
+
+In most cases, this is all you will need to change.
+
+### Failed!
+
+If you get a "Internal Server Error" or "No input file specified" error, try changing:
+
+ RewriteRule ^(?:application|modules|system)\b - [F,L]
+
+Instead, we can try a slash:
+
+ RewriteRule ^(application|modules|system)/ - [F,L]
+
+If that doesn't work, try changing:
+
+ RewriteRule .* index.php/$0 [PT]
+
+To something more simple:
+
+ RewriteRule .* index.php [PT]
+
+### Still Failed!
+
+If you are still getting errors, check to make sure that your host supports URL `mod_rewrite`. If you can change the Apache configuration, add these lines to the the configuration, usually `httpd.conf`:
+
+ <Directory "/var/www/html/myapp">
+ Order allow,deny
+ Allow from all
+ AllowOverride All
+ </Directory>
+
+## NGINX
+
+It is hard to give examples of nginx configuration, but here is a sample for a server:
+
+ location / {
+ index index.php index.html index.htm;
+ try_files $uri $uri/ index.php$uri?$args;
+ }
+
+ location ~ ^(.+\.php)(.*)$ {
+ fastcgi_split_path_info ^(.+\.php)(.*)$;
+ fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+ fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+
+ include fastcgi.conf;
+
+ fastcgi_pass 127.0.0.1:9000;
+ fastcgi_index index.php;
+ }
+
+The two things to note are the use of [try_files](http://wiki.nginx.org/NginxHttpCoreModule#try_files) and [fastcgi_split_path_info](http://wiki.nginx.org/NginxHttpFcgiModule#fastcgi_split_path_info).
+
+[!!] This assumes that you are running PHP as a FastCGI server on port 9000 and are using nginx v0.7.31 or later.
+
+If you are having issues getting this working, enable debug level logging in nginx and check the access and error logs.
View
158 guide/nl/tutorials.urls.md 100644 → 100755
@@ -1,3 +1,159 @@
# Routes, URLs, and Links
-[!!] stub
+This section will provide you with the basic idea behind Kohana's request routing, url generation and links.
+
+## Routing
+
+As mentioned in the [Request Flow](about.flow) section, a request is handled by the [Request] class which finds a matching [Route] and loads the appropriate controller to handle the request. This system provides much flexibility as well as a common sense default behavior.
+
+If you look in `APPPATH/bootstrap.php` you will see the following code which is run immediately before the request is handed off to [Request::instance]:
+
+ Route::set('default', '(<controller>(/<action>(/<id>)))')
+ ->defaults(array(
+ 'controller' => 'welcome',
+ 'action' => 'index',
+ ));
+
+This sets the `default` route with a uri in the format of `(<controller>(/<action>(/<id>)))`. The tokens surrounded with `<>` are *keys* and the tokens surrounded with `()` are *optional* parts of the uri. In this case, the entire uri is optional, so a blank uri would match and the default controller and action would be assumed resulting in the `Controller_Welcome` class being loaded and eventually the `action_index` method being called to handle the request.
+
+Notice that in Kohana routes, any characters are allowed aside from `()<>` and the `/` has no special meaning. In the default route the `/` is used as a static separator but as long as the regex makes sense there is no restriction to how you can format your routes.
+
+### Directories
+
+For organizational purposes you may wish to place some of your controllers in subdirectories. A common case is for an admin backend to your site:
+
+ Route::set('admin', 'admin(/<controller>(/<action>(/<id>)))')
+ ->defaults(array(
+ 'directory' => 'admin',
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+This route specifies that the uri must begin with `admin` to match and the directory is statically assigned to `admin` in the defaults. Now a request to `admin/users/create` would load the `Controller_Admin_Users` class and call the `action_create` method.
+
+### Patterns
+
+The Kohana route system uses perl compatible regular expressions in its matching process. By default the keys (surrounded by `<>`) are matched by `[a-zA-Z0-9_]++` but you can define your own patterns for each key by passing an associative array of keys and patterns as an additional argument to [Route::set]. To extend our previous example let's say you have an admin section and an affiliates section. You could specify those in separate routes or you could do something like this:
+
+ Route::set('sections', '<directory>(/<controller>(/<action>(/<id>)))',
+ array(
+ 'directory' => '(admin|affiliate)'
+ ))
+ ->defaults(array(
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+This would provide you with two sections of your site, 'admin' and 'affiliate' which would let you organize the controllers for each into subdirectories but otherwise work like the default route.
+
+### More Route Examples
+
+There are countless other possibilities for routes. Here are some more examples:
+
+ /*
+ * Authentication shortcuts
+ */
+ Route::set('auth', '<action>',
+ array(
+ 'action' => '(login|logout)'
+ ))
+ ->defaults(array(
+ 'controller' => 'auth'
+ ));
+
+ /*
+ * Multi-format feeds
+ * 452346/comments.rss
+ * 5373.json
+ */
+ Route::set('feeds', '<user_id>(/<action>).<format>',
+ array(
+ 'user_id' => '\d+',
+ 'format' => '(rss|atom|json)',
+ ))
+ ->defaults(array(
+ 'controller' => 'feeds',
+ 'action' => 'status',
+ ));
+
+ /*
+ * Static pages
+ */
+ Route::set('static', '<path>.html',
+ array(
+ 'path' => '[a-zA-Z0-9_/]+',
+ ))
+ ->defaults(array(
+ 'controller' => 'static',
+ 'action' => 'index',
+ ));
+
+ /*
+ * You don't like slashes?
+ * EditGallery:bahamas
+ * Watch:wakeboarding
+ */
+ Route::set('gallery', '<action>(<controller>):<id>',
+ array(
+ 'controller' => '[A-Z][a-z]++',
+ 'action' => '[A-Z][a-z]++',
+ ))
+ ->defaults(array(
+ 'controller' => 'Slideshow',
+ ));
+
+ /*
+ * Quick search
+ */
+ Route::set('search', ':<query>', array('query' => '.*'))
+ ->defaults(array(
+ 'controller' => 'search',
+ 'action' => 'index',
+ ));
+
+Routes are matched in the order specified so be aware that if you set routes after the modules have been loaded a module could specify a route that conflicts with your own. This is also the reason that the default route is set last, so that custom routes will be tested first.
+
+### Request Parameters
+
+The directory, controller and action can be accessed from the [Request] instance in either of these two ways:
+
+ $this->request->action;
+ Request::instance()->action;
+
+All other keys specified in a route can be accessed from within the controller via:
+
+ $this->request->param('key_name');
+
+The [Request::param] method takes an optional second argument to specify a default return value in case the key is not set by the route. If no arguments are given, all keys are returned as an associative array.
+
+### Convention
+
+The established convention is to either place your custom routes in the `MODPATH/<module>/init.php` file of your module if the routes belong to a module, or simply insert them into the `APPPATH/bootstrap.php` file above the default route if they are specific to the application. Of course, they could also be included from an external file or even generated dynamically.
+
+## URLs
+
+Along with Kohana's powerful routing capabilities are included some methods for generating URLs for your routes' uris. You can always specify your uris as a string using [URL::site] to create a full URL like so:
+
+ URL::site('admin/edit/user/'.$user_id);
+
+However, Kohana also provides a method to generate the uri from the route's definition. This is extremely useful if your routing could ever change since it would relieve you from having to go back through your code and change everywhere that you specified a uri as a string. Here is an example of dynamic generation that corresponds to the `feeds` route example from above:
+
+ Route::get('feeds')->uri(array(
+ 'user_id' => $user_id,
+ 'action' => 'comments',
+ 'format' => 'rss'
+ ));
+
+Let's say you decided later to make that route definition more verbose by changing it to `feeds/<user_id>(/<action>).<format>`. If you wrote your code with the above uri generation method you wouldn't have to change a single line! When a part of the uri is enclosed in parentheses and specifies a key for which there in no value provided for uri generation and no default value specified in the route, then that part will be removed from the uri. An example of this is the `(/<id>)` part of the default route; this will not be included in the generated uri if an id is not provided.
+
+One method you might use frequently is the shortcut [Request::uri] which is the same as the above except it assumes the current route, directory, controller and action. If our current route is the default and the uri was `users/list`, we can do the following to generate uris in the format `users/view/$id`:
+
+ $this->request->uri(array('action' => 'view', 'id' => $user_id));
+
+Or if within a view, the preferable method is:
+
+ Request::instance()->uri(array('action' => 'view', 'id' => $user_id));
+
+## Links
+
+[!!] links stub
View
95 guide/nl/using.autoloading.md
@@ -0,0 +1,95 @@
+# Loading Classes
+
+Kohana takes advantage of PHP [autoloading](http://php.net/manual/language.oop5.autoload.php). This removes the need to call [include](http://php.net/include) or [require](http://php.net/require) before using a class. For instance, when you want to use the [Cookie::set] method, you just call:
+
+ Cookie::set('mycookie', 'any string value');
+
+Or to load an [Encrypt] instance, just call [Encrypt::instance]:
+
+ $encrypt = Encrypt::instance();
+
+Classes are loaded via the [Kohana::auto_load] method, which makes a simple conversion from class name to file name:
+
+1. Classes are placed in the `classes/` directory of the [filesystem](about.filesystem)
+2. Any underscore characters are converted to slashes
+2. The filename is lowercase
+
+When calling a class that has not been loaded (eg: `Session_Cookie`), Kohana will search the filesystem using [Kohana::find_file] for a file named `classes/session/cookie.php`.
+
+## Custom Autoloaders
+
+The default autoloader is enabled in `application/bootstrap.php` using [spl_autoload_register](http://php.net/spl_autoload_register):
+
+ spl_autoload_register(array('Kohana', 'auto_load'));
+
+This allows [Kohana::auto_load] to attempt to load any class that does not yet exist when the class is first used.
+
+# Transparent Class Extension {#class-extension}
+
+The [cascading filesystem](about.filesystem) allows transparent class extension. For instance, the class [Cookie] is defined in `SYSPATH/classes/cookie.php` as:
+
+ class Cookie extends Kohana_Cookie {}
+
+The default Kohana classes, and many extensions, use this definition so that almost all classes can be extended. You extend any class transparently, by defining your own class in `APPPATH/classes/cookie.php` to add your own methods.
+
+[!!] You should **never** modify any of the files that are distributed with Kohana. Always make modifications to classes using extensions to prevent upgrade issues.
+
+For instance, if you wanted to create method that sets encrypted cookies using the [Encrypt] class:
+
+ <?php defined('SYSPATH') or die('No direct script access.');
+
+ class Cookie extends Kohana_Cookie {
+
+ /**
+ * @var mixed default encryption instance
+ */
+ public static $encryption = 'default';
+
+ /**
+ * Sets an encrypted cookie.
+ *
+ * @uses Cookie::set
+ * @uses Encrypt::encode
+ */
+ public static function encrypt($name, $value, $expiration = NULL)
+ {
+ $value = Encrypt::instance(Cookie::$encrpytion)->encode((string) $value);
+
+ parent::set($name, $value, $expiration);
+ }
+
+ /**
+ * Gets an encrypted cookie.
+ *
+ * @uses Cookie::get
+ * @uses Encrypt::decode
+ */
+ public static function decrypt($name, $default = NULL)
+ {
+ if ($value = parent::get($name, NULL))
+ {
+ $value = Encrypt::instance(Cookie::$encryption)->decode($value);
+ }
+
+ return isset($value) ? $value : $default;
+ }
+
+ } // End Cookie
+
+Now calling `Cookie::encrypt('secret', $data)` will create an encrypted cookie which we can decrypt with `$data = Cookie::decrypt('secret')`.
+
+## Multiple Levels of Extension {#multiple-extensions}
+
+If you are extending a Kohana class in a module, you should maintain transparent extensions. Instead of making the [Cookie] extension extend Kohana, you can create `MODPATH/mymod/encrypted/cookie.php`:
+
+ class Encrypted_Cookie extends Kohana_Cookie {
+
+ // Use the same encrypt() and decrypt() methods as above
+
+ }
+
+And create `MODPATH/mymod/cookie.php`:
+
+ class Cookie extends Encrypted_Cookie {}
+
+This will still allow users to add their own extension to [Cookie] with your extensions intact. However, the next extension of [Cookie] will have to extend `Encrypted_Cookie` instead of `Kohana_Cookie`.
View
57 guide/nl/using.configuration.md
@@ -0,0 +1,57 @@
+# General Configuration
+
+Kohana uses both static properties and files for configuration. Static properties are typically used for static classes, such as [Cookie], [Security], and [Upload]. Files are typically used for objects such as [Database], [Encrypt], and [Session].
+
+Static properties can be set in `APPPATH/bootstrap.php` or by [class extension](using.autoloading#class-extension). The benefit of static properties is that no additional files need to be loaded. The problem with this method is that it causes the class to be loaded when the property is set, if you do not use an extension. However, using extensions will overload extensions made in modules. It is generally recommended to do static property configuration in the bootstrap.
+
+[!!] When using opcode caching, such as [APC](http://php.net/apc) or [eAccelerator](http://eaccelerator.net/), class loading time is significantly reduced. It is highly recommended to use opcode caching with *any* production website, no matter the size.
+
+## Initial Settings
+
+Every new Kohana installation will require changing [Kohana::init] settings in `APPPATH/bootstrap.php`. Any setting that is not set will use the default setting. These settings can be accessed and modified later by using the static property of the [Kohana] class. For instance, to get the current character set, read the [Kohana::$charset] property.
+
+## Security Settings
+
+There are several settings which need to be changed to make Kohana secure. The most important of these is [Cookie::$salt], which is used to create a "signature" on cookies that prevents them from being modified outside of Kohana.
+
+If you plan to use the [Encrypt] class, you will also need to create an `encrypt` configuration file and set the encryption `key` value. The encryption key should include letters, numbers, and symbols for the best security.
+
+[!!] **Do not use a hash for the encryption key!** Doing so will make the encryption key much easier to crack.
+
+# Configuration Files {#config-files}
+
+Configuration files are slig